1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
/* 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
|