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