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