ScummVM API documentation
data_ext.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22  //=============================================================================
23  //
24  // A simple data extension format which may be useful as an amendment to
25  // any data file in the game.
26  //
27  // Format consists of a list of "blocks", each preceded with an integer and an
28  // optional string identifier, and a size in bytes which lets a reader to skip
29  // the block completely if necessary.
30  // Because the serialization algorithm was accommodated to be shared among
31  // several existing data files, few things in the meta info may be read
32  // slightly differently depending on flags passed into the function.
33  //
34  //-----------------------------------------------------------------------------
35  //
36  // Extension format description.
37  //
38  // Each block starts with the header.
39  // * 1 or 4 bytes (depends on flags) - an old-style unsigned numeric ID :
40  // - where 0 would indicate following string ID,
41  // - and -1 (0xFF in case of 1 byte ID) indicates an end of the block list.
42  // * 16 bytes - fixed-len string ID of an extension (if num ID == 0).
43  // * 4 bytes - total length of the data in bytes;
44  // - does not include the size of the header (only following data).
45  // - new style blocks (w string ID) always use 8 bytes for a block len;
46  // * Then goes regular data.
47  //
48  // After the block is read the stream position supposed to be at
49  // (start + length of block), where "start" is a first byte of the header.
50  // If it's further - the reader bails out with error. If it's not far enough -
51  // the function logs a warning and skips remaining bytes.
52  // Blocks are read until meeting ID == -1 in the next header, which indicates
53  // the end of extension list. An EOF met earlier is considered an error.
54  //
55  //=============================================================================
56 
57 #ifndef AGS_SHARED_UTIL_DATA_EXT_H
58 #define AGS_SHARED_UTIL_DATA_EXT_H
59 
60 #include "ags/shared/util/error.h"
61 #include "ags/shared/util/string.h"
62 
63 namespace AGS3 {
64 namespace AGS {
65 namespace Shared {
66 
67 enum DataExtFlags {
68  // Size of a numeric ID in bytes
69  kDataExt_NumID8 = 0x0000, // default
70  kDataExt_NumID32 = 0x0001,
71  // 32-bit or 64-bit file offset support
72  // NOTE: for historical reasons this refers to blocks with numeric ID ONLY;
73  // new-style blocks with a 16-char ID always write 64-bit offset
74  kDataExt_File32 = 0x0000, // default
75  kDataExt_File64 = 0x0002
76 };
77 
78 enum DataExtErrorType {
79  kDataExtErr_NoError,
80  kDataExtErr_UnexpectedEOF,
81  kDataExtErr_BlockNotFound,
82  kDataExtErr_BlockDataOverlapping
83 };
84 
85 String GetDataExtErrorText(DataExtErrorType err);
86 typedef TypedCodeError<DataExtErrorType, GetDataExtErrorText> DataExtError;
87 
88 
89 // DataExtReader parses a generic extendable block list and
90 // does format checks, but does not read any data itself.
91 // Use it to open blocks, and assert reading correctness.
93 public:
94  DataExtParser(Stream *in, int flags) : _in(in), _flags(flags) {
95  }
96  virtual ~DataExtParser() = default;
97 
98  // Returns the conventional string ID for an old-style block with numeric ID
99  virtual String GetOldBlockName(int blockId) const {
100  return String::FromFormat("id:%d", blockId);
101  }
102 
103  // Provides a leeway for over-reading (reading past the reported block length):
104  // the parser will not error if the mistake is in this range of bytes
105  virtual soff_t GetOverLeeway(int /*block_id*/) const {
106  return 0;
107  }
108 
109  // Gets a stream
110  inline Stream *GetStream() {
111  return _in;
112  }
113  // Tells if the end of the block list was reached
114  inline bool AtEnd() const {
115  return _blockID < 0;
116  }
117  // Tries to opens a next standard block from the stream,
118  // fills in identifier and length on success
119  HError OpenBlock();
120  // Skips current block
121  void SkipBlock();
122  // Asserts current stream position after a block was read
123  HError PostAssert();
124  // Parses a block list in search for a particular block,
125  // if found opens it.
126  HError FindOne(int id);
127 
128 protected:
129  Stream *_in = nullptr;
130  int _flags = 0;
131 
132  int _blockID = -1;
133  String _extID;
134  soff_t _blockStart = 0;
135  soff_t _blockLen = 0;
136 };
137 
138 // DataExtReader is a virtual base class of a block list reader; provides
139 // a helper method for reading all the blocks one by one, but leaves data
140 // reading for the child classes. A child class must override ReadBlock method.
141 // TODO: don't extend Parser, but have it as a member?
142 class DataExtReader : protected DataExtParser {
143 public:
144  virtual ~DataExtReader() = default;
145 
146  // Parses a block list, calls ReadBlock for each found block
147  HError Read();
148 
149 protected:
150  DataExtReader(Stream *in, int flags) : DataExtParser(in, flags) {
151  }
152  // Reads a single data block and tell whether to continue reading;
153  // default implementation skips the block
154  virtual HError ReadBlock(int block_id, const String &ext_id,
155  soff_t block_len, bool &read_next) = 0;
156 };
157 
158 
159 // Type of function that writes a single data block.
160 typedef void(*PfnWriteExtBlock)(Stream *out);
161 void WriteExtBlock(int block, const String &ext_id, const PfnWriteExtBlock &writer, int flags, Stream *out);
162 // Writes a block with a new-style string id
163 inline void WriteExtBlock(const String &ext_id, PfnWriteExtBlock writer, int flags, Stream *out) {
164  WriteExtBlock(0, ext_id, writer, flags, out);
165 }
166 // Writes a block with a old-style numeric id
167 inline void WriteExtBlock(int block, PfnWriteExtBlock writer, int flags, Stream *out) {
168  WriteExtBlock(block, String(), writer, flags, out);
169 }
170 
171 } // namespace Shared
172 } // namespace AGS
173 } // namespace AGS3
174 
175 #endif
Definition: achievements_tables.h:27
Definition: data_ext.h:142
Definition: data_ext.h:92
Definition: string.h:62
Definition: stream.h:52
Definition: error.h:110
Definition: ags.h:40