1 /*
2 WAD.C
3
4 Copyright (C) 1991-2001 and beyond by Bungie Studios, Inc.
5 and the "Aleph One" developers.
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 This license is contained in the file "COPYING",
18 which is included with this source code; it is available online at
19 http://www.gnu.org/licenses/gpl.html
20
21 Thursday, June 30, 1994 10:54:39 PM
22
23 Tuesday, December 13, 1994 4:31:46 PM- allowed for application specific data in the directory
24 data. This lets me put the names and entry flags in one tight logical place.
25
26 Sunday, February 5, 1995 1:55:01 AM- allow for offset for inplace creation of data, added
27 version control so that I can read things that are old, implemented a checksum value
28 for the header, allowed for poor man's search criteria.
29
30 Three open calls:
31 1) open_wad_file_for_reading -> standard open call.
32 2) open_union_wad_file_for_reading-> opens multiple files, based on file type and
33 alphabetical.
34 3) open_union_wad_file_for_reading_by_list-> opens multiple files, based on array
35 passed in.
36
37 Future Options:
38 ��Reentrancy
39 ��Use malloc to actually make it possible to read the data as changes are made in the future?
40
41 Saturday, October 28, 1995 1:13:38 PM- whoops. Had a huge memory leak in the inflate wad
42 data. I�m fired.
43
44 Jan 30, 2000 (Loren Petrich):
45 Did some typecasts
46
47 Feb 3, 2000 (Loren Petrich):
48 Added WADFILE_HAS_INFINITY_STUFF to list of recognized wad types,
49 for Marathon Infinity compatibility.
50
51 Aug 12, 2000 (Loren Petrich):
52 Using object-oriented file handler
53
54 Aug 15, 2000 (Loren Petrich):
55 Suppressed union-wad stuff; that was probably some abortive attempt at creating some sort of
56 fancy archive format.
57
58 Aug 25, 2000 (Loren Petrich):
59 Fixed a stupid bug in my reworking of the file handling --
60 "if (!open_...)" is now "if (open...)" -- checksumming should now work correctly.
61
62 Sep 11, 2000 (Loren Petrich):
63 Made get_flat_data() and inflate_flat_data() pack and unpack properly...
64
65 July 6, 2001 (Loren Petrich):
66 Added Thomas Herzog's more careful wad-version error checking
67
68 Sep 30, 2001 (Loren Petrich):
69 Added support for reading Marathon 1 map files
70 (not sure if anyone really wants to write them);
71 also added a "between levels" flag so that this may be used with 3D models.
72
73 Jan 25, 2002 (Br'fin (Jeremy Parsons)):
74 Adjusted Carbon flow to avoid a p2cstr
75 */
76
77 // Note that level_transition_malloc is specific to marathon...
78
79 #include "cseries.h"
80
81 #include <string.h>
82 #include <stdlib.h>
83
84 #include "wad.h"
85 #include "tags.h"
86 #include "crc.h"
87 #include "game_errors.h"
88 #include "interface.h" // for strERRORS
89
90 #include "FileHandler.h"
91 #include "Packing.h"
92
93 // Formerly in portable_files.h
memory_error()94 inline short memory_error() {return 0;}
95
96 /* ---------------- private structures */
97 // LP: no more union wads
98
99 // Indicates whether the wad is being loaded between levels;
100 // should be "false" for something that may be cleared by some other
101 // "between-levels" loading, such as 3D models.
102 bool BetweenLevels = true;
103
104 /* ---------------- private global data */
105 struct wad_internal_data *internal_data[MAXIMUM_OPEN_WADFILES]= {NULL, NULL, NULL};
106
107 /* ---------------- private prototypes */
108 static int32 calculate_directory_offset(struct wad_header *header, short index);
109 static short get_directory_base_length(struct wad_header *header);
110 static short get_entry_header_length(struct wad_header *header);
111 static bool read_indexed_directory_data(OpenedFile& OFile, struct wad_header *header,
112 short index, struct directory_entry *entry);
113 static int32 calculate_raw_wad_length(struct wad_header *file_header, uint8 *wad);
114 static bool read_indexed_wad_from_file_into_buffer(OpenedFile& OFile,
115 struct wad_header *header, short index, void *buffer, int32 *length);
116 static short count_raw_tags(uint8 *raw_wad);
117 static struct wad_data *convert_wad_from_raw(struct wad_header *header, uint8 *data, int32 wad_start_offset,
118 int32 raw_length);
119 static struct wad_data *convert_wad_from_raw_modifiable(struct wad_header *header, uint8 *raw_wad, int32 raw_length);
120 //static void patch_wad_from_raw(struct wad_header *header, uint8 *raw_wad, struct wad_data *read_wad);
121 static bool size_of_indexed_wad(OpenedFile& OFile, struct wad_header *header, short index,
122 int32 *length);
123
124 static bool write_to_file(OpenedFile& OFile, int32 offset, void *data, int32 length);
125 static bool read_from_file(OpenedFile& OFile, int32 offset, void *data, int32 length);
126
127 // LP: routines for packing and unpacking the data from streams of bytes
128 static uint8 *unpack_wad_header(uint8 *Stream, wad_header *Objects, size_t Count);
129 static uint8 *pack_wad_header(uint8 *Stream, wad_header *Objects, size_t Count);
130 static uint8 *unpack_old_directory_entry(uint8 *Stream, old_directory_entry *Objects, size_t Count);
131 static uint8 *pack_old_directory_entry(uint8 *Stream, old_directory_entry *Objects, size_t Count);
132 static uint8 *unpack_directory_entry(uint8 *Stream, directory_entry *Objects, size_t Count);
133 static uint8 *pack_directory_entry(uint8 *Stream, directory_entry *Objects, size_t Count);
134 //static uint8 *unpack_old_entry_header(uint8 *Stream, old_entry_header *Objects, size_t Count);
135 static uint8 *pack_old_entry_header(uint8 *Stream, old_entry_header *Objects, size_t Count);
136 static uint8 *unpack_entry_header(uint8 *Stream, entry_header *Objects, size_t Count);
137 static uint8 *pack_entry_header(uint8 *Stream, entry_header *Objects, size_t Count);
138
139 /* ------------------ Code Begins */
140
read_wad_header(OpenedFile & OFile,struct wad_header * header)141 bool read_wad_header(
142 OpenedFile& OFile,
143 struct wad_header *header)
144 {
145 int error = 0;
146 bool success= true;
147
148 uint8 buffer[SIZEOF_wad_header];
149 error = !read_from_file(OFile, 0, buffer, SIZEOF_wad_header);
150 unpack_wad_header(buffer,header,1);
151
152 if(error)
153 {
154 set_game_error(systemError, error);
155 success= false;
156 } else {
157 // Thomas Herzog made this error checking more careful
158 if((header->version>CURRENT_WADFILE_VERSION) || (header->data_version > 2) || (header->wad_count < 1))
159 {
160 set_game_error(gameError, errUnknownWadVersion);
161 success= false;
162 }
163 }
164
165 return success;
166 }
167
168 extern void *level_transition_malloc(size_t size);
169
170 /* This could be improved. Under the current implementation, it requires 2X sizeof level worth */
171 /* of memory to load... (This makes writing wads easier, but isn't really useful for loading */
172 /* Note that this does the correct thing for union wadfiles... */
read_indexed_wad_from_file(OpenedFile & OFile,struct wad_header * header,short index,bool read_only)173 struct wad_data *read_indexed_wad_from_file(
174 OpenedFile& OFile,
175 struct wad_header *header,
176 short index,
177 bool read_only)
178 {
179 struct wad_data *read_wad= (struct wad_data *) NULL;
180 uint8 *raw_wad = NULL;
181 int32 length = 0;
182 int error = 0;
183
184 // if(file_id>=0) /* NOT a union wadfile... */
185 {
186 if (size_of_indexed_wad(OFile, header, index, &length))
187 {
188 // The padding is so that one can use later-Marathon entry-header reading
189 // on Marathon 1 wadfiles, which have a shorter entry header
190 int32 padded_length = length + (SIZEOF_entry_header-SIZEOF_old_entry_header);
191
192 raw_wad= BetweenLevels ?
193 (uint8 *) level_transition_malloc(padded_length) :
194 (uint8 *) malloc(padded_length);
195
196 if(raw_wad)
197 {
198 /* Read into the buffer */
199 if (read_indexed_wad_from_file_into_buffer(OFile, header, index, raw_wad, &length))
200 {
201 /* Got the raw wad. Convert it into our internal representation... */
202 if(read_only)
203 {
204 read_wad= convert_wad_from_raw(header, raw_wad, 0, length);
205 } else {
206 read_wad= convert_wad_from_raw_modifiable(header, raw_wad, length);
207 }
208 if(!read_wad)
209 {
210 /* Error.. */
211 error= memory_error();
212 }
213 if(!read_wad || !read_only)
214 {
215 free(raw_wad);
216 raw_wad = NULL;
217 }
218 }
219 else
220 {
221 free(raw_wad);
222 raw_wad = NULL;
223 }
224 } else {
225 error= memory_error();
226 }
227 }
228 }
229
230 if(error)
231 {
232 set_game_error(systemError, error);
233 }
234
235 return read_wad;
236 }
237
extract_type_from_wad(struct wad_data * wad,WadDataType type,size_t * length)238 void *extract_type_from_wad(
239 struct wad_data *wad,
240 WadDataType type,
241 size_t *length)
242 {
243 void *return_value= NULL;
244 short index;
245
246 *length= 0;
247
248 assert(wad);
249 for(index= 0; index<wad->tag_count; ++index)
250 {
251 if(wad->tag_data[index].tag==type)
252 {
253 return_value= wad->tag_data[index].data;
254 *length= wad->tag_data[index].length;
255 assert(wad->tag_data[index].length >= 0);
256 break;
257 }
258 }
259
260 return return_value;
261 }
262
wad_file_has_checksum(FileSpecifier & File,uint32 checksum)263 bool wad_file_has_checksum(
264 FileSpecifier& File,
265 uint32 checksum)
266 {
267 bool has_checksum= false;
268
269 if(checksum==read_wad_file_checksum(File))
270 {
271 has_checksum= true;
272 }
273
274 return has_checksum;
275 }
276
read_wad_file_checksum(FileSpecifier & File)277 uint32 read_wad_file_checksum(FileSpecifier& File)
278 {
279 struct wad_header header;
280 uint32 checksum= 0;
281
282 OpenedFile OFile;
283 if (open_wad_file_for_reading(File,OFile))
284 {
285 if(read_wad_header(OFile, &header))
286 {
287 checksum= header.checksum;
288 }
289
290 close_wad_file(OFile);
291 }
292
293 return checksum;
294 }
295
read_wad_file_parent_checksum(FileSpecifier & File)296 uint32 read_wad_file_parent_checksum(FileSpecifier& File)
297 {
298 // fileref file_id;
299 struct wad_header header;
300 uint32 checksum= 0;
301
302 OpenedFile OFile;
303 if (open_wad_file_for_reading(File,OFile))
304 {
305 if(read_wad_header(OFile, &header))
306 {
307 checksum= header.parent_checksum;
308 }
309
310 close_wad_file(OFile);
311 }
312
313 return checksum;
314 }
315
wad_file_has_parent_checksum(FileSpecifier & File,uint32 parent_checksum)316 bool wad_file_has_parent_checksum(
317 FileSpecifier& File,
318 uint32 parent_checksum)
319 {
320 // fileref file_id;
321 bool has_checksum= false;
322 struct wad_header header;
323
324 OpenedFile OFile;
325 if (open_wad_file_for_reading(File,OFile))
326 // file_id= open_wad_file_for_reading(file);
327 // if(file_id>=0)
328 {
329 if(read_wad_header(OFile, &header))
330 // if(read_wad_header(file_id, &header))
331 {
332 if(header.parent_checksum==parent_checksum)
333 {
334 /* Found a match! */
335 has_checksum= true;
336 }
337 }
338
339 close_wad_file(OFile);
340 // close_wad_file(file_id);
341 }
342
343 return has_checksum;
344 }
345
346 /* ------------ Writing functions */
create_empty_wad(void)347 struct wad_data *create_empty_wad(void)
348 {
349 struct wad_data *wad;
350
351 wad= (struct wad_data *) malloc(sizeof(struct wad_data));
352 if(wad)
353 {
354 obj_clear(*wad); /* IMPORTANT! */
355 }
356
357 return wad;
358 }
359
fill_default_wad_header(FileSpecifier & File,short wadfile_version,short data_version,short wad_count,short application_directory_data_size,struct wad_header * header)360 void fill_default_wad_header(
361 FileSpecifier& File,
362 short wadfile_version,
363 short data_version,
364 short wad_count,
365 short application_directory_data_size,
366 struct wad_header *header)
367 {
368 obj_clear(*header);
369 header->version= wadfile_version;
370 header->data_version= data_version;
371 File.GetName(header->file_name);
372 header->wad_count= wad_count;
373 header->application_specific_directory_data_size= application_directory_data_size;
374
375 header->entry_header_size= get_entry_header_length(header);
376 if(!header->entry_header_size)
377 {
378 /* Default.. */
379 header->entry_header_size = SIZEOF_entry_header;
380 }
381
382 header->directory_entry_base_size= get_directory_base_length(header);
383 if(!header->directory_entry_base_size)
384 {
385 header->directory_entry_base_size = SIZEOF_directory_entry;
386 }
387
388 /* Things left for caller to fill in: */
389 /* uint32 checksum, int32 directory_offset, uint32 parent_checksum */
390 }
391
write_wad_header(OpenedFile & OFile,struct wad_header * header)392 bool write_wad_header(
393 OpenedFile& OFile,
394 struct wad_header *header)
395 {
396 bool success= true;
397
398 uint8 buffer[SIZEOF_wad_header];
399 obj_clear(buffer);
400 pack_wad_header(buffer,header,1);
401 write_to_file(OFile, 0, buffer, SIZEOF_wad_header);
402
403 return success;
404 }
405
406 // Takes raw, unswapped directory data
write_directorys(OpenedFile & OFile,struct wad_header * header,void * entries)407 bool write_directorys(
408 OpenedFile& OFile,
409 struct wad_header *header,
410 void *entries)
411 {
412 int32 size_to_write= get_size_of_directory_data(header);
413 bool success= true;
414
415 assert(header->version>=WADFILE_HAS_DIRECTORY_ENTRY);
416 write_to_file(OFile, header->directory_offset, entries,
417 size_to_write);
418
419 return success;
420 }
421
422 /* Note wad_count better be correct! */
get_size_of_directory_data(struct wad_header * header)423 int32 get_size_of_directory_data(
424 struct wad_header *header)
425 {
426 short base_entry_size= get_directory_base_length(header);
427
428 assert(header->wad_count);
429 assert(header->version>=WADFILE_HAS_DIRECTORY_ENTRY || header->application_specific_directory_data_size==0);
430
431 return (header->wad_count*
432 (header->application_specific_directory_data_size+base_entry_size));
433 }
434
435 // Takes raw, unswapped directory data
get_indexed_directory_data(struct wad_header * header,short index,void * directories)436 void *get_indexed_directory_data(
437 struct wad_header *header,
438 short index,
439 void *directories)
440 {
441 // LP: changed "char *" to "uint8 *"
442 uint8 *data_ptr= (uint8 *)directories;
443 short base_entry_size= get_directory_base_length(header);
444
445 assert(header->version>=WADFILE_HAS_DIRECTORY_ENTRY);
446 assert(index>=0 && index<header->wad_count);
447 data_ptr += index*(header->application_specific_directory_data_size+base_entry_size);
448 data_ptr += base_entry_size; /* Because the application specific junk follows the standard entries */
449
450 return ((void *) data_ptr);
451 }
452
set_indexed_directory_offset_and_length(struct wad_header * header,void * entries,short index,int32 offset,int32 length,short wad_index)453 void set_indexed_directory_offset_and_length(
454 struct wad_header *header,
455 void *entries,
456 short index,
457 int32 offset,
458 int32 length,
459 short wad_index)
460 {
461 uint8 *data_ptr= (uint8 *)entries;
462 int32 data_offset;
463
464 assert(header->version>=WADFILE_HAS_DIRECTORY_ENTRY);
465
466 /* calculate_directory_offset is for the file, by subtracting the base, we get the actual offset.. */
467 data_offset= calculate_directory_offset(header, index) - header->directory_offset;
468
469 data_ptr+= data_offset;
470
471 // LP: eliminating this dangerous sort of casting;
472 // should work correctly for wadfiles with size more than 1
473 /*
474 entry= (struct directory_entry *) data_ptr;
475
476 entry->length= length;
477 entry->offset_to_start= offset;
478
479 if(header->version>=WADFILE_SUPPORTS_OVERLAYS)
480 {
481 entry->index= wad_index;
482 }
483 */
484
485 // LP: should be correct for packing also
486 if (header->version>=WADFILE_SUPPORTS_OVERLAYS)
487 {
488 directory_entry entry;
489
490 entry.length = length;
491 entry.offset_to_start = offset;
492 entry.index = wad_index;
493
494 pack_directory_entry(data_ptr, &entry, 1);
495 }
496 else
497 {
498 old_directory_entry entry;
499
500 entry.length = length;
501 entry.offset_to_start = offset;
502
503 pack_old_directory_entry(data_ptr, &entry, 1);
504 }
505 }
506
507 // Returns raw, unswapped directory data
read_directory_data(OpenedFile & OFile,struct wad_header * header)508 void *read_directory_data(
509 OpenedFile& OFile,
510 struct wad_header *header)
511 {
512 int32 size;
513 uint8 *data;
514
515 assert(header->version>=WADFILE_HAS_DIRECTORY_ENTRY);
516
517 size= get_size_of_directory_data(header);
518 data= (uint8 *)malloc(size);
519 if(data)
520 {
521 read_from_file(OFile, header->directory_offset, data, size);
522 }
523
524 return data;
525 }
526
append_data_to_wad(struct wad_data * wad,WadDataType type,const void * data,size_t size,size_t offset)527 struct wad_data *append_data_to_wad(
528 struct wad_data *wad,
529 WadDataType type,
530 const void *data,
531 size_t size,
532 size_t offset) /* Allows for inplace creation of wadfiles */
533 {
534 short index;
535
536 assert(size); /* You can't append zero length data anymore! */
537 assert(wad);
538 assert(!wad->read_only_data);
539
540 /* Find the index to replace */
541 for(index= 0; index<wad->tag_count; ++index)
542 {
543 if(wad->tag_data[index].tag==type)
544 {
545 /* Just free it, and let it go. */
546 free(wad->tag_data[index].data);
547 break;
548 }
549 }
550
551 /* If we are appending... */
552 if(index==wad->tag_count)
553 {
554 struct tag_data *old_data= wad->tag_data;
555
556 wad->tag_count++;
557 wad->tag_data= (struct tag_data *) malloc(wad->tag_count*sizeof(struct tag_data));
558
559 if(!wad->tag_data)
560 {
561 alert_out_of_memory();
562 }
563
564 assert(wad->tag_data);
565 objlist_clear(wad->tag_data, wad->tag_count);
566 if(old_data)
567 {
568 objlist_copy(wad->tag_data, old_data, (wad->tag_count-1));
569 free(old_data);
570 }
571 }
572
573 /* Copy it in.. */
574 assert(index>=0 && index<wad->tag_count);
575 wad->tag_data[index].data= (uint8 *) malloc(size);
576 if(!wad->tag_data[index].data)
577 {
578 alert_out_of_memory();
579 }
580 assert(wad->tag_data[index].data);
581
582 memcpy(wad->tag_data[index].data, data, size);
583
584 /* Setup the tag data. */
585 wad->tag_data[index].tag= type;
586 wad->tag_data[index].length= size;
587 wad->tag_data[index].offset= offset;
588
589 return wad;
590 }
591
remove_tag_from_wad(struct wad_data * wad,WadDataType type)592 void remove_tag_from_wad(
593 struct wad_data *wad,
594 WadDataType type)
595 {
596 short index;
597
598 assert(wad);
599 assert(!wad->read_only_data);
600
601 /* Find the index to replace */
602 for(index= 0; index<wad->tag_count; ++index)
603 {
604 if(wad->tag_data[index].tag==type) break;
605 }
606
607 /* If we are appending... */
608 if(index!=wad->tag_count)
609 {
610 struct tag_data *old_data= wad->tag_data;
611
612 wad->tag_count-= 1;
613 wad->tag_data= (struct tag_data *) malloc(wad->tag_count*sizeof(struct tag_data));
614
615 if(!wad->tag_data)
616 {
617 alert_out_of_memory();
618 }
619
620 assert(wad->tag_data);
621 objlist_clear(wad->tag_data, wad->tag_count);
622 if(old_data)
623 {
624 /* Copy the stuff below it. */
625 objlist_copy(wad->tag_data, old_data, index);
626
627 /* Copy the stuff above it. */
628 objlist_copy(&wad->tag_data[index], &old_data[index+1], (wad->tag_count-index));
629
630 free(old_data);
631 }
632 }
633 }
634
635 /* Now uses CRC to checksum.. */
calculate_and_store_wadfile_checksum(OpenedFile & OFile)636 void calculate_and_store_wadfile_checksum(OpenedFile& OFile)
637 {
638 struct wad_header header;
639
640 /* read the header */
641 read_wad_header(OFile, &header);
642
643 /* Make sure we don't checksum the checksum value.. */
644 header.checksum= 0l;
645 write_wad_header(OFile, &header);
646
647 /* Unused bytes better ALWAYS be initialized to zero.. */
648 header.checksum= calculate_crc_for_opened_file(OFile);
649
650 /* Save it.. */
651 write_wad_header(OFile, &header);
652 }
653
write_wad(OpenedFile & OFile,struct wad_header * file_header,struct wad_data * wad,int32 offset)654 bool write_wad(
655 OpenedFile& OFile,
656 struct wad_header *file_header,
657 struct wad_data *wad,
658 int32 offset)
659 {
660 int error = 0;
661 bool success;
662 short entry_header_length= get_entry_header_length(file_header);
663 short index;
664 struct entry_header header;
665 int32 running_offset= 0l;
666
667 assert(wad);
668 assert(!wad->read_only_data);
669
670 for(index=0; !error && index<wad->tag_count; ++index)
671 {
672 header.tag= wad->tag_data[index].tag;
673 header.length= wad->tag_data[index].length;
674
675 /* On older versions, this will get overwritten by the copy.. */
676 header.offset= wad->tag_data[index].offset;
677
678 if(index==wad->tag_count-1)
679 {
680 /* Last one's next offset is zero.. */
681 header.next_offset= 0;
682 } else {
683 running_offset+= header.length+entry_header_length;
684 header.next_offset= running_offset;
685 }
686
687 /* Write this to the file... */
688 uint8 buffer[MAX(SIZEOF_old_entry_header,SIZEOF_entry_header)];
689 switch (entry_header_length)
690 {
691 case SIZEOF_old_entry_header:
692 pack_old_entry_header(buffer,(old_entry_header *)&header,1);
693 break;
694 case SIZEOF_entry_header:
695 pack_entry_header(buffer,&header,1);
696 break;
697 default:
698 vassert(false,csprintf(temporary,"Unrecognized entry-header length: %d",entry_header_length));
699 }
700 if (write_to_file(OFile, offset, buffer, entry_header_length))
701 {
702 offset+= entry_header_length;
703
704 /* Write the data.. */
705 write_to_file(OFile, offset, wad->tag_data[index].data, wad->tag_data[index].length);
706 {
707 offset+= wad->tag_data[index].length;
708 }
709 }
710 }
711
712 if(error)
713 {
714 success= false;
715 set_game_error(systemError, error);
716 } else {
717 success= true;
718 }
719
720 return success;
721 }
722
number_of_wads_in_file(FileSpecifier & File)723 short number_of_wads_in_file(FileSpecifier& File)
724 {
725 short count= NONE;
726
727 OpenedFile OFile;
728 if (open_wad_file_for_reading(File,OFile))
729 {
730 struct wad_header header;
731
732 /* read the header */
733 read_wad_header(OFile, &header);
734
735 count= header.wad_count;
736
737 close_wad_file(OFile);
738 }
739
740 return count;
741 }
742
free_wad(struct wad_data * wad)743 void free_wad(
744 struct wad_data *wad)
745 {
746 short ii;
747
748 assert(wad);
749
750 /* Free all of the tags */
751 if(wad->read_only_data)
752 {
753 /* Read only wad.. */
754 free(wad->read_only_data);
755 free(wad->tag_data);
756 } else {
757 /* Modifiable */
758 for(ii=0; ii<wad->tag_count; ++ii)
759 {
760 assert(wad->tag_data[ii].data);
761 free(wad->tag_data[ii].data);
762 }
763 free(wad->tag_data);
764 }
765
766 /* And free the total data.. */
767 free(wad);
768 }
769
calculate_wad_length(struct wad_header * file_header,struct wad_data * wad)770 int32 calculate_wad_length(
771 struct wad_header *file_header,
772 struct wad_data *wad)
773 {
774 short ii;
775 short header_length= get_entry_header_length(file_header);
776 int32 running_length= 0l;
777
778 for(ii= 0; ii<wad->tag_count; ++ii)
779 {
780 running_length += wad->tag_data[ii].length + header_length;
781 }
782
783 return running_length;
784 }
785
786 /* ------------ Transfer type functions */
787 #define CURRENT_FLAT_MAGIC_COOKIE (0xDEADDEAD)
788
789 /*
790 LP: ought not to use such a struct directly, because this is supposed to be packed data
791 Format:
792 4 bytes -- magic cookie
793 4 bytes -- length
794 SIZEOF_wad_header -- packed wad header
795 */
796 const int SIZEOF_encapsulated_wad_data = 2*4 + SIZEOF_wad_header;
797
798
get_flat_data(FileSpecifier & File,bool use_union,short wad_index)799 void *get_flat_data(
800 FileSpecifier& File,
801 bool use_union,
802 short wad_index)
803 {
804 struct wad_header header;
805 bool success= false;
806 uint8 *data= NULL;
807
808 assert(!use_union);
809
810 OpenedFile OFile;
811 if (open_wad_file_for_reading(File,OFile))
812 {
813 /* Read the file */
814 success= read_wad_header(OFile, &header);
815
816 if (success)
817 {
818 int32 length;
819 int error = 0;
820
821 /* Allocate the conglomerate data.. */
822 if (size_of_indexed_wad(OFile, &header, wad_index, &length))
823 {
824 data= (uint8 *)malloc(length+SIZEOF_encapsulated_wad_data);
825 if(data)
826 {
827 uint8 *buffer= data + SIZEOF_encapsulated_wad_data;
828
829 // Pack the encapsulated header
830 uint8 *S = data;
831 ValueToStream(S,uint32(CURRENT_FLAT_MAGIC_COOKIE));
832 ValueToStream(S,int32(length + SIZEOF_encapsulated_wad_data));
833 S = pack_wad_header(S,&header,1);
834 assert((S - data) == SIZEOF_encapsulated_wad_data);
835
836 /* Read into our buffer... */
837 success = read_indexed_wad_from_file_into_buffer(OFile, &header, wad_index,
838 buffer, &length);
839
840 if (!success)
841 {
842 /* Error-> didn't get it.. */
843 free(data);
844 data= NULL;
845 error = OFile.GetError();
846 }
847 }
848 else
849 {
850 error= memory_error();
851 }
852 }
853
854 set_game_error(systemError, error);
855 }
856
857 /* Close the file.. */
858 close_wad_file(OFile);
859 }
860
861 return data;
862 }
863
get_flat_data_length(void * data)864 int32 get_flat_data_length(
865 void *data)
866 {
867 int32 Length;
868 uint8 *S = (uint8 *)data;
869 S += 4;
870 StreamToValue(S,Length);
871 return Length;
872 }
873
874 /* This is how you dispose of it-> you inflate it, then use free_wad() */
inflate_flat_data(void * data,struct wad_header * header)875 struct wad_data *inflate_flat_data(
876 void *data,
877 struct wad_header *header)
878 {
879 struct wad_data *wad= NULL;
880 uint8 *buffer= ((uint8 *) data)+SIZEOF_encapsulated_wad_data;
881 int32 raw_length;
882
883 assert(data);
884 assert(header);
885
886 uint32 MagicCookie;
887 uint8 *S = (uint8 *)data;
888 StreamToValue(S,MagicCookie);
889 assert(MagicCookie==CURRENT_FLAT_MAGIC_COOKIE);
890
891 // Get the length here, where it's convenient
892 int32 Length;
893 StreamToValue(S,Length);
894
895 S = unpack_wad_header(S,header,1);
896 assert((S - (uint8 *)data) == SIZEOF_encapsulated_wad_data);
897
898 raw_length= calculate_raw_wad_length(header, buffer);
899 assert(raw_length==Length-SIZEOF_encapsulated_wad_data);
900
901 /* Now inflate.. */
902 wad= convert_wad_from_raw(header, (uint8 *)data, SIZEOF_encapsulated_wad_data, raw_length);
903
904 return wad;
905 }
906
907 /* ---------- debugging routines. */
dump_wad(struct wad_data * wad)908 void dump_wad(
909 struct wad_data *wad)
910 {
911 short index;
912 struct tag_data *tag= wad->tag_data;
913
914 dprintf("---Dumping---");
915 dprintf("Tag Count: %d", wad->tag_count);
916 for(index= 0; index<wad->tag_count; ++index)
917 {
918 assert(tag);
919 dprintf("Tag: %x data: %p length: %d offset: %d", tag->tag, tag->data, tag->length,
920 tag->offset);
921 tag++;
922 }
923 dprintf("---End of Dump---");
924 }
925
926 /* ---------- file management routines */
create_wadfile(FileSpecifier & File,Typecode Type)927 bool create_wadfile(FileSpecifier& File, Typecode Type)
928 {
929 return File.Create(Type);
930 }
931
open_wad_file_for_reading(FileSpecifier & File,OpenedFile & OFile)932 bool open_wad_file_for_reading(FileSpecifier& File, OpenedFile& OFile)
933 {
934 return File.Open(OFile);
935 }
936
open_wad_file_for_writing(FileSpecifier & File,OpenedFile & OFile)937 bool open_wad_file_for_writing(FileSpecifier& File, OpenedFile& OFile)
938 {
939 return File.Open(OFile,true);
940 }
941
close_wad_file(OpenedFile & File)942 void close_wad_file(OpenedFile& File)
943 {
944 File.Close();
945 }
946
947 /* ------------------------------ Private Code --------------- */
size_of_indexed_wad(OpenedFile & OFile,struct wad_header * header,short index,int32 * length)948 static bool size_of_indexed_wad(
949 OpenedFile& OFile,
950 struct wad_header *header,
951 short index,
952 int32 *length)
953 {
954 struct directory_entry entry;
955 // FileError error;
956
957 // assert(file_id>=0); /* No union wads! */
958
959 if (read_indexed_directory_data(OFile, header, index, &entry))
960 {
961 *length= entry.length;
962 }
963 else return false;
964
965 return true;
966 }
967
calculate_directory_offset(struct wad_header * header,short index)968 static int32 calculate_directory_offset(
969 struct wad_header *header,
970 short index)
971 {
972 int32 offset;
973 int32 unit_size;
974
975 switch(header->version)
976 {
977 case PRE_ENTRY_POINT_WADFILE_VERSION:
978 assert(header->application_specific_directory_data_size==0);
979 // OK for Marathon 1
980 case WADFILE_HAS_DIRECTORY_ENTRY:
981 case WADFILE_SUPPORTS_OVERLAYS:
982 // LP addition:
983 case WADFILE_HAS_INFINITY_STUFF:
984 assert(header->application_specific_directory_data_size>=0);
985 unit_size= header->application_specific_directory_data_size+get_directory_base_length(header);
986 break;
987
988 default:
989 vhalt(csprintf(temporary, "what is version %d?", header->version));
990 break;
991 }
992
993 /* Now actually calculate it (Note that the directory_entry data is first) */
994 offset= header->directory_offset+(index*unit_size);
995
996 return offset;
997 }
998
get_entry_header_length(struct wad_header * header)999 static short get_entry_header_length(
1000 struct wad_header *header)
1001 {
1002 short size;
1003
1004 assert(header);
1005
1006 switch(header->version)
1007 {
1008 case PRE_ENTRY_POINT_WADFILE_VERSION:
1009 case WADFILE_HAS_DIRECTORY_ENTRY:
1010 size = SIZEOF_old_entry_header;
1011 break;
1012
1013 default:
1014 /* After this point, I stored it. */
1015 size = header->entry_header_size;
1016 break;
1017 }
1018
1019 return size;
1020 }
1021
get_directory_base_length(struct wad_header * header)1022 static short get_directory_base_length(
1023 struct wad_header *header)
1024 {
1025 short size;
1026
1027 assert(header);
1028 assert(header->version<=CURRENT_WADFILE_VERSION);
1029
1030 switch(header->version)
1031 {
1032 case PRE_ENTRY_POINT_WADFILE_VERSION:
1033 case WADFILE_HAS_DIRECTORY_ENTRY:
1034 size = SIZEOF_old_directory_entry;
1035 break;
1036
1037 default:
1038 /* After this point, I stored it. */
1039 size = header->directory_entry_base_size;
1040 break;
1041 }
1042
1043 return size;
1044 }
1045
1046 /* This searches the directories for the given index, to allow for special replacements. */
read_indexed_directory_data(OpenedFile & OFile,struct wad_header * header,short index,struct directory_entry * entry)1047 static bool read_indexed_directory_data(
1048 OpenedFile& OFile,
1049 struct wad_header *header,
1050 short index,
1051 struct directory_entry *entry)
1052 {
1053 short base_entry_size;
1054 int32 offset;
1055
1056 /* Get the sizes of the data structures */
1057 base_entry_size= get_directory_base_length(header);
1058
1059 /* For old files, the index==the actual index */
1060 if(header->version<=WADFILE_HAS_DIRECTORY_ENTRY)
1061 {
1062 /* Calculate the offset */
1063 offset= calculate_directory_offset(header, index);
1064
1065 /* Read it! */
1066 assert(base_entry_size<=SIZEOF_directory_entry);
1067
1068 uint8 buffer[MAX(SIZEOF_old_directory_entry,SIZEOF_directory_entry)];
1069 if (!read_from_file(OFile, offset, buffer, base_entry_size))
1070 return false;
1071 switch (base_entry_size)
1072 {
1073 case SIZEOF_old_directory_entry:
1074 unpack_old_directory_entry(buffer,(old_directory_entry *)entry,1);
1075 break;
1076 case SIZEOF_directory_entry:
1077 unpack_directory_entry(buffer,entry,1);
1078 break;
1079 default:
1080 vassert(false,csprintf(temporary,"Unrecognized base-entry length: %d",base_entry_size));
1081 }
1082 return true;
1083
1084 } else {
1085
1086 short directory_index;
1087
1088 /* Pin it, so we can try to read future file formats */
1089 if(base_entry_size>SIZEOF_directory_entry)
1090 {
1091 base_entry_size= SIZEOF_directory_entry;
1092 }
1093
1094 /* We have to loop.. */
1095 for(directory_index= 0; directory_index<header->wad_count; ++directory_index)
1096 {
1097 /* We use a hint, that the index is the real index, to help make this have */
1098 /* a "hit" on the first try */
1099 short test_index= (index+directory_index)%header->wad_count;
1100
1101 /* Calculate the offset */
1102 offset= calculate_directory_offset(header, test_index);
1103
1104 /* Read it.. */
1105 uint8 buffer[MAX(SIZEOF_old_directory_entry,SIZEOF_directory_entry)];
1106 if (!read_from_file(OFile, offset, buffer, base_entry_size))
1107 return false;
1108 switch (base_entry_size)
1109 {
1110 case SIZEOF_old_directory_entry:
1111 unpack_old_directory_entry(buffer,(old_directory_entry *)entry,1);
1112 break;
1113 case SIZEOF_directory_entry:
1114 unpack_directory_entry(buffer,entry,1);
1115 break;
1116 default:
1117 vassert(false,csprintf(temporary,"Unrecognized base-entry length: %d",base_entry_size));
1118 }
1119 if(entry->index==index)
1120 {
1121 return true; /* Got it */
1122 }
1123 }
1124 }
1125
1126 /* Not found */
1127 return false;
1128 }
1129
1130 /* Internal function.. */
read_indexed_wad_from_file_into_buffer(OpenedFile & OFile,struct wad_header * header,short index,void * buffer,int32 * length)1131 static bool read_indexed_wad_from_file_into_buffer(
1132 OpenedFile& OFile,
1133 struct wad_header *header,
1134 short index,
1135 void *buffer,
1136 int32 *length) /* Length of maximum buffer on entry, actual length on return */
1137 {
1138 struct directory_entry entry;
1139 bool success = false;
1140
1141 /* Read the directory entry first */
1142 if (read_indexed_directory_data(OFile, header, index, &entry))
1143 {
1144 /* Some sanity checks */
1145 assert(*length<=entry.length);
1146 assert(buffer);
1147
1148 /* Set the length */
1149 *length= entry.length;
1150
1151 /* Read into it. */
1152 if (entry.length > 0) {
1153 success = read_from_file(OFile, entry.offset_to_start, buffer, entry.length);
1154
1155 /* Veracity Check */
1156 /* ! an error, it has a length non-zero and calculated != actual */
1157 assert(entry.length==calculate_raw_wad_length(header, (uint8 *)buffer));
1158 }
1159 }
1160
1161 return success;
1162 }
1163
1164 /* This *MUST* be a base wad.. */
convert_wad_from_raw(struct wad_header * header,uint8 * data,int32 wad_start_offset,int32 raw_length)1165 static struct wad_data *convert_wad_from_raw(
1166 struct wad_header *header,
1167 uint8 *data,
1168 int32 wad_start_offset,
1169 int32 raw_length)
1170 {
1171 struct wad_data *wad;
1172 uint8 *raw_wad;
1173
1174 /* In case we are somewhere else, like, for example, in a net transferred level.. */
1175 raw_wad= data+wad_start_offset;
1176
1177 wad= (struct wad_data *) malloc(sizeof(struct wad_data));
1178 if(wad)
1179 {
1180 short tag_count;
1181
1182 /* Clear it */
1183 obj_clear(*wad);
1184
1185 /* If the wad is of non-zero length... */
1186 if(raw_length)
1187 {
1188 /* Count the tags */
1189 tag_count= count_raw_tags(raw_wad);
1190
1191 /* Allocate the tags.. */
1192 wad->tag_count= tag_count;
1193 wad->tag_data= (struct tag_data *) malloc(tag_count * sizeof(struct tag_data));
1194 if(wad->tag_data)
1195 {
1196 short index;
1197 short entry_header_size;
1198
1199 /* Clear it */
1200 objlist_clear(wad->tag_data, tag_count);
1201
1202 entry_header_size= get_entry_header_length(header);
1203 entry_header wad_entry_header;
1204 uint8 *raw_wad_entry_header = raw_wad;
1205 // Will work OK for Marathon 1
1206 unpack_entry_header(raw_wad_entry_header, &wad_entry_header, 1);
1207
1208 /* Note that this is a read only wad.. */
1209 wad->read_only_data= data;
1210
1211 for(index= 0; index<tag_count; ++index)
1212 {
1213 assert(header->version<WADFILE_SUPPORTS_OVERLAYS || wad_entry_header.offset == 0);
1214 wad->tag_data[index].tag = wad_entry_header.tag;
1215 wad->tag_data[index].length = wad_entry_header.length;
1216 wad->tag_data[index].offset = 0;
1217 wad->tag_data[index].data = raw_wad_entry_header + entry_header_size;
1218
1219 raw_wad_entry_header = raw_wad + wad_entry_header.next_offset;
1220 // Will work OK for Marathon 1
1221 unpack_entry_header(raw_wad_entry_header, &wad_entry_header, 1);
1222 }
1223 } else {
1224 alert_out_of_memory();
1225 }
1226 }
1227 }
1228
1229 return wad;
1230 }
1231
1232 /* This *MUST* be a base wad.. */
convert_wad_from_raw_modifiable(struct wad_header * header,uint8 * raw_wad,int32 raw_length)1233 static struct wad_data *convert_wad_from_raw_modifiable(
1234 struct wad_header *header,
1235 uint8 *raw_wad,
1236 int32 raw_length)
1237 {
1238 struct wad_data *wad;
1239
1240 wad= (struct wad_data *) malloc(sizeof(struct wad_data));
1241 if(wad)
1242 {
1243 short tag_count;
1244
1245 /* Clear it */
1246 obj_clear(*wad);
1247
1248 /* If the wad is of non-zero length... */
1249 if(raw_length)
1250 {
1251 /* Count the tags */
1252 tag_count= count_raw_tags(raw_wad);
1253
1254 /* Allocate the tags.. */
1255 wad->tag_count= tag_count;
1256 wad->tag_data= (struct tag_data *) malloc(tag_count * sizeof(struct tag_data));
1257 if(wad->tag_data)
1258 {
1259 short index;
1260 short entry_header_size;
1261
1262 /* Clear it */
1263 objlist_clear(wad->tag_data, tag_count);
1264
1265 entry_header_size= get_entry_header_length(header);
1266 entry_header wad_entry_header;
1267 uint8 *raw_wad_entry_header = raw_wad;
1268 // Will work OK for Marathon 1
1269 unpack_entry_header(raw_wad_entry_header, &wad_entry_header, 1);
1270
1271 for(index= 0; index<tag_count; ++index)
1272 {
1273 wad->tag_data[index].tag = wad_entry_header.tag;
1274 wad->tag_data[index].length = wad_entry_header.length;
1275 wad->tag_data[index].data = (uint8 *) malloc(wad->tag_data[index].length);
1276 if(!wad->tag_data[index].data)
1277 {
1278 alert_out_of_memory();
1279 }
1280 wad->tag_data[index].offset= 0l;
1281
1282 /* This MUST be a base! */
1283 assert(header->version<WADFILE_SUPPORTS_OVERLAYS || wad_entry_header.offset == 0);
1284
1285 /* Copy the data.. */
1286 memcpy(wad->tag_data[index].data, raw_wad_entry_header + entry_header_size, wad->tag_data[index].length);
1287 raw_wad_entry_header = raw_wad + wad_entry_header.next_offset;
1288 // Will work OK for Marathon 1
1289 unpack_entry_header(raw_wad_entry_header, &wad_entry_header, 1);
1290 }
1291 }
1292 }
1293 }
1294
1295 return wad;
1296 }
1297
1298 // Will work OK for Marathon 1
count_raw_tags(uint8 * raw_wad)1299 static short count_raw_tags(
1300 uint8 *raw_wad)
1301 {
1302 int tag_count = 0;
1303
1304 entry_header header;
1305 unpack_entry_header(raw_wad, &header, 1);
1306 while (true) {
1307 tag_count++;
1308 uint32 next_offset = header.next_offset;
1309 if (next_offset == 0)
1310 break;
1311 unpack_entry_header(raw_wad + next_offset, &header, 1);
1312 }
1313
1314 return tag_count;
1315 }
1316
1317 // Will work OK for Marathon 1
calculate_raw_wad_length(struct wad_header * file_header,uint8 * wad)1318 static int32 calculate_raw_wad_length(
1319 struct wad_header *file_header,
1320 uint8 *wad)
1321 {
1322 int entry_header_size = get_entry_header_length(file_header);
1323 int32 length = 0;
1324
1325 entry_header header;
1326 unpack_entry_header(wad, &header, 1);
1327 while (true) {
1328 length += header.length + entry_header_size;
1329 uint32 next_offset = header.next_offset;
1330 if (next_offset == 0)
1331 break;
1332 unpack_entry_header(wad + next_offset, &header, 1);
1333 }
1334
1335 return length;
1336 }
1337
write_to_file(OpenedFile & OFile,int32 offset,void * data,int32 length)1338 static bool write_to_file(
1339 OpenedFile& OFile,
1340 int32 offset,
1341 void *data,
1342 int32 length)
1343 {
1344 if (!OFile.SetPosition(offset)) return false;
1345 return OFile.Write(length, data);
1346 }
1347
read_from_file(OpenedFile & OFile,int32 offset,void * data,int32 length)1348 static bool read_from_file(
1349 OpenedFile& OFile,
1350 int32 offset,
1351 void *data,
1352 int32 length)
1353 {
1354 if (!OFile.SetPosition(offset)) return false;
1355 return OFile.Read(length, data);
1356 }
1357
unpack_wad_header(uint8 * Stream,wad_header * Objects,size_t Count)1358 static uint8 *unpack_wad_header(uint8 *Stream, wad_header *Objects, size_t Count)
1359 {
1360 uint8* S = Stream;
1361 wad_header* ObjPtr = Objects;
1362
1363 for (size_t k = 0; k < Count; k++, ObjPtr++)
1364 {
1365 StreamToValue(S,ObjPtr->version);
1366 StreamToValue(S,ObjPtr->data_version);
1367 StreamToBytes(S,ObjPtr->file_name,MAXIMUM_WADFILE_NAME_LENGTH);
1368 StreamToValue(S,ObjPtr->checksum);
1369 StreamToValue(S,ObjPtr->directory_offset);
1370 StreamToValue(S,ObjPtr->wad_count);
1371 StreamToValue(S,ObjPtr->application_specific_directory_data_size);
1372 StreamToValue(S,ObjPtr->entry_header_size);
1373 StreamToValue(S,ObjPtr->directory_entry_base_size);
1374 StreamToValue(S,ObjPtr->parent_checksum);
1375 S += 2*20;
1376 }
1377
1378 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_wad_header));
1379 return S;
1380 }
1381
pack_wad_header(uint8 * Stream,wad_header * Objects,size_t Count)1382 static uint8 *pack_wad_header(uint8 *Stream, wad_header *Objects, size_t Count)
1383 {
1384 uint8* S = Stream;
1385 wad_header* ObjPtr = Objects;
1386
1387 for (size_t k = 0; k < Count; k++, ObjPtr++)
1388 {
1389 ValueToStream(S,ObjPtr->version);
1390 ValueToStream(S,ObjPtr->data_version);
1391 BytesToStream(S,ObjPtr->file_name,MAXIMUM_WADFILE_NAME_LENGTH);
1392 ValueToStream(S,ObjPtr->checksum);
1393 ValueToStream(S,ObjPtr->directory_offset);
1394 ValueToStream(S,ObjPtr->wad_count);
1395 ValueToStream(S,ObjPtr->application_specific_directory_data_size);
1396 ValueToStream(S,ObjPtr->entry_header_size);
1397 ValueToStream(S,ObjPtr->directory_entry_base_size);
1398 ValueToStream(S,ObjPtr->parent_checksum);
1399 S += 2*20;
1400 }
1401
1402 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_wad_header));
1403 return S;
1404 }
1405
1406
unpack_old_directory_entry(uint8 * Stream,old_directory_entry * Objects,size_t Count)1407 static uint8 *unpack_old_directory_entry(uint8 *Stream, old_directory_entry *Objects, size_t Count)
1408 {
1409 uint8* S = Stream;
1410 old_directory_entry* ObjPtr = Objects;
1411
1412 for (size_t k = 0; k < Count; k++, ObjPtr++)
1413 {
1414 StreamToValue(S,ObjPtr->offset_to_start);
1415 StreamToValue(S,ObjPtr->length);
1416 }
1417
1418 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_old_directory_entry));
1419 return S;
1420 }
1421
pack_old_directory_entry(uint8 * Stream,old_directory_entry * Objects,size_t Count)1422 static uint8 *pack_old_directory_entry(uint8 *Stream, old_directory_entry *Objects, size_t Count)
1423 {
1424 uint8* S = Stream;
1425 old_directory_entry* ObjPtr = Objects;
1426
1427 for (size_t k = 0; k < Count; k++, ObjPtr++)
1428 {
1429 ValueToStream(S,ObjPtr->offset_to_start);
1430 ValueToStream(S,ObjPtr->length);
1431 }
1432
1433 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_old_directory_entry));
1434 return S;
1435 }
1436
1437
unpack_directory_entry(uint8 * Stream,directory_entry * Objects,size_t Count)1438 static uint8 *unpack_directory_entry(uint8 *Stream, directory_entry *Objects, size_t Count)
1439 {
1440 uint8* S = Stream;
1441 directory_entry* ObjPtr = Objects;
1442
1443 for (size_t k = 0; k < Count; k++, ObjPtr++)
1444 {
1445 StreamToValue(S,ObjPtr->offset_to_start);
1446 StreamToValue(S,ObjPtr->length);
1447 StreamToValue(S,ObjPtr->index);
1448 }
1449
1450 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_directory_entry));
1451 return S;
1452 }
1453
pack_directory_entry(uint8 * Stream,directory_entry * Objects,size_t Count)1454 static uint8 *pack_directory_entry(uint8 *Stream, directory_entry *Objects, size_t Count)
1455 {
1456 uint8* S = Stream;
1457 directory_entry* ObjPtr = Objects;
1458
1459 for (size_t k = 0; k < Count; k++, ObjPtr++)
1460 {
1461 ValueToStream(S,ObjPtr->offset_to_start);
1462 ValueToStream(S,ObjPtr->length);
1463 ValueToStream(S,ObjPtr->index);
1464 }
1465
1466 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_directory_entry));
1467 return S;
1468 }
1469
1470
1471 #if 0
1472 static uint8 *unpack_old_entry_header(uint8 *Stream, old_entry_header *Objects, size_t Count)
1473 {
1474 uint8* S = Stream;
1475 old_entry_header* ObjPtr = Objects;
1476
1477 for (size_t k = 0; k < Count; k++, ObjPtr++)
1478 {
1479 StreamToValue(S,ObjPtr->tag);
1480 StreamToValue(S,ObjPtr->next_offset);
1481 StreamToValue(S,ObjPtr->length);
1482 }
1483
1484 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_old_entry_header));
1485 return S;
1486 }
1487 #endif
1488
pack_old_entry_header(uint8 * Stream,old_entry_header * Objects,size_t Count)1489 static uint8 *pack_old_entry_header(uint8 *Stream, old_entry_header *Objects, size_t Count)
1490 {
1491 uint8* S = Stream;
1492 old_entry_header* ObjPtr = Objects;
1493
1494 for (size_t k = 0; k < Count; k++, ObjPtr++)
1495 {
1496 ValueToStream(S,ObjPtr->tag);
1497 ValueToStream(S,ObjPtr->next_offset);
1498 ValueToStream(S,ObjPtr->length);
1499 }
1500
1501 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_old_entry_header));
1502 return S;
1503 }
1504
1505
unpack_entry_header(uint8 * Stream,entry_header * Objects,size_t Count)1506 static uint8 *unpack_entry_header(uint8 *Stream, entry_header *Objects, size_t Count)
1507 {
1508 uint8* S = Stream;
1509 entry_header* ObjPtr = Objects;
1510
1511 for (size_t k = 0; k < Count; k++, ObjPtr++)
1512 {
1513 StreamToValue(S,ObjPtr->tag);
1514 StreamToValue(S,ObjPtr->next_offset);
1515 StreamToValue(S,ObjPtr->length);
1516 StreamToValue(S,ObjPtr->offset);
1517 }
1518
1519 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_entry_header));
1520 return S;
1521 }
1522
pack_entry_header(uint8 * Stream,entry_header * Objects,size_t Count)1523 static uint8 *pack_entry_header(uint8 *Stream, entry_header *Objects, size_t Count)
1524 {
1525 uint8* S = Stream;
1526 entry_header* ObjPtr = Objects;
1527
1528 for (size_t k = 0; k < Count; k++, ObjPtr++)
1529 {
1530 ValueToStream(S,ObjPtr->tag);
1531 ValueToStream(S,ObjPtr->next_offset);
1532 ValueToStream(S,ObjPtr->length);
1533 ValueToStream(S,ObjPtr->offset);
1534 }
1535
1536 assert((S - Stream) == static_cast<ptrdiff_t>(Count*SIZEOF_entry_header));
1537 return S;
1538 }
1539
SetBetweenlevels(bool _BetweenLevels)1540 void SetBetweenlevels(bool _BetweenLevels) {BetweenLevels = _BetweenLevels;}
IsBetweenLevels()1541 bool IsBetweenLevels() {return BetweenLevels;}
1542