chunkio.h 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /* $Header: /Commando/Code/wwlib/chunkio.h 23 3/14/02 1:25p Greg_h $ */
  19. /***********************************************************************************************
  20. *** Confidential - Westwood Studios ***
  21. ***********************************************************************************************
  22. * *
  23. * Project Name : Tiberian Sun / Commando / G Library *
  24. * *
  25. * $Archive:: /Commando/Code/wwlib/chunkio.h $*
  26. * *
  27. * $Author:: Greg_h $*
  28. * *
  29. * $Modtime:: 3/04/02 3:41p $*
  30. * *
  31. * $Revision:: 23 $*
  32. * *
  33. *---------------------------------------------------------------------------------------------*
  34. * Functions: *
  35. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  36. #if _MSC_VER >= 1000
  37. #pragma once
  38. #endif // _MSC_VER >= 1000
  39. #ifndef CHUNKIO_H
  40. #define CHUNKIO_H
  41. #ifndef ALWAYS_H
  42. #include "always.h"
  43. #endif
  44. #ifndef BITTYPE_H
  45. #include "bittype.h"
  46. #endif
  47. #ifndef WWFILE_H
  48. #include "wwfile.h"
  49. #endif
  50. #ifndef IOSTRUCT_H
  51. #include "iostruct.h"
  52. #endif
  53. /************************************************************************************
  54. ChunkIO
  55. (gth) This module provides classes for reading and writing chunk-based files.
  56. For example, all of the w3d files are stored in a hierarchical-chunk format.
  57. Basically the format is similar to IFF. All data in the file has chunk headers
  58. wrapped around it. A chunk header contains an ID, and a Size. The size
  59. is the number of bytes in the chunk (not including the header). The
  60. contents of a chunk may be either: more "sub-chunks" or raw data. These classes
  61. will automatically keep track of your positions within all of the sub and parent
  62. chunks (to some maximum recursion depth).
  63. Sept 3, 1999
  64. (gth) Adding the new concept of "micro-chunks". Instead of filling the contents of a
  65. chunk with data, you can fill it with "micro-chunks" which contain a single byte
  66. id and a single byte size. Micro-chunks are used for storing simple variables
  67. in a form that can survive revisions to the file format without paying the price
  68. for a full chunk header. You CANNOT recursively embed micro-chunks due to their
  69. size limitations....
  70. Sept 24, 1999
  71. (gth) Using the MSB of the chunksize to indicate whether a chunk contains other
  72. chunks or pure data. If the MSB is 0, the chunk contains data (so that the reader
  73. I'm going to write doesn't break on older files) and if it is 1 then it is
  74. assumed to contain other chunks. This does not apply to micro-chunks as they
  75. are considered data.
  76. **************************************************************************************/
  77. struct ChunkHeader
  78. {
  79. // Functions.
  80. ChunkHeader() : ChunkType(0), ChunkSize(0) {}
  81. ChunkHeader(uint32 type, uint32 size) {ChunkType = type; ChunkSize = size;}
  82. // Use these accessors to ensure you correctly deal with the data in the chunk header
  83. void Set_Type(uint32 type) { ChunkType = type; }
  84. uint32 Get_Type(void) { return ChunkType; }
  85. void Set_Size(uint32 size) { ChunkSize &= 0x80000000; ChunkSize |= (size & 0x7FFFFFFF); }
  86. void Add_Size(uint32 add) { Set_Size(Get_Size() + add); }
  87. uint32 Get_Size(void) { return (ChunkSize & 0x7FFFFFFF); }
  88. void Set_Sub_Chunk_Flag(bool onoff) { if (onoff) { ChunkSize |= 0x80000000; } else { ChunkSize &= 0x7FFFFFFF; } }
  89. int Get_Sub_Chunk_Flag(void) { return (ChunkSize & 0x80000000); }
  90. // Chunk type and size.
  91. // Note: MSB of ChunkSize is used to indicate whether this chunk
  92. // contains other chunks or data.
  93. uint32 ChunkType;
  94. uint32 ChunkSize;
  95. };
  96. struct MicroChunkHeader
  97. {
  98. MicroChunkHeader() {}
  99. MicroChunkHeader(uint8 type, uint8 size) { ChunkType = type, ChunkSize = size; }
  100. void Set_Type(uint8 type) { ChunkType = type; }
  101. uint8 Get_Type(void) { return ChunkType; }
  102. void Set_Size(uint8 size) { ChunkSize = size; }
  103. void Add_Size(uint8 add) { Set_Size(Get_Size() + add); }
  104. uint8 Get_Size(void) { return ChunkSize; }
  105. uint8 ChunkType;
  106. uint8 ChunkSize;
  107. };
  108. /**************************************************************************************
  109. **
  110. ** ChunkSaveClass
  111. ** Wrap an instance of this class around an opened file for easy chunk
  112. ** creation.
  113. **
  114. **************************************************************************************/
  115. class ChunkSaveClass
  116. {
  117. public:
  118. ChunkSaveClass(FileClass * file);
  119. // Chunk methods
  120. bool Begin_Chunk(uint32 id);
  121. bool End_Chunk();
  122. int Cur_Chunk_Depth();
  123. // Micro chunk methods
  124. bool Begin_Micro_Chunk(uint32 id);
  125. bool End_Micro_Chunk();
  126. // Write data into the file
  127. uint32 Write(const void *buf, uint32 nbytes);
  128. uint32 Write(const IOVector2Struct & v);
  129. uint32 Write(const IOVector3Struct & v);
  130. uint32 Write(const IOVector4Struct & v);
  131. uint32 Write(const IOQuaternionStruct & q);
  132. private:
  133. enum { MAX_STACK_DEPTH = 256 };
  134. FileClass * File;
  135. // Chunk building support
  136. int StackIndex;
  137. int PositionStack[MAX_STACK_DEPTH];
  138. ChunkHeader HeaderStack[MAX_STACK_DEPTH];
  139. // MicroChunk building support
  140. bool InMicroChunk;
  141. int MicroChunkPosition;
  142. MicroChunkHeader MCHeader;
  143. };
  144. /**************************************************************************************
  145. **
  146. ** ChunkLoadClass
  147. ** wrap an instance of one of these objects around an opened file
  148. ** to easily parse the chunks in the file
  149. **
  150. **************************************************************************************/
  151. class ChunkLoadClass
  152. {
  153. public:
  154. ChunkLoadClass(FileClass * file);
  155. // Chunk methods
  156. bool Open_Chunk();
  157. bool Close_Chunk();
  158. uint32 Cur_Chunk_ID();
  159. uint32 Cur_Chunk_Length();
  160. int Cur_Chunk_Depth();
  161. int Contains_Chunks();
  162. // Micro Chunk methods
  163. bool Open_Micro_Chunk();
  164. bool Close_Micro_Chunk();
  165. uint32 Cur_Micro_Chunk_ID();
  166. uint32 Cur_Micro_Chunk_Length();
  167. // Read a block of bytes from the output stream.
  168. uint32 Read(void *buf, uint32 nbytes);
  169. uint32 Read(IOVector2Struct * v);
  170. uint32 Read(IOVector3Struct * v);
  171. uint32 Read(IOVector4Struct * v);
  172. uint32 Read(IOQuaternionStruct * q);
  173. // Seek over a block of bytes in the stream (same as Read but don't copy the data to a buffer)
  174. uint32 Seek(uint32 nbytes);
  175. // Sneak peek at the next chunk that will be opened. Beware, if you need
  176. // this, then you are probably hacking so be careful!
  177. bool Peek_Next_Chunk(uint32 * set_id,uint32 * set_size);
  178. private:
  179. enum { MAX_STACK_DEPTH = 256 };
  180. FileClass * File;
  181. // Chunk reading support
  182. int StackIndex;
  183. uint32 PositionStack[MAX_STACK_DEPTH];
  184. ChunkHeader HeaderStack[MAX_STACK_DEPTH];
  185. // Micro-chunk reading support
  186. bool InMicroChunk;
  187. int MicroChunkPosition;
  188. MicroChunkHeader MCHeader;
  189. };
  190. /*
  191. ** WRITE_WWSTRING_CHUNK - use this one-line macro to easily create a chunk to save a potentially
  192. ** long string. Note: This macro does NOT create a micro chunk...
  193. ** Example:
  194. **
  195. ** csave.Begin_Chunk(CHUNKID_PARENT);
  196. ** ParentClass::Save (csave);
  197. ** csave.End_Chunk();
  198. **
  199. ** WRITE_WWSTRING_CHUNK(csave, CHUNKID_NAME, string);
  200. ** WRITE_WIDESTRING_CHUNK(csave, CHUNKID_WIDE_NAME, wide_string);
  201. **
  202. ** csave.Begin_Chunk(PHYSGRID_CHUNK_VARIABLES);
  203. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_VERSION,version);
  204. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
  205. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
  206. ** csave.End_Chunk();
  207. **
  208. */
  209. #define WRITE_WWSTRING_CHUNK(csave,id,var) { \
  210. csave.Begin_Chunk(id); \
  211. csave.Write((const TCHAR *)var, var.Get_Length () + 1); \
  212. csave.End_Chunk(); }
  213. #define WRITE_WIDESTRING_CHUNK(csave,id,var) { \
  214. csave.Begin_Chunk(id); \
  215. csave.Write((const WCHAR *)var, (var.Get_Length () + 1) * 2); \
  216. csave.End_Chunk(); }
  217. /*
  218. ** READ_WWSTRING_CHUNK - use this macro in a switch statement to read the contents
  219. ** of a chunk into a string object.
  220. ** Example:
  221. **
  222. ** while (cload.Open_Chunk()) {
  223. **
  224. ** switch(cload.Cur_Chunk_ID()) {
  225. ** READ_WWSTRING_CHUNK(cload,CHUNKID_NAME,string);
  226. ** READ_WIDESTRING_CHUNK(cload,CHUNKID_WIDE_NAME,wide_string);
  227. ** }
  228. ** cload.Close_Chunk();
  229. ** }
  230. **
  231. */
  232. #define READ_WWSTRING_CHUNK(cload,id,var) \
  233. case (id): cload.Read(var.Get_Buffer(cload.Cur_Chunk_Length()),cload.Cur_Chunk_Length()); break; \
  234. #define READ_WIDESTRING_CHUNK(cload,id,var) \
  235. case (id): cload.Read(var.Get_Buffer((cload.Cur_Chunk_Length()+1)/2),cload.Cur_Chunk_Length()); break; \
  236. /*
  237. ** WRITE_MICRO_CHUNK - use this one-line macro to easily make a micro chunk for an individual variable.
  238. ** Note that you should always wrap your micro-chunks inside a normal chunk.
  239. ** Example:
  240. **
  241. ** csave.Begin_Chunk(PHYSGRID_CHUNK_VARIABLES);
  242. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_VERSION,version);
  243. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
  244. ** WRITE_MICRO_CHUNK(csave,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
  245. ** csave.End_Chunk();
  246. */
  247. #define WRITE_MICRO_CHUNK(csave,id,var) { \
  248. csave.Begin_Micro_Chunk(id); \
  249. csave.Write(&var,sizeof(var)); \
  250. csave.End_Micro_Chunk(); }
  251. #define WRITE_SAFE_MICRO_CHUNK(csave,id,var,type) { \
  252. csave.Begin_Micro_Chunk(id); \
  253. type data = (type)var; \
  254. csave.Write(&data,sizeof(data)); \
  255. csave.End_Micro_Chunk(); }
  256. #define WRITE_MICRO_CHUNK_STRING(csave,id,var) { \
  257. csave.Begin_Micro_Chunk(id); \
  258. csave.Write(var, strlen(var) + 1); \
  259. csave.End_Micro_Chunk(); }
  260. #define WRITE_MICRO_CHUNK_WWSTRING(csave,id,var) { \
  261. csave.Begin_Micro_Chunk(id); \
  262. csave.Write((const TCHAR *)var, var.Get_Length () + 1); \
  263. csave.End_Micro_Chunk(); }
  264. #define WRITE_MICRO_CHUNK_WIDESTRING(csave,id,var) { \
  265. csave.Begin_Micro_Chunk(id); \
  266. csave.Write((const WCHAR *)var, (var.Get_Length () + 1) * 2); \
  267. csave.End_Micro_Chunk(); }
  268. /*
  269. ** READ_MICRO_CHUNK - use this macro in a switch statement to read a micro chunk into a variable
  270. ** Example:
  271. **
  272. ** while (cload.Open_Micro_Chunk()) {
  273. **
  274. ** switch(cload.Cur_Micro_Chunk_ID()) {
  275. ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_VERSION,version);
  276. ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_DUMMYVISID,DummyVisId);
  277. ** READ_MICRO_CHUNK(cload,PHYSGRID_VARIABLE_BASEVISID,BaseVisId);
  278. ** }
  279. ** cload.Close_Micro_Chunk();
  280. ** }
  281. */
  282. #define READ_MICRO_CHUNK(cload,id,var) \
  283. case (id): cload.Read(&var,sizeof(var)); break; \
  284. /*
  285. ** Like READ_MICRO_CHUNK but reads items straight into the data safe.
  286. */
  287. #define READ_SAFE_MICRO_CHUNK(cload,id,var,type) \
  288. case (id): { \
  289. void *temp_read_buffer_on_the_stack = _alloca(sizeof(type)); \
  290. cload.Read(temp_read_buffer_on_the_stack, sizeof(type)); \
  291. var = *((type*)temp_read_buffer_on_the_stack); \
  292. break; \
  293. }
  294. #define READ_MICRO_CHUNK_STRING(cload,id,var,size) \
  295. case (id): WWASSERT(cload.Cur_Micro_Chunk_Length() <= size); cload.Read(var,cload.Cur_Micro_Chunk_Length()); break; \
  296. #define READ_MICRO_CHUNK_WWSTRING(cload,id,var) \
  297. case (id): cload.Read(var.Get_Buffer(cload.Cur_Micro_Chunk_Length()),cload.Cur_Micro_Chunk_Length()); break; \
  298. #define READ_MICRO_CHUNK_WIDESTRING(cload,id,var) \
  299. case (id): cload.Read(var.Get_Buffer((cload.Cur_Micro_Chunk_Length()+1)/2),cload.Cur_Micro_Chunk_Length()); break; \
  300. /*
  301. ** These load macros make it easier to add extra code to a specifc case
  302. */
  303. #define LOAD_MICRO_CHUNK(cload,var) \
  304. cload.Read(&var,sizeof(var)); \
  305. #define LOAD_MICRO_CHUNK_WWSTRING(cload,var) \
  306. cload.Read(var.Get_Buffer(cload.Cur_Micro_Chunk_Length()),cload.Cur_Micro_Chunk_Length()); \
  307. #define LOAD_MICRO_CHUNK_WIDESTRING(cload,var) \
  308. cload.Read(var.Get_Buffer((cload.Cur_Micro_Chunk_Length()+1)/2),cload.Cur_Micro_Chunk_Length()); \
  309. /*
  310. ** OBSOLETE_MICRO_CHUNK - use this macro in a switch statement when you want your code
  311. ** to skip a given micro chunk but not fall through to your 'default' case statement which
  312. ** prints an "unrecognized chunk" warning message.
  313. */
  314. #define OBSOLETE_MICRO_CHUNK(id) \
  315. case (id): break;
  316. #endif CHUNKIO_H