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