176 lines
6.2 KiB
C++
176 lines
6.2 KiB
C++
/* ScummVM - Graphic Adventure Engine
|
|
*
|
|
* ScummVM is the legal property of its developers, whose names
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
* file distributed with this source distribution.
|
|
*
|
|
* 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/>.
|
|
*
|
|
*/
|
|
|
|
//=============================================================================
|
|
//
|
|
// A simple data extension format which may be useful as an amendment to
|
|
// any data file in the game.
|
|
//
|
|
// Format consists of a list of "blocks", each preceded with an integer and an
|
|
// optional string identifier, and a size in bytes which lets a reader to skip
|
|
// the block completely if necessary.
|
|
// Because the serialization algorithm was accommodated to be shared among
|
|
// several existing data files, few things in the meta info may be read
|
|
// slightly differently depending on flags passed into the function.
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
// Extension format description.
|
|
//
|
|
// Each block starts with the header.
|
|
// * 1 or 4 bytes (depends on flags) - an old-style unsigned numeric ID :
|
|
// - where 0 would indicate following string ID,
|
|
// - and -1 (0xFF in case of 1 byte ID) indicates an end of the block list.
|
|
// * 16 bytes - fixed-len string ID of an extension (if num ID == 0).
|
|
// * 4 bytes - total length of the data in bytes;
|
|
// - does not include the size of the header (only following data).
|
|
// - new style blocks (w string ID) always use 8 bytes for a block len;
|
|
// * Then goes regular data.
|
|
//
|
|
// After the block is read the stream position supposed to be at
|
|
// (start + length of block), where "start" is a first byte of the header.
|
|
// If it's further - the reader bails out with error. If it's not far enough -
|
|
// the function logs a warning and skips remaining bytes.
|
|
// Blocks are read until meeting ID == -1 in the next header, which indicates
|
|
// the end of extension list. An EOF met earlier is considered an error.
|
|
//
|
|
//=============================================================================
|
|
|
|
#ifndef AGS_SHARED_UTIL_DATA_EXT_H
|
|
#define AGS_SHARED_UTIL_DATA_EXT_H
|
|
|
|
#include "ags/shared/util/error.h"
|
|
#include "ags/shared/util/string.h"
|
|
|
|
namespace AGS3 {
|
|
namespace AGS {
|
|
namespace Shared {
|
|
|
|
enum DataExtFlags {
|
|
// Size of a numeric ID in bytes
|
|
kDataExt_NumID8 = 0x0000, // default
|
|
kDataExt_NumID32 = 0x0001,
|
|
// 32-bit or 64-bit file offset support
|
|
// NOTE: for historical reasons this refers to blocks with numeric ID ONLY;
|
|
// new-style blocks with a 16-char ID always write 64-bit offset
|
|
kDataExt_File32 = 0x0000, // default
|
|
kDataExt_File64 = 0x0002
|
|
};
|
|
|
|
enum DataExtErrorType {
|
|
kDataExtErr_NoError,
|
|
kDataExtErr_UnexpectedEOF,
|
|
kDataExtErr_BlockNotFound,
|
|
kDataExtErr_BlockDataOverlapping
|
|
};
|
|
|
|
String GetDataExtErrorText(DataExtErrorType err);
|
|
typedef TypedCodeError<DataExtErrorType, GetDataExtErrorText> DataExtError;
|
|
|
|
|
|
// DataExtReader parses a generic extendable block list and
|
|
// does format checks, but does not read any data itself.
|
|
// Use it to open blocks, and assert reading correctness.
|
|
class DataExtParser {
|
|
public:
|
|
DataExtParser(Stream *in, int flags) : _in(in), _flags(flags) {
|
|
}
|
|
virtual ~DataExtParser() = default;
|
|
|
|
// Returns the conventional string ID for an old-style block with numeric ID
|
|
virtual String GetOldBlockName(int blockId) const {
|
|
return String::FromFormat("id:%d", blockId);
|
|
}
|
|
|
|
// Provides a leeway for over-reading (reading past the reported block length):
|
|
// the parser will not error if the mistake is in this range of bytes
|
|
virtual soff_t GetOverLeeway(int /*block_id*/) const {
|
|
return 0;
|
|
}
|
|
|
|
// Gets a stream
|
|
inline Stream *GetStream() {
|
|
return _in;
|
|
}
|
|
// Tells if the end of the block list was reached
|
|
inline bool AtEnd() const {
|
|
return _blockID < 0;
|
|
}
|
|
// Tries to opens a next standard block from the stream,
|
|
// fills in identifier and length on success
|
|
HError OpenBlock();
|
|
// Skips current block
|
|
void SkipBlock();
|
|
// Asserts current stream position after a block was read
|
|
HError PostAssert();
|
|
// Parses a block list in search for a particular block,
|
|
// if found opens it.
|
|
HError FindOne(int id);
|
|
|
|
protected:
|
|
Stream *_in = nullptr;
|
|
int _flags = 0;
|
|
|
|
int _blockID = -1;
|
|
String _extID;
|
|
soff_t _blockStart = 0;
|
|
soff_t _blockLen = 0;
|
|
};
|
|
|
|
// DataExtReader is a virtual base class of a block list reader; provides
|
|
// a helper method for reading all the blocks one by one, but leaves data
|
|
// reading for the child classes. A child class must override ReadBlock method.
|
|
// TODO: don't extend Parser, but have it as a member?
|
|
class DataExtReader : protected DataExtParser {
|
|
public:
|
|
virtual ~DataExtReader() = default;
|
|
|
|
// Parses a block list, calls ReadBlock for each found block
|
|
HError Read();
|
|
|
|
protected:
|
|
DataExtReader(Stream *in, int flags) : DataExtParser(in, flags) {
|
|
}
|
|
// Reads a single data block and tell whether to continue reading;
|
|
// default implementation skips the block
|
|
virtual HError ReadBlock(int block_id, const String &ext_id,
|
|
soff_t block_len, bool &read_next) = 0;
|
|
};
|
|
|
|
|
|
// Type of function that writes a single data block.
|
|
typedef void(*PfnWriteExtBlock)(Stream *out);
|
|
void WriteExtBlock(int block, const String &ext_id, const PfnWriteExtBlock &writer, int flags, Stream *out);
|
|
// Writes a block with a new-style string id
|
|
inline void WriteExtBlock(const String &ext_id, PfnWriteExtBlock writer, int flags, Stream *out) {
|
|
WriteExtBlock(0, ext_id, writer, flags, out);
|
|
}
|
|
// Writes a block with a old-style numeric id
|
|
inline void WriteExtBlock(int block, PfnWriteExtBlock writer, int flags, Stream *out) {
|
|
WriteExtBlock(block, String(), writer, flags, out);
|
|
}
|
|
|
|
} // namespace Shared
|
|
} // namespace AGS
|
|
} // namespace AGS3
|
|
|
|
#endif
|