225 lines
6.1 KiB
C++
Executable File
225 lines
6.1 KiB
C++
Executable File
// This file is part of Noggit3, licensed under GNU General Public License (version 3).
|
|
|
|
#pragma once
|
|
|
|
#include <cassert>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <stdexcept>
|
|
#include <cstring>
|
|
#include <algorithm>
|
|
#include <memory>
|
|
#include <iostream>
|
|
#include <blizzard-archive-library/include/ClientData.hpp>
|
|
#include <noggit/Log.h>
|
|
|
|
class DBCFile
|
|
{
|
|
public:
|
|
explicit DBCFile(const std::string& filename);
|
|
|
|
// Open database. It must be openened before it can be used.
|
|
void open(std::shared_ptr<BlizzardArchive::ClientData> clientData);
|
|
void save();
|
|
|
|
class NotFound : public std::runtime_error
|
|
{
|
|
public:
|
|
NotFound() : std::runtime_error("Key was not found.")
|
|
{ }
|
|
};
|
|
|
|
class AlreadyExists : public std::runtime_error
|
|
{
|
|
public:
|
|
AlreadyExists() : std::runtime_error("Key already exists.")
|
|
{ }
|
|
};
|
|
|
|
class Iterator;
|
|
class Record
|
|
{
|
|
public:
|
|
const float& getFloat(size_t field) const
|
|
{
|
|
assert(field < file.fieldCount);
|
|
return *reinterpret_cast<float*>(offset + field * 4);
|
|
}
|
|
const unsigned int& getUInt(size_t field) const
|
|
{
|
|
assert(field < file.fieldCount);
|
|
return *reinterpret_cast<unsigned int*>(offset + field * 4);
|
|
}
|
|
const int& getInt(size_t field) const
|
|
{
|
|
assert(field < file.fieldCount);
|
|
return *reinterpret_cast<int*>(offset + field * 4);
|
|
}
|
|
const char *getString(size_t field) const
|
|
{
|
|
assert(field < file.fieldCount);
|
|
size_t stringOffset = getUInt(field);
|
|
assert(stringOffset < file.stringSize);
|
|
return file.stringTable.data() + stringOffset;
|
|
}
|
|
const char *getLocalizedString(size_t field, int locale = -1) const
|
|
{
|
|
int loc = locale;
|
|
if (locale == -1)
|
|
{
|
|
assert(field < file.fieldCount - 8);
|
|
for (loc = 0; loc < 15; loc++)
|
|
{
|
|
size_t stringOffset = getUInt(field + loc);
|
|
if (stringOffset != 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
assert(field + loc < file.fieldCount);
|
|
size_t stringOffset = getUInt(field + loc);
|
|
assert(stringOffset < file.stringSize);
|
|
return file.stringTable.data() + stringOffset;
|
|
}
|
|
|
|
template<typename T> inline
|
|
void write(size_t field, T val)
|
|
{
|
|
static_assert(sizeof(T) == 4, "This function only writes int/uint/float values.");
|
|
assert(field < file.fieldCount);
|
|
*reinterpret_cast<T*>(offset + field * 4) = val;
|
|
}
|
|
|
|
void writeString(size_t field, const std::string& val)
|
|
{
|
|
assert(field < file.fieldCount);
|
|
|
|
if (!val.size())
|
|
{
|
|
*reinterpret_cast<unsigned int*>(offset + field * 4) = 0;
|
|
return;
|
|
}
|
|
|
|
size_t old_size = file.stringTable.size();
|
|
*reinterpret_cast<unsigned int*>(offset + field * 4) = static_cast<unsigned int>(file.stringTable.size());
|
|
file.stringTable.resize(old_size + val.size() + 1);
|
|
std::copy(val.c_str(), val.c_str() + val.size() + 1, file.stringTable.data() + old_size);
|
|
file.stringSize += static_cast<std::uint32_t>(val.size() + 1);
|
|
}
|
|
|
|
void writeLocalizedString(size_t field, const std::string& val, int locale)
|
|
{
|
|
assert(field < file.fieldCount);
|
|
assert(locale < 16);
|
|
|
|
if (!val.size())
|
|
{
|
|
*reinterpret_cast<unsigned int*>(offset + ((field + locale) * 4)) = 0;
|
|
return;
|
|
}
|
|
|
|
size_t old_size = file.stringTable.size();
|
|
*reinterpret_cast<unsigned int*>(offset + ((field + locale) * 4)) = static_cast<unsigned int>(file.stringTable.size());
|
|
file.stringTable.resize(old_size + val.size() + 1);
|
|
std::copy(val.c_str(), val.c_str() + val.size() + 1, file.stringTable.data() + old_size);
|
|
file.stringSize += static_cast<std::uint32_t>(val.size() + 1);
|
|
}
|
|
|
|
private:
|
|
Record(DBCFile &pfile, unsigned char *poffset) : file(pfile), offset(poffset) {}
|
|
DBCFile &file;
|
|
unsigned char *offset;
|
|
|
|
friend class DBCFile;
|
|
friend class DBCFile::Iterator;
|
|
};
|
|
/** Iterator that iterates over records
|
|
*/
|
|
class Iterator
|
|
{
|
|
public:
|
|
Iterator(DBCFile &file, unsigned char *offset) :
|
|
record(file, offset) {}
|
|
/// Advance (prefix only)
|
|
Iterator & operator++() {
|
|
record.offset += record.file.recordSize;
|
|
return *this;
|
|
}
|
|
/// Return address of current instance
|
|
Record & operator*() { return record; }
|
|
Record* operator->() {
|
|
return &record;
|
|
}
|
|
/// Comparison
|
|
bool operator==( Iterator const &b) const
|
|
{
|
|
return record.offset == b.record.offset;
|
|
}
|
|
private:
|
|
Record record;
|
|
};
|
|
|
|
inline Record getRecord(size_t id)
|
|
{
|
|
return Record(*this, data.data() + id*recordSize);
|
|
}
|
|
|
|
inline Iterator begin()
|
|
{
|
|
return Iterator(*this, data.data());
|
|
}
|
|
inline Iterator end()
|
|
{
|
|
return Iterator(*this, data.data() + data.size());
|
|
}
|
|
|
|
inline size_t getRecordCount() { return recordCount; }
|
|
inline size_t getFieldCount() { return fieldCount; }
|
|
inline Record getByID(unsigned int id, size_t field = 0)
|
|
{
|
|
for (Iterator i = begin(); i != end(); ++i)
|
|
{
|
|
if (i->getUInt(field) == id)
|
|
return (*i);
|
|
}
|
|
LogDebug << "Tried to get a not existing row in " << filename << " (ID = " << id << ")!" << std::endl;
|
|
throw NotFound();
|
|
}
|
|
inline bool CheckIfIdExists(unsigned int id, size_t field = 0)
|
|
{
|
|
for (Iterator i = begin(); i != end(); ++i)
|
|
{
|
|
if (i->getUInt(field) == id)
|
|
return (true);
|
|
}
|
|
return (false);
|
|
}
|
|
inline int getRecordRowId(unsigned int id, size_t field = 0)
|
|
{
|
|
int row_id = 0;
|
|
for (Iterator i = begin(); i != end(); ++i)
|
|
{
|
|
if (i->getUInt(field) == id)
|
|
return row_id;
|
|
|
|
row_id++;
|
|
}
|
|
LogError << "Tried to get a not existing row in " << filename << " (ID = " << id << ")!" << std::endl;
|
|
throw NotFound();
|
|
}
|
|
|
|
Record addRecord(size_t id, size_t id_field = 0);
|
|
Record addRecordCopy(size_t id, size_t id_from, size_t id_field = 0);
|
|
void removeRecord(size_t id, size_t id_field = 0);
|
|
int getEmptyRecordID(size_t id_field = 0);
|
|
|
|
private:
|
|
std::string filename;
|
|
std::uint32_t recordSize;
|
|
std::uint32_t recordCount;
|
|
std::uint32_t fieldCount;
|
|
std::uint32_t stringSize;
|
|
std::vector<unsigned char> data;
|
|
std::vector<char> stringTable;
|
|
};
|