mirror of
https://github.com/Kelsidavis/WoWee.git
synced 2026-03-24 00:00:13 +00:00
Add expansion DBC CSVs, Turtle support, and server-specific login
This commit is contained in:
parent
7092844b5e
commit
f247d53309
139 changed files with 676758 additions and 91 deletions
|
|
@ -1,14 +1,42 @@
|
|||
#include "pipeline/dbc_loader.hpp"
|
||||
#include "core/logger.hpp"
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
namespace wowee {
|
||||
namespace pipeline {
|
||||
|
||||
namespace {
|
||||
std::string trimAscii(std::string s) {
|
||||
size_t b = 0;
|
||||
while (b < s.size() && std::isspace(static_cast<unsigned char>(s[b]))) {
|
||||
++b;
|
||||
}
|
||||
size_t e = s.size();
|
||||
while (e > b && std::isspace(static_cast<unsigned char>(s[e - 1]))) {
|
||||
--e;
|
||||
}
|
||||
return s.substr(b, e - b);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DBCFile::DBCFile() = default;
|
||||
DBCFile::~DBCFile() = default;
|
||||
|
||||
bool DBCFile::load(const std::vector<uint8_t>& dbcData) {
|
||||
if (dbcData.empty()) {
|
||||
LOG_ERROR("DBC data is empty");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Detect CSV format: starts with '#'
|
||||
if (dbcData[0] == '#') {
|
||||
return loadCSV(dbcData);
|
||||
}
|
||||
|
||||
if (dbcData.size() < sizeof(DBCHeader)) {
|
||||
LOG_ERROR("DBC data too small for header");
|
||||
return false;
|
||||
|
|
@ -158,5 +186,140 @@ void DBCFile::buildIdCache() const {
|
|||
LOG_DEBUG("Built DBC ID cache with ", idToIndexCache.size(), " entries");
|
||||
}
|
||||
|
||||
bool DBCFile::loadCSV(const std::vector<uint8_t>& csvData) {
|
||||
std::string text(reinterpret_cast<const char*>(csvData.data()), csvData.size());
|
||||
std::istringstream stream(text);
|
||||
std::string line;
|
||||
|
||||
// --- Parse metadata line: # fields=N strings=I,J,K ---
|
||||
if (!std::getline(stream, line) || line.empty() || line[0] != '#') {
|
||||
LOG_ERROR("CSV DBC: missing metadata line");
|
||||
return false;
|
||||
}
|
||||
|
||||
fieldCount = 0;
|
||||
std::set<uint32_t> stringCols;
|
||||
|
||||
// Parse "fields=N"
|
||||
auto fieldsPos = line.find("fields=");
|
||||
if (fieldsPos != std::string::npos) {
|
||||
try {
|
||||
fieldCount = static_cast<uint32_t>(std::stoul(line.substr(fieldsPos + 7)));
|
||||
} catch (...) {
|
||||
fieldCount = 0;
|
||||
}
|
||||
}
|
||||
if (fieldCount == 0) {
|
||||
LOG_ERROR("CSV DBC: invalid field count");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse "strings=I,J,K"
|
||||
auto stringsPos = line.find("strings=");
|
||||
if (stringsPos != std::string::npos) {
|
||||
std::istringstream ss(line.substr(stringsPos + 8));
|
||||
std::string tok;
|
||||
while (std::getline(ss, tok, ',')) {
|
||||
tok = trimAscii(tok);
|
||||
if (!tok.empty()) {
|
||||
try {
|
||||
stringCols.insert(static_cast<uint32_t>(std::stoul(tok)));
|
||||
} catch (...) {
|
||||
LOG_WARNING("CSV DBC: invalid string column index token: '", tok, "'");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recordSize = fieldCount * 4;
|
||||
|
||||
// --- Build string block with initial null byte ---
|
||||
stringBlock.clear();
|
||||
stringBlock.push_back(0); // offset 0 = empty string
|
||||
|
||||
// --- Parse data rows ---
|
||||
struct RowData {
|
||||
std::vector<uint32_t> fields;
|
||||
};
|
||||
std::vector<RowData> rows;
|
||||
|
||||
while (std::getline(stream, line)) {
|
||||
if (line.empty()) continue;
|
||||
|
||||
RowData row;
|
||||
row.fields.resize(fieldCount, 0);
|
||||
|
||||
uint32_t col = 0;
|
||||
size_t pos = 0;
|
||||
|
||||
while (col < fieldCount && pos < line.size()) {
|
||||
if (stringCols.count(col) && pos < line.size() && line[pos] == '"') {
|
||||
// Quoted string field
|
||||
pos++; // skip opening quote
|
||||
std::string str;
|
||||
while (pos < line.size()) {
|
||||
if (line[pos] == '"') {
|
||||
if (pos + 1 < line.size() && line[pos + 1] == '"') {
|
||||
str += '"'; // escaped quote
|
||||
pos += 2;
|
||||
} else {
|
||||
pos++; // closing quote
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
str += line[pos++];
|
||||
}
|
||||
}
|
||||
// Skip comma after closing quote
|
||||
if (pos < line.size() && line[pos] == ',') pos++;
|
||||
|
||||
// Store string in string block
|
||||
if (str.empty()) {
|
||||
row.fields[col] = 0; // points to empty string at offset 0
|
||||
} else {
|
||||
uint32_t offset = static_cast<uint32_t>(stringBlock.size());
|
||||
stringBlock.insert(stringBlock.end(), str.begin(), str.end());
|
||||
stringBlock.push_back(0); // null terminator
|
||||
row.fields[col] = offset;
|
||||
}
|
||||
} else {
|
||||
// Numeric field — read until comma or end of line
|
||||
size_t end = line.find(',', pos);
|
||||
if (end == std::string::npos) end = line.size();
|
||||
std::string tok = line.substr(pos, end - pos);
|
||||
if (!tok.empty()) {
|
||||
row.fields[col] = static_cast<uint32_t>(std::stoul(tok));
|
||||
}
|
||||
pos = (end < line.size()) ? end + 1 : line.size();
|
||||
}
|
||||
col++;
|
||||
}
|
||||
|
||||
rows.push_back(std::move(row));
|
||||
}
|
||||
|
||||
// --- Build record data (binary layout identical to WDBC) ---
|
||||
recordCount = static_cast<uint32_t>(rows.size());
|
||||
stringBlockSize = static_cast<uint32_t>(stringBlock.size());
|
||||
|
||||
recordData.resize(static_cast<size_t>(recordCount) * recordSize);
|
||||
for (uint32_t i = 0; i < recordCount; ++i) {
|
||||
uint8_t* dst = recordData.data() + static_cast<size_t>(i) * recordSize;
|
||||
for (uint32_t f = 0; f < fieldCount; ++f) {
|
||||
uint32_t val = rows[i].fields[f];
|
||||
std::memcpy(dst + f * 4, &val, 4);
|
||||
}
|
||||
}
|
||||
|
||||
loaded = true;
|
||||
idCacheBuilt = false;
|
||||
idToIndexCache.clear();
|
||||
|
||||
LOG_DEBUG("Loaded CSV DBC: ", recordCount, " records, ",
|
||||
fieldCount, " fields, ", stringCols.size(), " string cols, ",
|
||||
stringBlockSize, " string bytes");
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace pipeline
|
||||
} // namespace wowee
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue