/* OLEcode - Generate a Microsoft OLE 2 file from given streams. Copyright 1998, 1999 Roberto Arturo Tena Sanchez This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* Arturo Tena */ #include #include #include #include "cole.h" #include "support.h" #include "internal.h" #ifdef COLE_VERBOSE #define VERBOSE #else #undef VERBOSE #endif /* warning: some lines are longer than 80 characters */ struct str_MY_FILE { enum { real, MY_FILE_list, block_list, root_list } type; U32 size; /* size of list _itself_ */ U32 *blocks; /* size in big blocks (0x0200) of the information that contains this file */ union union_file { struct str_real { char *name; /* file name */ U32 ppsnumber; /* ppsnumber of pps property in stream_list */ } real; struct str_MY_FILE *MY_FILE_list; U32 *block_list; U8 *root_list; } file; }; typedef struct str_MY_FILE MY_FILE; static MY_FILE Input; static MY_FILE *sbfile; static MY_FILE *SDepot; static MY_FILE *BDepot; static MY_FILE *bbd_list; static MY_FILE *Root; /* starting and sizing blocks (calculated in calculate_blocks()) */ static U32 header_blocks; /* how many blocks takes header */ static U32 big_streams_blocks; /* how many blocks takes big streams */ static U32 sbfile_blocks; /* how many blocks takes sbfile (small streams) */ static U32 SDepot_blocks; /* how many blocks takes SDepot */ static U32 BDepot_blocks; /* how many blocks takes BDepot */ static U32 Root_blocks; /* how many blocks takes Root */ static U32 sbfile_start_block; /* where sbfile starts */ static U32 SDepot_start_block; /* where SDepot starts */ static U32 Root_start_block; /* where Root starts */ static U32 BDepot_start_block; /* where BDepot starts */ static FILE *output_file; /* the output file OLE2 file */ static U8 output_block[0x0200]; /* used as buffer to write later to output_file */ static U16 pos_block; /* position inside output_block from where the next write will happen, when it cames 0x0200 must write the block to output_file and make pos_block = 0x00 */ static U32 next_block; /* number of the next block to be written in output_file. the first block is -1, the second 0, the third 1 and so on */ /* process stage functions */ static int process_Root (pps_entry * pps_list, U32 root); static U32 max_pps_referenced (pps_entry * pps_list, U32 node); static U32 max3 (U32 a, U32 b, U32 c, U32 d); static int process_streams (pps_entry * pps_list, pps_entry * root); static int add_stream_to_sbfile_and_SDepot (U32 size, char *name, U32 ppsnumber); static int add_stream_to_Input_and_BDepot (U32 size, char *name, U32 ppsnumber); static int add_entry_to_Root (pps_entry * node, U32 start_block); static U32 add_MY_FILE_entry (MY_FILE * list, U32 size); static int pps2root (U8 pps[0x80], pps_entry * node, U32 start_block); static void reset_links_in_Input (void); static void reset_links_in_BDepot (void); static void reset_links_in_SDepot (void); /* generate starge functions */ static int generate_ole2_file (const char *filename, int trunc); static int generate_header (void); static int generate_recursive (MY_FILE * list); static int generate_SDepot (void); static int generate_Root (void); static int generate_BDepot (void); static int generate_real_file (MY_FILE * MY_FILE_file); static int write_block_list (U32 start_count, MY_FILE *list, int write_end_chain); static int write_root_list (MY_FILE * list); static void calculate_blocks (void); /* support functions for both stages */ static U32 sum_block_list (MY_FILE * list); /* useless function by now, may be later */ /* static U32 sum_MY_FILE_list (MY_FILE * list); */ static U32 sum_blocks_MY_FILE_list (MY_FILE * list); static void ends (void); #define size2blocks(s,b) (!(s) ? 1 : (1+((s)-1)/(b))) #define size2blocks_preserve_zero(s,b) (!(s) ? 0 : (1+((s)-1)/(b))) #define clean_block(b,s) memset((b),0xff,(s)) #define init_MY_FILE(n, t, s, b, f) { \ (n)->type = t; \ (n)->size = (s); \ (n)->blocks = (b); \ (n)->file.t = (f); \ } #define init_MY_FILE_real(n, t, s, b, f, p) { \ n->type = t; \ n->size = s; \ n->blocks = b; \ n->file.real.name = f; \ n->file.real.ppsnumber = p; \ } #define reset_links() { reset_links_in_Input(); reset_links_in_BDepot(); reset_links_in_SDepot(); } /* if this block is full, write it to output_file and restart using this block */ #define check_output_block_boundary() { \ if (pos_block == 0x0200) { \ test_exitf (fwrite (output_block, 0x0200, 1, output_file) == 1, 1, dummy()); \ next_block++; \ pos_block = 0x00; \ } \ } #define write_until_output_block_boundary(clean) { \ if (pos_block != 0x00) { \ if (clean && pos_block%0x0200) \ clean_block (output_block + pos_block, (pos_block/0x0200 + 1)*0x0200 - pos_block); \ test_exitf (fwrite (output_block, 1, 0x0200, output_file) == 0x0200, 1, dummy ()); \ next_block++; \ pos_block = 0x00; \ } \ } #define write_until_output_block_small_boundary(clean) { \ if (pos_block % 0x40) { \ if (clean) \ clean_block (output_block + pos_block, (pos_block/0x40 + 1)*0x40 - pos_block); \ assert (pos_block+(pos_block/0x40 + 1)*0x40 - pos_block == (pos_block/0x40 + 1)*0x40); \ pos_block += (U16)((pos_block/0x40 + 1)*0x40 - pos_block); \ } \ } #define write_rest_of_output_block_with_null_pps() { \ if (pos_block != 0x00) { \ int zzzi; \ U16 U16zero = 0x0000U; \ clean_block (output_block + pos_block, 0x0200 - pos_block); \ for (zzzi = 0; zzzi < 4; zzzi++) \ if (zzzi*0x80 >= pos_block) \ fil_swriteU16 (output_block + zzzi*0x80 + 0x40, &U16zero); \ } \ } #define dummy() /* may be should be (means no op, do nothing): #define dummy() {1;} or may be: #define dummy() {;} */ /* Exit codes: 0 = All goes OK. 1 = error writting in OLEfilename, can use perror 2 = trunc == 0 and file exist 3 = can't create OLEfilename, can use perror 10 = Error allocating memory, there's no more memory 11 = Error reading streams files 12 = Error reading stream_list, it's broken */ int __OLEcode (const char *OLEfilename, int trunc, pps_entry * stream_list, U32 root) { verbose ("calling: OLEcode ()"); assert (OLEfilename != NULL); assert (stream_list != NULL); /* -- init static things -- */ output_file = NULL; clean_block (output_block, 0x0200); /* just needed clean up once, for security reasons */ pos_block = 0x00; next_block = 0xffffffffUL; /* it need to be 0xffffffffUL, because next time we make next_block++ it must be zero (bad hack? next_block is always 32 bits) */ BDepot = SDepot = bbd_list = NULL; Root = NULL; sbfile = NULL; /* -- allocate initial memory needed for my files (Structure called at HACKING) -- */ /* Input: 5 entries in Input: for sbfile, for SDepot, for BDepot, for bbd_list and for Root */ init_MY_FILE ((&Input), MY_FILE_list, 5 * sizeof (MY_FILE), NULL, malloc (Input.size)); /* Input->blocks is not needed */ test_exitf (Input.file.MY_FILE_list != NULL, 10, ends ()); reset_links_in_Input (); /* bbd_list */ init_MY_FILE (bbd_list, block_list, sizeof (U32), NULL, malloc (bbd_list->size)); /* bbd_list is not needed */ /* bbd_list->blocks is not needed */ test_exitf (bbd_list->file.block_list != NULL, 10, ends ()); bbd_list->file.block_list[0] = 1; /* because BDepot starts with 3 entries */ /* BDepot */ init_MY_FILE (BDepot, block_list, 3 * sizeof (U32), bbd_list->file.block_list, malloc (BDepot->size)); test_exitf (BDepot->file.block_list != NULL, 10, ends ()); BDepot->file.block_list[0] = BDepot->file.block_list[1] = BDepot->file.block_list[2] = 0; /* sbfile, SDepot and Root are size 0 by now */ /* sbfile */ init_MY_FILE (sbfile, MY_FILE_list, 0, BDepot->file.block_list, NULL); /* SDepot */ init_MY_FILE (SDepot, block_list, 0, BDepot->file.block_list + 1, NULL); /* Root */ init_MY_FILE (Root, root_list, 0, BDepot->file.block_list + 2, NULL); /* -- process streams -- */ test_call (process_Root (stream_list, root), int); test_call (process_streams (stream_list, &stream_list[root]), int); /* how can I call ends() if process_streams fails? */ /* -- actually generate ole2 file -- */ test_call (generate_ole2_file (OLEfilename, trunc), int); return 0; } /* reviewed when coding ole2 file */ static int process_Root (pps_entry * pps_list, U32 root) { U32 pps_list_entries; U32 i; verbose ("calling: process_Root ()"); pps_list_entries = (1 + max_pps_referenced (pps_list, root)); verboseU32 (pps_list_entries); for (i = 0; i < pps_list_entries; i++) test_call (add_entry_to_Root (pps_list + i, 0x00000000UL), int); /* start_block = 0x00000000UL is a dummy value. The real start block: for files in SDepot is written in Root in generate_real_file(), for files in BDepot is written in Root in generate_real_file(), and for sbfile: the default is written in process_streams() and the real, if any, is written when generating the first small stream in generate_real_file(). But 0x00000000UL is a perfect value to directory entries (type=1) About sizes: every pps have its size, incluiding sbfile, that value will be comparated later in generate_real_file(). */ return 0; } #define MAX(a,b) ((a) > (b) ? (a) : (b)) static U32 max3 (U32 a, U32 b, U32 c, U32 d) { U32 m = 0; /*verbose ("calling: max3 ()");*/ m = MAX (m, a); m = MAX (m, b); m = MAX (m, c); m = MAX (m, d); return m; } static U32 max_pps_referenced (pps_entry * pps_list, U32 node) { U32 max_pps; /*verbose ("calling: max_pps_referenced ()");*/ max_pps = max3 (node, pps_list[node].previous != 0xffffffffUL ? pps_list[node].previous : 0, pps_list[node].next != 0xffffffffUL ? pps_list[node].next : 0, pps_list[node].dir != 0xffffffffUL ? pps_list[node].dir : 0); if (pps_list[node].previous != 0xffffffffUL) max_pps = MAX (max_pps, max_pps_referenced (pps_list, pps_list[node].previous)); if (pps_list[node].next != 0xffffffffUL) max_pps = MAX (max_pps, max_pps_referenced (pps_list, pps_list[node].next)); if (pps_list[node].dir != 0xffffffffUL) max_pps = MAX (max_pps, max_pps_referenced (pps_list, pps_list[node].dir)); return max_pps; } /* reviewed when coding ole2 file */ static int process_streams (pps_entry * pps_list, pps_entry * node) { U32 U32end_chain = 0xfffffffeUL; verbose ("calling: process_streams ()"); test_exitf (node->name[0], 12, dummy()); switch (node->type) { case 1: /* dir */ warning (node->size == 0); if (node->dir != 0xffffffffUL) test_call (process_streams (pps_list, &pps_list[node->dir]), int); if (node->next != 0xffffffffUL) test_call (process_streams (pps_list, &pps_list[node->next]), int); break; case 5: /* root dir */ assert (*(Root->file.root_list + 0x42) == 5); /* write the default start block of SDepot: empty if there are no sbfile at all */ fil_swriteU32 (Root->file.root_list + 0x74, &U32end_chain); if (node->dir != 0xffffffffUL) test_call (process_streams (pps_list, &pps_list[node->dir]), int); if (node->next != 0xffffffffUL) test_call (process_streams (pps_list, &pps_list[node->next]), int); break; case 2: /* file */ test_exitf (node->dir == 0xffffffffUL, 12, dummy()); if (node->size < 0x1000) /* must be in sbfile, and its block list in SDepot */ test_call (add_stream_to_sbfile_and_SDepot ( node->size, node->filename, node->ppsnumber), int) else /* node->size >= 0x1000 */ /* must be in Input, and its block list in BDepot */ test_call (add_stream_to_Input_and_BDepot ( node->size, node->filename, node->ppsnumber), int); if (node->next != 0xffffffffUL) test_call (process_streams (pps_list, &pps_list[node->next]), int); break; default: return 12; } return 0; } /* reviewed when processing Root */ static int add_entry_to_Root (pps_entry * node, U32 start_block) { U32 entry_number; U8 * new_entry_Root; verbose ("calling: add_entry_to_Root ()"); /* 1. add entry in Root */ entry_number = add_MY_FILE_entry (Root, 0 /*dummy value, not used*/); test_exitf (entry_number != 0xffffffffUL, 10, dummy ()); new_entry_Root = Root->file.root_list + entry_number * 0x80; /* 2. write info about the new stream in the new entry in Root */ pps2root (new_entry_Root, node, start_block); /* 3. update Input entry of Root */ /* update blocks size of Root (need Root->size updated) */ *(Root->blocks) = size2blocks (Root->size, 0x0200); /* 4. update Input entry of BDepot */ /* update blocks size of BDepot (need blocks size of Root updated */ *(BDepot->blocks) = size2blocks (sum_block_list (BDepot) * sizeof (U32), 0x0200); return 0; } /* reviewed when processing streams */ static int add_stream_to_sbfile_and_SDepot (U32 size, char *name, U32 ppsnumber) { U32 entry_number; U32 * new_entry_SDepot; MY_FILE * new_entry_sbfile; verbose ("calling: add_stream_to_sbfile_and_SDepot ()"); /* must be called only by process streams */ /* 1. add entries in SDepot and sbfile */ /* add in SDepot */ entry_number = add_MY_FILE_entry (SDepot, size); test_exitf (entry_number != 0xffffffffUL, 10, dummy ()); new_entry_SDepot = SDepot->file.block_list + entry_number; /* add in sbfile */ entry_number = add_MY_FILE_entry (sbfile, size); test_exitf (entry_number != 0xffffffffUL, 10, dummy ()); new_entry_sbfile = sbfile->file.MY_FILE_list + entry_number; /* 2. write info about the new stream in the new entry in SDepot */ /* write info in new entry (info related with parameter size) */ /* write blocks size of stream added */ *new_entry_SDepot = size2blocks (size, 0x40); /* 0x40 because is small stream */ /* 3. update Input entry of SDepot */ /* update blocks size of SDepot (need info in new_entry_SDepot written) */ *(SDepot->blocks) = size2blocks (sum_block_list (SDepot) * sizeof (U32), 0x0200); /* 4. write info about the new stream in the new entry in sbfile */ /* write info in new entry (info related with parameter size */ /* and block size depot) */ /* write size of stream added */ /* and write blocks size link of stream added */ init_MY_FILE_real (new_entry_sbfile, real, size, new_entry_SDepot, name, ppsnumber); /* 5. update Input entry of sbfile */ /* update blocks size of sbfile (need info in new_entry_sbfile->size */ /* and new_entry_sbfile->blocks written) */ *(sbfile->blocks) = size2blocks (sum_blocks_MY_FILE_list (sbfile) * 0x40, 0x0200); /* 6. update Input entry of BDepot */ /* update blocks size of BDepot (need blocks size of */ /* SDepot and sbfile updated */ *(BDepot->blocks) = size2blocks (sum_block_list (BDepot) * sizeof (U32), 0x0200); return 0; } /* reviewed when processing streams */ static int add_stream_to_Input_and_BDepot (U32 size, char *name, U32 ppsnumber) { U32 entry_number; MY_FILE * new_entry_Input; U32 * new_entry_BDepot; verbose ("calling: add_stream_to_Input_and_BDepot ()"); /* must be called only by process streams */ /* 1. add entries in BDepot and Input */ /* add in BDepot */ entry_number = add_MY_FILE_entry (BDepot, size); test_exitf (entry_number != 0xffffffffUL, 10, dummy ()); new_entry_BDepot = BDepot->file.block_list + entry_number; /* add in Input */ entry_number = add_MY_FILE_entry (&Input, size); test_exitf (entry_number != 0xffffffffUL, 10, dummy ()); new_entry_Input = Input.file.MY_FILE_list + entry_number; /* 2. write info about the new stream in the new entry in BDepot */ /* write info in new entry (info related with parameter size) */ /* write blocks size of stream added */ *new_entry_BDepot = size2blocks (size, 0x0200); /* 0x0200 because is big stream */ /* 3. update Input entry of BDepot */ /* update blocks size of BDepot (need info in new_entry_BDepot written) */ *(BDepot->blocks) = size2blocks (sum_block_list (BDepot) * sizeof (U32), 0x0200); /* 4. write info about the new stream in the new entry in Input */ /* write info in new entry (info related with parameter size */ /* and block size depot) */ /* write size of stream added */ /* and write blocks size link of stream added */ init_MY_FILE_real (new_entry_Input, real, size, new_entry_BDepot, name, ppsnumber); /* 5. ?? update Input entry of Input ?? */ /* this seems to be not aplicable here, not needed */ /* 6. update Input entry of BDepot */ /* update blocks size of BDepot (need blocks size of */ /* BbDepot and ?? Input ?? updated */ *(BDepot->blocks) = size2blocks (sum_block_list (BDepot) * sizeof (U32), 0x0200); return 0; } /* reviewed all conditions */ static U32 add_MY_FILE_entry (MY_FILE * list, U32 size) { #if __GNUC__ == 2 static char cff[] = "cole2357"; #define nextff(var) static void * nextff_##var = (&nextff_##var); \ nextff_##var=&##var; nextff (cff); #endif (void) size; /*UNUSED*/ verbose ("calling: add_MY_FILE_entry ()"); #ifdef VERBOSE if (list == &Input) { verbose_wonl ("add_MY_FILE_entry: list = &Input, "); } else if (list == sbfile) { verbose_wonl ("add_MY_FILE_entry: list = sbfile, "); } else if (list == SDepot) { verbose_wonl ("add_MY_FILE_entry: list = SDepot, "); } else if (list == BDepot) { verbose_wonl ("add_MY_FILE_entry: list = BDepot, "); } else if (list == bbd_list) { verbose_wonl ("add_MY_FILE_entry: list = bbd_list, "); } else if (list == Root) { verbose_wonl ("add_MY_FILE_entry: list = Root, "); } else { verbose_wonl ("add_MY_FILE_entry: list = UNKNOWN (ERROR!), "); } verboseU32 (size); #endif assert (list != NULL); switch (list->type) { /* reviewed when adding to sbfile */ /* reviewed when adding to Input */ case MY_FILE_list: { MY_FILE *new_MY_FILE_list; U32 new_entry; assert (list == sbfile || list == &Input); /* actually add */ list->size = list->size + sizeof (MY_FILE); new_MY_FILE_list = realloc (list->file.MY_FILE_list, list->size); list->file.MY_FILE_list = new_MY_FILE_list; test_exitf (new_MY_FILE_list != NULL, 0xffffffffUL, dummy ()); new_entry = list->size / sizeof (MY_FILE) - 1; reset_links (); return new_entry; } /* reviewed when adding to SDepot */ /* reviewed when adding to BDepot */ case block_list: { U32 *new_block_list; U32 new_entry; assert (list == SDepot || list == BDepot); /* actually add */ list->size = list->size + sizeof (U32); new_block_list = realloc (list->file.block_list, list->size); list->file.block_list = new_block_list; test_exitf (new_block_list != NULL, 0xffffffffUL, dummy ()); new_entry = list->size / sizeof (U32) - 1; reset_links (); return new_entry; } /* reviewed when adding to Root */ case root_list: { U32 new_entry; U8 *new_root_list; assert (list == Root); /* actually add */ list->size = list->size + 0x80; new_root_list = realloc (list->file.root_list, list->size); list->file.root_list = new_root_list; test_exitf (new_root_list != NULL, 0xffffffffUL, dummy ()); new_entry = list->size / 0x80 - 1; reset_links (); return new_entry; } default: if (SDepot->file.block_list!=NULL) { verboseU32Array (SDepot->file.block_list, SDepot->size / sizeof(U32)); verboseU32 (Root->size); verboseU32Array (BDepot->file.block_list, BDepot->size / sizeof(U32)); verboseU32 (*(bbd_list->file.block_list)); } assert ("list->type UNKNOWN in add_MY_FILE_entry" == NULL); return 0; } } /* reviewed when adding to Root */ static int pps2root (U8 pps[0x80], pps_entry * node, U32 start_block) { U16 i; U16 size_of_name; /* next vars are constant, should it be static for performance? */ U8 U8magiczero = 0x00; U32 U32magiczero = 0x00000000UL; U32 U32magic1 = 0x00020900UL; U32 U32magic2 = 0x46000000UL; verbose ("calling: pps2root ()"); verboseU32 (node->ppsnumber); verboseU32 ((U32) (pps - Root->file.root_list)); assert (node->ppsnumber == (U32)(pps - Root->file.root_list)/0x80); clean_block (pps, 0x80); /* name and its size */ size_of_name = (U16)(2 * (strlen (node->name) + 1)); /* 2 * because zero follow each char */ for (i = 0; i < size_of_name; i++) *(pps + i) = (U8)(i % 2 ? 0x00 : *(node->name + (i / 2))); fil_swriteU16 (pps + 0x40, &size_of_name); /* other variables */ *(pps + 0x42) = node->type; fil_swriteU32 (pps + 0x44, &node->previous); fil_swriteU32 (pps + 0x48, &node->next); fil_swriteU32 (pps + 0x4c, &node->dir); fil_swriteU32 (pps + 0x64, &node->seconds1); fil_swriteU32 (pps + 0x68, &node->days1); fil_swriteU32 (pps + 0x6c, &node->seconds2); fil_swriteU32 (pps + 0x70, &node->days1); fil_swriteU32 (pps + 0x74, &start_block); fil_swriteU32 (pps + 0x78, &node->size); /* constant magic numbers */ *(pps + 0x43) = U8magiczero; fil_swriteU32 (pps + 0x50, &U32magic1); fil_swriteU32 (pps + 0x54, &U32magiczero); fil_swriteU32 (pps + 0x58, &U32magiczero); fil_swriteU32 (pps + 0x5c, &U32magic2); fil_swriteU32 (pps + 0x60, &U32magiczero); fil_swriteU32 (pps + 0x7c, &U32magiczero); verboseU8Array (pps, 1, 0x80); return 0; } static U32 sum_block_list (MY_FILE * list) { U32 sum = 0; U32 *block; /*verbose ("calling: sum_block_list ()");*/ assert (list != NULL); assert (list->type == block_list); for (block = list->file.block_list; (U32)(((U8 *) block - (U8 *) list->file.block_list)) < list->size; block++) sum += *block; return sum; } /* static U32 sum_MY_FILE_list (MY_FILE * list) { U32 sum = 0; MY_FILE *file; verbose ("calling: sum_MY_FILE_list ()"); assert (list != NULL); assert (list->type == MY_FILE_list); for (file = list->file.MY_FILE_list; ((U8 *) file - (U8 *) list->file.MY_FILE_list) < list->size; file++) sum += file->size; return sum; } */ static U32 sum_blocks_MY_FILE_list (MY_FILE * list) { U32 sum = 0; MY_FILE *file; /*verbose ("calling: sum_blocks_MY_FILE_list ()");*/ assert (list != NULL); assert (list->type == MY_FILE_list); for (file = list->file.MY_FILE_list; (U32)((U8 *) file - (U8 *) list->file.MY_FILE_list) < list->size; file++) if (file->blocks != NULL) sum += *(file->blocks); return sum; } static void reset_links_in_Input (void) { verbose ("calling: reset_links_in_Input ()"); sbfile = Input.file.MY_FILE_list + 4; SDepot = Input.file.MY_FILE_list + 3; BDepot = Input.file.MY_FILE_list + 1; bbd_list = Input.file.MY_FILE_list; Root = Input.file.MY_FILE_list + 2; } static void reset_links_in_BDepot (void) { U32 i; verbose ("calling: reset_links_in_BDepot ()"); sbfile->blocks = BDepot->file.block_list; SDepot->blocks = BDepot->file.block_list + 1; Root->blocks = BDepot->file.block_list + 2; /* relink big streams block sizes in Input with BDepot entries */ for (i = 0; i < (Input.size / sizeof (MY_FILE)) - 5; i++) Input.file.MY_FILE_list[i + 5].blocks = BDepot->file.block_list + i + 3; } static void reset_links_in_SDepot (void) { U32 i; verbose ("calling: reset_links_in_SDepot ()"); /* relink small streams block sizes in sbfile with SDepot entries */ for (i = 0; i < sbfile->size / sizeof (MY_FILE); i++) sbfile->file.MY_FILE_list[i].blocks = SDepot->file.block_list + i; } static int generate_ole2_file (const char *filename, int trunc) { verbose ("calling: generate_ole2_file ()"); if (!trunc) { output_file = fopen (filename, "r"); test_exitf (output_file == NULL, 2, ends ()); } output_file = fopen (filename, "wb"); test_exitf (output_file != NULL, 3, ends ()); test_call (generate_header (), int); test_call (generate_recursive (&Input), int); /* some tricky here: if there are only small streams, no big streams, next line wich is in generate_real_file will never be executed. so we do it here, if only is correct is harmless calling it here */ write_until_output_block_boundary (1); test_call (generate_SDepot (), int); test_call (generate_Root (), int); test_call (generate_BDepot (), int); fclose (output_file); return 0; } static int generate_header (void) { U32 identifier1 = 0xe011cfd0UL; U32 identifier2 = 0xe11ab1a1UL; U32 U32magiczero = 0x00000000UL; U32 U32magic1 = 0x0003003bUL; U32 U32magic2 = 0x0009fffeUL; U32 U32magic3 = 0x00000006UL; U32 U32magic4 = 0x00001000UL; U32 U32magic5 = 0x00000001UL; U32 U32magic6 = 0xfffffffeUL; verbose ("calling: generate_header ()"); calculate_blocks (); fil_swriteU32 (output_block + 0x30, &Root_start_block); fil_swriteU32 (output_block + 0x3c, &SDepot_start_block); fil_swriteU32 (output_block + 0x2c, &BDepot_blocks); /* constant magic numbers */ fil_swriteU32 (output_block + 0x00, &identifier1); fil_swriteU32 (output_block + 0x04, &identifier2); fil_swriteU32 (output_block + 0x08, &U32magiczero); fil_swriteU32 (output_block + 0x0c, &U32magiczero); fil_swriteU32 (output_block + 0x10, &U32magiczero); fil_swriteU32 (output_block + 0x14, &U32magiczero); fil_swriteU32 (output_block + 0x18, &U32magic1); fil_swriteU32 (output_block + 0x1c, &U32magic2); fil_swriteU32 (output_block + 0x20, &U32magic3); fil_swriteU32 (output_block + 0x24, &U32magiczero); fil_swriteU32 (output_block + 0x28, &U32magiczero); fil_swriteU32 (output_block + 0x34, &U32magiczero); fil_swriteU32 (output_block + 0x38, &U32magic4); fil_swriteU32 (output_block + 0x40, &U32magic5); fil_swriteU32 (output_block + 0x44, &U32magic6); fil_swriteU32 (output_block + 0x48, &U32magiczero); pos_block = 0x4c; return 0; } /* reviewed all cases */ static int generate_recursive (MY_FILE * list) { MY_FILE *p_MY_FILE_list; verbose ("calling: generate_recursive ()"); switch (list->type) { /* reviewed when generating Input and sbfile */ case MY_FILE_list: for (p_MY_FILE_list = list->file.MY_FILE_list; (U32)((U8*)p_MY_FILE_list - (U8*)list->file.root_list) < list->size; p_MY_FILE_list++) test_call (generate_recursive (p_MY_FILE_list), int); break; /* reviewed when generating bbd_list, BDepot and SDepot */ case block_list: if (list == bbd_list) { test_call (write_block_list (BDepot_start_block, bbd_list, 0), int); write_until_output_block_boundary (1); break; } else if (list == BDepot) /* we want to skip generate BDepot by now */ break; else if (list == SDepot) /* we want to skip generate SDepot by now */ break; else assert ("list->type==block_list but list UNKNOWN in generate_recursive"==NULL); /* reviewed when generating Root */ case root_list: /* we want to skip generate Root by now */ assert (list == Root); break; case real: /* we are generating big and small streams here */ test_call (generate_real_file (list), int); break; default: assert ("list->type UNKNOWN in generate_recursive" == NULL); } return 0; } static int generate_SDepot (void) { verbose ("calling: generate_SDepot ()"); test_call (write_block_list (1, SDepot, 1), int); write_until_output_block_boundary (1); return 0; } static int generate_Root (void) { verbose ("calling: generate_Root ()"); test_call (write_root_list (Root), int); write_rest_of_output_block_with_null_pps (); write_until_output_block_boundary (0); return 0; } static int generate_BDepot (void) { MY_FILE SDepot_and_Root_block_list; MY_FILE file_block_list; U32 next_block_link; verbose ("calling: generate_BDepot ()"); next_block_link = 0xffffffffUL + header_blocks + 1; /* + 1 because is the ___next link___ */ /* 1. generate sbfile block list */ assert (next_block_link == sbfile_start_block + 1); /* + 1 because is the ___next link___ */ init_MY_FILE ((&file_block_list), block_list, sizeof (U32), NULL, BDepot->file.block_list); /* the blocks that takes sbfile are in the first entry in BDepot */ verboseU32 ((*(BDepot->file.block_list))); test_call (write_block_list (next_block_link, &file_block_list, 1), int); /* update next_block_link */ next_block_link += sbfile_blocks; /* 2. generate big streams block list */ assert (next_block_link == sbfile_start_block + sbfile_blocks + 1); /* + 1 because is the ___next link___ */ init_MY_FILE ((&file_block_list), block_list, BDepot->size - 3 * sizeof (U32), NULL, BDepot->file.block_list + 3); /* 3 because the first three entries in BDepot are sbfile, SDepot and Root */ test_call (write_block_list (next_block_link, &file_block_list, 1), int); /* update next_block_link */ next_block_link += sum_block_list (&file_block_list); /* 3. generate SDepot and Root block list */ if (sbfile->size > 0) /* if there are sbfile */ assert (next_block_link == SDepot_start_block + 1); /* + 1 because is the ___next link___ */ init_MY_FILE ((&SDepot_and_Root_block_list), block_list, 2 * sizeof (U32), NULL, BDepot->file.block_list + 1); /* + 1 because the first entry in BDepot is sbfile block */ test_call (write_block_list (next_block_link, &SDepot_and_Root_block_list, 1), int); /* 4. finish */ write_until_output_block_boundary (1); return 0; } static int generate_real_file (MY_FILE * MY_FILE_file) { FILE *file; int n_read; U8 * pps; U32 total_bytes; U32 sbfile_size; static U32 last_small_stream_next_block = 0; static int sbfile_start_block_set = 0; static int sbfile_may_need_write_until_boundary = 0; verbose ("calling: generate_real_file ()"); /* FIXME MARK 3 */ /* all seems to be working until here... I hope. Test is welcome! */ /*verboseU16 (sbfile_start_block_set);*/ /*verboseU16 (sbfile_may_need_write_until_boundary);*/ /*verboseU16 (pos_block);*/ assert (pos_block <= 0x0200); assert (pos_block % 0x40 == 0); /* open real file */ assert (MY_FILE_file->file.real.name[0]); file = fopen (MY_FILE_file->file.real.name, "rb"); test_exitf (file != NULL, 11, dummy ()); /* write Root start_block's of this file */ if (MY_FILE_file->size >= 0x1000) { /* generating big stream */ /* first, end writting sbfile, if any */ if (sbfile_may_need_write_until_boundary) { /* this happens after all small strams have been generated but before the first big stream is generated */ write_until_output_block_boundary (1); sbfile_may_need_write_until_boundary = 0; } /* then, continue with this big stream */ /* write start block of this stream in its Root pps */ verboseU32 (next_block); pps = Root->file.root_list + MY_FILE_file->file.real.ppsnumber * 0x80; fil_swriteU32 (pps + 0x74, &next_block); } else { /* generating small stream */ /* do nothing, start block of small streams was written in proces_streams */ /* write start block of this stream in its Root pps */ pps = Root->file.root_list + MY_FILE_file->file.real.ppsnumber * 0x80; fil_swriteU32 (pps + 0x74, &last_small_stream_next_block); last_small_stream_next_block += *(MY_FILE_file->blocks); /* small blocks */ /* write sbfile start block in root directory pps in Root, if not done */ if (!sbfile_start_block_set) { /* this happens before generate the first small stream that makes sbfile */ verbose ("generating sbfile"); verbose ("SUPPOSING: first entry in Root is root entry"); /* write start block of sbfile */ assert (sbfile_start_block == next_block); fil_swriteU32 (Root->file.root_list + 0x74, &next_block); sbfile_size = sum_blocks_MY_FILE_list (sbfile) * 0x40; verboseU32 (sbfile_size); /* compare calculated sbfile size with original */ assert (sbfile_size == fil_sreadU32 (Root->file.root_list + 0x78)); sbfile_start_block_set = 1; sbfile_may_need_write_until_boundary = 1; } } verboseS (MY_FILE_file->file.real.name); total_bytes = 0; /* copy real file to output_file */ while (!feof (file)) { n_read = fread (output_block+pos_block, 1, 0x0200-pos_block, file); test_exitf (!ferror (file), 11, dummy ()); if (n_read < 0x0200-pos_block) /* if it was readed less than it could be possible */ assert (feof (file)); pos_block += (U16)n_read; total_bytes += n_read; check_output_block_boundary (); } test_exitf (total_bytes == MY_FILE_file->size, 12, dummy()); if (MY_FILE_file->size >= 0x1000) /* generating big stream */ write_until_output_block_boundary (1) else /* generating small stream */ write_until_output_block_small_boundary (1); /* close real file */ fclose (file); return 0; } int write_block_list (U32 start_count, MY_FILE * list, int write_end_chain) { U32 *p; U32 n; U32 end_chain = 0xfffffffeUL; U32 value_to_write; U32 delta; verbose ("calling: write_block_list ()"); assert (list->type == block_list); /* Was (list->type = block_list) - SG */ assert (pos_block <= 0x01fc); /* 0x01fc = 0x0200 - sizeof(U32) */ delta = start_count; if (list->size == 0) return 0; /* we are done */ for (p = list->file.block_list; (U32)((U8 *) p - (U8 *) list->file.block_list) < list->size; p++) { for (n = 0; n < *p; n++) { /* this allow bbd_list runs over the block number 2 */ check_output_block_boundary (); /* if it's the last block in chain */ if (write_end_chain && !(n + 1 < *p)) value_to_write = end_chain; else value_to_write = n + delta; fil_swriteU32 (output_block + pos_block, &value_to_write); pos_block += (U16)sizeof (U32); assert (pos_block <= 0x0200); } delta += n; } check_output_block_boundary (); return 0; } static int write_root_list (MY_FILE * list) { U8 * p; verbose ("calling: write_root_list ()"); assert (list != NULL); assert (pos_block == 0); assert (list->type == root_list); assert (list->size > 0); for (p = list->file.root_list; (U32)(p - list->file.root_list) < list->size; p += 0x80) { memcpy (output_block+(p-list->file.root_list)%0x0200, p, 0x80); /*verboseU8Array ((output_block+(p-list->file.root_list)%0x0200), 1, 0x80);*/ verboseU32 (fil_sreadU32 (p + 0x74)); #ifdef VERBOSE { U32 written_start_block; U32 file_size; U8 * h; written_start_block = fil_sreadU32 ( output_block+(p-list->file.root_list)%0x0200 + 0x74 ); file_size = fil_sreadU32 ( output_block+(p-list->file.root_list)%0x0200 + 0x78 ); for (h = output_block+(p-list->file.root_list)%0x0200; *h; h+=2) if (isprint (*h)) printf ("%c", *h); else printf ("\\0x%02x", *h); printf (": "); verboseU32 (written_start_block); verboseU32 (file_size); } #endif pos_block += (U16)0x80; assert (pos_block <= (U16)0x0200); check_output_block_boundary (); } return 0; } static void ends (void) { #ifdef VERBOSE static int called; verbose ("calling: ends ()"); if (called++) verbose ("DANGER: ends called more than once"); #endif /* sbfile SDepot BDepot Root Input */ if (output_file != NULL) fclose (output_file); } /* reviewed when done */ static void calculate_blocks (void) { MY_FILE big_streams_list; verbose ("calling: calculate_blocks ()"); /* preparing */ init_MY_FILE ((&big_streams_list), MY_FILE_list, Input.size - 5 * sizeof (MY_FILE), NULL, Input.file.MY_FILE_list + 5); /* 5 because the first 5 entries in Input are for bbd_list, BDepot, Root, SDepot and sbfile */ /* calculate sizes */ assert (*(BDepot->blocks) == *(bbd_list->file.block_list)); assert (*(Root->blocks) == size2blocks (Root->size, 0x0200)); verboseU32 ((*(sbfile->blocks))); verboseU32 ((size2blocks (sum_blocks_MY_FILE_list (sbfile) * 0x40, 0x0200))); assert (*(sbfile->blocks) == size2blocks_preserve_zero ( sum_blocks_MY_FILE_list (sbfile) * 0x40, 0x0200)); assert (*(SDepot->blocks) == size2blocks_preserve_zero ( (sum_block_list (SDepot) * sizeof (U32)), 0x0200)); BDepot_blocks = *(BDepot->blocks); SDepot_blocks = *(SDepot->blocks); Root_blocks = *(Root->blocks); sbfile_blocks = *(sbfile->blocks); big_streams_blocks = sum_blocks_MY_FILE_list (&big_streams_list); header_blocks = size2blocks ((19 + BDepot_blocks) * sizeof(U32), 0x0200); /* 19 + because the first 19 U32 doesn't belong to BDepot_blocks, but to header */ /* calculate starting */ sbfile_start_block = 0xffffffffUL + header_blocks; /* if there are sbfile, should start in sbfile_start_block */ Root_start_block = 0xffffffffUL + header_blocks + sbfile_blocks + big_streams_blocks + SDepot_blocks; /* 0xffffffffUL because first block is -1, second 0, third 1 and son on */ if (SDepot_blocks > 0) /* if there are small streams*/ SDepot_start_block = 0xffffffffUL + header_blocks + sbfile_blocks + big_streams_blocks; else /* SDepot_blocks == 0 */ /* if there are not small streams, neither sbfile, neither SDepot */ SDepot_start_block = 0xfffffffeUL; BDepot_start_block = 0xffffffffUL + header_blocks + sbfile_blocks + big_streams_blocks + SDepot_blocks + Root_blocks; verboseU32 (header_blocks); verboseU32 (big_streams_blocks); verboseU32 (sbfile_blocks); verboseU32 (SDepot_blocks); } #undef VERBOSE /* You can use next lines to print some parts of Structure */ /* verboseU32Array (SDepot->file.block_list, SDepot->size / sizeof(U32)); verboseU8Array (Root->file.root_list, Root->size / 0x80, 0x80); verboseU32Array (BDepot->file.block_list, BDepot->size / sizeof (U32)); verboseU32 (*(bbd_list->file.block_list)); */