Add exl parser, make exd category configurable
Now you can easily parse EXL files, and this commit includes changes to GameData to easily extract a EXH from just an excel sheet name like "Item" or "Map". However this still doesn't work for language seperated excel sheets yet.
This commit is contained in:
parent
851ae281e6
commit
7006cc4662
7 changed files with 109 additions and 6 deletions
|
@ -70,7 +70,8 @@ add_library(libxiv STATIC
|
||||||
src/exhparser.cpp
|
src/exhparser.cpp
|
||||||
src/exdparser.cpp
|
src/exdparser.cpp
|
||||||
src/installextract.cpp
|
src/installextract.cpp
|
||||||
src/patch.cpp)
|
src/patch.cpp
|
||||||
|
src/exlparser.cpp)
|
||||||
target_include_directories(libxiv PUBLIC include PRIVATE src)
|
target_include_directories(libxiv PUBLIC include PRIVATE src)
|
||||||
target_link_libraries(libxiv PUBLIC ${LIBRARIES})
|
target_link_libraries(libxiv PUBLIC ${LIBRARIES})
|
||||||
target_link_directories(libxiv PUBLIC ${LIB_DIRS})
|
target_link_directories(libxiv PUBLIC ${LIB_DIRS})
|
|
@ -19,4 +19,6 @@ struct EXD {
|
||||||
std::vector<Row> rows;
|
std::vector<Row> rows;
|
||||||
};
|
};
|
||||||
|
|
||||||
EXD readEXD(EXH& exh, ExcelDataPagination& page);
|
std::string getEXDFilename(EXH& exh, std::string_view name, ExcelDataPagination& page);
|
||||||
|
|
||||||
|
EXD readEXD(EXH& exh, std::string_view path, ExcelDataPagination& page);
|
16
include/exlparser.h
Normal file
16
include/exlparser.h
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
struct EXLRow {
|
||||||
|
std::string name;
|
||||||
|
int id;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct EXL {
|
||||||
|
std::vector<EXLRow> rows;
|
||||||
|
};
|
||||||
|
|
||||||
|
EXL readEXL(std::string_view path);
|
|
@ -2,6 +2,9 @@
|
||||||
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <optional>
|
||||||
|
#include "exhparser.h"
|
||||||
|
#include "exlparser.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This handles reading/extracting the raw data from game data packs, such as dat0, index and index2 files.
|
* This handles reading/extracting the raw data from game data packs, such as dat0, index and index2 files.
|
||||||
|
@ -23,6 +26,10 @@ public:
|
||||||
*/
|
*/
|
||||||
void extractFile(std::string_view dataFilePath, std::string_view outPath);
|
void extractFile(std::string_view dataFilePath, std::string_view outPath);
|
||||||
|
|
||||||
|
std::optional<EXH> readExcelSheet(std::string_view name);
|
||||||
|
|
||||||
|
std::vector<std::string> getAllSheetNames();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/*
|
/*
|
||||||
* This returns a proper SQEX-style filename for index, index2, and dat files.
|
* This returns a proper SQEX-style filename for index, index2, and dat files.
|
||||||
|
@ -41,4 +48,6 @@ private:
|
||||||
uint64_t calculateHash(std::string_view path);
|
uint64_t calculateHash(std::string_view path);
|
||||||
|
|
||||||
std::string dataDirectory;
|
std::string dataDirectory;
|
||||||
|
|
||||||
|
EXL rootEXL;
|
||||||
};
|
};
|
|
@ -36,10 +36,14 @@ std::string readData(FILE* file, int offset) {
|
||||||
return std::to_string(value);
|
return std::to_string(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
EXD readEXD(EXH& exh, ExcelDataPagination& page) {
|
std::string getEXDFilename(EXH& exh, std::string_view name, ExcelDataPagination& page) {
|
||||||
EXD exd;
|
auto path = fmt::format("{}_{}.exd", name, page.startId);
|
||||||
|
|
||||||
auto path = fmt::format("{}_{}.exd", "map", page.startId);
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
EXD readEXD(EXH& exh, std::string_view path, ExcelDataPagination& page) {
|
||||||
|
EXD exd;
|
||||||
|
|
||||||
FILE* file = fopen(path.data(), "rb");
|
FILE* file = fopen(path.data(), "rb");
|
||||||
if(file == nullptr) {
|
if(file == nullptr) {
|
||||||
|
@ -90,7 +94,7 @@ EXD readEXD(EXH& exh, ExcelDataPagination& page) {
|
||||||
std::string string;
|
std::string string;
|
||||||
|
|
||||||
int ch;
|
int ch;
|
||||||
while ((ch = fgetc(file)) != '\0') {
|
while ((ch = fgetc(file)) != '\0' && ch != '\377') {
|
||||||
string.push_back((char)ch);
|
string.push_back((char)ch);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
27
src/exlparser.cpp
Normal file
27
src/exlparser.cpp
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
#include "exlparser.h"
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
EXL readEXL(std::string_view path) {
|
||||||
|
FILE* file = fopen(path.data(), "rb");
|
||||||
|
if(!file) {
|
||||||
|
throw std::runtime_error("Failed to read exl file from " + std::string(path.data()));
|
||||||
|
}
|
||||||
|
|
||||||
|
EXL exl;
|
||||||
|
|
||||||
|
char* data = nullptr;
|
||||||
|
size_t len = 0;
|
||||||
|
|
||||||
|
while ((getline(&data, &len, file)) != -1) {
|
||||||
|
std::string line = data;
|
||||||
|
|
||||||
|
const size_t comma = line.find_first_of(',');
|
||||||
|
|
||||||
|
std::string name = line.substr(0, comma);
|
||||||
|
std::string id = line.substr(comma + 1, line.length());
|
||||||
|
|
||||||
|
exl.rows.push_back({name, std::stoi(id)});
|
||||||
|
}
|
||||||
|
|
||||||
|
return exl;
|
||||||
|
}
|
|
@ -3,6 +3,7 @@
|
||||||
#include "crc32checksum.h"
|
#include "crc32checksum.h"
|
||||||
#include "compression.h"
|
#include "compression.h"
|
||||||
#include "string_utils.h"
|
#include "string_utils.h"
|
||||||
|
#include "exlparser.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
@ -31,6 +32,20 @@ std::unordered_map<std::string_view, int> categoryToID = {
|
||||||
|
|
||||||
GameData::GameData(const std::string_view dataDirectory) {
|
GameData::GameData(const std::string_view dataDirectory) {
|
||||||
this->dataDirectory = dataDirectory;
|
this->dataDirectory = dataDirectory;
|
||||||
|
|
||||||
|
extractFile("exd/root.exl", "root.exl");
|
||||||
|
|
||||||
|
rootEXL = readEXL("root.exl");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> GameData::getAllSheetNames() {
|
||||||
|
std::vector<std::string> names;
|
||||||
|
|
||||||
|
for(auto row : rootEXL.rows) {
|
||||||
|
names.push_back(row.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return names;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t GameData::calculateHash(const std::string_view path) {
|
uint64_t GameData::calculateHash(const std::string_view path) {
|
||||||
|
@ -86,6 +101,8 @@ void GameData::extractFile(std::string_view dataFilePath, std::string_view outPa
|
||||||
// TODO: handle platforms other than win32
|
// TODO: handle platforms other than win32
|
||||||
auto indexFilename = calculateFilename(categoryToID[category], getExpansionID(repository), 0, "win32", "index");
|
auto indexFilename = calculateFilename(categoryToID[category], getExpansionID(repository), 0, "win32", "index");
|
||||||
|
|
||||||
|
fmt::print("calculated index filename: ");
|
||||||
|
|
||||||
// TODO: handle hashes in index2 files (we can read them but it's not setup yet.)
|
// TODO: handle hashes in index2 files (we can read them but it's not setup yet.)
|
||||||
auto indexFile = readIndexFile(dataDirectory + "/" + repository + "/" + indexFilename);
|
auto indexFile = readIndexFile(dataDirectory + "/" + repository + "/" + indexFilename);
|
||||||
|
|
||||||
|
@ -93,6 +110,8 @@ void GameData::extractFile(std::string_view dataFilePath, std::string_view outPa
|
||||||
if(entry.hash == hash) {
|
if(entry.hash == hash) {
|
||||||
auto dataFilename = calculateFilename(categoryToID[category], getExpansionID(repository), entry.dataFileId, "win32", "dat0");
|
auto dataFilename = calculateFilename(categoryToID[category], getExpansionID(repository), entry.dataFileId, "win32", "dat0");
|
||||||
|
|
||||||
|
fmt::print("Opening data file {}...\n", dataFilename);
|
||||||
|
|
||||||
FILE* file = fopen((dataDirectory + "/" + repository + "/" + dataFilename).c_str(), "rb");
|
FILE* file = fopen((dataDirectory + "/" + repository + "/" + dataFilename).c_str(), "rb");
|
||||||
if(file == nullptr) {
|
if(file == nullptr) {
|
||||||
throw std::runtime_error("Failed to open data file: " + dataFilename);
|
throw std::runtime_error("Failed to open data file: " + dataFilename);
|
||||||
|
@ -187,3 +206,28 @@ void GameData::extractFile(std::string_view dataFilePath, std::string_view outPa
|
||||||
|
|
||||||
fmt::print("Extracted {} to {}\n", dataFilePath, outPath);
|
fmt::print("Extracted {} to {}\n", dataFilePath, outPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<EXH> GameData::readExcelSheet(std::string_view name) {
|
||||||
|
fmt::print("Beginning to read excel sheet {}...\n", name);
|
||||||
|
|
||||||
|
for(auto row : rootEXL.rows) {
|
||||||
|
if(row.name == name) {
|
||||||
|
fmt::print("Found row {} at id {}!\n", name, row.id);
|
||||||
|
|
||||||
|
// we want it as lowercase (Item -> item)
|
||||||
|
std::string newFilename = name.data();
|
||||||
|
std::transform(newFilename.begin(), newFilename.end(), newFilename.begin(),
|
||||||
|
[](unsigned char c){ return std::tolower(c); });
|
||||||
|
|
||||||
|
std::string exhFilename = "exd/" + newFilename + ".exh";
|
||||||
|
|
||||||
|
extractFile(exhFilename, newFilename + ".exh");
|
||||||
|
|
||||||
|
fmt::print("Done extracting files, now parsing...\n");
|
||||||
|
|
||||||
|
return readEXH(newFilename + ".exh");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
Reference in a new issue