mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
301 lines
11 KiB
C++
301 lines
11 KiB
C++
#include "config.h"
|
|
|
|
ExtractConfig::ExtractConfig(std::string configsDirectory, std::string filename, std::string outDirectory){
|
|
std::cout << "Reading config \"" << filename << "\"" << std::endl;
|
|
configJSON = json::JSON::Load(read_file(configsDirectory + '/' + filename));
|
|
|
|
this->outDirectory = outDirectory + "/assets/" + configJSON["subfolder"].ToString();
|
|
|
|
this->name = configJSON["config-name"].ToString();
|
|
this->subfolder = configJSON["subfolder"].ToString();
|
|
this->md5 = configJSON["checksum-md5"].ToString();
|
|
this->notSupported = configJSON["not-supported"].ToString() == "true";
|
|
}
|
|
|
|
ExtractConfig::~ExtractConfig(){
|
|
}
|
|
|
|
bool is_binary_type(std::string type) {
|
|
return type == "Binary" || type == "GameText" || type == "MenuText" || type == "Sprites" || type == "Miscellaneous"
|
|
|| type == "LevelHeaders" || type == "LevelNames" || type == "ObjectHeaders"
|
|
|| type == "Audio" || type == "Particles" || type == "ParticleBehaviors" || type == "TTGhosts";
|
|
}
|
|
|
|
bool is_compressed_type(std::string type) {
|
|
return type == "Compressed" || type == "LevelObjectMap" || type == "LevelModels"
|
|
|| type == "ObjectModels" || type == "ObjectAnimations";
|
|
}
|
|
|
|
std::string get_extension_from_type(std::string type) {
|
|
if(is_compressed_type(type)) {
|
|
return ".cbin";
|
|
} else if(type == "Textures") {
|
|
return ".png";
|
|
} else {
|
|
return ".bin";
|
|
}
|
|
}
|
|
|
|
void ExtractConfig::extract_file(ROM& rom, std::string type, std::string folder, std::string filename, std::vector<uint8_t> data) {
|
|
|
|
}
|
|
|
|
uint32_t get_uint_from_table(std::vector<uint8_t>& table, int index) {
|
|
return (table[index * 4] << 24) | (table[index * 4 + 1] << 16) | (table[index * 4 + 2] << 8) | table[index * 4 + 3];
|
|
}
|
|
|
|
std::vector<std::vector<uint8_t>> get_section_files(std::vector<uint8_t>& section, std::vector<uint8_t>& table, std::string tableType, json::JSON& outputAsset) {
|
|
std::vector<std::vector<uint8_t>> out;
|
|
|
|
int currentTableIndex = 0;
|
|
int scale = 1; // Each table entry is 4-bytes long.
|
|
int offset = 0;
|
|
|
|
if(tableType == "MenuText") {
|
|
outputAsset["text-entry-count"] = get_uint_from_table(table, currentTableIndex);
|
|
currentTableIndex = 1; // The first entry is the number of text entries for each language.
|
|
} else if(tableType == "TTGhosts") {
|
|
scale = 2; // Each table entry is 8-bytes long.
|
|
offset = 1; // offset by 4 bytes.
|
|
outputAsset["meta"] = json::Array();
|
|
} else if (tableType == "GameText") {
|
|
outputAsset["textTypes"] = json::Array();
|
|
}
|
|
|
|
uint32_t currentTableValue = get_uint_from_table(table, currentTableIndex * scale + offset);
|
|
uint32_t nextTableValue = get_uint_from_table(table, (currentTableIndex + 1) * scale + offset);
|
|
|
|
if (tableType == "Audio") {
|
|
// The audio table doesn't start at offset 0, so this check will extract
|
|
// the first actual part of the audio section.
|
|
std::vector<uint8_t> file(section.begin(), section.begin() + currentTableValue);
|
|
out.push_back(file);
|
|
}
|
|
|
|
while(nextTableValue != 0xFFFFFFFF) {
|
|
|
|
if(tableType == "GameText") {
|
|
bool isDialog = (currentTableValue & 0x80000000) == 0x80000000;
|
|
outputAsset["textTypes"].append(isDialog ? 1 : 0);
|
|
currentTableValue &= 0x7FFFFFFF;
|
|
nextTableValue &= 0x7FFFFFFF;
|
|
} else if(tableType == "Miscellaneous") {
|
|
currentTableValue *= 4;
|
|
nextTableValue *= 4;
|
|
} else if (tableType == "TTGhosts") {
|
|
json::JSON metaObj = json::Object();
|
|
metaObj["levelID"] = table[currentTableIndex * 8 + 0];
|
|
metaObj["vehicleID"] = table[currentTableIndex * 8 + 1];
|
|
outputAsset["meta"].append(metaObj);
|
|
}
|
|
|
|
int length = nextTableValue - currentTableValue;
|
|
if(length < 0) {
|
|
std::cout << "Error: Invalid table length: " << length << std::endl;
|
|
std::cout << std::hex << "currentTableValue: " << currentTableValue << std::dec << std::endl;
|
|
std::cout << std::hex << "nextTableValue: " << nextTableValue << std::dec << std::endl;
|
|
throw 1;
|
|
}
|
|
|
|
std::vector<uint8_t> file(section.begin() + currentTableValue, section.begin() + nextTableValue);
|
|
out.push_back(file);
|
|
|
|
currentTableIndex++;
|
|
currentTableValue = get_uint_from_table(table, currentTableIndex * scale + offset);
|
|
nextTableValue = get_uint_from_table(table, (currentTableIndex + 1) * scale + offset);
|
|
}
|
|
|
|
return out;
|
|
}
|
|
|
|
void ExtractConfig::extract(ROM& rom, json::JSON& assetsJson) {
|
|
currentROMOffset = 0;
|
|
|
|
/* Extract the code sections */
|
|
json::JSON codeSections = configJSON["code"]["sections"];
|
|
int numCodeSections = codeSections.length();
|
|
for(int i = 0; i < numCodeSections; i++) {
|
|
json::JSON section = codeSections[i];
|
|
json::JSON files = section["files"];
|
|
std::string folder = this->outDirectory + "/" + section["folder"].ToString();
|
|
std::string type = section["type"].ToString();
|
|
|
|
int numFiles = files.length();
|
|
for(int j = 0; j < numFiles; j++) {
|
|
json::JSON file = files[j];
|
|
int length = std::stoul(file["length"].ToString(), nullptr, 16);
|
|
extractions.push_back(ExtractInfo(type, folder, file["filename"].ToString(),
|
|
rom.get_bytes_from_range(currentROMOffset, length)));
|
|
currentROMOffset += length;
|
|
}
|
|
}
|
|
|
|
/* Make sure the assets section has the correct number of entries. */
|
|
json::JSON assetSections = configJSON["assets"]["sections"];
|
|
int numAssetSections = assetSections.length();
|
|
int numAssetSectionsFromTable = rom.get_uint(currentROMOffset);
|
|
|
|
if(numAssetSections != numAssetSectionsFromTable) {
|
|
std::cout << "Error: Number of asset sections in config does not match ROM." << std::endl;
|
|
std::cout << "Number of asset sections in Config: " << numAssetSections << std::endl;
|
|
std::cout << "Number of asset sections in ROM: " << numAssetSectionsFromTable << std::endl;
|
|
throw 1;
|
|
}
|
|
|
|
currentROMOffset += 4;
|
|
int tableOffset = currentROMOffset;
|
|
currentROMOffset += (numAssetSections + 1) * 4;
|
|
int assetsStart = currentROMOffset;
|
|
|
|
std::vector<std::vector<uint8_t>> sectionsData;
|
|
|
|
/* Read the main assets look-up table for section lengths */
|
|
for(int i = 0; i < numAssetSections; i++) {
|
|
int assetSectionLength = rom.get_uint(tableOffset + ((i + 1) * 4)) - rom.get_uint(tableOffset + (i * 4));
|
|
sectionsData.push_back(rom.get_bytes_from_range(currentROMOffset, assetSectionLength));
|
|
currentROMOffset += assetSectionLength;
|
|
}
|
|
|
|
currentROMOffset = assetsStart;
|
|
|
|
assetsJson["assets"] = json::Array();
|
|
|
|
/* Extract the assets sections */
|
|
for(int i = 0; i < numAssetSections; i++) {
|
|
json::JSON section = assetSections[i];
|
|
std::string folder = this->outDirectory + "/" + section["folder"].ToString();
|
|
std::string type = section["type"].ToString();
|
|
|
|
json::JSON outputAsset = json::Object();
|
|
outputAsset["type"] = type;
|
|
|
|
if(type == "Table") {
|
|
assetsJson["assets"].append(outputAsset);
|
|
continue;
|
|
} else if(type == "Textures") {
|
|
outputAsset["flip-textures"] = section["flip-textures"];
|
|
}
|
|
|
|
outputAsset["folder"] = section["folder"].ToString();
|
|
outputAsset["filenames"] = json::Array();
|
|
|
|
if(section.hasKey("table")) {
|
|
int tableIndex = section["table"].ToInt();
|
|
std::vector<std::vector<uint8_t>> sectionFiles
|
|
= get_section_files(sectionsData[i], sectionsData[tableIndex], type, outputAsset);
|
|
int numberOfSectionFiles = sectionFiles.size();
|
|
|
|
outputAsset["table"] = tableIndex;
|
|
|
|
for(int j = 0; j < numberOfSectionFiles; j++) {
|
|
std::string filename = "";
|
|
if(section.hasKey("filenames")) {
|
|
filename = section["filenames"][j].ToString();
|
|
}
|
|
if(filename == "") {
|
|
std::stringstream filenameStream;
|
|
filenameStream << "unknown_" << i << "_" << j;
|
|
filename = filenameStream.str();
|
|
}
|
|
outputAsset["filenames"].append(filename + get_extension_from_type(type));
|
|
extractions.push_back(ExtractInfo(type, folder, filename, sectionFiles[j]));
|
|
}
|
|
} else {
|
|
// No lookup table associated, so the section is assumed to be 1 file.
|
|
std::string filename = "";
|
|
if(section.hasKey("filenames")) {
|
|
filename = section["filenames"][0].ToString();
|
|
}
|
|
if(filename == "") {
|
|
std::stringstream filenameStream;
|
|
filenameStream << "unknown_" << i;
|
|
filename = filenameStream.str();
|
|
}
|
|
outputAsset["filenames"].append(filename + get_extension_from_type(type));
|
|
extractions.push_back(ExtractInfo(type, folder, filename, sectionsData[i]));
|
|
}
|
|
currentROMOffset += sectionsData[i].size();
|
|
assetsJson["assets"].append(outputAsset);
|
|
}
|
|
|
|
ThreadPool pool(std::thread::hardware_concurrency());
|
|
|
|
for(int i = 0; i < extractions.size(); i++) {
|
|
std::string type = extractions[i].type;
|
|
std::string folder = extractions[i].folder;
|
|
std::string filename = extractions[i].filename;
|
|
std::vector<uint8_t> data = extractions[i].data;
|
|
pool.enqueue([&rom, type, folder, filename, data] {
|
|
if(!fs::is_directory(folder)) {
|
|
fs::create_directories(folder);
|
|
}
|
|
|
|
std::string outFilepath = folder + "/" + filename;
|
|
|
|
if(is_binary_type(type)) {
|
|
ExtractBinary(data, rom, outFilepath + ".bin");
|
|
} else if(is_compressed_type(type)) {
|
|
ExtractCompressed(data, rom, outFilepath + ".cbin");
|
|
} else if(type == "Textures") {
|
|
ExtractTextures(data, rom, outFilepath + ".png");
|
|
} else if(type == "Empty") {
|
|
} else if(type != "NoExtract") {
|
|
std::cout << "Unknown extraction type: " << type << std::endl;
|
|
throw 1;
|
|
}
|
|
});
|
|
}
|
|
|
|
}
|
|
|
|
bool ExtractConfig::is_supported(){
|
|
return !notSupported;
|
|
}
|
|
|
|
std::string ExtractConfig::get_name(){
|
|
return name;
|
|
}
|
|
|
|
std::string ExtractConfig::get_md5(){
|
|
return md5;
|
|
}
|
|
|
|
std::string ExtractConfig::get_subfolder(){
|
|
return subfolder;
|
|
}
|
|
|
|
std::string ExtractConfig::read_file(std::string filename) {
|
|
std::ifstream t(filename);
|
|
t.seekg(0, std::ios::end);
|
|
size_t size = t.tellg();
|
|
std::string buffer(size, ' ');
|
|
t.seekg(0);
|
|
t.read(&buffer[0], size);
|
|
return buffer;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|