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