liblcf
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 
17 namespace lcf {
18 // Statics
19 
20 std::string LcfReader::error_str;
21 
22 LcfReader::LcfReader(std::istream& filestream, std::string encoding)
23  : stream(filestream)
24  , encoder(std::move(encoding))
25 {
26  offset = filestream.tellg();
27 }
28 
29 size_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 
46 void 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 
56 template <>
57 void LcfReader::Read<bool>(bool& ref) {
58  ref = ReadInt() > 0;
59 }
60 
61 template <>
62 void LcfReader::Read<int8_t>(int8_t& ref) {
63  Read(&ref, 1, 1);
64 }
65 
66 template <>
67 void LcfReader::Read<uint8_t>(uint8_t& ref) {
68  Read(&ref, 1, 1);
69 }
70 
71 template <>
72 void LcfReader::Read<int16_t>(int16_t& ref) {
73  Read(&ref, 2, 1);
74  SwapByteOrder(ref);
75 }
76 
77 template <>
78 void LcfReader::Read<uint32_t>(uint32_t& ref) {
79  Read(&ref, 4, 1);
80  SwapByteOrder(ref);
81 }
82 
83 int 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 
104 uint64_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 
125 template <>
126 void LcfReader::Read<int32_t>(int32_t& ref) {
127  ref = ReadInt();
128 }
129 
130 template <>
131 void LcfReader::Read<double>(double& ref) {
132  Read(&ref, 8, 1);
133  SwapByteOrder(ref);
134 }
135 
136 template <>
137 void 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 
147 template <>
148 void 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 
158 template <>
159 void 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 
174 template <>
175 void 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 
190 template <>
191 void 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 
206 void 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 
215 void 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 
221 void LcfReader::ReadString(DBString& ref, size_t size) {
222  auto& tmp = StrBuffer();
223  ReadString(tmp, size);
224  ref = DBString(tmp);
225 }
226 
227 bool LcfReader::IsOk() const {
228  return stream.good() && encoder.IsOk();
229 }
230 
231 bool LcfReader::Eof() const {
232  return stream.eof();
233 }
234 
235 void 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 
263 uint32_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 
274 int LcfReader::Peek() {
275  return stream.peek();
276 }
277 
278 void 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 
296 void 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 
309 const std::string& LcfReader::GetError() {
310  return error_str;
311 }
312 
313 void LcfReader::Encode(std::string& str) {
314  encoder.Encode(str);
315 }
316 
317 int 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 
326 int 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
336 void LcfReader::SwapByteOrder(uint16_t& us)
337 {
338  us = (us >> 8) |
339  (us << 8);
340 }
341 
342 void LcfReader::SwapByteOrder(uint32_t& ui)
343 {
344  ui = (ui >> 24) |
345  ((ui<<8) & 0x00FF0000) |
346  ((ui>>8) & 0x0000FF00) |
347  (ui << 24);
348 }
349 
350 void 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
359 void LcfReader::SwapByteOrder(uint16_t& /* us */) {}
360 void LcfReader::SwapByteOrder(uint32_t& /* ui */) {}
361 void LcfReader::SwapByteOrder(double& /* d */) {}
362 #endif
363 
364 void LcfReader::SwapByteOrder(int16_t& s)
365 {
366  SwapByteOrder((uint16_t&) s);
367 }
368 
369 void LcfReader::SwapByteOrder(int32_t& s)
370 {
371  SwapByteOrder((uint32_t&) s);
372 }
373 
374 } //namespace lcf
Definition: dbarray.cpp:13