////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program 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.
//
// This program 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 .
////////////////////////////////////////////////////////////////////////
#ifndef __FILELOADER__
#define __FILELOADER__
#include "otsystem.h"
struct NodeStruct;
typedef NodeStruct* NODE;
struct NodeStruct
{
NodeStruct()
{
start = propsSize = type = 0;
next = child = 0;
}
virtual ~NodeStruct() {}
uint32_t start, propsSize, type;
NodeStruct* next;
NodeStruct* child;
static void clearNet(NodeStruct* root) {if(root) clearChild(root); }
private:
static void clearNext(NodeStruct* node)
{
NodeStruct* deleteNode = node;
NodeStruct* nextNode;
while(deleteNode)
{
if(deleteNode->child)
clearChild(deleteNode->child);
nextNode = deleteNode->next;
delete deleteNode;
deleteNode = nextNode;
}
}
static void clearChild(NodeStruct* node)
{
if(node->child)
clearChild(node->child);
if(node->next)
clearNext(node->next);
delete node;
}
};
#define NO_NODE 0
enum FILELOADER_ERRORS
{
ERROR_NONE,
ERROR_INVALID_FILE_VERSION,
ERROR_CAN_NOT_OPEN,
ERROR_CAN_NOT_CREATE,
ERROR_EOF,
ERROR_SEEK_ERROR,
ERROR_NOT_OPEN,
ERROR_INVALID_NODE,
ERROR_INVALID_FORMAT,
ERROR_TELL_ERROR,
ERROR_COULDNOTWRITE,
ERROR_CACHE_ERROR,
};
class PropStream;
class FileLoader
{
public:
FileLoader();
virtual ~FileLoader();
bool openFile(const char* filename, bool write, bool caching = false);
const uint8_t* getProps(const NODE, uint32_t &size);
bool getProps(const NODE, PropStream& props);
const NODE getChildNode(const NODE parent, uint32_t &type);
const NODE getNextNode(const NODE prev, uint32_t &type);
void startNode(uint8_t type);
void endNode();
int32_t setProps(void* data, uint16_t size);
int32_t getError() {return m_lastError;}
void clearError() {m_lastError = ERROR_NONE;}
protected:
enum SPECIAL_BYTES
{
NODE_START = 0xFE,
NODE_END = 0xFF,
ESCAPE_CHAR = 0xFD,
};
bool parseNode(NODE node);
inline bool readByte(int32_t &value);
inline bool readBytes(unsigned char* buffer, int32_t size, int32_t pos);
inline bool checks(const NODE node);
inline bool safeSeek(uint32_t pos);
inline bool safeTell(int32_t &pos);
public:
inline bool writeData(const void* data, int32_t size, bool unescape)
{
for(int32_t i = 0; i < size; ++i)
{
uint8_t c = *(((uint8_t*)data) + i);
if(unescape && (c == NODE_START || c == NODE_END || c == ESCAPE_CHAR))
{
uint8_t escape = ESCAPE_CHAR;
size_t value = fwrite(&escape, 1, 1, m_file);
if(value != 1)
{
m_lastError = ERROR_COULDNOTWRITE;
return false;
}
}
size_t value = fwrite(&c, 1, 1, m_file);
if(value != 1)
{
m_lastError = ERROR_COULDNOTWRITE;
return false;
}
}
return true;
}
protected:
FILE* m_file;
FILELOADER_ERRORS m_lastError;
NODE m_root;
uint32_t m_buffer_size;
uint8_t* m_buffer;
bool m_use_cache;
struct _cache
{
uint32_t loaded, base;
uint8_t* data;
size_t size;
};
#define CACHE_BLOCKS 3
uint32_t m_cache_size;
_cache m_cached_data[CACHE_BLOCKS];
#define NO_VALID_CACHE 0xFFFFFFFF
uint32_t m_cache_index, m_cache_offset;
inline uint32_t getCacheBlock(uint32_t pos);
int32_t loadCacheBlock(uint32_t pos);
};
class PropStream
{
public:
PropStream() {end = NULL; p = NULL;}
virtual ~PropStream() {}
void init(const char* a, uint32_t size)
{
p = a;
end = a + size;
}
int32_t size() const {return end - p;}
template
inline bool GET_STRUCT(T* &ret)
{
if(size() < (int32_t)sizeof(T))
{
ret = NULL;
return false;
}
ret = (T*)p;
p += sizeof(T);
return true;
}
template
inline bool GET_VALUE(T &ret)
{
if(size() < (int32_t)sizeof(T))
return false;
ret = *((T*)p);
p += sizeof(T);
return true;
}
inline bool GET_TIME(time_t &ret) {return GET_VALUE(ret);}
inline bool GET_ULONG(uint32_t &ret) {return GET_VALUE(ret);}
inline bool GET_USHORT(uint16_t &ret) {return GET_VALUE(ret);}
inline bool GET_UCHAR(uint8_t &ret) {return GET_VALUE(ret);}
inline bool GET_STRING(std::string& ret)
{
uint16_t strLen;
if(!GET_USHORT(strLen))
return false;
if(size() < (int32_t)strLen)
return false;
char* str = new char[strLen + 1];
memcpy(str, p, strLen);
str[strLen] = 0;
ret.assign(str, strLen);
delete[] str;
p = p + strLen;
return true;
}
inline bool GET_LSTRING(std::string& ret)
{
uint32_t strLen;
if(!GET_ULONG(strLen))
return false;
if(size() < (int32_t)strLen)
return false;
char* str = new char[strLen + 1];
memcpy(str, p, strLen);
str[strLen] = 0;
ret.assign(str, strLen);
delete[] str;
p = p + strLen;
return true;
}
inline bool GET_NSTRING(uint16_t strLen, std::string& ret)
{
if(size() < (int32_t)strLen)
return false;
char* str = new char[strLen + 1];
memcpy(str, p, strLen);
str[strLen] = 0;
ret.assign(str, strLen);
delete[] str;
p += strLen;
return true;
}
inline bool SKIP_N(int16_t n)
{
if(size() < n)
return false;
p += n;
return true;
}
protected:
const char* p;
const char* end;
};
class PropWriteStream
{
public:
PropWriteStream()
{
buffer = (char*)malloc(32 * sizeof(char));
bufferSize = 32;
size = 0;
memset(buffer, 0, 32 * sizeof(char));
}
virtual ~PropWriteStream() {free(buffer);}
const char* getStream(uint32_t& _size) const
{
_size = size;
return buffer;
}
//TODO: might need temp buffer and zero fill the memory chunk allocated by realloc
template
inline void ADD_TYPE(T* add)
{
if((bufferSize - size) < sizeof(T))
{
bufferSize += ((sizeof(T) + 0x1F) & 0xFFFFFFE0);
buffer = (char*)realloc(buffer, bufferSize);
}
memcpy(&buffer[size], (char*)add, sizeof(T));
size += sizeof(T);
}
template
inline void ADD_VALUE(T add)
{
if((bufferSize - size) < sizeof(T))
{
bufferSize += ((sizeof(T) + 0x1F) & 0xFFFFFFE0);
buffer = (char*)realloc(buffer, bufferSize);
}
memcpy(&buffer[size], &add, sizeof(T));
size += sizeof(T);
}
inline void ADD_ULONG(uint32_t ret) {ADD_VALUE(ret);}
inline void ADD_USHORT(uint16_t ret) {ADD_VALUE(ret);}
inline void ADD_UCHAR(uint8_t ret) {ADD_VALUE(ret);}
inline void ADD_STRING(const std::string& add)
{
uint16_t strLen = add.size();
ADD_USHORT(strLen);
if((bufferSize - size) < strLen)
{
bufferSize += ((strLen + 0x1F) & 0xFFFFFFE0);
buffer = (char*)realloc(buffer, bufferSize);
}
memcpy(&buffer[size], add.c_str(), strLen);
size += strLen;
}
inline void ADD_LSTRING(const std::string& add)
{
uint16_t strLen = add.size();
ADD_USHORT(strLen);
if((bufferSize - size) < strLen)
{
bufferSize += ((strLen + 0x1F) & 0xFFFFFFE0);
buffer = (char*)realloc(buffer, bufferSize);
}
memcpy(&buffer[size], add.c_str(), strLen);
size += strLen;
}
protected:
char* buffer;
uint32_t bufferSize, size;
};
#endif