File: data_ext.h

package info (click to toggle)
scummvm 2.9.1%2Bdfsg-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 450,580 kB
  • sloc: cpp: 4,299,825; asm: 28,322; python: 12,901; sh: 11,302; java: 9,289; xml: 7,895; perl: 2,639; ansic: 2,465; yacc: 1,670; javascript: 1,020; makefile: 933; lex: 578; awk: 275; objc: 82; sed: 11; php: 1
file content (175 lines) | stat: -rw-r--r-- 6,324 bytes parent folder | download | duplicates (2)
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