| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- /* $Header: /Commando/Code/wwlib/chunkio.h 23 3/14/02 1:25p Greg_h $ */
- /***********************************************************************************************
- *** Confidential - Westwood Studios ***
- ***********************************************************************************************
- * *
- * Project Name : Tiberian Sun / Commando / G Library *
- * *
- * $Archive:: /Commando/Code/wwlib/chunkio.h $*
- * *
- * $Author:: Greg_h $*
- * *
- * $Modtime:: 3/04/02 3:41p $*
- * *
- * $Revision:: 23 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #if _MSC_VER >= 1000
- #pragma once
- #endif // _MSC_VER >= 1000
- #ifndef CHUNKIO_H
- #define CHUNKIO_H
- #ifndef ALWAYS_H
- #include "always.h"
- #endif
- #ifndef BITTYPE_H
- #include "bittype.h"
- #endif
- #ifndef WWFILE_H
- #include "wwfile.h"
- #endif
- #ifndef IOSTRUCT_H
- #include "iostruct.h"
- #endif
- /************************************************************************************
- ChunkIO
- (gth) This module provides classes for reading and writing chunk-based files.
- For example, all of the w3d files are stored in a hierarchical-chunk format.
- Basically the format is similar to IFF. All data in the file has chunk headers
- wrapped around it. A chunk header contains an ID, and a Size. The size
- is the number of bytes in the chunk (not including the header). The
- contents of a chunk may be either: more "sub-chunks" or raw data. These classes
- will automatically keep track of your positions within all of the sub and parent
- chunks (to some maximum recursion depth).
- Sept 3, 1999
- (gth) Adding the new concept of "micro-chunks". Instead of filling the contents of a
- chunk with data, you can fill it with "micro-chunks" which contain a single byte
- id and a single byte size. Micro-chunks are used for storing simple variables
- in a form that can survive revisions to the file format without paying the price
- for a full chunk header. You CANNOT recursively embed micro-chunks due to their
- size limitations....
- Sept 24, 1999
- (gth) Using the MSB of the chunksize to indicate whether a chunk contains other
- chunks or pure data. If the MSB is 0, the chunk contains data (so that the reader
- I'm going to write doesn't break on older files) and if it is 1 then it is
- assumed to contain other chunks. This does not apply to micro-chunks as they
- are considered data.
- **************************************************************************************/
- struct ChunkHeader
- {
- // Functions.
- ChunkHeader() : ChunkType(0), ChunkSize(0) {}
- ChunkHeader(uint32 type, uint32 size) {ChunkType = type; ChunkSize = size;}
- // Use these accessors to ensure you correctly deal with the data in the chunk header
- void Set_Type(uint32 type) { ChunkType = type; }
- uint32 Get_Type(void) { return ChunkType; }
- void Set_Size(uint32 size) { ChunkSize &= 0x80000000; ChunkSize |= (size & 0x7FFFFFFF); }
- void Add_Size(uint32 add) { Set_Size(Get_Size() + add); }
- uint32 Get_Size(void) { return (ChunkSize & 0x7FFFFFFF); }
- void Set_Sub_Chunk_Flag(bool onoff) { if (onoff) { ChunkSize |= 0x80000000; } else { ChunkSize &= 0x7FFFFFFF; } }
- int Get_Sub_Chunk_Flag(void) { return (ChunkSize & 0x80000000); }
- // Chunk type and size.
- // Note: MSB of ChunkSize is used to indicate whether this chunk
- // contains other chunks or data.
- uint32 ChunkType;
- uint32 ChunkSize;
- };
- struct MicroChunkHeader
- {
- MicroChunkHeader() {}
- MicroChunkHeader(uint8 type, uint8 size) { ChunkType = type, ChunkSize = size; }
- void Set_Type(uint8 type) { ChunkType = type; }
- uint8 Get_Type(void) { return ChunkType; }
- void Set_Size(uint8 size) { ChunkSize = size; }
- void Add_Size(uint8 add) { Set_Size(Get_Size() + add); }
- uint8 Get_Size(void) { return ChunkSize; }
- uint8 ChunkType;
- uint8 ChunkSize;
- };
- /**************************************************************************************
- **
- ** ChunkSaveClass
- ** Wrap an instance of this class around an opened file for easy chunk
- ** creation.
- **
- **************************************************************************************/
- class ChunkSaveClass
- {
- public:
- ChunkSaveClass(FileClass * file);
- // Chunk methods
- bool Begin_Chunk(uint32 id);
- bool End_Chunk();
- int Cur_Chunk_Depth();
- // Micro chunk methods
- bool Begin_Micro_Chunk(uint32 id);
- bool End_Micro_Chunk();
- // Write data into the file
- uint32 Write(const void *buf, uint32 nbytes);
- uint32 Write(const IOVector2Struct & v);
- uint32 Write(const IOVector3Struct & v);
- uint32 Write(const IOVector4Struct & v);
- uint32 Write(const IOQuaternionStruct & q);
- private:
- enum { MAX_STACK_DEPTH = 256 };
- FileClass * File;
- // Chunk building support
- int StackIndex;
- int PositionStack[MAX_STACK_DEPTH];
- ChunkHeader HeaderStack[MAX_STACK_DEPTH];
- // MicroChunk building support
- bool InMicroChunk;
- int MicroChunkPosition;
- MicroChunkHeader MCHeader;
- };
- /**************************************************************************************
- **
- ** ChunkLoadClass
- ** wrap an instance of one of these objects around an opened file
- ** to easily parse the chunks in the file
- **
- **************************************************************************************/
- class ChunkLoadClass
- {
- public:
- ChunkLoadClass(FileClass * file);
- // Chunk methods
- bool Open_Chunk();
- bool Close_Chunk();
- uint32 Cur_Chunk_ID();
- uint32 Cur_Chunk_Length();
- int Cur_Chunk_Depth();
- int Contains_Chunks();
- // Micro Chunk methods
- bool Open_Micro_Chunk();
- bool Close_Micro_Chunk();
- uint32 Cur_Micro_Chunk_ID();
- uint32 Cur_Micro_Chunk_Length();
- // Read a block of bytes from the output stream.
- uint32 Read(void *buf, uint32 nbytes);
- uint32 Read(IOVector2Struct * v);
- uint32 Read(IOVector3Struct * v);
- uint32 Read(IOVector4Struct * v);
- uint32 Read(IOQuaternionStruct * q);
- // Seek over a block of bytes in the stream (same as Read but don't copy the data to a buffer)
- uint32 Seek(uint32 nbytes);
- // Sneak peek at the next chunk that will be opened. Beware, if you need
- // this, then you are probably hacking so be careful!
- bool Peek_Next_Chunk(uint32 * set_id,uint32 * set_size);
- private:
- enum { MAX_STACK_DEPTH = 256 };
- FileClass * File;
- // Chunk reading support
- int StackIndex;
- uint32 PositionStack[MAX_STACK_DEPTH];
- ChunkHeader HeaderStack[MAX_STACK_DEPTH];
- // Micro-chunk reading support
- bool InMicroChunk;
- int MicroChunkPosition;
- MicroChunkHeader MCHeader;
- };
- /*
- ** WRITE_WWSTRING_CHUNK - use this one-line macro to easily create a chunk to save a potentially
- ** long string. Note: This macro does NOT create a micro chunk...
- ** Example:
- **
- ** csave.Begin_Chunk(CHUNKID_PARENT);
- ** ParentClass::Save (csave);
- ** csave.End_Chunk();
- **
- ** WRITE_WWSTRING_CHUNK(csave, CHUNKID_NAME, string);
- ** WRITE_WIDESTRING_CHUNK(csave, CHUNKID_WIDE_NAME, wide_string);
- **
- ** csave.Begin_Chunk(PHYSGRID_CHUNK_VARIABLES);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_VERSION,version);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
- ** csave.End_Chunk();
- **
- */
- #define WRITE_WWSTRING_CHUNK(csave,id,var) { \
- csave.Begin_Chunk(id); \
- csave.Write((const TCHAR *)var, var.Get_Length () + 1); \
- csave.End_Chunk(); }
- #define WRITE_WIDESTRING_CHUNK(csave,id,var) { \
- csave.Begin_Chunk(id); \
- csave.Write((const WCHAR *)var, (var.Get_Length () + 1) * 2); \
- csave.End_Chunk(); }
- /*
- ** READ_WWSTRING_CHUNK - use this macro in a switch statement to read the contents
- ** of a chunk into a string object.
- ** Example:
- **
- ** while (cload.Open_Chunk()) {
- **
- ** switch(cload.Cur_Chunk_ID()) {
- ** READ_WWSTRING_CHUNK(cload,CHUNKID_NAME,string);
- ** READ_WIDESTRING_CHUNK(cload,CHUNKID_WIDE_NAME,wide_string);
- ** }
- ** cload.Close_Chunk();
- ** }
- **
- */
- #define READ_WWSTRING_CHUNK(cload,id,var) \
- case (id): cload.Read(var.Get_Buffer(cload.Cur_Chunk_Length()),cload.Cur_Chunk_Length()); break; \
- #define READ_WIDESTRING_CHUNK(cload,id,var) \
- case (id): cload.Read(var.Get_Buffer((cload.Cur_Chunk_Length()+1)/2),cload.Cur_Chunk_Length()); break; \
- /*
- ** WRITE_MICRO_CHUNK - use this one-line macro to easily make a micro chunk for an individual variable.
- ** Note that you should always wrap your micro-chunks inside a normal chunk.
- ** Example:
- **
- ** csave.Begin_Chunk(PHYSGRID_CHUNK_VARIABLES);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_VERSION,version);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
- ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
- ** csave.End_Chunk();
- */
- #define WRITE_MICRO_CHUNK(csave,id,var) { \
- csave.Begin_Micro_Chunk(id); \
- csave.Write(&var,sizeof(var)); \
- csave.End_Micro_Chunk(); }
- #define WRITE_SAFE_MICRO_CHUNK(csave,id,var,type) { \
- csave.Begin_Micro_Chunk(id); \
- type data = (type)var; \
- csave.Write(&data,sizeof(data)); \
- csave.End_Micro_Chunk(); }
- #define WRITE_MICRO_CHUNK_STRING(csave,id,var) { \
- csave.Begin_Micro_Chunk(id); \
- csave.Write(var, strlen(var) + 1); \
- csave.End_Micro_Chunk(); }
- #define WRITE_MICRO_CHUNK_WWSTRING(csave,id,var) { \
- csave.Begin_Micro_Chunk(id); \
- csave.Write((const TCHAR *)var, var.Get_Length () + 1); \
- csave.End_Micro_Chunk(); }
- #define WRITE_MICRO_CHUNK_WIDESTRING(csave,id,var) { \
- csave.Begin_Micro_Chunk(id); \
- csave.Write((const WCHAR *)var, (var.Get_Length () + 1) * 2); \
- csave.End_Micro_Chunk(); }
- /*
- ** READ_MICRO_CHUNK - use this macro in a switch statement to read a micro chunk into a variable
- ** Example:
- **
- ** while (cload.Open_Micro_Chunk()) {
- **
- ** switch(cload.Cur_Micro_Chunk_ID()) {
- ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_VERSION,version);
- ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
- ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
- ** }
- ** cload.Close_Micro_Chunk();
- ** }
- */
- #define READ_MICRO_CHUNK(cload,id,var) \
- case (id): cload.Read(&var,sizeof(var)); break; \
- /*
- ** Like READ_MICRO_CHUNK but reads items straight into the data safe.
- */
- #define READ_SAFE_MICRO_CHUNK(cload,id,var,type) \
- case (id): { \
- void *temp_read_buffer_on_the_stack = _alloca(sizeof(type)); \
- cload.Read(temp_read_buffer_on_the_stack, sizeof(type)); \
- var = *((type*)temp_read_buffer_on_the_stack); \
- break; \
- }
- #define READ_MICRO_CHUNK_STRING(cload,id,var,size) \
- case (id): WWASSERT(cload.Cur_Micro_Chunk_Length() <= size); cload.Read(var,cload.Cur_Micro_Chunk_Length()); break; \
- #define READ_MICRO_CHUNK_WWSTRING(cload,id,var) \
- case (id): cload.Read(var.Get_Buffer(cload.Cur_Micro_Chunk_Length()),cload.Cur_Micro_Chunk_Length()); break; \
- #define READ_MICRO_CHUNK_WIDESTRING(cload,id,var) \
- case (id): cload.Read(var.Get_Buffer((cload.Cur_Micro_Chunk_Length()+1)/2),cload.Cur_Micro_Chunk_Length()); break; \
- /*
- ** These load macros make it easier to add extra code to a specifc case
- */
- #define LOAD_MICRO_CHUNK(cload,var) \
- cload.Read(&var,sizeof(var)); \
- #define LOAD_MICRO_CHUNK_WWSTRING(cload,var) \
- cload.Read(var.Get_Buffer(cload.Cur_Micro_Chunk_Length()),cload.Cur_Micro_Chunk_Length()); \
- #define LOAD_MICRO_CHUNK_WIDESTRING(cload,var) \
- cload.Read(var.Get_Buffer((cload.Cur_Micro_Chunk_Length()+1)/2),cload.Cur_Micro_Chunk_Length()); \
- /*
- ** OBSOLETE_MICRO_CHUNK - use this macro in a switch statement when you want your code
- ** to skip a given micro chunk but not fall through to your 'default' case statement which
- ** prints an "unrecognized chunk" warning message.
- */
- #define OBSOLETE_MICRO_CHUNK(id) \
- case (id): break;
- #endif CHUNKIO_H
|