Skip to content

Commit 32f2920

Browse files
committed
feat(datachunk): add platform-neutral DataChunk library
1 parent a463616 commit 32f2920

File tree

10 files changed

+1446
-0
lines changed

10 files changed

+1446
-0
lines changed
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
/*
2+
** Command & Conquer Generals(tm)
3+
** Copyright 2025 TheSuperHackers
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+
19+
// DataChunk.h
20+
// Main public API for DataChunk library
21+
// TheSuperHackers @feature bobtista 14/11/2025 Extract chunk I/O to platform-neutral library
22+
23+
#pragma once
24+
25+
#include "DataChunk/Types.h"
26+
#include "DataChunk/Stream.h"
27+
#include "DataChunk/TableOfContents.h"
28+
#include <string>
29+
30+
namespace DataChunk {
31+
32+
//----------------------------------------------------------------------
33+
// OutputChunk
34+
//----------------------------------------------------------------------
35+
/** Internal structure for tracking open output chunks. */
36+
struct OutputChunk
37+
{
38+
OutputChunk* next;
39+
ChunkUInt id; // chunk symbol type from table of contents
40+
ChunkInt filepos; // position of file at start of data offset
41+
42+
OutputChunk() : next(NULL), id(0), filepos(0) {}
43+
};
44+
45+
//----------------------------------------------------------------------
46+
// InputChunk
47+
//----------------------------------------------------------------------
48+
/** Internal structure for tracking open input chunks. */
49+
struct InputChunk
50+
{
51+
InputChunk* next;
52+
ChunkUInt id; // chunk symbol type from table of contents
53+
DataChunkVersionType version; // version of data
54+
ChunkInt chunkStart; // position of the start of chunk data (past header)
55+
ChunkInt dataSize; // total data size of chunk
56+
ChunkInt dataLeft; // data left to read in this chunk
57+
58+
InputChunk() : next(NULL), id(0), version(0), chunkStart(0), dataSize(0), dataLeft(0) {}
59+
};
60+
61+
//----------------------------------------------------------------------
62+
// DataChunkInfo
63+
//----------------------------------------------------------------------
64+
/** Information about a chunk being parsed. */
65+
struct DataChunkInfo
66+
{
67+
ChunkString label;
68+
ChunkString parentLabel;
69+
DataChunkVersionType version;
70+
ChunkInt dataSize;
71+
};
72+
73+
//----------------------------------------------------------------------
74+
// DataChunkParserPtr
75+
//----------------------------------------------------------------------
76+
/** Function pointer type for parsing chunks. */
77+
typedef bool (*DataChunkParserPtr)(class DataChunkInput& file, DataChunkInfo* info, void* userData);
78+
79+
//----------------------------------------------------------------------
80+
// UserParser
81+
//----------------------------------------------------------------------
82+
/** Internal structure for registered parsers. */
83+
struct UserParser
84+
{
85+
UserParser* next;
86+
DataChunkParserPtr parser; // the user parsing function
87+
ChunkString label; // the data chunk label to match
88+
ChunkString parentLabel; // the parent chunk's label (the scope)
89+
void* userData; // user data pointer
90+
91+
UserParser() : next(NULL), parser(NULL), userData(NULL) {}
92+
};
93+
94+
//----------------------------------------------------------------------
95+
// DataChunkOutput
96+
//----------------------------------------------------------------------
97+
/** Class for writing chunk-based data files.
98+
Platform-neutral replacement for engine's DataChunkOutput. */
99+
class DataChunkOutput
100+
{
101+
DataChunkOutputStream* m_pOut; // The actual output stream
102+
DataChunkTableOfContents m_contents; // table of contents of data chunk types
103+
OutputChunk* m_chunkStack; // current stack of open data chunks
104+
105+
// Internal buffer for writing (replaces temp file)
106+
char* m_buffer;
107+
unsigned int m_bufferSize;
108+
unsigned int m_bufferPos;
109+
110+
void growBuffer(unsigned int needed);
111+
112+
public:
113+
DataChunkOutput(DataChunkOutputStream* pOut);
114+
~DataChunkOutput();
115+
116+
/** Open a new data chunk.
117+
@param name Chunk type name (will be added to string table)
118+
@param ver Version number for this chunk */
119+
void openDataChunk(const char* name, DataChunkVersionType ver);
120+
121+
/** Close the current data chunk. */
122+
void closeDataChunk();
123+
124+
/** Write a float value. */
125+
void writeReal(ChunkReal r);
126+
127+
/** Write an integer value. */
128+
void writeInt(ChunkInt i);
129+
130+
/** Write a byte value. */
131+
void writeByte(ChunkByte b);
132+
133+
/** Write an ASCII string (length-prefixed, no null terminator). */
134+
void writeAsciiString(const ChunkString& string);
135+
136+
/** Write a Unicode string (length-prefixed, no null terminator). */
137+
void writeUnicodeString(const ChunkWideString& string);
138+
139+
/** Write an array of bytes. */
140+
void writeArrayOfBytes(const char* ptr, ChunkInt len);
141+
};
142+
143+
//----------------------------------------------------------------------
144+
// DataChunkInput
145+
//----------------------------------------------------------------------
146+
/** Class for reading chunk-based data files.
147+
Platform-neutral replacement for engine's DataChunkInput. */
148+
class DataChunkInput
149+
{
150+
enum { CHUNK_HEADER_BYTES = 4 }; // 2 shorts in chunk file header
151+
152+
DataChunkInputStream* m_file; // input file stream
153+
DataChunkTableOfContents m_contents; // table of contents of data chunk types
154+
ChunkInt m_fileposOfFirstChunk; // seek position of first data chunk
155+
UserParser* m_parserList; // list of all registered parsers
156+
InputChunk* m_chunkStack; // current stack of open data chunks
157+
158+
void clearChunkStack();
159+
void decrementDataLeft(ChunkInt size);
160+
161+
public:
162+
void* m_currentObject; // user parse routines can use this
163+
void* m_userData; // user data hook
164+
165+
DataChunkInput(DataChunkInputStream* pStream);
166+
~DataChunkInput();
167+
168+
/** Register a parser function for data chunks.
169+
@param label Chunk label to match
170+
@param parentLabel Parent chunk label (or empty for global scope)
171+
@param parser Parser function to call
172+
@param userData Optional user data to pass to parser */
173+
void registerParser(const ChunkString& label, const ChunkString& parentLabel,
174+
DataChunkParserPtr parser, void* userData = NULL);
175+
176+
/** Parse the chunk stream using registered parsers.
177+
@param userData Optional user data to pass to parsers
178+
@return true on success, false on failure */
179+
bool parse(void* userData = NULL);
180+
181+
/** Check if file has valid chunk format.
182+
@return true if valid format */
183+
bool isValidFileType();
184+
185+
/** Open the next data chunk.
186+
@param ver Output parameter for chunk version
187+
@return Chunk label name */
188+
ChunkString openDataChunk(DataChunkVersionType* ver);
189+
190+
/** Close the current chunk and move to next. */
191+
void closeDataChunk();
192+
193+
/** Check if at end of file.
194+
@return true if at end */
195+
bool atEndOfFile();
196+
197+
/** Check if at end of current chunk.
198+
@return true if all data read from chunk */
199+
bool atEndOfChunk();
200+
201+
/** Reset to just-opened state. */
202+
void reset();
203+
204+
/** Get label of current chunk.
205+
@return Chunk label name */
206+
ChunkString getChunkLabel();
207+
208+
/** Get version of current chunk.
209+
@return Chunk version number */
210+
DataChunkVersionType getChunkVersion();
211+
212+
/** Get size of data in current chunk.
213+
@return Data size in bytes */
214+
ChunkUInt getChunkDataSize();
215+
216+
/** Get size of data left to read in current chunk.
217+
@return Remaining data size in bytes */
218+
ChunkUInt getChunkDataSizeLeft();
219+
220+
/** Read a float value. */
221+
ChunkReal readReal();
222+
223+
/** Read an integer value. */
224+
ChunkInt readInt();
225+
226+
/** Read a byte value. */
227+
ChunkByte readByte();
228+
229+
/** Read an ASCII string. */
230+
ChunkString readAsciiString();
231+
232+
/** Read a Unicode string. */
233+
ChunkWideString readUnicodeString();
234+
235+
/** Read an array of bytes.
236+
@param ptr Buffer to read into
237+
@param len Number of bytes to read */
238+
void readArrayOfBytes(char* ptr, ChunkInt len);
239+
};
240+
241+
} // namespace DataChunk
242+
Lines changed: 105 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
# DataChunk Library
2+
3+
Platform-neutral library for reading and writing chunk-based data files (SCB format).
4+
5+
## Overview
6+
7+
This library provides the core chunk I/O functionality extracted from the game engine, making it available for use in both the engine and standalone tools. It maintains 100% binary compatibility with retail SCB files.
8+
9+
## Features
10+
11+
- ✅ VC6 compatible (no C++11 features)
12+
- ✅ Platform-neutral (works on Windows, macOS, Linux)
13+
- ✅ Binary compatible with retail SCB format
14+
- ✅ No engine dependencies
15+
- ✅ Simple stream-based interface
16+
17+
## Usage Example
18+
19+
### Reading a Chunk File
20+
21+
```cpp
22+
#include "DataChunk/DataChunk.h"
23+
#include "DataChunk/StreamAdapters.h"
24+
#include <cstdio>
25+
26+
using namespace DataChunk;
27+
28+
bool parseScript(DataChunkInput& file, DataChunkInfo* info, void* userData)
29+
{
30+
ChunkString scriptName = file.readAsciiString();
31+
ChunkString comment = file.readAsciiString();
32+
// ... read more fields ...
33+
return true;
34+
}
35+
36+
int main()
37+
{
38+
FILE* fp = fopen("script.scb", "rb");
39+
if (!fp) return 1;
40+
41+
FileInputStream stream(fp);
42+
DataChunkInput chunkInput(&stream);
43+
44+
chunkInput.registerParser("Script", "", parseScript);
45+
chunkInput.parse();
46+
47+
fclose(fp);
48+
return 0;
49+
}
50+
```
51+
52+
### Writing a Chunk File
53+
54+
```cpp
55+
#include "DataChunk/DataChunk.h"
56+
#include "DataChunk/StreamAdapters.h"
57+
#include <cstdio>
58+
59+
using namespace DataChunk;
60+
61+
int main()
62+
{
63+
FILE* fp = fopen("output.scb", "wb");
64+
if (!fp) return 1;
65+
66+
FileOutputStream stream(fp);
67+
DataChunkOutput chunkOutput(&stream);
68+
69+
chunkOutput.openDataChunk("Script", 2);
70+
chunkOutput.writeAsciiString("MyScript");
71+
chunkOutput.writeAsciiString("Comment");
72+
// ... write more fields ...
73+
chunkOutput.closeDataChunk();
74+
75+
fclose(fp);
76+
return 0;
77+
}
78+
```
79+
80+
## Architecture
81+
82+
- **Stream.h**: Abstract stream interfaces
83+
- **Types.h**: Type definitions (VC6 compatible)
84+
- **TableOfContents.h/cpp**: String-to-ID mapping
85+
- **DataChunk.h/cpp**: Main I/O classes
86+
- **StreamAdapters.h**: FILE* adapters for tools
87+
88+
## Binary Format
89+
90+
The library maintains exact binary compatibility with the engine's chunk format:
91+
92+
- Chunk header: 10 bytes (4-byte ID + 2-byte version + 4-byte size)
93+
- String table: "CkMp" magic + count + entries
94+
- Strings: Length-prefixed (2 bytes) + data (no null terminator)
95+
- All integers: Little-endian, 4 bytes
96+
- All floats: IEEE 754, 4 bytes
97+
98+
## VC6 Compatibility Notes
99+
100+
- Uses `std::string` (VC6 STL compatible)
101+
- Uses `NULL` instead of `nullptr`
102+
- No C++11 features
103+
- Raw pointers (no smart pointers)
104+
- Standard C++98/C++03
105+

0 commit comments

Comments
 (0)