223 lines
4.7 KiB
C++
223 lines
4.7 KiB
C++
// ======================= ZoneTool =======================
|
|
// zonetool, a fastfile linker for various
|
|
// Call of Duty titles.
|
|
//
|
|
// Project: https://github.com/ZoneTool/zonetool
|
|
// Author: RektInator (https://github.com/RektInator)
|
|
// License: GNU GPL v3.0
|
|
// ========================================================
|
|
#include "stdafx.hpp"
|
|
|
|
namespace ZoneTool
|
|
{
|
|
namespace IW4
|
|
{
|
|
// LEGACY ZONETOOL CODE, FIX ME!
|
|
class CSV
|
|
{
|
|
protected:
|
|
std::string _name;
|
|
std::vector<std::vector<std::string>> _data;
|
|
|
|
public:
|
|
CSV(std::string name, char sep = ',')
|
|
: _name(name)
|
|
{
|
|
auto fp = FileSystem::FileOpen(name, "r"s);
|
|
|
|
if (fp)
|
|
{
|
|
long len = FileSystem::FileSize(fp);
|
|
auto buf = std::make_unique<char[]>(len + 1);
|
|
memset(buf.get(), 0, len + 1);
|
|
fread(buf.get(), len, 1, fp);
|
|
fclose(fp);
|
|
|
|
std::vector<std::string> rows = split(std::string(buf.get()), '\n');
|
|
|
|
for (auto& row : rows)
|
|
{
|
|
// Replace literal characters
|
|
std::size_t pos;
|
|
while ((pos = row.find("\\n")) != std::string::npos)
|
|
{
|
|
row.replace(pos, 2, "\n");
|
|
}
|
|
|
|
while ((pos = row.find("\\t")) != std::string::npos)
|
|
{
|
|
row.replace(pos, 2, "\t");
|
|
}
|
|
|
|
_data.push_back(split(row, sep));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::string entry(std::size_t row, std::size_t column)
|
|
{
|
|
return _data[row][column];
|
|
}
|
|
|
|
std::size_t rows()
|
|
{
|
|
return _data.size();
|
|
}
|
|
|
|
std::size_t columns(std::size_t row)
|
|
{
|
|
return _data[row].size();
|
|
}
|
|
|
|
std::size_t max_columns()
|
|
{
|
|
std::size_t _max = 0;
|
|
|
|
for (std::size_t row = 0; row < this->rows(); row++)
|
|
{
|
|
if (_max < this->columns(row))
|
|
_max = this->columns(row);
|
|
}
|
|
|
|
return _max;
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
for (std::size_t i = 0; i < _data.size(); i++)
|
|
{
|
|
for (std::size_t j = 0; j < _data[i].size(); j++)
|
|
_data[i][j].clear();
|
|
|
|
_data[i].clear();
|
|
}
|
|
|
|
_data.clear();
|
|
}
|
|
};
|
|
|
|
int StringTable_Hash(const char* string)
|
|
{
|
|
int hash = 0;
|
|
char* data = _strdup(string);
|
|
|
|
while (*data != 0)
|
|
{
|
|
hash = tolower(*data) + (31 * hash);
|
|
data++;
|
|
}
|
|
|
|
return hash;
|
|
}
|
|
|
|
StringTable* StringTable_Parse(std::string name, ZoneMemory* mem)
|
|
{
|
|
auto table = std::make_unique<CSV>(name);
|
|
auto* stringtable = mem->Alloc<StringTable>();
|
|
|
|
stringtable->name = mem->StrDup(name);
|
|
stringtable->rows = static_cast<int>(table->rows());
|
|
stringtable->columns = static_cast<int>(table->max_columns());
|
|
stringtable->strings = mem->Alloc<StringTableCell>(stringtable->rows * stringtable->columns);
|
|
|
|
for (size_t row = 0; row < table->rows(); row++)
|
|
{
|
|
for (size_t col = 0; col < table->columns(row); col++)
|
|
{
|
|
size_t entry = (row * stringtable->columns) + col;
|
|
stringtable->strings[entry].string = mem->StrDup(table->entry(row, col));
|
|
stringtable->strings[entry].hash = StringTable_Hash(stringtable->strings[entry].string);
|
|
}
|
|
}
|
|
|
|
return stringtable;
|
|
}
|
|
|
|
void IStringTable::init(const std::string& name, ZoneMemory* mem)
|
|
{
|
|
this->name_ = name;
|
|
this->asset_ = DB_FindXAssetHeader(this->type(), this->name().c_str()).stringtable;
|
|
|
|
if (FileSystem::FileExists(name))
|
|
{
|
|
ZONETOOL_INFO("Parsing stringtable %s...", name.c_str());
|
|
this->asset_ = StringTable_Parse(name, mem);
|
|
}
|
|
}
|
|
|
|
void IStringTable::prepare(ZoneBuffer* buf, ZoneMemory* mem)
|
|
{
|
|
}
|
|
|
|
void IStringTable::load_depending(IZone* zone)
|
|
{
|
|
}
|
|
|
|
std::string IStringTable::name()
|
|
{
|
|
return this->name_;
|
|
}
|
|
|
|
std::int32_t IStringTable::type()
|
|
{
|
|
return stringtable;
|
|
}
|
|
|
|
void IStringTable::write(IZone* zone, ZoneBuffer* buf)
|
|
{
|
|
auto* data = this->asset_;
|
|
auto* dest = buf->write(data);
|
|
|
|
buf->push_stream(3);
|
|
START_LOG_STREAM;
|
|
|
|
dest->name = buf->write_str(this->name());
|
|
|
|
if (data->strings)
|
|
{
|
|
buf->align(3);
|
|
auto* dest_strings = buf->write(data->strings, data->columns * data->rows);
|
|
|
|
if (data->columns * data->rows > 0)
|
|
{
|
|
for (auto i = 0; i < data->columns * data->rows; i++)
|
|
{
|
|
if (data->strings[i].string)
|
|
{
|
|
dest_strings[i].string = buf->write_str(data->strings[i].string);
|
|
}
|
|
}
|
|
}
|
|
|
|
ZoneBuffer::clear_pointer(&dest->strings);
|
|
}
|
|
|
|
END_LOG_STREAM;
|
|
buf->pop_stream();
|
|
}
|
|
|
|
void IStringTable::dump(StringTable* asset)
|
|
{
|
|
const std::string path = asset->name;
|
|
auto* file = FileSystem::FileOpen(path, "w"s);
|
|
|
|
for (auto row = 0; row < asset->rows; row++)
|
|
{
|
|
for (auto column = 0; column < asset->columns; column++)
|
|
{
|
|
fprintf(
|
|
file,
|
|
"%s%s",
|
|
(asset->strings[(row * asset->columns) + column].string)
|
|
? asset->strings[(row * asset->columns) + column].string
|
|
: "",
|
|
(column == asset->columns - 1) ? "\n" : ","
|
|
);
|
|
}
|
|
}
|
|
|
|
FileSystem::FileClose(file);
|
|
}
|
|
}
|
|
}
|