1//////////////////////////////////////////////////////////////////////
2// This file is part of Remere's Map Editor
3//////////////////////////////////////////////////////////////////////
4// Remere's Map Editor is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8//
9// Remere's Map Editor is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13//
14// You should have received a copy of the GNU General Public License
15// along with this program. If not, see <http://www.gnu.org/licenses/>.
16//////////////////////////////////////////////////////////////////////
17
18#include "main.h"
19
20#include "filehandle.h"
21
22#include <stdio.h>
23#include <assert.h>
24
25uint8_t NodeFileWriteHandle::NODE_START = ::NODE_START;
26uint8_t NodeFileWriteHandle::NODE_END = ::NODE_END;
27uint8_t NodeFileWriteHandle::ESCAPE_CHAR = ::ESCAPE_CHAR;
28
29void FileHandle::close() {
30 if (file) {
31 fclose(file);
32 file = nullptr;
33 }
34}
35
36std::string FileHandle::getErrorMessage() {
37 switch (error_code) {
38 case FILE_NO_ERROR:
39 return "No error";
40 case FILE_COULD_NOT_OPEN:
41 return "Could not open file";
42 case FILE_INVALID_IDENTIFIER:
43 return "File magic number not recognized";
44 case FILE_STRING_TOO_LONG:
45 return "Too long string encountered";
46 case FILE_READ_ERROR:
47 return "Failed to read from file";
48 case FILE_WRITE_ERROR:
49 return "Failed to write to file";
50 case FILE_SYNTAX_ERROR:
51 return "Node file syntax error";
52 case FILE_PREMATURE_END:
53 return "File end encountered unexpectedly";
54 }
55 if (file == nullptr) {
56 return "Could not open file (2)";
57 }
58 if (ferror(file)) {
59 return "Internal file error #" + i2s(ferror(file));
60 }
61 return "No error";
62}
63
64//=============================================================================
65// File read handle
66
67FileReadHandle::FileReadHandle(const std::string& name) :
68 file_size(0) {
69#if defined __VISUALC__ && defined _UNICODE
70 file = _wfopen(string2wstring(name).c_str(), L"rb");
71#else
72 file = fopen(name.c_str(), "rb");
73#endif
74 if (!file || ferror(file)) {
75 error_code = FILE_COULD_NOT_OPEN;
76 } else {
77 fseek(file, 0, SEEK_END);
78 file_size = ftell(file);
79 fseek(file, 0, SEEK_SET);
80 }
81}
82
83FileReadHandle::~FileReadHandle() {
84 ////
85}
86
87void FileReadHandle::close() {
88 file_size = 0;
89 FileHandle::close();
90}
91
92bool FileReadHandle::getRAW(uint8_t* ptr, size_t sz) {
93 size_t o = fread(ptr, 1, sz, file);
94 if (o != sz) {
95 error_code = FILE_READ_ERROR;
96 return false;
97 }
98 return true;
99}
100
101bool FileReadHandle::getRAW(std::string& str, size_t sz) {
102 str.resize(sz);
103 size_t o = fread(const_cast<char*>(str.data()), 1, sz, file);
104 if (o != sz) {
105 error_code = FILE_READ_ERROR;
106 return false;
107 }
108 return true;
109}
110
111bool FileReadHandle::getString(std::string& str) {
112 uint16_t sz;
113 if (!getU16(sz)) {
114 error_code = FILE_READ_ERROR;
115 return false;
116 }
117 return getRAW(str, sz);
118}
119
120bool FileReadHandle::getLongString(std::string& str) {
121 uint32_t sz;
122 if (!getU32(sz)) {
123 error_code = FILE_READ_ERROR;
124 return false;
125 }
126 return getRAW(str, sz);
127}
128
129bool FileReadHandle::seek(size_t offset) {
130 return fseek(file, long(offset), SEEK_SET) == 0;
131}
132
133bool FileReadHandle::seekRelative(size_t offset) {
134 return fseek(file, long(offset), SEEK_CUR) == 0;
135}
136
137//=============================================================================
138// Node file read handle
139
140NodeFileReadHandle::NodeFileReadHandle() :
141 last_was_start(false),
142 cache(nullptr),
143 cache_size(32768),
144 cache_length(0),
145 local_read_index(0),
146 root_node(nullptr) {
147 ////
148}
149
150NodeFileReadHandle::~NodeFileReadHandle() {
151 while (!unused.empty()) {
152 free(unused.top());
153 unused.pop();
154 }
155}
156
157BinaryNode* NodeFileReadHandle::getNode(BinaryNode* parent) {
158 void* mem;
159 if (unused.empty()) {
160 mem = malloc(sizeof(BinaryNode));
161 } else {
162 mem = unused.top();
163 unused.pop();
164 }
165 return new (mem) BinaryNode(this, parent);
166}
167
168void NodeFileReadHandle::freeNode(BinaryNode* node) {
169 if (node) {
170 node->~BinaryNode();
171 unused.push(node);
172 }
173}
174
175//=============================================================================
176// Memory based node file read handle
177
178MemoryNodeFileReadHandle::MemoryNodeFileReadHandle(const uint8_t* data, size_t size) {
179 assign(data, size);
180}
181
182void MemoryNodeFileReadHandle::assign(const uint8_t* data, size_t size) {
183 freeNode(root_node);
184 root_node = nullptr;
185 // Highly volatile, but we know we're not gonna modify
186 cache = const_cast<uint8_t*>(data);
187 cache_size = cache_length = size;
188 local_read_index = 0;
189}
190
191MemoryNodeFileReadHandle::~MemoryNodeFileReadHandle() {
192 freeNode(root_node);
193}
194
195void MemoryNodeFileReadHandle::close() {
196 assign(nullptr, 0);
197}
198
199bool MemoryNodeFileReadHandle::renewCache() {
200 return false;
201}
202
203BinaryNode* MemoryNodeFileReadHandle::getRootNode() {
204 assert(root_node == nullptr); // You should never do this twice
205
206 local_read_index++; // Skip first NODE_START
207 last_was_start = true;
208 root_node = getNode(nullptr);
209 root_node->load();
210 return root_node;
211}
212
213//=============================================================================
214// File based node file read handle
215
216DiskNodeFileReadHandle::DiskNodeFileReadHandle(const std::string& name, const std::vector<std::string>& acceptable_identifiers) :
217 file_size(0) {
218#if defined __VISUALC__ && defined _UNICODE
219 file = _wfopen(string2wstring(name).c_str(), L"rb");
220#else
221 file = fopen(name.c_str(), "rb");
222#endif
223 if (!file || ferror(file)) {
224 error_code = FILE_COULD_NOT_OPEN;
225 } else {
226 char ver[4];
227 if (fread(ver, 1, 4, file) != 4) {
228 fclose(file);
229 error_code = FILE_SYNTAX_ERROR;
230 return;
231 }
232
233 // 0x00 00 00 00 is accepted as a wildcard version
234
235 if (ver[0] != 0 || ver[1] != 0 || ver[2] != 0 || ver[3] != 0) {
236 bool accepted = false;
237 for (std::vector<std::string>::const_iterator id_iter = acceptable_identifiers.begin(); id_iter != acceptable_identifiers.end(); ++id_iter) {
238 if (memcmp(ver, id_iter->c_str(), 4) == 0) {
239 accepted = true;
240 break;
241 }
242 }
243
244 if (!accepted) {
245 fclose(file);
246 error_code = FILE_SYNTAX_ERROR;
247 return;
248 }
249 }
250
251 fseek(file, 0, SEEK_END);
252 file_size = ftell(file);
253 fseek(file, 4, SEEK_SET);
254 }
255}
256
257DiskNodeFileReadHandle::~DiskNodeFileReadHandle() {
258 close();
259}
260
261void DiskNodeFileReadHandle::close() {
262 freeNode(root_node);
263 file_size = 0;
264 FileHandle::close();
265 free(cache);
266}
267
268bool DiskNodeFileReadHandle::renewCache() {
269 if (!cache) {
270 cache = (uint8_t*)malloc(cache_size);
271 }
272 cache_length = fread(cache, 1, cache_size, file);
273
274 if (cache_length == 0 || ferror(file)) {
275 return false;
276 }
277 local_read_index = 0;
278 return true;
279}
280
281BinaryNode* DiskNodeFileReadHandle::getRootNode() {
282 assert(root_node == nullptr); // You should never do this twice
283 uint8_t first;
284 fread(&first, 1, 1, file);
285 if (first == NODE_START) {
286 root_node = getNode(nullptr);
287 root_node->load();
288 return root_node;
289 } else {
290 error_code = FILE_SYNTAX_ERROR;
291 return nullptr;
292 }
293}
294
295//=============================================================================
296// Binary file node
297
298BinaryNode::BinaryNode(NodeFileReadHandle* file, BinaryNode* parent) :
299 read_offset(0),
300 file(file),
301 parent(parent),
302 child(nullptr) {
303 ////
304}
305
306BinaryNode::~BinaryNode() {
307 file->freeNode(child);
308}
309
310BinaryNode* BinaryNode::getChild() {
311 ASSERT(file);
312 ASSERT(child == nullptr);
313
314 if (file->last_was_start) {
315 child = file->getNode(this);
316 child->load();
317 return child;
318 }
319 return nullptr;
320}
321
322bool BinaryNode::getRAW(uint8_t* ptr, size_t sz) {
323 if (read_offset + sz > data.size()) {
324 read_offset = data.size();
325 return false;
326 }
327 memcpy(ptr, data.data() + read_offset, sz);
328 read_offset += sz;
329 return true;
330}
331
332bool BinaryNode::getRAW(std::string& str, size_t sz) {
333 if (read_offset + sz > data.size()) {
334 read_offset = data.size();
335 return false;
336 }
337 str.assign(data.data() + read_offset, sz);
338 read_offset += sz;
339 return true;
340}
341
342bool BinaryNode::getString(std::string& str) {
343 uint16_t len;
344 if (!getU16(len)) {
345 return false;
346 }
347 return getRAW(str, len);
348}
349
350bool BinaryNode::getLongString(std::string& str) {
351 uint32_t len;
352 if (!getU32(len)) {
353 return false;
354 }
355 return getRAW(str, len);
356}
357
358BinaryNode* BinaryNode::advance() {
359 // Advance this to the next position
360 ASSERT(file);
361
362 if (file->error_code != FILE_NO_ERROR) {
363 return nullptr;
364 }
365
366 if (child == nullptr) {
367 getChild();
368 }
369 // We need to move the cursor to the next node, since we're still iterating our child node!
370 while (child) {
371 // both functions modify ourselves and sets child to nullptr, so loop will be aborted
372 // possibly change to assignment ?
373 child->getChild();
374 child->advance();
375 }
376
377 if (file->last_was_start) {
378 return nullptr;
379 } else {
380 // Last was end (0xff)
381 // Read next byte to decide if there is another node following this
382 uint8_t*& cache = file->cache;
383 size_t& cache_length = file->cache_length;
384 size_t& local_read_index = file->local_read_index;
385
386 if (local_read_index >= cache_length) {
387 if (!file->renewCache()) {
388 // Failed to renew, exit
389 parent->child = nullptr;
390 file->freeNode(this);
391 return nullptr;
392 }
393 }
394
395 uint8_t op = cache[local_read_index];
396 ++local_read_index;
397
398 if (op == NODE_START) {
399 // Another node follows this.
400 // Load this node as the next one
401 read_offset = 0;
402 data.clear();
403 load();
404 return this;
405 } else if (op == NODE_END) {
406 // End of this child-tree
407 parent->child = nullptr;
408 file->last_was_start = false;
409 file->freeNode(this);
410 return nullptr;
411 } else {
412 file->error_code = FILE_SYNTAX_ERROR;
413 return nullptr;
414 }
415 }
416}
417
418void BinaryNode::load() {
419 ASSERT(file);
420 // Read until next node starts
421 uint8_t*& cache = file->cache;
422 size_t& cache_length = file->cache_length;
423 size_t& local_read_index = file->local_read_index;
424 while (true) {
425 if (local_read_index >= cache_length) {
426 if (!file->renewCache()) {
427 // Failed to renew, exit
428 file->error_code = FILE_PREMATURE_END;
429 return;
430 }
431 }
432
433 uint8_t op = cache[local_read_index];
434 ++local_read_index;
435
436 switch (op) {
437 case NODE_START: {
438 file->last_was_start = true;
439 return;
440 }
441
442 case NODE_END: {
443 file->last_was_start = false;
444 return;
445 }
446
447 case ESCAPE_CHAR: {
448 if (local_read_index >= cache_length) {
449 if (!file->renewCache()) {
450 // Failed to renew, exit
451 file->error_code = FILE_PREMATURE_END;
452 return;
453 }
454 }
455
456 op = cache[local_read_index];
457 ++local_read_index;
458 break;
459 }
460
461 default:
462 break;
463 }
464 // std::cout << "Appending..." << std::endl;
465 data.append(1, op);
466 }
467}
468
469//=============================================================================
470// node file binary write handle
471
472FileWriteHandle::FileWriteHandle(const std::string& name) {
473#if defined __VISUALC__ && defined _UNICODE
474 file = _wfopen(string2wstring(name).c_str(), L"wb");
475#else
476 file = fopen(name.c_str(), "wb");
477#endif
478 if (file == nullptr || ferror(file)) {
479 error_code = FILE_COULD_NOT_OPEN;
480 }
481}
482
483FileWriteHandle::~FileWriteHandle() {
484 ////
485}
486
487bool FileWriteHandle::addString(const std::string& str) {
488 if (str.size() > 0xFFFF) {
489 error_code = FILE_STRING_TOO_LONG;
490 return false;
491 }
492 addU16(uint16_t(str.size()));
493 addRAW(str);
494 return true;
495}
496
497bool FileWriteHandle::addString(const char* str) {
498 size_t len = strlen(str);
499 if (len > 0xFFFF) {
500 error_code = FILE_STRING_TOO_LONG;
501 return false;
502 }
503 addU16(uint16_t(len));
504 fwrite(str, 1, len, file);
505 return true;
506}
507
508bool FileWriteHandle::addLongString(const std::string& str) {
509 addU32(uint32_t(str.size()));
510 addRAW(str);
511 return true;
512}
513
514bool FileWriteHandle::addRAW(const std::string& str) {
515 fwrite(str.c_str(), 1, str.size(), file);
516 return ferror(file) == 0;
517}
518
519bool FileWriteHandle::addRAW(const uint8_t* ptr, size_t sz) {
520 fwrite(ptr, 1, sz, file);
521 return ferror(file) == 0;
522}
523
524//=============================================================================
525// Disk based node file write handle
526
527DiskNodeFileWriteHandle::DiskNodeFileWriteHandle(const std::string& name, const std::string& identifier) {
528#if defined __VISUALC__ && defined _UNICODE
529 file = _wfopen(string2wstring(name).c_str(), L"wb");
530#else
531 file = fopen(name.c_str(), "wb");
532#endif
533 if (!file || ferror(file)) {
534 error_code = FILE_COULD_NOT_OPEN;
535 return;
536 }
537 if (identifier.length() != 4) {
538 error_code = FILE_INVALID_IDENTIFIER;
539 return;
540 }
541
542 fwrite(identifier.c_str(), 1, 4, file);
543 if (!cache) {
544 cache = (uint8_t*)malloc(cache_size + 1);
545 }
546 local_write_index = 0;
547}
548
549DiskNodeFileWriteHandle::~DiskNodeFileWriteHandle() {
550 close();
551}
552
553void DiskNodeFileWriteHandle::close() {
554 if (file) {
555 renewCache();
556 fclose(file);
557 file = nullptr;
558 error_code = FILE_NO_ERROR;
559 }
560}
561
562void DiskNodeFileWriteHandle::renewCache() {
563 if (cache) {
564 fwrite(cache, local_write_index, 1, file);
565 if (ferror(file) != 0) {
566 error_code = FILE_WRITE_ERROR;
567 }
568 } else {
569 cache = (uint8_t*)malloc(cache_size + 1);
570 }
571 local_write_index = 0;
572}
573
574//=============================================================================
575// Memory based node file write handle
576
577MemoryNodeFileWriteHandle::MemoryNodeFileWriteHandle() {
578 if (!cache) {
579 cache = (uint8_t*)malloc(cache_size + 1);
580 }
581 local_write_index = 0;
582}
583
584MemoryNodeFileWriteHandle::~MemoryNodeFileWriteHandle() {
585 close();
586}
587
588void MemoryNodeFileWriteHandle::reset() {
589 memset(cache, 0xAA, cache_size);
590 local_write_index = 0;
591}
592
593void MemoryNodeFileWriteHandle::close() {
594 free(cache);
595 cache = nullptr;
596}
597
598uint8_t* MemoryNodeFileWriteHandle::getMemory() {
599 return cache;
600}
601
602size_t MemoryNodeFileWriteHandle::getSize() {
603 return local_write_index;
604}
605
606void MemoryNodeFileWriteHandle::renewCache() {
607 if (cache) {
608 cache_size = cache_size * 2;
609 cache = (uint8_t*)realloc(cache, cache_size);
610 if (!cache) {
611 exit(1);
612 }
613 } else {
614 cache = (uint8_t*)malloc(cache_size + 1);
615 }
616}
617
618//=============================================================================
619// Node file write handle
620
621NodeFileWriteHandle::NodeFileWriteHandle() :
622 cache(nullptr),
623 cache_size(0x7FFF),
624 local_write_index(0) {
625 ////
626}
627
628NodeFileWriteHandle::~NodeFileWriteHandle() {
629 free(cache);
630}
631
632bool NodeFileWriteHandle::addNode(uint8_t nodetype) {
633 cache[local_write_index++] = NODE_START;
634 if (local_write_index >= cache_size) {
635 renewCache();
636 }
637
638 cache[local_write_index++] = nodetype;
639 if (local_write_index >= cache_size) {
640 renewCache();
641 }
642
643 return error_code == FILE_NO_ERROR;
644}
645
646bool NodeFileWriteHandle::endNode() {
647 cache[local_write_index++] = NODE_END;
648 if (local_write_index >= cache_size) {
649 renewCache();
650 }
651
652 return error_code == FILE_NO_ERROR;
653}
654
655bool NodeFileWriteHandle::addU8(uint8_t u8) {
656 writeBytes(&u8, sizeof(u8));
657 return error_code == FILE_NO_ERROR;
658}
659
660bool NodeFileWriteHandle::addByte(uint8_t u8) {
661 writeBytes(&u8, sizeof(u8));
662 return error_code == FILE_NO_ERROR;
663}
664
665bool NodeFileWriteHandle::addU16(uint16_t u16) {
666 writeBytes(reinterpret_cast<uint8_t*>(&u16), sizeof(u16));
667 return error_code == FILE_NO_ERROR;
668}
669
670bool NodeFileWriteHandle::addU32(uint32_t u32) {
671 writeBytes(reinterpret_cast<uint8_t*>(&u32), sizeof(u32));
672 return error_code == FILE_NO_ERROR;
673}
674
675bool NodeFileWriteHandle::addU64(uint64_t u64) {
676 writeBytes(reinterpret_cast<uint8_t*>(&u64), sizeof(u64));
677 return error_code == FILE_NO_ERROR;
678}
679
680bool NodeFileWriteHandle::addString(const std::string& str) {
681 if (str.size() > 0xFFFF) {
682 error_code = FILE_STRING_TOO_LONG;
683 return false;
684 }
685 addU16(uint16_t(str.size()));
686 addRAW((const uint8_t*)str.c_str(), str.size());
687 return error_code == FILE_NO_ERROR;
688}
689
690bool NodeFileWriteHandle::addLongString(const std::string& str) {
691 addU32(uint32_t(str.size()));
692 addRAW((const uint8_t*)str.c_str(), str.size());
693 return error_code == FILE_NO_ERROR;
694}
695
696bool NodeFileWriteHandle::addRAW(std::string& str) {
697 writeBytes(reinterpret_cast<uint8_t*>(const_cast<char*>(str.data())), str.size());
698 return error_code == FILE_NO_ERROR;
699}
700
701bool NodeFileWriteHandle::addRAW(const uint8_t* ptr, size_t sz) {
702 writeBytes(ptr, sz);
703 return error_code == FILE_NO_ERROR;
704}
705