////////////////////////////////////////////////////////////////////// // This file is part of Remere's Map Editor ////////////////////////////////////////////////////////////////////// // Remere's Map Editor is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // Remere's Map Editor is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . ////////////////////////////////////////////////////////////////////// #include "main.h" #include "filehandle.h" uint8_t NodeFileWriteHandle::NODE_START = ::NODE_START; uint8_t NodeFileWriteHandle::NODE_END = ::NODE_END; uint8_t NodeFileWriteHandle::ESCAPE_CHAR = ::ESCAPE_CHAR; bool FileHandle::seek(size_t offset, int origin) { if (file) { return fseek(file, static_cast(offset), origin) == 0; } return false; } size_t FileHandle::tell() { if (file) { return ftell(file); } return 0; } void FileHandle::close() { if (file) { fclose(file); file = nullptr; } } std::string FileHandle::getErrorMessage() { switch (error_code) { case FILE_NO_ERROR: return "No error"; case FILE_COULD_NOT_OPEN: return "Could not open file"; case FILE_INVALID_IDENTIFIER: return "File magic number not recognized"; case FILE_STRING_TOO_LONG: return "Too long string encountered"; case FILE_READ_ERROR: return "Failed to read from file"; case FILE_WRITE_ERROR: return "Failed to write to file"; case FILE_SYNTAX_ERROR: return "Node file syntax error"; case FILE_PREMATURE_END: return "File end encountered unexpectedly"; } if (file == nullptr) { return "Could not open file (2)"; } if (ferror(file)) { return "Internal file error #" + i2s(ferror(file)); } return "No error"; } //============================================================================= // File read handle FileReadHandle::FileReadHandle(const std::string &name) : file_size(0) { #if defined __VISUALC__ && defined _UNICODE file = _wfopen(string2wstring(name).c_str(), L"rb"); #else file = fopen(name.c_str(), "rb"); #endif if (!file || ferror(file)) { error_code = FILE_COULD_NOT_OPEN; } else { fseek(file, 0, SEEK_END); file_size = ftell(file); fseek(file, 0, SEEK_SET); } } FileReadHandle::~FileReadHandle() { //// } void FileReadHandle::close() { file_size = 0; FileHandle::close(); } bool FileReadHandle::getRAW(uint8_t* ptr, size_t sz) { size_t o = fread(ptr, 1, sz, file); if (o != sz) { error_code = FILE_READ_ERROR; return false; } return true; } bool FileReadHandle::getRAW(std::string &str, size_t sz) { str.resize(sz); size_t o = fread(const_cast(str.data()), 1, sz, file); if (o != sz) { error_code = FILE_READ_ERROR; return false; } return true; } bool FileReadHandle::getString(std::string &str) { uint16_t sz; if (!getU16(sz)) { error_code = FILE_READ_ERROR; return false; } return getRAW(str, sz); } bool FileReadHandle::getLongString(std::string &str) { uint32_t sz; if (!getU32(sz)) { error_code = FILE_READ_ERROR; return false; } return getRAW(str, sz); } //============================================================================= // Node file read handle NodeFileReadHandle::NodeFileReadHandle() : last_was_start(false), cache(nullptr), cache_size(32768), cache_length(0), local_read_index(0), root_node(nullptr) { //// } NodeFileReadHandle::~NodeFileReadHandle() { while (!unused.empty()) { free(unused.top()); unused.pop(); } } BinaryNode* NodeFileReadHandle::getNode(BinaryNode* parent) { void* mem; if (unused.empty()) { mem = malloc(sizeof(BinaryNode)); } else { mem = unused.top(); unused.pop(); } return new (mem) BinaryNode(this, parent); } void NodeFileReadHandle::freeNode(BinaryNode* node) { if (node) { node->~BinaryNode(); unused.push(node); } } //============================================================================= // Memory based node file read handle MemoryNodeFileReadHandle::MemoryNodeFileReadHandle(const uint8_t* data, size_t size) { assign(data, size); } void MemoryNodeFileReadHandle::assign(const uint8_t* data, size_t size) { freeNode(root_node); root_node = nullptr; // Highly volatile, but we know we're not gonna modify cache = const_cast(data); cache_size = cache_length = size; local_read_index = 0; } MemoryNodeFileReadHandle::~MemoryNodeFileReadHandle() { freeNode(root_node); } void MemoryNodeFileReadHandle::close() { assign(nullptr, 0); } bool MemoryNodeFileReadHandle::renewCache() { return false; } BinaryNode* MemoryNodeFileReadHandle::getRootNode() { assert(root_node == nullptr); // You should never do this twice local_read_index++; // Skip first NODE_START last_was_start = true; root_node = getNode(nullptr); root_node->load(); return root_node; } //============================================================================= // File based node file read handle DiskNodeFileReadHandle::DiskNodeFileReadHandle(const std::string &name, const std::vector &acceptable_identifiers) : file_size(0) { #if defined __VISUALC__ && defined _UNICODE file = _wfopen(string2wstring(name).c_str(), L"rb"); #else file = fopen(name.c_str(), "rb"); #endif if (!file || ferror(file)) { error_code = FILE_COULD_NOT_OPEN; } else { char ver[4]; if (fread(ver, 1, 4, file) != 4) { fclose(file); error_code = FILE_SYNTAX_ERROR; return; } // 0x00 00 00 00 is accepted as a wildcard version if (ver[0] != 0 || ver[1] != 0 || ver[2] != 0 || ver[3] != 0) { bool accepted = false; for (std::vector::const_iterator id_iter = acceptable_identifiers.begin(); id_iter != acceptable_identifiers.end(); ++id_iter) { if (memcmp(ver, id_iter->c_str(), 4) == 0) { accepted = true; break; } } if (!accepted) { fclose(file); error_code = FILE_SYNTAX_ERROR; return; } } fseek(file, 0, SEEK_END); file_size = ftell(file); fseek(file, 4, SEEK_SET); } } DiskNodeFileReadHandle::~DiskNodeFileReadHandle() { close(); } void DiskNodeFileReadHandle::close() { freeNode(root_node); file_size = 0; FileHandle::close(); free(cache); } bool DiskNodeFileReadHandle::renewCache() { if (!cache) { cache = (uint8_t*)malloc(cache_size); } cache_length = fread(cache, 1, cache_size, file); if (cache_length == 0 || ferror(file)) { return false; } local_read_index = 0; return true; } BinaryNode* DiskNodeFileReadHandle::getRootNode() { assert(root_node == nullptr); // You should never do this twice uint8_t first; size_t numRead = fread(&first, 1, 1, file); if (numRead != 1) { error_code = FILE_READ_ERROR; return nullptr; } if (first == NODE_START) { root_node = getNode(nullptr); root_node->load(); return root_node; } else { error_code = FILE_SYNTAX_ERROR; return nullptr; } } //============================================================================= // Binary file node BinaryNode::BinaryNode(NodeFileReadHandle* file, BinaryNode* parent) : read_offset(0), file(file), parent(parent), child(nullptr) { //// } BinaryNode::~BinaryNode() { file->freeNode(child); } BinaryNode* BinaryNode::getChild() { ASSERT(file); ASSERT(child == nullptr); if (file->last_was_start) { child = file->getNode(this); child->load(); return child; } return nullptr; } bool BinaryNode::getRAW(uint8_t* ptr, size_t sz) { if (read_offset + sz > data.size()) { read_offset = data.size(); return false; } memcpy(ptr, data.data() + read_offset, sz); read_offset += sz; return true; } bool BinaryNode::getRAW(std::string &str, size_t sz) { if (read_offset + sz > data.size()) { read_offset = data.size(); return false; } str.assign(data.data() + read_offset, sz); read_offset += sz; return true; } bool BinaryNode::getString(std::string &str) { uint16_t len; if (!getU16(len)) { return false; } return getRAW(str, len); } bool BinaryNode::getLongString(std::string &str) { uint32_t len; if (!getU32(len)) { return false; } return getRAW(str, len); } BinaryNode* BinaryNode::advance() { // Advance this to the next position ASSERT(file); if (file->error_code != FILE_NO_ERROR) { return nullptr; } if (child == nullptr) { getChild(); } // We need to move the cursor to the next node, since we're still iterating our child node! while (child) { // both functions modify ourselves and sets child to nullptr, so loop will be aborted // possibly change to assignment ? child->getChild(); child->advance(); } if (file->last_was_start) { return nullptr; } else { // Last was end (0xff) // Read next byte to decide if there is another node following this uint8_t*&cache = file->cache; size_t &cache_length = file->cache_length; size_t &local_read_index = file->local_read_index; if (local_read_index >= cache_length) { if (!file->renewCache()) { // Failed to renew, exit parent->child = nullptr; file->freeNode(this); return nullptr; } } uint8_t op = cache[local_read_index]; ++local_read_index; if (op == NODE_START) { // Another node follows this. // Load this node as the next one read_offset = 0; data.clear(); load(); return this; } else if (op == NODE_END) { // End of this child-tree parent->child = nullptr; file->last_was_start = false; file->freeNode(this); return nullptr; } else { file->error_code = FILE_SYNTAX_ERROR; return nullptr; } } } void BinaryNode::load() { ASSERT(file); // Read until next node starts uint8_t*&cache = file->cache; size_t &cache_length = file->cache_length; size_t &local_read_index = file->local_read_index; while (true) { if (local_read_index >= cache_length) { if (!file->renewCache()) { // Failed to renew, exit file->error_code = FILE_PREMATURE_END; return; } } uint8_t op = cache[local_read_index]; ++local_read_index; switch (op) { case NODE_START: { file->last_was_start = true; return; } case NODE_END: { file->last_was_start = false; return; } case ESCAPE_CHAR: { if (local_read_index >= cache_length) { if (!file->renewCache()) { // Failed to renew, exit file->error_code = FILE_PREMATURE_END; return; } } op = cache[local_read_index]; ++local_read_index; break; } default: break; } // std::cout << "Appending..." << std::endl; data.append(1, op); } } //============================================================================= // node file binary write handle FileWriteHandle::FileWriteHandle(const std::string &name) { #if defined __VISUALC__ && defined _UNICODE file = _wfopen(string2wstring(name).c_str(), L"wb"); #else file = fopen(name.c_str(), "wb"); #endif if (file == nullptr || ferror(file)) { error_code = FILE_COULD_NOT_OPEN; } } FileWriteHandle::~FileWriteHandle() { //// } bool FileWriteHandle::addString(const std::string &str) { if (str.size() > 0xFFFF) { error_code = FILE_STRING_TOO_LONG; return false; } addU16(uint16_t(str.size())); addRAW(str); return true; } bool FileWriteHandle::addString(const char* str) { size_t len = strlen(str); if (len > 0xFFFF) { error_code = FILE_STRING_TOO_LONG; return false; } addU16(uint16_t(len)); fwrite(str, 1, len, file); return true; } bool FileWriteHandle::addLongString(const std::string &str) { addU32(uint32_t(str.size())); addRAW(str); return true; } bool FileWriteHandle::addRAW(const std::string &str) { fwrite(str.c_str(), 1, str.size(), file); return ferror(file) == 0; } bool FileWriteHandle::addRAW(const uint8_t* ptr, size_t sz) { fwrite(ptr, 1, sz, file); return ferror(file) == 0; } void FileWriteHandle::flush() { if (file) { fflush(file); } } //============================================================================= // Disk based node file write handle DiskNodeFileWriteHandle::DiskNodeFileWriteHandle(const std::string &name, const std::string &identifier) { #if defined __VISUALC__ && defined _UNICODE file = _wfopen(string2wstring(name).c_str(), L"wb"); #else file = fopen(name.c_str(), "wb"); #endif if (!file || ferror(file)) { error_code = FILE_COULD_NOT_OPEN; return; } if (identifier.length() != 4) { error_code = FILE_INVALID_IDENTIFIER; return; } fwrite(identifier.c_str(), 1, 4, file); if (!cache) { cache = (uint8_t*)malloc(cache_size + 1); } local_write_index = 0; } DiskNodeFileWriteHandle::~DiskNodeFileWriteHandle() { close(); } void DiskNodeFileWriteHandle::close() { if (file) { renewCache(); fclose(file); file = nullptr; error_code = FILE_NO_ERROR; } } void DiskNodeFileWriteHandle::renewCache() { if (cache) { fwrite(cache, local_write_index, 1, file); if (ferror(file) != 0) { error_code = FILE_WRITE_ERROR; } } else { cache = (uint8_t*)malloc(cache_size + 1); } local_write_index = 0; } //============================================================================= // Memory based node file write handle MemoryNodeFileWriteHandle::MemoryNodeFileWriteHandle() { if (!cache) { cache = (uint8_t*)malloc(cache_size + 1); } local_write_index = 0; } MemoryNodeFileWriteHandle::~MemoryNodeFileWriteHandle() { close(); } void MemoryNodeFileWriteHandle::reset() { memset(cache, 0xAA, cache_size); local_write_index = 0; } void MemoryNodeFileWriteHandle::close() { free(cache); cache = nullptr; } uint8_t* MemoryNodeFileWriteHandle::getMemory() { return cache; } size_t MemoryNodeFileWriteHandle::getSize() { return local_write_index; } void MemoryNodeFileWriteHandle::renewCache() { if (cache) { cache_size = cache_size * 2; cache = (uint8_t*)realloc(cache, cache_size); if (!cache) { exit(1); } } else { cache = (uint8_t*)malloc(cache_size + 1); } } //============================================================================= // Node file write handle NodeFileWriteHandle::NodeFileWriteHandle() : cache(nullptr), cache_size(0x7FFF), local_write_index(0) { //// } NodeFileWriteHandle::~NodeFileWriteHandle() { free(cache); } bool NodeFileWriteHandle::addNode(uint8_t nodetype) { cache[local_write_index++] = NODE_START; if (local_write_index >= cache_size) { renewCache(); } cache[local_write_index++] = nodetype; if (local_write_index >= cache_size) { renewCache(); } return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::endNode() { cache[local_write_index++] = NODE_END; if (local_write_index >= cache_size) { renewCache(); } return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addU8(uint8_t u8) { writeBytes(&u8, sizeof(u8)); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addByte(uint8_t u8) { writeBytes(&u8, sizeof(u8)); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addU16(uint16_t u16) { writeBytes(reinterpret_cast(&u16), sizeof(u16)); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addU32(uint32_t u32) { writeBytes(reinterpret_cast(&u32), sizeof(u32)); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addU64(uint64_t u64) { writeBytes(reinterpret_cast(&u64), sizeof(u64)); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addString(const std::string &str) { if (str.size() > 0xFFFF) { error_code = FILE_STRING_TOO_LONG; return false; } addU16(uint16_t(str.size())); addRAW((const uint8_t*)str.c_str(), str.size()); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addLongString(const std::string &str) { addU32(uint32_t(str.size())); addRAW((const uint8_t*)str.c_str(), str.size()); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addRAW(std::string &str) { writeBytes(reinterpret_cast(const_cast(str.data())), str.size()); return error_code == FILE_NO_ERROR; } bool NodeFileWriteHandle::addRAW(const uint8_t* ptr, size_t sz) { writeBytes(ptr, sz); return error_code == FILE_NO_ERROR; }