Skip to content
1 change: 1 addition & 0 deletions Core/Libraries/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ add_subdirectory(Source/debug)
add_subdirectory(Source/EABrowserDispatch)
add_subdirectory(Source/EABrowserEngine)
add_subdirectory(Source/Compression)
add_subdirectory(Source/DataChunk)
242 changes: 242 additions & 0 deletions Core/Libraries/Include/DataChunk/DataChunk.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,242 @@
/*
** Command & Conquer Generals(tm)
** Copyright 2025 TheSuperHackers
**
** 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/>.
*/

// DataChunk.h
// Main public API for DataChunk library
// TheSuperHackers @feature bobtista 14/11/2025 Extract chunk I/O to platform-neutral library

#pragma once

#include "DataChunk/Types.h"
#include "DataChunk/Stream.h"
#include "DataChunk/TableOfContents.h"
#include <string>

namespace DataChunk {

//----------------------------------------------------------------------
// OutputChunk
//----------------------------------------------------------------------
/** Internal structure for tracking open output chunks. */
struct OutputChunk
{
OutputChunk* next;
ChunkUInt id; // chunk symbol type from table of contents
ChunkInt filepos; // position of file at start of data offset

OutputChunk() : next(NULL), id(0), filepos(0) {}
};

//----------------------------------------------------------------------
// InputChunk
//----------------------------------------------------------------------
/** Internal structure for tracking open input chunks. */
struct InputChunk
{
InputChunk* next;
ChunkUInt id; // chunk symbol type from table of contents
DataChunkVersionType version; // version of data
ChunkInt chunkStart; // position of the start of chunk data (past header)
ChunkInt dataSize; // total data size of chunk
ChunkInt dataLeft; // data left to read in this chunk

InputChunk() : next(NULL), id(0), version(0), chunkStart(0), dataSize(0), dataLeft(0) {}
};

//----------------------------------------------------------------------
// DataChunkInfo
//----------------------------------------------------------------------
/** Information about a chunk being parsed. */
struct DataChunkInfo
{
ChunkString label;
ChunkString parentLabel;
DataChunkVersionType version;
ChunkInt dataSize;
};

//----------------------------------------------------------------------
// DataChunkParserPtr
//----------------------------------------------------------------------
/** Function pointer type for parsing chunks. */
typedef bool (*DataChunkParserPtr)(class DataChunkInput& file, DataChunkInfo* info, void* userData);

//----------------------------------------------------------------------
// UserParser
//----------------------------------------------------------------------
/** Internal structure for registered parsers. */
struct UserParser
{
UserParser* next;
DataChunkParserPtr parser; // the user parsing function
ChunkString label; // the data chunk label to match
ChunkString parentLabel; // the parent chunk's label (the scope)
void* userData; // user data pointer

UserParser() : next(NULL), parser(NULL), userData(NULL) {}
};

//----------------------------------------------------------------------
// DataChunkOutput
//----------------------------------------------------------------------
/** Class for writing chunk-based data files.
Platform-neutral replacement for engine's DataChunkOutput. */
class DataChunkOutput
{
DataChunkOutputStream* m_pOut; // The actual output stream
DataChunkTableOfContents m_contents; // table of contents of data chunk types
OutputChunk* m_chunkStack; // current stack of open data chunks

// Internal buffer for writing (replaces temp file)
char* m_buffer;
unsigned int m_bufferSize;
unsigned int m_bufferPos;

void growBuffer(unsigned int needed);

public:
DataChunkOutput(DataChunkOutputStream* pOut);
~DataChunkOutput();

/** Open a new data chunk.
@param name Chunk type name (will be added to string table)
@param ver Version number for this chunk */
void openDataChunk(const char* name, DataChunkVersionType ver);

/** Close the current data chunk. */
void closeDataChunk();

/** Write a float value. */
void writeReal(ChunkReal r);

/** Write an integer value. */
void writeInt(ChunkInt i);

/** Write a byte value. */
void writeByte(ChunkByte b);

/** Write an ASCII string (length-prefixed, no null terminator). */
void writeAsciiString(const ChunkString& string);

/** Write a Unicode string (length-prefixed, no null terminator). */
void writeUnicodeString(const ChunkWideString& string);

/** Write an array of bytes. */
void writeArrayOfBytes(const char* ptr, ChunkInt len);
};

//----------------------------------------------------------------------
// DataChunkInput
//----------------------------------------------------------------------
/** Class for reading chunk-based data files.
Platform-neutral replacement for engine's DataChunkInput. */
class DataChunkInput
{
enum { CHUNK_HEADER_BYTES = 4 }; // 2 shorts in chunk file header

DataChunkInputStream* m_file; // input file stream
DataChunkTableOfContents m_contents; // table of contents of data chunk types
ChunkInt m_fileposOfFirstChunk; // seek position of first data chunk
UserParser* m_parserList; // list of all registered parsers
InputChunk* m_chunkStack; // current stack of open data chunks

void clearChunkStack();
void decrementDataLeft(ChunkInt size);

public:
void* m_currentObject; // user parse routines can use this
void* m_userData; // user data hook

DataChunkInput(DataChunkInputStream* pStream);
~DataChunkInput();

/** Register a parser function for data chunks.
@param label Chunk label to match
@param parentLabel Parent chunk label (or empty for global scope)
@param parser Parser function to call
@param userData Optional user data to pass to parser */
void registerParser(const ChunkString& label, const ChunkString& parentLabel,
DataChunkParserPtr parser, void* userData = NULL);

/** Parse the chunk stream using registered parsers.
@param userData Optional user data to pass to parsers
@return true on success, false on failure */
bool parse(void* userData = NULL);

/** Check if file has valid chunk format.
@return true if valid format */
bool isValidFileType();

/** Open the next data chunk.
@param ver Output parameter for chunk version
@return Chunk label name */
ChunkString openDataChunk(DataChunkVersionType* ver);

/** Close the current chunk and move to next. */
void closeDataChunk();

/** Check if at end of file.
@return true if at end */
bool atEndOfFile();

/** Check if at end of current chunk.
@return true if all data read from chunk */
bool atEndOfChunk();

/** Reset to just-opened state. */
void reset();

/** Get label of current chunk.
@return Chunk label name */
ChunkString getChunkLabel();

/** Get version of current chunk.
@return Chunk version number */
DataChunkVersionType getChunkVersion();

/** Get size of data in current chunk.
@return Data size in bytes */
ChunkUInt getChunkDataSize();

/** Get size of data left to read in current chunk.
@return Remaining data size in bytes */
ChunkUInt getChunkDataSizeLeft();

/** Read a float value. */
ChunkReal readReal();

/** Read an integer value. */
ChunkInt readInt();

/** Read a byte value. */
ChunkByte readByte();

/** Read an ASCII string. */
ChunkString readAsciiString();

/** Read a Unicode string. */
ChunkWideString readUnicodeString();

/** Read an array of bytes.
@param ptr Buffer to read into
@param len Number of bytes to read */
void readArrayOfBytes(char* ptr, ChunkInt len);
};

} // namespace DataChunk

105 changes: 105 additions & 0 deletions Core/Libraries/Include/DataChunk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# DataChunk Library

Platform-neutral library for reading and writing chunk-based data files (SCB format).

## Overview

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.

## Features

- ✅ VC6 compatible (no C++11 features)
- ✅ Platform-neutral (works on Windows, macOS, Linux)
- ✅ Binary compatible with retail SCB format
- ✅ No engine dependencies
- ✅ Simple stream-based interface

## Usage Example

### Reading a Chunk File

```cpp
#include "DataChunk/DataChunk.h"
#include "DataChunk/StreamAdapters.h"
#include <cstdio>

using namespace DataChunk;

bool parseScript(DataChunkInput& file, DataChunkInfo* info, void* userData)
{
ChunkString scriptName = file.readAsciiString();
ChunkString comment = file.readAsciiString();
// ... read more fields ...
return true;
}

int main()
{
FILE* fp = fopen("script.scb", "rb");
if (!fp) return 1;

FileInputStream stream(fp);
DataChunkInput chunkInput(&stream);

chunkInput.registerParser("Script", "", parseScript);
chunkInput.parse();

fclose(fp);
return 0;
}
```

### Writing a Chunk File

```cpp
#include "DataChunk/DataChunk.h"
#include "DataChunk/StreamAdapters.h"
#include <cstdio>

using namespace DataChunk;

int main()
{
FILE* fp = fopen("output.scb", "wb");
if (!fp) return 1;

FileOutputStream stream(fp);
DataChunkOutput chunkOutput(&stream);

chunkOutput.openDataChunk("Script", 2);
chunkOutput.writeAsciiString("MyScript");
chunkOutput.writeAsciiString("Comment");
// ... write more fields ...
chunkOutput.closeDataChunk();

fclose(fp);
return 0;
}
```

## Architecture

- **Stream.h**: Abstract stream interfaces
- **Types.h**: Type definitions (VC6 compatible)
- **TableOfContents.h/cpp**: String-to-ID mapping
- **DataChunk.h/cpp**: Main I/O classes
- **StreamAdapters.h**: FILE* adapters for tools

## Binary Format

The library maintains exact binary compatibility with the engine's chunk format:

- Chunk header: 10 bytes (4-byte ID + 2-byte version + 4-byte size)
- String table: "CkMp" magic + count + entries
- Strings: Length-prefixed (2 bytes) + data (no null terminator)
- All integers: Little-endian, 4 bytes
- All floats: IEEE 754, 4 bytes

## VC6 Compatibility Notes

- Uses `std::string` (VC6 STL compatible)
- Uses `NULL` instead of `nullptr`
- No C++11 features
- Raw pointers (no smart pointers)
- Standard C++98/C++03

Loading
Loading