liblcf
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 
18 std::string LcfReader::error_str;
19 
20 LcfReader::LcfReader(std::istream& filestream, std::string encoding)
21  : stream(filestream)
22  , encoder(std::move(encoding))
23 {
24  offset = filestream.tellg();
25 }
26 
28 }
29 
30 size_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 
47 void 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 
57 template <>
58 void LcfReader::Read<bool>(bool& ref) {
59  ref = ReadInt() > 0;
60 }
61 
62 template <>
63 void LcfReader::Read<int8_t>(int8_t& ref) {
64  Read(&ref, 1, 1);
65 }
66 
67 template <>
68 void LcfReader::Read<uint8_t>(uint8_t& ref) {
69  Read(&ref, 1, 1);
70 }
71 
72 template <>
73 void LcfReader::Read<int16_t>(int16_t& ref) {
74  Read(&ref, 2, 1);
75  SwapByteOrder(ref);
76 }
77 
78 template <>
79 void 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 
105 template <>
106 void LcfReader::Read<int32_t>(int32_t& ref) {
107  ref = ReadInt();
108 }
109 
110 template <>
111 void LcfReader::Read<double>(double& ref) {
112  Read(&ref, 8, 1);
113  SwapByteOrder(ref);
114 }
115 
116 template <>
117 void 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 
127 template <>
128 void 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 
138 template <>
139 void 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 
154 template <>
155 void 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 
170 template <>
171 void 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 
186 void 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 
192 bool LcfReader::IsOk() const {
193  return stream.good() && encoder.IsOk();
194 }
195 
196 bool LcfReader::Eof() const {
197  return stream.eof();
198 }
199 
200 void 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;
219  case LcfReader::FromEnd:
220  stream.seekg(pos, std::ios_base::end);
221  offset = stream.tellg();
222  break;
223  default:
224  assert(false && "Invalid SeekMode");
225  }
226 }
227 
228 uint32_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
244 void 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
273 void LcfReader::Skip(const struct LcfReader::Chunk& chunk_info) {
274  Seek((size_t)chunk_info.length, FromCurrent);
275 }
276 #endif
277 
278 void 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 
291 const std::string& LcfReader::GetError() {
292  return error_str;
293 }
294 
295 void LcfReader::Encode(std::string& str) {
296  encoder.Encode(str);
297 }
298 
299 int 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
309 void LcfReader::SwapByteOrder(uint16_t& us)
310 {
311  us = (us >> 8) |
312  (us << 8);
313 }
314 
315 void LcfReader::SwapByteOrder(uint32_t& ui)
316 {
317  ui = (ui >> 24) |
318  ((ui<<8) & 0x00FF0000) |
319  ((ui>>8) & 0x0000FF00) |
320  (ui << 24);
321 }
322 
323 void 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
333 void LcfReader::SwapByteOrder(uint16_t& /* us */) {}
334 void LcfReader::SwapByteOrder(uint32_t& /* ui */) {}
335 void LcfReader::SwapByteOrder(double& /* d */) {}
336 #endif
337 
338 void LcfReader::SwapByteOrder(int16_t& s)
339 {
340  SwapByteOrder((uint16_t&) s);
341 }
342 
343 void LcfReader::SwapByteOrder(int32_t& s)
344 {
345  SwapByteOrder((uint32_t&) s);
346 }
void Seek(size_t pos, SeekMode mode=FromStart)
Definition: reader_lcf.cpp:200
void Skip(const struct LcfReader::Chunk &chunk_info)
Definition: reader_lcf.cpp:273
static void SwapByteOrder(int16_t &us)
Definition: reader_lcf.cpp:338
std::vector< int32_t > buffer
Definition: reader_lcf.h:229
void Read(void *ptr, size_t size, size_t nmemb)
Definition: reader_lcf.cpp:47
bool Eof() const
Definition: reader_lcf.cpp:196
uint32_t length
Definition: reader_lcf.h:76
void Encode(std::string &str)
Definition: encoder.cpp:63
Encoder encoder
Definition: reader_lcf.h:227
size_t Read0(void *ptr, size_t size, size_t nmemb)
Definition: reader_lcf.cpp:30
LcfReader(std::istream &filestream, std::string encoding="")
Definition: reader_lcf.cpp:20
std::istream & stream
Definition: reader_lcf.h:221
int ReadInt()
Definition: reader_lcf.cpp:84
static const std::string & GetError()
Definition: reader_lcf.cpp:291
void ReadString(std::string &ref, size_t size)
Definition: reader_lcf.cpp:186
std::vector< RPG::Item > & items
Definition: data.cpp:18
int64_t offset
Definition: reader_lcf.h:223
bool IsOk() const
Definition: encoder.cpp:59
static int IntSize(unsigned int x)
Definition: reader_lcf.cpp:299
static void SetError(const char *fmt,...)
Definition: reader_lcf.cpp:278
uint32_t Tell()
Definition: reader_lcf.cpp:228
bool IsOk() const
Definition: reader_lcf.cpp:192
static std::string error_str
Definition: reader_lcf.h:225
int Peek()
Definition: reader_lcf.cpp:239
void Encode(std::string &str)
Definition: reader_lcf.cpp:295