1 /*
2 ** farchive.cpp
3 ** Implements an archiver for DObject serialization.
4 **
5 **---------------------------------------------------------------------------
6 ** Copyright 1998-2009 Randy Heit
7 ** All rights reserved.
8 **
9 ** Redistribution and use in source and binary forms, with or without
10 ** modification, are permitted provided that the following conditions
11 ** are met:
12 **
13 ** 1. Redistributions of source code must retain the above copyright
14 ** notice, this list of conditions and the following disclaimer.
15 ** 2. Redistributions in binary form must reproduce the above copyright
16 ** notice, this list of conditions and the following disclaimer in the
17 ** documentation and/or other materials provided with the distribution.
18 ** 3. The name of the author may not be used to endorse or promote products
19 ** derived from this software without specific prior written permission.
20 **
21 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 **---------------------------------------------------------------------------
32 **
33 ** The structure of the archive file generated is influenced heavily by the
34 ** description of the MFC archive format published somewhere in the MSDN
35 ** library.
36 **
37 ** Two major shortcomings of the format I use are that there is no version
38 ** control and no support for storing the non-default portions of objects.
39 ** The latter would allow for easier extension of objects in future
40 ** releases even without a versioning system.
41 */
42
43 #include <stddef.h>
44 #include <string.h>
45 #include <zlib.h>
46 #include <stdlib.h>
47
48 #include "doomtype.h"
49 #include "farchive.h"
50 #include "m_swap.h"
51 #include "m_crc32.h"
52 #include "cmdlib.h"
53 #include "i_system.h"
54 #include "c_cvars.h"
55 #include "c_dispatch.h"
56 #include "d_player.h"
57 #include "m_misc.h"
58 #include "dobject.h"
59
60 // These are special tokens found in the data stream of an archive.
61 // Whenever a new object is encountered, it gets created using new and
62 // is then asked to serialize itself before processing of the previous
63 // object continues. This can result in some very deep recursion if
64 // you aren't careful about how you organize your data.
65
66 #define NEW_OBJ ((BYTE)1) // Data for a new object follows
67 #define NEW_CLS_OBJ ((BYTE)2) // Data for a new class and object follows
68 #define OLD_OBJ ((BYTE)3) // Reference to an old object follows
69 #define NULL_OBJ ((BYTE)4) // Load as NULL
70 #define M1_OBJ ((BYTE)44) // Load as (DObject*)-1
71
72 #define NEW_PLYR_OBJ ((BYTE)5) // Data for a new player follows
73 #define NEW_PLYR_CLS_OBJ ((BYTE)6) // Data for a new class and player follows
74
75 #define NEW_NAME ((BYTE)27) // A new name follows
76 #define OLD_NAME ((BYTE)28) // Reference to an old name follows
77 #define NIL_NAME ((BYTE)33) // Load as NULL
78
79 #define NEW_SPRITE ((BYTE)11) // A new sprite name follows
80 #define OLD_SPRITE ((BYTE)12) // Reference to an old sprite name follows
81
82 #ifdef __BIG_ENDIAN__
SWAP_WORD(WORD x)83 static inline WORD SWAP_WORD(WORD x) { return x; }
SWAP_DWORD(DWORD x)84 static inline DWORD SWAP_DWORD(DWORD x) { return x; }
SWAP_QWORD(QWORD x)85 static inline QWORD SWAP_QWORD(QWORD x) { return x; }
SWAP_FLOAT(float x)86 static inline void SWAP_FLOAT(float x) { }
SWAP_DOUBLE(double & dst,double src)87 static inline void SWAP_DOUBLE(double &dst, double src) { dst = src; }
88 #else
89 #ifdef _MSC_VER
SWAP_WORD(WORD x)90 static inline WORD SWAP_WORD(WORD x) { return _byteswap_ushort(x); }
SWAP_DWORD(DWORD x)91 static inline DWORD SWAP_DWORD(DWORD x) { return _byteswap_ulong(x); }
SWAP_QWORD(QWORD x)92 static inline QWORD SWAP_QWORD(QWORD x) { return _byteswap_uint64(x); }
SWAP_DOUBLE(double & dst,double & src)93 static inline void SWAP_DOUBLE(double &dst, double &src)
94 {
95 union twiddle { QWORD q; double d; } tdst, tsrc;
96 tsrc.d = src;
97 tdst.q = _byteswap_uint64(tsrc.q);
98 dst = tdst.d;
99 }
100 #else
SWAP_WORD(WORD x)101 static inline WORD SWAP_WORD(WORD x) { return (((x)<<8) | ((x)>>8)); }
SWAP_DWORD(DWORD x)102 static inline DWORD SWAP_DWORD(DWORD x) { return x = (((x)>>24) | (((x)>>8)&0xff00) | (((x)<<8)&0xff0000) | ((x)<<24)); }
SWAP_QWORD(QWORD x)103 static inline QWORD SWAP_QWORD(QWORD x)
104 {
105 union { QWORD q; DWORD d[2]; } t, u;
106 t.q = x;
107 u.d[0] = SWAP_DWORD(t.d[1]);
108 u.d[1] = SWAP_DWORD(t.d[0]);
109 return u.q;
110 }
SWAP_DOUBLE(double & dst,double & src)111 static inline void SWAP_DOUBLE(double &dst, double &src)
112 {
113 union twiddle { double f; DWORD d[2]; } tdst, tsrc;
114 DWORD t;
115
116 tsrc.f = src;
117 t = tsrc.d[0];
118 tdst.d[0] = SWAP_DWORD(tsrc.d[1]);
119 tdst.d[1] = SWAP_DWORD(t);
120 dst = tdst.f;
121 }
122 #endif
SWAP_FLOAT(float & x)123 static inline void SWAP_FLOAT(float &x)
124 {
125 union twiddle { DWORD i; float f; } t;
126 t.f = x;
127 t.i = SWAP_DWORD(t.i);
128 x = t.f;
129 }
130 #endif
131
132 // Output buffer size for compression; need some extra space.
133 // I assume the description in zlib.h is accurate.
134 #define OUT_LEN(a) ((a) + (a) / 1000 + 12)
135
BeEmpty()136 void FCompressedFile::BeEmpty ()
137 {
138 m_Pos = 0;
139 m_BufferSize = 0;
140 m_MaxBufferSize = 0;
141 m_Buffer = NULL;
142 m_File = NULL;
143 m_NoCompress = false;
144 m_Mode = ENotOpen;
145 }
146
147 static const char LZOSig[4] = { 'F', 'L', 'Z', 'O' };
148 static const char ZSig[4] = { 'F', 'L', 'Z', 'L' };
149
FCompressedFile()150 FCompressedFile::FCompressedFile ()
151 {
152 BeEmpty ();
153 }
154
FCompressedFile(const char * name,EOpenMode mode,bool dontCompress)155 FCompressedFile::FCompressedFile (const char *name, EOpenMode mode, bool dontCompress)
156 {
157 BeEmpty ();
158 Open (name, mode);
159 m_NoCompress = dontCompress;
160 }
161
FCompressedFile(FILE * file,EOpenMode mode,bool dontCompress,bool postopen)162 FCompressedFile::FCompressedFile (FILE *file, EOpenMode mode, bool dontCompress, bool postopen)
163 {
164 BeEmpty ();
165 m_Mode = mode;
166 m_File = file;
167 m_NoCompress = dontCompress;
168 if (postopen)
169 {
170 PostOpen ();
171 }
172 }
173
~FCompressedFile()174 FCompressedFile::~FCompressedFile ()
175 {
176 Close ();
177 }
178
Open(const char * name,EOpenMode mode)179 bool FCompressedFile::Open (const char *name, EOpenMode mode)
180 {
181 Close ();
182 if (name == NULL)
183 return false;
184 m_Mode = mode;
185 m_File = fopen (name, mode == EReading ? "rb" : "wb");
186 PostOpen ();
187 return !!m_File;
188 }
189
PostOpen()190 void FCompressedFile::PostOpen ()
191 {
192 if (m_File && m_Mode == EReading)
193 {
194 char sig[4];
195 fread (sig, 4, 1, m_File);
196 if (sig[0] != ZSig[0] || sig[1] != ZSig[1] || sig[2] != ZSig[2] || sig[3] != ZSig[3])
197 {
198 fclose (m_File);
199 m_File = NULL;
200 if (sig[0] == LZOSig[0] && sig[1] == LZOSig[1] && sig[2] == LZOSig[2] && sig[3] == LZOSig[3])
201 {
202 Printf ("Compressed files from older ZDooms are not supported.\n");
203 }
204 return;
205 }
206 else
207 {
208 DWORD sizes[2];
209 fread (sizes, sizeof(DWORD), 2, m_File);
210 sizes[0] = SWAP_DWORD (sizes[0]);
211 sizes[1] = SWAP_DWORD (sizes[1]);
212 unsigned int len = sizes[0] == 0 ? sizes[1] : sizes[0];
213 m_Buffer = (BYTE *)M_Malloc (len+8);
214 fread (m_Buffer+8, len, 1, m_File);
215 sizes[0] = SWAP_DWORD (sizes[0]);
216 sizes[1] = SWAP_DWORD (sizes[1]);
217 ((DWORD *)m_Buffer)[0] = sizes[0];
218 ((DWORD *)m_Buffer)[1] = sizes[1];
219 Explode ();
220 }
221 }
222 }
223
Close()224 void FCompressedFile::Close ()
225 {
226 if (m_File)
227 {
228 if (m_Mode == EWriting)
229 {
230 Implode ();
231 fwrite (ZSig, 4, 1, m_File);
232 fwrite (m_Buffer, m_BufferSize + 8, 1, m_File);
233 }
234 fclose (m_File);
235 m_File = NULL;
236 }
237 if (m_Buffer)
238 {
239 M_Free (m_Buffer);
240 m_Buffer = NULL;
241 }
242 BeEmpty ();
243 }
244
Flush()245 void FCompressedFile::Flush ()
246 {
247 }
248
Mode() const249 FFile::EOpenMode FCompressedFile::Mode () const
250 {
251 return m_Mode;
252 }
253
IsOpen() const254 bool FCompressedFile::IsOpen () const
255 {
256 return !!m_File;
257 }
258
Write(const void * mem,unsigned int len)259 FFile &FCompressedFile::Write (const void *mem, unsigned int len)
260 {
261 if (m_Mode == EWriting)
262 {
263 if (m_Pos + len > m_MaxBufferSize)
264 {
265 do
266 {
267 m_MaxBufferSize = m_MaxBufferSize ? m_MaxBufferSize * 2 : 16384;
268 }
269 while (m_Pos + len > m_MaxBufferSize);
270 m_Buffer = (BYTE *)M_Realloc (m_Buffer, m_MaxBufferSize);
271 }
272 if (len == 1)
273 m_Buffer[m_Pos] = *(BYTE *)mem;
274 else
275 memcpy (m_Buffer + m_Pos, mem, len);
276 m_Pos += len;
277 if (m_Pos > m_BufferSize)
278 m_BufferSize = m_Pos;
279 }
280 else
281 {
282 I_Error ("Tried to write to reading cfile");
283 }
284 return *this;
285 }
286
Read(void * mem,unsigned int len)287 FFile &FCompressedFile::Read (void *mem, unsigned int len)
288 {
289 if (m_Mode == EReading)
290 {
291 if (m_Pos + len > m_BufferSize)
292 {
293 I_Error ("Attempt to read past end of cfile");
294 }
295 if (len == 1)
296 *(BYTE *)mem = m_Buffer[m_Pos];
297 else
298 memcpy (mem, m_Buffer + m_Pos, len);
299 m_Pos += len;
300 }
301 else
302 {
303 I_Error ("Tried to read from writing cfile");
304 }
305 return *this;
306 }
307
Tell() const308 unsigned int FCompressedFile::Tell () const
309 {
310 return m_Pos;
311 }
312
Seek(int pos,ESeekPos ofs)313 FFile &FCompressedFile::Seek (int pos, ESeekPos ofs)
314 {
315 if (ofs == ESeekRelative)
316 pos += m_Pos;
317 else if (ofs == ESeekEnd)
318 pos = m_BufferSize - pos;
319
320 if (pos < 0)
321 m_Pos = 0;
322 else if ((unsigned)pos > m_BufferSize)
323 m_Pos = m_BufferSize;
324 else
325 m_Pos = pos;
326
327 return *this;
328 }
329
330 CVAR (Bool, nofilecompression, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
331
Implode()332 void FCompressedFile::Implode ()
333 {
334 uLong outlen;
335 uLong len = m_BufferSize;
336 Byte *compressed = NULL;
337 BYTE *oldbuf = m_Buffer;
338 int r;
339
340 if (!nofilecompression && !m_NoCompress)
341 {
342 outlen = OUT_LEN(len);
343 do
344 {
345 compressed = new Bytef[outlen];
346 r = compress (compressed, &outlen, m_Buffer, len);
347 if (r == Z_BUF_ERROR)
348 {
349 delete[] compressed;
350 outlen += 1024;
351 }
352 } while (r == Z_BUF_ERROR);
353
354 // If the data could not be compressed, store it as-is.
355 if (r != Z_OK || outlen >= len)
356 {
357 DPrintf ("cfile could not be compressed\n");
358 outlen = 0;
359 }
360 else
361 {
362 DPrintf ("cfile shrank from %lu to %lu bytes\n", len, outlen);
363 }
364 }
365 else
366 {
367 outlen = 0;
368 }
369
370 m_MaxBufferSize = m_BufferSize = ((outlen == 0) ? len : outlen);
371 m_Buffer = (BYTE *)M_Malloc (m_BufferSize + 8);
372 m_Pos = 0;
373
374 DWORD *lens = (DWORD *)(m_Buffer);
375 lens[0] = BigLong((unsigned int)outlen);
376 lens[1] = BigLong((unsigned int)len);
377
378 if (outlen == 0)
379 memcpy (m_Buffer + 8, oldbuf, len);
380 else
381 memcpy (m_Buffer + 8, compressed, outlen);
382 if (compressed)
383 delete[] compressed;
384 M_Free (oldbuf);
385 }
386
Explode()387 void FCompressedFile::Explode ()
388 {
389 uLong expandsize, cprlen;
390 unsigned char *expand;
391
392 if (m_Buffer)
393 {
394 unsigned int *ints = (unsigned int *)(m_Buffer);
395 cprlen = BigLong(ints[0]);
396 expandsize = BigLong(ints[1]);
397
398 expand = (unsigned char *)M_Malloc (expandsize);
399 if (cprlen)
400 {
401 int r;
402 uLong newlen;
403
404 newlen = expandsize;
405 r = uncompress (expand, &newlen, m_Buffer + 8, cprlen);
406 if (r != Z_OK || newlen != expandsize)
407 {
408 M_Free (expand);
409 I_Error ("Could not decompress buffer: %s", M_ZLibError(r).GetChars());
410 }
411 }
412 else
413 {
414 memcpy (expand, m_Buffer + 8, expandsize);
415 }
416 if (FreeOnExplode ())
417 M_Free (m_Buffer);
418 m_Buffer = expand;
419 m_BufferSize = expandsize;
420 }
421 }
422
FCompressedMemFile()423 FCompressedMemFile::FCompressedMemFile ()
424 {
425 m_SourceFromMem = false;
426 m_ImplodedBuffer = NULL;
427 }
428
429 /*
430 FCompressedMemFile::FCompressedMemFile (const char *name, EOpenMode mode)
431 : FCompressedFile (name, mode)
432 {
433 m_SourceFromMem = false;
434 m_ImplodedBuffer = NULL;
435 }
436 */
437
~FCompressedMemFile()438 FCompressedMemFile::~FCompressedMemFile ()
439 {
440 if (m_ImplodedBuffer != NULL)
441 {
442 M_Free (m_ImplodedBuffer);
443 }
444 }
445
Open(const char * name,EOpenMode mode)446 bool FCompressedMemFile::Open (const char *name, EOpenMode mode)
447 {
448 if (mode == EWriting)
449 {
450 if (name)
451 {
452 I_Error ("FCompressedMemFile cannot write to disk");
453 }
454 else
455 {
456 return Open ();
457 }
458 }
459 else
460 {
461 bool res = FCompressedFile::Open (name, EReading);
462 if (res)
463 {
464 fclose (m_File);
465 m_File = NULL;
466 }
467 return res;
468 }
469 return false;
470 }
471
Open(void * memblock)472 bool FCompressedMemFile::Open (void *memblock)
473 {
474 Close ();
475 m_Mode = EReading;
476 m_Buffer = (BYTE *)memblock;
477 m_SourceFromMem = true;
478 Explode ();
479 m_SourceFromMem = false;
480 return !!m_Buffer;
481 }
482
Open()483 bool FCompressedMemFile::Open ()
484 {
485 Close ();
486 m_Mode = EWriting;
487 m_BufferSize = 0;
488 m_MaxBufferSize = 16384;
489 m_Buffer = (unsigned char *)M_Malloc (16384);
490 m_Pos = 0;
491 return true;
492 }
493
Reopen()494 bool FCompressedMemFile::Reopen ()
495 {
496 if (m_Buffer == NULL && m_ImplodedBuffer)
497 {
498 m_Mode = EReading;
499 m_Buffer = m_ImplodedBuffer;
500 m_SourceFromMem = true;
501 try
502 {
503 Explode ();
504 }
505 catch(...)
506 {
507 // If we just leave things as they are, m_Buffer and m_ImplodedBuffer
508 // both point to the same memory block and both will try to free it.
509 m_Buffer = NULL;
510 m_SourceFromMem = false;
511 throw;
512 }
513 m_SourceFromMem = false;
514 return true;
515 }
516 return false;
517 }
518
Close()519 void FCompressedMemFile::Close ()
520 {
521 if (m_Mode == EWriting)
522 {
523 Implode ();
524 m_ImplodedBuffer = m_Buffer;
525 m_Buffer = NULL;
526 }
527 }
528
Serialize(FArchive & arc)529 void FCompressedMemFile::Serialize (FArchive &arc)
530 {
531 if (arc.IsStoring ())
532 {
533 if (m_ImplodedBuffer == NULL)
534 {
535 I_Error ("FCompressedMemFile must be compressed before storing");
536 }
537 arc.Write (ZSig, 4);
538
539 DWORD sizes[2];
540 sizes[0] = SWAP_DWORD (((DWORD *)m_ImplodedBuffer)[0]);
541 sizes[1] = SWAP_DWORD (((DWORD *)m_ImplodedBuffer)[1]);
542 arc.Write (m_ImplodedBuffer, (sizes[0] ? sizes[0] : sizes[1])+8);
543 }
544 else
545 {
546 Close ();
547 m_Mode = EReading;
548
549 char sig[4];
550 DWORD sizes[2] = { 0, 0 };
551
552 arc.Read (sig, 4);
553
554 if (sig[0] != ZSig[0] || sig[1] != ZSig[1] || sig[2] != ZSig[2] || sig[3] != ZSig[3])
555 I_Error ("Expected to extract a compressed file");
556
557 arc << sizes[0] << sizes[1];
558 DWORD len = sizes[0] == 0 ? sizes[1] : sizes[0];
559
560 m_Buffer = (BYTE *)M_Malloc (len+8);
561 ((DWORD *)m_Buffer)[0] = SWAP_DWORD(sizes[0]);
562 ((DWORD *)m_Buffer)[1] = SWAP_DWORD(sizes[1]);
563 arc.Read (m_Buffer+8, len);
564 m_ImplodedBuffer = m_Buffer;
565 m_Buffer = NULL;
566 m_Mode = EWriting;
567 }
568 }
569
IsOpen() const570 bool FCompressedMemFile::IsOpen () const
571 {
572 return !!m_Buffer;
573 }
574
GetSizes(unsigned int & compressed,unsigned int & uncompressed) const575 void FCompressedMemFile::GetSizes(unsigned int &compressed, unsigned int &uncompressed) const
576 {
577 if (m_ImplodedBuffer != NULL)
578 {
579 compressed = BigLong(*(unsigned int *)m_ImplodedBuffer);
580 uncompressed = BigLong(*(unsigned int *)(m_ImplodedBuffer + 4));
581 }
582 else
583 {
584 compressed = 0;
585 uncompressed = m_BufferSize;
586 }
587 }
588
FPNGChunkFile(FILE * file,DWORD id)589 FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id)
590 : FCompressedFile (file, EWriting, true, false), m_ChunkID (id)
591 {
592 }
593
FPNGChunkFile(FILE * file,DWORD id,size_t chunklen)594 FPNGChunkFile::FPNGChunkFile (FILE *file, DWORD id, size_t chunklen)
595 : FCompressedFile (file, EReading, true, false), m_ChunkID (id)
596 {
597 m_Buffer = (BYTE *)M_Malloc (chunklen);
598 m_BufferSize = (unsigned int)chunklen;
599 fread (m_Buffer, chunklen, 1, m_File);
600 // Skip the CRC for now. Maybe later it will be used.
601 fseek (m_File, 4, SEEK_CUR);
602 }
603
604 // Unlike FCompressedFile::Close, m_File is left open
Close()605 void FPNGChunkFile::Close ()
606 {
607 DWORD data[2];
608 DWORD crc;
609
610 if (m_File)
611 {
612 if (m_Mode == EWriting)
613 {
614 crc = CalcCRC32 ((BYTE *)&m_ChunkID, 4);
615 crc = AddCRC32 (crc, (BYTE *)m_Buffer, m_BufferSize);
616
617 data[0] = BigLong(m_BufferSize);
618 data[1] = m_ChunkID;
619 fwrite (data, 8, 1, m_File);
620 fwrite (m_Buffer, m_BufferSize, 1, m_File);
621 crc = SWAP_DWORD (crc);
622 fwrite (&crc, 4, 1, m_File);
623 }
624 m_File = NULL;
625 }
626 FCompressedFile::Close ();
627 }
628
FPNGChunkArchive(FILE * file,DWORD id)629 FPNGChunkArchive::FPNGChunkArchive (FILE *file, DWORD id)
630 : FArchive (), Chunk (file, id)
631 {
632 AttachToFile (Chunk);
633 }
634
FPNGChunkArchive(FILE * file,DWORD id,size_t len)635 FPNGChunkArchive::FPNGChunkArchive (FILE *file, DWORD id, size_t len)
636 : FArchive (), Chunk (file, id, len)
637 {
638 AttachToFile (Chunk);
639 }
640
~FPNGChunkArchive()641 FPNGChunkArchive::~FPNGChunkArchive ()
642 {
643 // Close before FArchive's destructor, because Chunk will be
644 // destroyed before the FArchive is destroyed.
645 Close ();
646 }
647
648 //============================================
649 //
650 // FArchive
651 //
652 //============================================
653
FArchive()654 FArchive::FArchive ()
655 {
656 }
657
FArchive(FFile & file)658 FArchive::FArchive (FFile &file)
659 {
660 AttachToFile (file);
661 }
662
AttachToFile(FFile & file)663 void FArchive::AttachToFile (FFile &file)
664 {
665 unsigned int i;
666
667 m_HubTravel = false;
668 m_File = &file;
669 m_MaxObjectCount = m_ObjectCount = 0;
670 m_ObjectMap = NULL;
671 if (file.Mode() == FFile::EReading)
672 {
673 m_Loading = true;
674 m_Storing = false;
675 }
676 else
677 {
678 m_Loading = false;
679 m_Storing = true;
680 }
681 m_Persistent = file.IsPersistent();
682 m_TypeMap = NULL;
683 m_TypeMap = new TypeMap[PClass::m_Types.Size()];
684 for (i = 0; i < PClass::m_Types.Size(); i++)
685 {
686 m_TypeMap[i].toArchive = TypeMap::NO_INDEX;
687 m_TypeMap[i].toCurrent = NULL;
688 }
689 m_ClassCount = 0;
690 for (i = 0; i < EObjectHashSize; i++)
691 {
692 m_ObjectHash[i] = ~0;
693 m_NameHash[i] = NameMap::NO_INDEX;
694 }
695 m_NumSprites = 0;
696 m_SpriteMap = new int[sprites.Size()];
697 for (size_t s = 0; s < sprites.Size(); ++s)
698 {
699 m_SpriteMap[s] = -1;
700 }
701 }
702
~FArchive()703 FArchive::~FArchive ()
704 {
705 Close ();
706 if (m_TypeMap)
707 delete[] m_TypeMap;
708 if (m_ObjectMap)
709 M_Free (m_ObjectMap);
710 if (m_SpriteMap)
711 delete[] m_SpriteMap;
712 }
713
Write(const void * mem,unsigned int len)714 void FArchive::Write (const void *mem, unsigned int len)
715 {
716 m_File->Write (mem, len);
717 }
718
Read(void * mem,unsigned int len)719 void FArchive::Read (void *mem, unsigned int len)
720 {
721 m_File->Read (mem, len);
722 }
723
Close()724 void FArchive::Close ()
725 {
726 if (m_File)
727 {
728 m_File->Close ();
729 m_File = NULL;
730 DPrintf ("Processed %u objects\n", m_ObjectCount);
731 }
732 }
733
WriteCount(DWORD count)734 void FArchive::WriteCount (DWORD count)
735 {
736 BYTE out;
737
738 do
739 {
740 out = count & 0x7f;
741 if (count >= 0x80)
742 out |= 0x80;
743 Write (&out, sizeof(BYTE));
744 count >>= 7;
745 } while (count);
746
747 }
748
ReadCount()749 DWORD FArchive::ReadCount ()
750 {
751 BYTE in;
752 DWORD count = 0;
753 int ofs = 0;
754
755 do
756 {
757 Read (&in, sizeof(BYTE));
758 count |= (in & 0x7f) << ofs;
759 ofs += 7;
760 } while (in & 0x80);
761
762 return count;
763 }
764
WriteName(const char * name)765 void FArchive::WriteName (const char *name)
766 {
767 BYTE id;
768
769 if (name == NULL)
770 {
771 id = NIL_NAME;
772 Write (&id, 1);
773 }
774 else
775 {
776 DWORD index = FindName (name);
777 if (index != NameMap::NO_INDEX)
778 {
779 id = OLD_NAME;
780 Write (&id, 1);
781 WriteCount (index);
782 }
783 else
784 {
785 AddName (name);
786 id = NEW_NAME;
787 Write (&id, 1);
788 WriteString (name);
789 }
790 }
791 }
792
ReadName()793 const char *FArchive::ReadName ()
794 {
795 BYTE id;
796
797 operator<< (id);
798 if (id == NIL_NAME)
799 {
800 return NULL;
801 }
802 else if (id == OLD_NAME)
803 {
804 DWORD index = ReadCount ();
805 if (index >= m_Names.Size())
806 {
807 I_Error ("Name %u has not been read yet\n", index);
808 }
809 return &m_NameStorage[m_Names[index].StringStart];
810 }
811 else if (id == NEW_NAME)
812 {
813 DWORD index;
814 DWORD size = ReadCount ();
815 char *str;
816
817 index = (DWORD)m_NameStorage.Reserve (size);
818 str = &m_NameStorage[index];
819 Read (str, size-1);
820 str[size-1] = 0;
821 AddName (index);
822 return str;
823 }
824 else
825 {
826 I_Error ("Expected a name but got something else\n");
827 return NULL;
828 }
829 }
830
WriteString(const char * str)831 void FArchive::WriteString (const char *str)
832 {
833 if (str == NULL)
834 {
835 WriteCount (0);
836 }
837 else
838 {
839 DWORD size = (DWORD)(strlen (str) + 1);
840 WriteCount (size);
841 Write (str, size - 1);
842 }
843 }
844
operator <<(char * & str)845 FArchive &FArchive::operator<< (char *&str)
846 {
847 if (m_Storing)
848 {
849 WriteString (str);
850 }
851 else
852 {
853 DWORD size = ReadCount ();
854 char *str2;
855
856 if (size == 0)
857 {
858 str2 = NULL;
859 }
860 else
861 {
862 str2 = new char[size];
863 size--;
864 Read (str2, size);
865 str2[size] = 0;
866 ReplaceString ((char **)&str, str2);
867 }
868 if (str)
869 {
870 delete[] str;
871 }
872 str = str2;
873 }
874 return *this;
875 }
876
operator <<(FString & str)877 FArchive &FArchive::operator<< (FString &str)
878 {
879 if (m_Storing)
880 {
881 WriteString (str.GetChars());
882 }
883 else
884 {
885 DWORD size = ReadCount();
886
887 if (size == 0)
888 {
889 str = "";
890 }
891 else
892 {
893 char *str2 = (char *)alloca(size*sizeof(char));
894 size--;
895 Read (str2, size);
896 str2[size] = 0;
897 str = str2;
898 }
899 }
900 return *this;
901 }
902
operator <<(BYTE & c)903 FArchive &FArchive::operator<< (BYTE &c)
904 {
905 if (m_Storing)
906 Write (&c, sizeof(BYTE));
907 else
908 Read (&c, sizeof(BYTE));
909 return *this;
910 }
911
operator <<(WORD & w)912 FArchive &FArchive::operator<< (WORD &w)
913 {
914 if (m_Storing)
915 {
916 WORD temp = SWAP_WORD(w);
917 Write (&temp, sizeof(WORD));
918 }
919 else
920 {
921 Read (&w, sizeof(WORD));
922 w = SWAP_WORD(w);
923 }
924 return *this;
925 }
926
operator <<(DWORD & w)927 FArchive &FArchive::operator<< (DWORD &w)
928 {
929 if (m_Storing)
930 {
931 DWORD temp = SWAP_DWORD(w);
932 Write (&temp, sizeof(DWORD));
933 }
934 else
935 {
936 Read (&w, sizeof(DWORD));
937 w = SWAP_DWORD(w);
938 }
939 return *this;
940 }
941
operator <<(QWORD & w)942 FArchive &FArchive::operator<< (QWORD &w)
943 {
944 if (m_Storing)
945 {
946 QWORD temp = SWAP_QWORD(w);
947 Write (&temp, sizeof(QWORD));
948 }
949 else
950 {
951 Read (&w, sizeof(QWORD));
952 w = SWAP_QWORD(w);
953 }
954 return *this;
955 }
956
operator <<(float & w)957 FArchive &FArchive::operator<< (float &w)
958 {
959 if (m_Storing)
960 {
961 float temp = w;
962 SWAP_FLOAT(temp);
963 Write (&temp, sizeof(float));
964 }
965 else
966 {
967 Read (&w, sizeof(float));
968 SWAP_FLOAT(w);
969 }
970 return *this;
971 }
972
operator <<(double & w)973 FArchive &FArchive::operator<< (double &w)
974 {
975 if (m_Storing)
976 {
977 double temp;
978 SWAP_DOUBLE(temp,w);
979 Write (&temp, sizeof(double));
980 }
981 else
982 {
983 Read (&w, sizeof(double));
984 SWAP_DOUBLE(w,w);
985 }
986 return *this;
987 }
988
operator <<(FName & n)989 FArchive &FArchive::operator<< (FName &n)
990 { // In an archive, a "name" is a string that might be stored multiple times,
991 // so it is only stored once. It is still treated as a normal string. In the
992 // rest of the game, a name is a unique identifier for a number.
993 if (m_Storing)
994 {
995 WriteName (n.GetChars());
996 }
997 else
998 {
999 n = FName(ReadName());
1000 }
1001 return *this;
1002 }
1003
SerializePointer(void * ptrbase,BYTE ** ptr,DWORD elemSize)1004 FArchive &FArchive::SerializePointer (void *ptrbase, BYTE **ptr, DWORD elemSize)
1005 {
1006 DWORD w;
1007
1008 if (m_Storing)
1009 {
1010 if (*(void **)ptr)
1011 {
1012 w = DWORD(((size_t)*ptr - (size_t)ptrbase) / elemSize);
1013 }
1014 else
1015 {
1016 w = ~0u;
1017 }
1018 WriteCount (w);
1019 }
1020 else
1021 {
1022 w = ReadCount ();
1023 if (w != ~0u)
1024 {
1025 *(void **)ptr = (BYTE *)ptrbase + w * elemSize;
1026 }
1027 else
1028 {
1029 *(void **)ptr = NULL;
1030 }
1031 }
1032 return *this;
1033 }
1034
SerializeObject(DObject * & object,PClass * type)1035 FArchive &FArchive::SerializeObject (DObject *&object, PClass *type)
1036 {
1037 if (IsStoring ())
1038 {
1039 return WriteObject (object);
1040 }
1041 else
1042 {
1043 return ReadObject (object, type);
1044 }
1045 }
1046
WriteObject(DObject * obj)1047 FArchive &FArchive::WriteObject (DObject *obj)
1048 {
1049 player_t *player;
1050 BYTE id[2];
1051
1052 if (obj == NULL)
1053 {
1054 id[0] = NULL_OBJ;
1055 Write (id, 1);
1056 }
1057 else if (obj == (DObject*)~0)
1058 {
1059 id[0] = M1_OBJ;
1060 Write (id, 1);
1061 }
1062 else if (obj->ObjectFlags & OF_EuthanizeMe)
1063 {
1064 // Objects that want to die are not saved to the archive, but
1065 // we leave the pointers to them alone.
1066 id[0] = NULL_OBJ;
1067 Write (id, 1);
1068 }
1069 else
1070 {
1071 const PClass *type = RUNTIME_TYPE(obj);
1072
1073 if (type == RUNTIME_CLASS(DObject))
1074 {
1075 //I_Error ("Tried to save an instance of DObject.\n"
1076 // "This should not happen.\n");
1077 id[0] = NULL_OBJ;
1078 Write (id, 1);
1079 }
1080 else if (m_TypeMap[type->ClassIndex].toArchive == TypeMap::NO_INDEX)
1081 {
1082 // No instances of this class have been written out yet.
1083 // Write out the class, then write out the object. If this
1084 // is an actor controlled by a player, make note of that
1085 // so that it can be overridden when moving around in a hub.
1086 if (obj->IsKindOf (RUNTIME_CLASS (AActor)) &&
1087 (player = static_cast<AActor *>(obj)->player) &&
1088 player->mo == obj)
1089 {
1090 id[0] = NEW_PLYR_CLS_OBJ;
1091 id[1] = (BYTE)(player - players);
1092 Write (id, 2);
1093 }
1094 else
1095 {
1096 id[0] = NEW_CLS_OBJ;
1097 Write (id, 1);
1098 }
1099 WriteClass (type);
1100 // Printf ("Make class %s (%u)\n", type->Name, m_File->Tell());
1101 MapObject (obj);
1102 obj->SerializeUserVars (*this);
1103 obj->Serialize (*this);
1104 obj->CheckIfSerialized ();
1105 }
1106 else
1107 {
1108 // An instance of this class has already been saved. If
1109 // this object has already been written, save a reference
1110 // to the saved object. Otherwise, save a reference to the
1111 // class, then save the object. Again, if this is a player-
1112 // controlled actor, remember that.
1113 DWORD index = FindObjectIndex (obj);
1114
1115 if (index == TypeMap::NO_INDEX)
1116 {
1117
1118 if (obj->IsKindOf (RUNTIME_CLASS (AActor)) &&
1119 (player = static_cast<AActor *>(obj)->player) &&
1120 player->mo == obj)
1121 {
1122 id[0] = NEW_PLYR_OBJ;
1123 id[1] = (BYTE)(player - players);
1124 Write (id, 2);
1125 }
1126 else
1127 {
1128 id[0] = NEW_OBJ;
1129 Write (id, 1);
1130 }
1131 WriteCount (m_TypeMap[type->ClassIndex].toArchive);
1132 // Printf ("Reuse class %s (%u)\n", type->Name, m_File->Tell());
1133 MapObject (obj);
1134 obj->SerializeUserVars (*this);
1135 obj->Serialize (*this);
1136 obj->CheckIfSerialized ();
1137 }
1138 else
1139 {
1140 id[0] = OLD_OBJ;
1141 Write (id, 1);
1142 WriteCount (index);
1143 }
1144 }
1145 }
1146 return *this;
1147 }
1148
ReadObject(DObject * & obj,PClass * wanttype)1149 FArchive &FArchive::ReadObject (DObject* &obj, PClass *wanttype)
1150 {
1151 BYTE objHead;
1152 const PClass *type;
1153 BYTE playerNum;
1154 DWORD index;
1155
1156 operator<< (objHead);
1157
1158 switch (objHead)
1159 {
1160 case NULL_OBJ:
1161 obj = NULL;
1162 break;
1163
1164 case M1_OBJ:
1165 obj = (DObject *)~0;
1166 break;
1167
1168 case OLD_OBJ:
1169 index = ReadCount ();
1170 if (index >= m_ObjectCount)
1171 {
1172 I_Error ("Object reference too high (%u; max is %u)\n", index, m_ObjectCount);
1173 }
1174 obj = (DObject *)m_ObjectMap[index].object;
1175 break;
1176
1177 case NEW_PLYR_CLS_OBJ:
1178 operator<< (playerNum);
1179 if (m_HubTravel)
1180 {
1181 // If travelling inside a hub, use the existing player actor
1182 type = ReadClass (wanttype);
1183 // Printf ("New player class: %s (%u)\n", type->Name, m_File->Tell());
1184 obj = players[playerNum].mo;
1185
1186 // But also create a new one so that we can get past the one
1187 // stored in the archive.
1188 AActor *tempobj = static_cast<AActor *>(type->CreateNew ());
1189 MapObject (obj != NULL ? obj : tempobj);
1190 tempobj->SerializeUserVars (*this);
1191 tempobj->Serialize (*this);
1192 tempobj->CheckIfSerialized ();
1193 // If this player is not present anymore, keep the new body
1194 // around just so that the load will succeed.
1195 if (obj != NULL)
1196 {
1197 // When the temporary player's inventory items were loaded,
1198 // they became owned by the real player. Undo that now.
1199 for (AInventory *item = tempobj->Inventory; item != NULL; item = item->Inventory)
1200 {
1201 item->Owner = tempobj;
1202 }
1203 tempobj->Destroy ();
1204 }
1205 else
1206 {
1207 obj = tempobj;
1208 players[playerNum].mo = static_cast<APlayerPawn *>(obj);
1209 }
1210 break;
1211 }
1212 /* fallthrough when not travelling to a previous level */
1213 case NEW_CLS_OBJ:
1214 type = ReadClass (wanttype);
1215 // Printf ("New class: %s (%u)\n", type->Name, m_File->Tell());
1216 obj = type->CreateNew ();
1217 MapObject (obj);
1218 obj->SerializeUserVars (*this);
1219 obj->Serialize (*this);
1220 obj->CheckIfSerialized ();
1221 break;
1222
1223 case NEW_PLYR_OBJ:
1224 operator<< (playerNum);
1225 if (m_HubTravel)
1226 {
1227 type = ReadStoredClass (wanttype);
1228 // Printf ("Use player class: %s (%u)\n", type->Name, m_File->Tell());
1229 obj = players[playerNum].mo;
1230
1231 AActor *tempobj = static_cast<AActor *>(type->CreateNew ());
1232 MapObject (obj != NULL ? obj : tempobj);
1233 tempobj->SerializeUserVars (*this);
1234 tempobj->Serialize (*this);
1235 tempobj->CheckIfSerialized ();
1236 if (obj != NULL)
1237 {
1238 for (AInventory *item = tempobj->Inventory;
1239 item != NULL; item = item->Inventory)
1240 {
1241 item->Owner = tempobj;
1242 }
1243 tempobj->Destroy ();
1244 }
1245 else
1246 {
1247 obj = tempobj;
1248 players[playerNum].mo = static_cast<APlayerPawn *>(obj);
1249 }
1250 break;
1251 }
1252 /* fallthrough when not travelling to a previous level */
1253 case NEW_OBJ:
1254 type = ReadStoredClass (wanttype);
1255 // Printf ("Use class: %s (%u)\n", type->Name, m_File->Tell());
1256 obj = type->CreateNew ();
1257 MapObject (obj);
1258 obj->SerializeUserVars (*this);
1259 obj->Serialize (*this);
1260 obj->CheckIfSerialized ();
1261 break;
1262
1263 default:
1264 I_Error ("Unknown object code (%d) in archive\n", objHead);
1265 }
1266 return *this;
1267 }
1268
WriteSprite(int spritenum)1269 void FArchive::WriteSprite (int spritenum)
1270 {
1271 BYTE id;
1272
1273 if ((unsigned)spritenum >= (unsigned)sprites.Size())
1274 {
1275 spritenum = 0;
1276 }
1277
1278 if (m_SpriteMap[spritenum] < 0)
1279 {
1280 m_SpriteMap[spritenum] = (int)(m_NumSprites++);
1281 id = NEW_SPRITE;
1282 Write (&id, 1);
1283 Write (sprites[spritenum].name, 4);
1284
1285 // Write the current sprite number as a hint, because
1286 // these will only change between different versions.
1287 WriteCount (spritenum);
1288 }
1289 else
1290 {
1291 id = OLD_SPRITE;
1292 Write (&id, 1);
1293 WriteCount (m_SpriteMap[spritenum]);
1294 }
1295 }
1296
ReadSprite()1297 int FArchive::ReadSprite ()
1298 {
1299 BYTE id;
1300
1301 Read (&id, 1);
1302 if (id == OLD_SPRITE)
1303 {
1304 DWORD index = ReadCount ();
1305 if (index >= m_NumSprites)
1306 {
1307 I_Error ("Sprite %u has not been read yet\n", index);
1308 }
1309 return m_SpriteMap[index];
1310 }
1311 else if (id == NEW_SPRITE)
1312 {
1313 DWORD name;
1314 DWORD hint;
1315
1316 Read (&name, 4);
1317 hint = ReadCount ();
1318
1319 if (hint >= NumStdSprites || sprites[hint].dwName != name)
1320 {
1321 for (hint = NumStdSprites; hint-- != 0; )
1322 {
1323 if (sprites[hint].dwName == name)
1324 {
1325 break;
1326 }
1327 }
1328 if (hint >= sprites.Size())
1329 { // Don't know this sprite, so just use the first one
1330 hint = 0;
1331 }
1332 }
1333 m_SpriteMap[m_NumSprites++] = hint;
1334 return hint;
1335 }
1336 else
1337 {
1338 I_Error ("Expected a sprite but got something else\n");
1339 return 0;
1340 }
1341 }
1342
AddName(const char * name)1343 DWORD FArchive::AddName (const char *name)
1344 {
1345 DWORD index;
1346 unsigned int hash = MakeKey (name) % EObjectHashSize;
1347
1348 index = FindName (name, hash);
1349 if (index == NameMap::NO_INDEX)
1350 {
1351 DWORD namelen = (DWORD)(strlen (name) + 1);
1352 DWORD strpos = (DWORD)m_NameStorage.Reserve (namelen);
1353 NameMap mapper = { strpos, (DWORD)m_NameHash[hash] };
1354
1355 memcpy (&m_NameStorage[strpos], name, namelen);
1356 m_NameHash[hash] = index = (DWORD)m_Names.Push (mapper);
1357 }
1358 return index;
1359 }
1360
AddName(unsigned int start)1361 DWORD FArchive::AddName (unsigned int start)
1362 {
1363 DWORD hash = MakeKey (&m_NameStorage[start]) % EObjectHashSize;
1364 NameMap mapper = { (DWORD)start, (DWORD)m_NameHash[hash] };
1365 return (DWORD)(m_NameHash[hash] = m_Names.Push (mapper));
1366 }
1367
FindName(const char * name) const1368 DWORD FArchive::FindName (const char *name) const
1369 {
1370 return FindName (name, MakeKey (name) % EObjectHashSize);
1371 }
1372
FindName(const char * name,unsigned int bucket) const1373 DWORD FArchive::FindName (const char *name, unsigned int bucket) const
1374 {
1375 unsigned int map = m_NameHash[bucket];
1376
1377 while (map != NameMap::NO_INDEX)
1378 {
1379 const NameMap *mapping = &m_Names[map];
1380 if (strcmp (name, &m_NameStorage[mapping->StringStart]) == 0)
1381 {
1382 return (DWORD)map;
1383 }
1384 map = mapping->HashNext;
1385 }
1386 return (DWORD)map;
1387 }
1388
WriteClass(const PClass * info)1389 DWORD FArchive::WriteClass (const PClass *info)
1390 {
1391 if (m_ClassCount >= PClass::m_Types.Size())
1392 {
1393 I_Error ("Too many unique classes have been written.\nOnly %u were registered\n",
1394 PClass::m_Types.Size());
1395 }
1396 if (m_TypeMap[info->ClassIndex].toArchive != TypeMap::NO_INDEX)
1397 {
1398 I_Error ("Attempt to write '%s' twice.\n", info->TypeName.GetChars());
1399 }
1400 m_TypeMap[info->ClassIndex].toArchive = m_ClassCount;
1401 m_TypeMap[m_ClassCount].toCurrent = info;
1402 WriteString (info->TypeName.GetChars());
1403 return m_ClassCount++;
1404 }
1405
ReadClass()1406 const PClass *FArchive::ReadClass ()
1407 {
1408 struct String {
1409 String() { val = NULL; }
1410 ~String() { if (val) delete[] val; }
1411 char *val;
1412 } typeName;
1413
1414 if (m_ClassCount >= PClass::m_Types.Size())
1415 {
1416 I_Error ("Too many unique classes have been read.\nOnly %u were registered\n",
1417 PClass::m_Types.Size());
1418 }
1419 operator<< (typeName.val);
1420 FName zaname(typeName.val, true);
1421 if (zaname != NAME_None)
1422 {
1423 for (unsigned int i = PClass::m_Types.Size(); i-- > 0; )
1424 {
1425 if (PClass::m_Types[i]->TypeName == zaname)
1426 {
1427 m_TypeMap[i].toArchive = m_ClassCount;
1428 m_TypeMap[m_ClassCount].toCurrent = PClass::m_Types[i];
1429 m_ClassCount++;
1430 return PClass::m_Types[i];
1431 }
1432 }
1433 }
1434 I_Error ("Unknown class '%s'\n", typeName.val);
1435 return NULL;
1436 }
1437
ReadClass(const PClass * wanttype)1438 const PClass *FArchive::ReadClass (const PClass *wanttype)
1439 {
1440 const PClass *type = ReadClass ();
1441 if (!type->IsDescendantOf (wanttype))
1442 {
1443 I_Error ("Expected to extract an object of type '%s'.\n"
1444 "Found one of type '%s' instead.\n",
1445 wanttype->TypeName.GetChars(), type->TypeName.GetChars());
1446 }
1447 return type;
1448 }
1449
ReadStoredClass(const PClass * wanttype)1450 const PClass *FArchive::ReadStoredClass (const PClass *wanttype)
1451 {
1452 DWORD index = ReadCount ();
1453 if (index >= m_ClassCount)
1454 {
1455 I_Error ("Class reference too high (%u; max is %u)\n", index, m_ClassCount);
1456 }
1457 const PClass *type = m_TypeMap[index].toCurrent;
1458 if (!type->IsDescendantOf (wanttype))
1459 {
1460 I_Error ("Expected to extract an object of type '%s'.\n"
1461 "Found one of type '%s' instead.\n",
1462 wanttype->TypeName.GetChars(), type->TypeName.GetChars());
1463 }
1464 return type;
1465 }
1466
MapObject(const DObject * obj)1467 DWORD FArchive::MapObject (const DObject *obj)
1468 {
1469 DWORD i;
1470
1471 if (m_ObjectCount >= m_MaxObjectCount)
1472 {
1473 m_MaxObjectCount = m_MaxObjectCount ? m_MaxObjectCount * 2 : 1024;
1474 m_ObjectMap = (ObjectMap *)M_Realloc (m_ObjectMap, sizeof(ObjectMap)*m_MaxObjectCount);
1475 for (i = m_ObjectCount; i < m_MaxObjectCount; i++)
1476 {
1477 m_ObjectMap[i].hashNext = ~0;
1478 m_ObjectMap[i].object = NULL;
1479 }
1480 }
1481
1482 DWORD index = m_ObjectCount++;
1483 DWORD hash = HashObject (obj);
1484
1485 m_ObjectMap[index].object = obj;
1486 m_ObjectMap[index].hashNext = m_ObjectHash[hash];
1487 m_ObjectHash[hash] = index;
1488
1489 return index;
1490 }
1491
HashObject(const DObject * obj) const1492 DWORD FArchive::HashObject (const DObject *obj) const
1493 {
1494 return (DWORD)((size_t)obj % EObjectHashSize);
1495 }
1496
FindObjectIndex(const DObject * obj) const1497 DWORD FArchive::FindObjectIndex (const DObject *obj) const
1498 {
1499 DWORD index = m_ObjectHash[HashObject (obj)];
1500 while (index != TypeMap::NO_INDEX && m_ObjectMap[index].object != obj)
1501 {
1502 index = m_ObjectMap[index].hashNext;
1503 }
1504 return index;
1505 }
1506
UserWriteClass(const PClass * type)1507 void FArchive::UserWriteClass (const PClass *type)
1508 {
1509 BYTE id;
1510
1511 if (type == NULL)
1512 {
1513 id = 2;
1514 Write (&id, 1);
1515 }
1516 else
1517 {
1518 if (m_TypeMap[type->ClassIndex].toArchive == TypeMap::NO_INDEX)
1519 {
1520 id = 1;
1521 Write (&id, 1);
1522 WriteClass (type);
1523 }
1524 else
1525 {
1526 id = 0;
1527 Write (&id, 1);
1528 WriteCount (m_TypeMap[type->ClassIndex].toArchive);
1529 }
1530 }
1531 }
1532
UserReadClass(const PClass * & type)1533 void FArchive::UserReadClass (const PClass *&type)
1534 {
1535 BYTE newclass;
1536
1537 Read (&newclass, 1);
1538 switch (newclass)
1539 {
1540 case 0:
1541 type = ReadStoredClass (RUNTIME_CLASS(DObject));
1542 break;
1543 case 1:
1544 type = ReadClass ();
1545 break;
1546 case 2:
1547 type = NULL;
1548 break;
1549 default:
1550 I_Error ("Unknown class type %d in archive.\n", newclass);
1551 break;
1552 }
1553 }
1554
operator <<(FArchive & arc,const PClass * & info)1555 FArchive &operator<< (FArchive &arc, const PClass * &info)
1556 {
1557 if (arc.IsStoring ())
1558 {
1559 arc.UserWriteClass (info);
1560 }
1561 else
1562 {
1563 arc.UserReadClass (info);
1564 }
1565 return arc;
1566 }
1567
operator <<(FArchive & arc,sector_t * & sec)1568 FArchive &operator<< (FArchive &arc, sector_t *&sec)
1569 {
1570 return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors));
1571 }
1572
operator <<(FArchive & arc,const sector_t * & sec)1573 FArchive &operator<< (FArchive &arc, const sector_t *&sec)
1574 {
1575 return arc.SerializePointer (sectors, (BYTE **)&sec, sizeof(*sectors));
1576 }
1577
operator <<(FArchive & arc,line_t * & line)1578 FArchive &operator<< (FArchive &arc, line_t *&line)
1579 {
1580 return arc.SerializePointer (lines, (BYTE **)&line, sizeof(*lines));
1581 }
1582
operator <<(FArchive & arc,vertex_t * & vert)1583 FArchive &operator<< (FArchive &arc, vertex_t *&vert)
1584 {
1585 return arc.SerializePointer (vertexes, (BYTE **)&vert, sizeof(*vertexes));
1586 }
1587
operator <<(FArchive & arc,side_t * & side)1588 FArchive &operator<< (FArchive &arc, side_t *&side)
1589 {
1590 return arc.SerializePointer (sides, (BYTE **)&side, sizeof(*sides));
1591 }
1592