liblcf
Loading...
Searching...
No Matches
reader_lcf.cpp
Go to the documentation of this file.
1/*
2 * This file is part of liblcf. Copyright (c) 2021 liblcf authors.
3 * https://github.com/EasyRPG/liblcf - https://easyrpg.org
4 *
5 * liblcf is Free/Libre Open Source Software, released under the MIT License.
6 * For the full copyright and license information, please view the COPYING
7 * file that was distributed with this source code.
8 */
9
10#include <cstdarg>
11#include <cstdio>
12#include <istream>
13
14#include "lcf/reader_lcf.h"
15
16namespace lcf {
17// Statics
18
19std::string LcfReader::error_str;
20
21LcfReader::LcfReader(std::istream& filestream, std::string encoding)
22 : stream(filestream)
23 , encoder(std::move(encoding))
24{
25 offset = filestream.tellg();
26}
27
28size_t LcfReader::Read0(void *ptr, size_t size, size_t nmemb) {
29 if (size == 0) { //avoid division by 0
30 return 0;
31 }
32 //Read nmemb elements of size and return the number of read elements
33 stream.read(reinterpret_cast<char*>(ptr), size*nmemb);
34 auto bytes_read = stream.gcount();
35 offset += bytes_read;
36 size_t result = bytes_read / size;
37#ifdef NDEBUG
38 if (result != nmemb && !Eof()) {
39 perror("Reading error: ");
40 }
41#endif
42 return result;
43}
44
45void LcfReader::Read(void *ptr, size_t size, size_t nmemb) {
46#ifdef NDEBUG
47 Read0(ptr, size, nmemb);
48#else
49 if (Read0(ptr, size, nmemb) != nmemb) {
50 fprintf(stderr, "Read error at %" PRIu32 ". The file is probably corrupted\n", Tell());
51 }
52#endif
53}
54
55template <>
56void LcfReader::Read<bool>(bool& ref) {
57 ref = ReadInt() > 0;
58}
59
60template <>
61void LcfReader::Read<int8_t>(int8_t& ref) {
62 Read(&ref, 1, 1);
63}
64
65template <>
66void LcfReader::Read<uint8_t>(uint8_t& ref) {
67 Read(&ref, 1, 1);
68}
69
70template <>
71void LcfReader::Read<int16_t>(int16_t& ref) {
72 Read(&ref, 2, 1);
73 SwapByteOrder(ref);
74}
75
76template <>
77void LcfReader::Read<uint32_t>(uint32_t& ref) {
78 Read(&ref, 4, 1);
79 SwapByteOrder(ref);
80}
81
82int LcfReader::ReadInt() {
83 int value = 0;
84 unsigned char temp = 0;
85 int loops = 0;
86 do {
87 value <<= 7;
88 if (Read0(&temp, 1, 1) == 0) {
89 assert(value == 0);
90 return 0;
91 }
92 value |= temp & 0x7F;
93
94 if (loops > 5) {
95 fprintf(stderr, "Invalid compressed integer at %" PRIu32 "\n", Tell());
96 }
97 ++loops;
98 } while (temp & 0x80);
99
100 return loops > 5 ? 0 : value;
101}
102
103template <>
104void LcfReader::Read<int32_t>(int32_t& ref) {
105 ref = ReadInt();
106}
107
108template <>
109void LcfReader::Read<double>(double& ref) {
110 Read(&ref, 8, 1);
111 SwapByteOrder(ref);
112}
113
114template <>
115void LcfReader::Read<bool>(std::vector<bool> &buffer, size_t size) {
116 buffer.clear();
117
118 for (unsigned i = 0; i < size; ++i) {
119 uint8_t val;
120 Read(&val, 1, 1);
121 buffer.push_back(val > 0);
122 }
123}
124
125template <>
126void LcfReader::Read<uint8_t>(std::vector<uint8_t> &buffer, size_t size) {
127 buffer.clear();
128
129 for (unsigned int i = 0; i < size; ++i) {
130 uint8_t val;
131 Read(&val, 1, 1);
132 buffer.push_back(val);
133 }
134}
135
136template <>
137void LcfReader::Read<int16_t>(std::vector<int16_t> &buffer, size_t size) {
138 buffer.clear();
139 size_t items = size / 2;
140 for (unsigned int i = 0; i < items; ++i) {
141 int16_t val;
142 Read(&val, 2, 1);
143 SwapByteOrder(val);
144 buffer.push_back(val);
145 }
146 if (size % 2 != 0) {
147 Seek(1, FromCurrent);
148 buffer.push_back(0);
149 }
150}
151
152template <>
153void LcfReader::Read<int32_t>(std::vector<int32_t> &buffer, size_t size) {
154 buffer.clear();
155 size_t items = size / 4;
156 for (unsigned int i = 0; i < items; ++i) {
157 int32_t val;
158 Read(&val, 4, 1);
159 SwapByteOrder(val);
160 buffer.push_back(val);
161 }
162 if (size % 4 != 0) {
163 Seek(size % 4, FromCurrent);
164 buffer.push_back(0);
165 }
166}
167
168template <>
169void LcfReader::Read<uint32_t>(std::vector<uint32_t> &buffer, size_t size) {
170 buffer.clear();
171 size_t items = size / 4;
172 for (unsigned int i = 0; i < items; ++i) {
173 uint32_t val;
174 Read(&val, 4, 1);
175 SwapByteOrder(val);
176 buffer.push_back(val);
177 }
178 if (size % 4 != 0) {
179 Seek(size % 4, FromCurrent);
180 buffer.push_back(0);
181 }
182}
183
184void LcfReader::ReadBits(DBBitArray &buffer, size_t size) {
185 buffer = DBBitArray(size);
186 for (size_t i = 0; i < size; ++i) {
187 uint8_t val;
188 Read(&val, sizeof(val), 1);
189 buffer[i] = static_cast<bool>(val);
190 }
191}
192
193void LcfReader::ReadString(std::string& ref, size_t size) {
194 ref.resize(size);
195 Read((size > 0 ? &ref.front(): nullptr), 1, size);
196 Encode(ref);
197}
198
199void LcfReader::ReadString(DBString& ref, size_t size) {
200 auto& tmp = StrBuffer();
201 ReadString(tmp, size);
202 ref = DBString(tmp);
203}
204
205
206
207bool LcfReader::IsOk() const {
208 return stream.good() && encoder.IsOk();
209}
210
211bool LcfReader::Eof() const {
212 return stream.eof();
213}
214
215void LcfReader::Seek(size_t pos, SeekMode mode) {
216 constexpr auto fast_seek_size = 32;
217 switch (mode) {
218 case LcfReader::FromStart:
219 stream.seekg(pos, std::ios_base::beg);
220 offset = stream.tellg();
221 break;
222 case LcfReader::FromCurrent:
223 if (pos <= fast_seek_size) {
224 // seekg() always results in a system call which is slow.
225 // For small values just read and throwaway.
226 char buf[fast_seek_size];
227 stream.read(buf, pos);
228 offset += stream.gcount();
229 } else {
230 stream.seekg(pos, std::ios_base::cur);
231 offset = stream.tellg();
232 }
233 break;
234 case LcfReader::FromEnd:
235 stream.seekg(pos, std::ios_base::end);
236 offset = stream.tellg();
237 break;
238 default:
239 assert(false && "Invalid SeekMode");
240 }
241}
242
243uint32_t LcfReader::Tell() {
244 // Calling iostream tellg() results in a system call everytime and was found
245 // to dominate the runtime of lcf reading. So we cache our own offset.
246 // The result of this was shown to have a 30-40% improvement in LDB loading times.
247 // return (uint32_t)stream.tellg();
248 // This assert can be enabled to verify this method is correct. Disabled by
249 // default as it will slow down debug loading considerably.
250 // assert(stream.tellg() == offset);
251 return offset;
252}
253
254int LcfReader::Peek() {
255 return stream.peek();
256}
257
258void LcfReader::Skip(const struct LcfReader::Chunk& chunk_info, const char* where) {
259 fprintf(stderr, "Skipped Chunk %02X (%" PRIu32 " byte) in lcf at %" PRIX32 " (%s)\n",
260 chunk_info.ID, chunk_info.length, Tell(), where);
261
262 for (uint32_t i = 0; i < chunk_info.length; ++i) {
263 uint8_t byte;
264 LcfReader::Read(byte);
265 fprintf(stderr, "%02X ", byte);
266 if ((i+1) % 16 == 0) {
267 fprintf(stderr, "\n");
268 }
269 if (Eof()) {
270 break;
271 }
272 }
273 fprintf(stderr, "\n");
274}
275
276void LcfReader::SetError(const char* fmt, ...) {
277 va_list args;
278 va_start(args, fmt);
279
280 char str[256];
281 vsprintf(str, fmt, args);
282
283 error_str = str;
284 //Output::ErrorStr((std::string)str);
285
286 va_end(args);
287}
288
289const std::string& LcfReader::GetError() {
290 return error_str;
291}
292
293void LcfReader::Encode(std::string& str) {
294 encoder.Encode(str);
295}
296
297int LcfReader::IntSize(unsigned int x) {
298 int result = 0;
299 do {
300 x >>= 7;
301 result++;
302 } while (x != 0);
303 return result;
304}
305
306#ifdef WORDS_BIGENDIAN
307void LcfReader::SwapByteOrder(uint16_t& us)
308{
309 us = (us >> 8) |
310 (us << 8);
311}
312
313void LcfReader::SwapByteOrder(uint32_t& ui)
314{
315 ui = (ui >> 24) |
316 ((ui<<8) & 0x00FF0000) |
317 ((ui>>8) & 0x0000FF00) |
318 (ui << 24);
319}
320
321void LcfReader::SwapByteOrder(double& d)
322{
323 uint32_t *p = reinterpret_cast<uint32_t *>(&d);
324 SwapByteOrder(p[0]);
325 SwapByteOrder(p[1]);
326 uint32_t tmp = p[0];
327 p[0] = p[1];
328 p[1] = tmp;
329}
330#else
331void LcfReader::SwapByteOrder(uint16_t& /* us */) {}
332void LcfReader::SwapByteOrder(uint32_t& /* ui */) {}
333void LcfReader::SwapByteOrder(double& /* d */) {}
334#endif
335
336void LcfReader::SwapByteOrder(int16_t& s)
337{
338 SwapByteOrder((uint16_t&) s);
339}
340
341void LcfReader::SwapByteOrder(int32_t& s)
342{
343 SwapByteOrder((uint32_t&) s);
344}
345
346} //namespace lcf
Definition: dbarray.cpp:13