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