1) Makefile: changes needed to compile on Gentoo linux 2) hhm.c line 152: window size according to http://www.speakeasy.org/~russotto/chm/chmformat.html 3) hhm.c line 214: added buffer slot to allow adding a "file" which is actually in memory, not on disk 4) line 341: added 0 for new slot in item_t structs 5) line 416: prototypes for new functions 6) line 494: calls to new functions, adding internal "files" 7) line 700: 0 value crashes xchm, 1 seems to work OK 8) line 917: initialize new item_t slot 9) line 952: initialize new item_t slot 10) line 1003: getbytes must now accomodate in-memory "files" 11) line 1311: free_all must now accomodate in-memory "files" 12) line 1329: new functions for in-memory "files" Common subdirectories: hhm/CVS and hhm-new/CVS diff -u hhm/Makefile hhm-new/Makefile --- hhm/Makefile 2006-08-22 01:21:43.000000000 -0500 +++ hhm-new/Makefile 2007-11-15 04:01:33.000000000 -0600 @@ -1,4 +1,4 @@ -CFLAGS=-Wall -g -DDEBUG -Ilzx_compress +CFLAGS=-Wall -g -DDEBUG -I/usr/include/lzx_compress LDFLAGS=-g -Llzx_compress LDLIBS=-llzxcomp -lm VERSION=0.1 diff -u hhm/hhm.c hhm-new/hhm.c --- hhm/hhm.c 2007-07-25 21:16:30.000000000 -0500 +++ hhm-new/hhm.c 2007-11-15 04:21:11.000000000 -0600 @@ -152,8 +152,8 @@ /* All the options below are set to what MS' implementation uses */ DWORD hri = 1; /* Huffman reset interval in 0x8000 byte blocks */ -DWORD wsc = 1; /* Window size code (ws in 0x8000 byte blocks) */ -DWORD ws = 15; /* Window size used by compressor */ +DWORD wsc = 2; /* Window size code (ws in 0x8000 byte blocks) */ +DWORD ws = 16; /* Window size used by compressor */ DWORD ihv = 3; /* Initial header version */ DWORD iht = 0; /* Initial header timestamp */ DWORD dhv = 1; /* Directory header version */ @@ -214,6 +214,7 @@ ENCINT offset; ENCINT length; BYTE flags; + BYTE * buffer; // added to indicate an "in-memory" file } item_t; item_t** files = NULL; @@ -341,15 +342,15 @@ FIXME: Check older versions of HHW/HHC/HHA & allow HHM to use the same output order */ /* All the directories & empty files go here (len, "name", 0, 0, 0) */ -item_t InstanceData_item = { 95, "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/", 0, 0, 0, F_DONT_FREE }; +item_t InstanceData_item = { 95, "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/", 0, 0, 0, F_DONT_FREE, 0 }; /* Now the non-empty files */ -item_t NameList_item = { 20, "::DataSpace/NameList", 0, 0, nlfs, F_DONT_FREE }; -item_t Transform_List_item = { 47, "::DataSpace/Storage/MSCompressed/Transform/List", 0, nlfs, tlfs, F_DONT_FREE }; -item_t SpanInfo_item = { 41, "::DataSpace/Storage/MSCompressed/SpanInfo", 0, nlfs+tlfs, sifs, F_DONT_FREE }; -item_t ControlData_item = { 44, "::DataSpace/Storage/MSCompressed/ControlData", 0, nlfs+tlfs+sifs, cdfs, F_DONT_FREE }; -item_t ResetTable_item = { 105, "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable", 0, nlfs+tlfs+sifs+cdfs, 0, F_DONT_FREE }; /* Last 1 will be updated */ +item_t NameList_item = { 20, "::DataSpace/NameList", 0, 0, nlfs, F_DONT_FREE, 0 }; +item_t Transform_List_item = { 47, "::DataSpace/Storage/MSCompressed/Transform/List", 0, nlfs, tlfs, F_DONT_FREE, 0 }; +item_t SpanInfo_item = { 41, "::DataSpace/Storage/MSCompressed/SpanInfo", 0, nlfs+tlfs, sifs, F_DONT_FREE, 0 }; +item_t ControlData_item = { 44, "::DataSpace/Storage/MSCompressed/ControlData", 0, nlfs+tlfs+sifs, cdfs, F_DONT_FREE, 0 }; +item_t ResetTable_item = { 105, "::DataSpace/Storage/MSCompressed/Transform/{7FC28940-9D31-11D0-9B27-00A0C91E9C7C}/InstanceData/ResetTable", 0, nlfs+tlfs+sifs+cdfs, 0, F_DONT_FREE, 0 }; /* Last 1 will be updated */ /* The cs 0 author & internal files go here */ -item_t Content_item = { 40, "::DataSpace/Storage/MSCompressed/Content", 0, 0, 0, F_DONT_FREE }; /* Last 2 will be updated */ +item_t Content_item = { 40, "::DataSpace/Storage/MSCompressed/Content", 0, 0, 0, F_DONT_FREE, 0 }; /* Last 2 will be updated */ @@ -416,6 +417,13 @@ +/* Internal CHM files */ + +item_t * idxhdr(void); +item_t * strings( char ** , int); + + + /* The entry point */ /* 0 success, -1 failure for now */ @@ -494,6 +502,14 @@ if( ad ) add_item( &InstanceData_item ); add_item( &ResetTable_item ); + // internal chm files: + add_item(idxhdr()); + /* #STRINGS will need to be constructed. + char * sv[1]; + sv[0] = strdup("docHelp"); + add_item(strings(sv, 1)); + */ + sort_items(); /* FIXME: When we get HHP processing this will need to be changed */ @@ -700,7 +716,7 @@ fwrite_DWORD( f, 2 ); fwrite_DWORD( f, hri ); fwrite_DWORD( f, wsc ); - fwrite_DWORD( f, 0 ); /* FIXME: Work out what this means (sometimes 2, sometimes 1, sometimes 0) */ + fwrite_DWORD( f, 1 ); /* FIXME: cache size? (sometimes 2, sometimes 1, sometimes 0) */ fwrite_DWORD( f, 0 ); } @@ -917,6 +933,7 @@ new_item->offset = new_item->length = 0; new_item->flags = 0; + new_item->buffer = (BYTE*)0; files[files_len++] = new_item; return 0; } else { @@ -952,6 +969,7 @@ new_item->name = new_name; new_item->cs = def_cs; new_item->flags = 0; + new_item->buffer = (BYTE*)0; /* Other fields will be updated later */ files[files_len++] = new_item; return 0; @@ -1002,34 +1020,57 @@ static int get_bytes( void* arg, int n, void* buf ){ int read = 0, just_read, to_read = n; for(; current_file_i < files_len ; current_file_i++){ + item_t * it = files[current_file_i]; if( !files[current_file_i]->cs ) continue; - if( !current_file ){ - current_file = fopen(files[current_file_i]->name+1, "rb"); - if( !current_file ){ - files[current_file_i]->cs = - files[current_file_i]->offset = - files[current_file_i]->length = 0; - printf( "ERROR: Failed opening %s\n", files[current_file_i]->name+1 ); - continue; - } - printf( "Reading %s ... ", files[current_file_i]->name+1 ); - current_len = 0; - files[current_file_i]->offset = current_offset; + if(it->buffer==0){ + if( !current_file ){ + current_file = fopen(files[current_file_i]->name+1, "rb"); + if( !current_file ){ + files[current_file_i]->cs = + files[current_file_i]->offset = + files[current_file_i]->length = 0; + printf( "ERROR: Failed opening %s\n", files[current_file_i]->name+1 ); + continue; + } + printf( "Reading %s ... ", files[current_file_i]->name+1 ); + current_len = 0; + files[current_file_i]->offset = current_offset; + } + just_read = fread( buf+read, 1, to_read, current_file ); + to_read -= just_read; + read += just_read; + current_len += just_read; + current_offset += just_read; + if( read < n ){ + printf( "done\n" ); + fclose(current_file); current_file = NULL; + files[current_file_i]->length = current_len; + if(!current_len){ + files[current_file_i]->cs = + files[current_file_i]->offset = 0; + } + } else return read; + } else { + // if the buffer slot is set, then all bytes are in memory + if (to_read > it->length){ + memcpy(buf+read, it->buffer, it->length); + just_read = it->length; + } else { + memcpy(buf+read, it->buffer, to_read); + just_read += to_read; + // since we stopped in the middle of the buffer, move everything + // up to the beginning for next read: + BYTE * newbuf = (BYTE *)malloc(it->length - to_read); + memcpy(newbuf, it->buffer+to_read, it->length - to_read); + FREE(it->buffer); + it->buffer = newbuf; + it->length -= to_read; + } + to_read -= just_read; + read += just_read; + current_len += just_read; + current_offset += just_read; } - just_read = fread( buf+read, 1, to_read, current_file ); - to_read -= just_read; - read += just_read; - current_len += just_read; - current_offset += just_read; - if( read < n ){ - printf( "done\n" ); - fclose(current_file); current_file = NULL; - files[current_file_i]->length = current_len; - if(!current_len){ - files[current_file_i]->cs = - files[current_file_i]->offset = 0; - } - } else return read; } return read; } @@ -1311,6 +1352,8 @@ for( i = 0; i < files_len; i++ ){ if( !(files[i]->flags & F_DONT_FREE) ){ FREE( files[i]->name ); + if (files[i]->buffer) + FREE(files[i]->buffer); FREE( files[i] ); } } @@ -1329,3 +1372,96 @@ FREE( dir_chunks ); } } + +/* Internal CHM files */ + +// helpers for in-memory buffer copying +// returns pointer to next available dest byte +BYTE * bcpy(void * dest, void * src, int len){ + memcpy(dest, src, len); + return dest+len; +} + +BYTE * bDWORD(BYTE * dest, DWORD val){ + return bcpy(dest, (BYTE *)&val, 4); +} + +item_t * idxhdr( void ){ + char name[] = "/#IDXHDR"; + item_t * it = (item_t *)malloc(sizeof(item_t)); + it->name_len = strlen(name); + it->name = strdup(name); + it->cs = def_cs; + it->offset = 0; + it->length = 4096; + it->buffer = (BYTE *)malloc(4096); + + memset(it->buffer, 0, 4096); + + BYTE * b = it->buffer; + b = bcpy(b, "T#SM", 4); + b = bDWORD(b, 0); // timestamp + b = bDWORD(b, 1); // unknown + b = bDWORD(b, files_len); // number of topics + b = bDWORD(b, 0); // unknown + + // Offset in the #STRINGS file of the ImageList param of the "text/site properties" object of the sitemap contents (0/-1 = none): + b = bDWORD(b, 0xffffffff); + + b = bDWORD(b, 0); // unknown + + // 1 if the value of the ImageType param of the "text/site properties" object of the sitemap contents is Folder. 0 otherwise.: + b = bDWORD(b, 1); + + // The value of the Background param of the "text/site properties" object of the sitemap contents: + b = bDWORD(b, -1); + + // The value of the Foreground param of the "text/site properties" object of the sitemap contents: + b = bDWORD(b, 0xffffffff); + + // Offset in the #STRINGS file of the Font param of the "text/site properties" object of the sitemap contents (0/-1 = none): + b = bDWORD(b, 0xffffffff); + + // The value of the Window Styles param of the "text/site properties" object of the sitemap contents: + b = bDWORD(b, 0xffffffff); + + // The value of the ExWindow Styles param of the "text/site properties" object of the sitemap contents: + b = bDWORD(b, 0xffffffff); + + // Unknown. Often -1. Sometimes 0.: + b = bDWORD(b, 0xffffffff); + + // Offset in the #STRINGS file of the FrameName param of the "text/site properties" object of the sitemap contents (0/-1 = none): + b = bDWORD(b, 0xffffffff); + + // Offset in the #STRINGS file of the WindowName param of the "text/site properties" object of the sitemap contents (0/-1 = none): + b = bDWORD(b, 0xffffffff); + + return it; +} +// each string in stringv goes into #STRINGS table +// divided into blocks of 4096, but last block can be shorter. +item_t * strings( char ** stringv, int count){ + char name[] = "/#STRINGS"; + item_t * it = (item_t *)malloc(sizeof(item_t)); + it->name_len = strlen(name); + it->name = strdup(name); + it->cs = def_cs; + it->offset = 0; + it->length = 1; + int i; + for(i=0; ilength += strlen(stringv[i]) + 1; + + it->buffer = (BYTE *)malloc(it->length); + + memset(it->buffer, 0, it->length); + + BYTE * b = it->buffer+1; // first byte is always 0 + for(i=0; i