mirror of
https://github.com/izzy2lost/Diddy-Kong-Racing.git
synced 2026-06-19 01:16:26 -07:00
Added support for building gametext textboxes, also fixed an issue with multithreaded extracting
This commit is contained in:
+4
-3
@@ -1,6 +1,7 @@
|
||||
CC := gcc
|
||||
CXX := g++
|
||||
CFLAGS := -I . -Wall -Wextra -Wno-unused-parameter -pedantic -std=c99 -O2 -s
|
||||
CXXFLAGS := --std=c++17 -lstdc++fs -lcrypto -lssl -lpthread
|
||||
LDFLAGS := -lm
|
||||
PROGRAMS := n64crc
|
||||
CXX_PROGRAMS := dkr_assets_tool
|
||||
@@ -37,12 +38,12 @@ dkr_assets_tool_OBJ_FILES := $(foreach file,$(dkr_assets_tool_C_FILES),$(BUILD_D
|
||||
DUMMY != mkdir -p $(sort $(dir $(dkr_assets_tool_OBJ_FILES)))
|
||||
|
||||
$(BUILD_DIR)/%.o: %.c
|
||||
$(CXX) -c $^ -o $@ --std=c++17 -lstdc++fs -lcrypto -lssl -lpthread $(LDFLAGS)
|
||||
$(CXX) -c $^ -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||
$(BUILD_DIR)/%.o: %.cpp
|
||||
$(CXX) -c $< -o $@ --std=c++17 -lstdc++fs -lcrypto -lssl -lpthread $(LDFLAGS)
|
||||
$(CXX) -c $< -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||
|
||||
dkr_assets_tool: $(dkr_assets_tool_OBJ_FILES)
|
||||
$(CXX) $^ -o $@ --std=c++17 -lstdc++fs -lcrypto -lssl -lpthread $(LDFLAGS)
|
||||
$(CXX) $^ -o $@ $(CXXFLAGS) $(LDFLAGS)
|
||||
|
||||
.PHONY: all clean distclean default
|
||||
|
||||
|
||||
@@ -13,26 +13,26 @@ BuildGameText::BuildGameText(std::string srcPath, std::string dstPath) {
|
||||
|
||||
|
||||
// TODO: Figure this out!
|
||||
/*
|
||||
|
||||
std::string textType = get_string_from_json(srcPath, "text-type");
|
||||
if(textType == "Dialog") {
|
||||
build_dialog(out, inputJSON);
|
||||
// This is temporary until the game text format is figured out
|
||||
int numRawBytes = get_array_length_from_json(srcPath, "raw");
|
||||
|
||||
for(int i = 0; i < numRawBytes; i++) {
|
||||
out.push_back(get_int_from_json(srcPath, "raw", i));
|
||||
}
|
||||
//build_dialog(out, srcPath);
|
||||
} else {
|
||||
build_textbox(out, inputJSON);
|
||||
build_textbox(out, srcPath);
|
||||
}
|
||||
*/
|
||||
|
||||
// This is temporary until the game text format is figured out
|
||||
int numRawBytes = get_array_length_from_json(srcPath, "raw");
|
||||
|
||||
for(int i = 0; i < numRawBytes; i++) {
|
||||
out.push_back(get_int_from_json(srcPath, "raw", i));
|
||||
}
|
||||
|
||||
|
||||
while(out.size() % 8 != 0) {
|
||||
out.push_back(0); // Make sure the data is 8-byte aligned.
|
||||
}
|
||||
|
||||
|
||||
write_binary_file(out, dstPath);
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ BuildGameText::~BuildGameText() {
|
||||
}
|
||||
|
||||
/*
|
||||
void BuildGameText::build_dialog(std::vector<uint8_t> &out, json::JSON inputJSON) {
|
||||
void BuildGameText::build_dialog(std::vector<uint8_t> &out, std::string &srcPath) {
|
||||
int numberOfLines = GET_ARRAY_LENGTH("lines");
|
||||
for(int i = 0; i < numberOfLines; i++) {
|
||||
if(inputJSON["lines"][i].JSONType() == json::JSON::Class::String) {
|
||||
@@ -68,6 +68,7 @@ void BuildGameText::build_dialog(std::vector<uint8_t> &out, json::JSON inputJSON
|
||||
//}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
void write_basic_command(std::vector<uint8_t> &out, uint8_t cmdId, uint8_t value) {
|
||||
out.push_back(cmdId);
|
||||
@@ -82,10 +83,10 @@ void write_basic_command_4_args(std::vector<uint8_t> &out, uint8_t cmdId, uint8_
|
||||
out.push_back(d);
|
||||
}
|
||||
|
||||
void BuildGameText::build_textbox(std::vector<uint8_t> &out, json::JSON inputJSON) {
|
||||
int numberOfPages = GET_ARRAY_LENGTH("pages");
|
||||
void BuildGameText::build_textbox(std::vector<uint8_t> &out, std::string &srcPath) {
|
||||
int numberOfPages = get_array_length_from_json(srcPath, "pages");
|
||||
for(int page = 0; page < numberOfPages; page++) {
|
||||
int numberOfCommandsInPage = GET_ARRAY_LENGTH("pages", page);
|
||||
int numberOfCommandsInPage = get_array_length_from_json(srcPath, "pages", page);
|
||||
for(int cmd = 0; cmd < numberOfCommandsInPage; cmd++) {
|
||||
std::string cmdType = get_string_from_json(srcPath, "pages", page, cmd, "command");
|
||||
if(cmdType == "Text") {
|
||||
@@ -107,7 +108,7 @@ void BuildGameText::build_textbox(std::vector<uint8_t> &out, json::JSON inputJSO
|
||||
get_int_from_json(srcPath, "pages", page, cmd, "value", "alpha")
|
||||
);
|
||||
} else if(cmdType == "SetAlignment") {
|
||||
write_basic_command(out, 6, get_string_from_json(srcPath, "pages", page, cmd, "value") == "ALIGN_CENTER" ? 0 : 4);
|
||||
write_basic_command(out, 6, get_string_from_json(srcPath, "pages", page, cmd, "value") == "Center" ? 0 : 4);
|
||||
} else if(cmdType == "Unknown") {
|
||||
write_basic_command(out, 7, get_int_from_json(srcPath, "pages", page, cmd, "value"));
|
||||
} else if(cmdType == "AddVerticalSpacing") {
|
||||
@@ -117,7 +118,7 @@ void BuildGameText::build_textbox(std::vector<uint8_t> &out, json::JSON inputJSO
|
||||
} else if(cmdType == "SetTimer") {
|
||||
write_basic_command(out, 10, get_int_from_json(srcPath, "pages", page, cmd, "value"));
|
||||
} else if(cmdType == "AllowUserInput") {
|
||||
write_basic_command(out, 10, GET_BOOL("pages", page, cmd, "value") ? 1 : 0);
|
||||
write_basic_command(out, 11, get_bool_from_json(srcPath, "pages", page, cmd, "value") ? 1 : 0);
|
||||
}
|
||||
}
|
||||
if(page < numberOfPages - 1) {
|
||||
@@ -125,4 +126,3 @@ void BuildGameText::build_textbox(std::vector<uint8_t> &out, json::JSON inputJSO
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -8,7 +8,7 @@ public:
|
||||
~BuildGameText();
|
||||
private:
|
||||
/*
|
||||
void build_dialog(std::vector<uint8_t> &out, json::JSON &inputJSON);
|
||||
void build_textbox(std::vector<uint8_t> &out, json::JSON &inputJSON);
|
||||
void build_dialog(std::vector<uint8_t> &out, std::string &srcPath);
|
||||
*/
|
||||
void build_textbox(std::vector<uint8_t> &out, std::string &srcPath);
|
||||
};
|
||||
|
||||
@@ -123,30 +123,33 @@ void load_enums_cache() {
|
||||
}
|
||||
}
|
||||
|
||||
std::mutex enumsMutex;
|
||||
|
||||
void make_sure_enums_are_loaded() {
|
||||
if(hasLoadedEnums) {
|
||||
return;
|
||||
}
|
||||
std::string assetsFolderPath = get_asset_folder_path();
|
||||
ensure_that_path_exists("build/"); // Make sure build folder exists
|
||||
if(path_exists(ENUMS_CACHE_PATH)) {
|
||||
load_enums_cache();
|
||||
} else {
|
||||
// Generate enums map
|
||||
path_must_exist("include/enums.h");
|
||||
std::string enumsIncludeText = read_text_file("include/enums.h");
|
||||
std::regex enumRegex("enum ([^\\s{]+)\\s*[{]((?:.*\\n)*?)[}]");
|
||||
std::smatch sm;
|
||||
std::string text = enumsIncludeText;
|
||||
while (regex_search(text, sm, enumRegex)) {
|
||||
std::string enumName = sm[1];
|
||||
std::string enumValuesText = sm[2];
|
||||
get_enum_values(enumName, enumValuesText);
|
||||
text = sm.suffix();
|
||||
enumsMutex.lock();
|
||||
if(!hasLoadedEnums) {
|
||||
std::string assetsFolderPath = get_asset_folder_path();
|
||||
ensure_that_path_exists("build/"); // Make sure build folder exists
|
||||
if(path_exists(ENUMS_CACHE_PATH)) {
|
||||
load_enums_cache();
|
||||
} else {
|
||||
// Generate enums map
|
||||
path_must_exist("include/enums.h");
|
||||
std::string enumsIncludeText = read_text_file("include/enums.h");
|
||||
std::regex enumRegex("enum ([^\\s{]+)\\s*[{]((?:.*\\n)*?)[}]");
|
||||
std::smatch sm;
|
||||
std::string text = enumsIncludeText;
|
||||
while (regex_search(text, sm, enumRegex)) {
|
||||
std::string enumName = sm[1];
|
||||
std::string enumValuesText = sm[2];
|
||||
get_enum_values(enumName, enumValuesText);
|
||||
text = sm.suffix();
|
||||
}
|
||||
save_enums_cache();
|
||||
}
|
||||
save_enums_cache();
|
||||
hasLoadedEnums = true;
|
||||
}
|
||||
hasLoadedEnums = true;
|
||||
enumsMutex.unlock();
|
||||
}
|
||||
|
||||
std::string get_enum_string_from_value(std::string enumName, int value) {
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <regex>
|
||||
#include <mutex>
|
||||
|
||||
#include "../../libs/calculator.hpp"
|
||||
|
||||
|
||||
@@ -64,8 +64,7 @@ bool path_exists(std::string path) {
|
||||
|
||||
void path_must_exist(std::string path) {
|
||||
if(!fs::exists(path)) {
|
||||
std::cout << "Error: Path \"" << path << "\" does not exist!" << std::endl;
|
||||
throw 1;
|
||||
display_error_and_abort("Error: Path \"", path, "\" does not exist!");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <filesystem>
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
#include "errorHelper.h"
|
||||
|
||||
bool is_asset_folder_path_defined();
|
||||
void set_assets_folder_path(std::string path);
|
||||
std::string get_asset_folder_path();
|
||||
|
||||
@@ -15,6 +15,7 @@ std::unordered_map<std::string, json::JSON> jsonFileCache;
|
||||
|
||||
json::JSON load_assets_json(std::string assetsFolder, std::string jsonName) {
|
||||
std::string jsonFilepath = assetsFolder + "/" + jsonName;
|
||||
path_must_exist(jsonFilepath);
|
||||
return json::JSON::Load(read_text_file(jsonFilepath));
|
||||
}
|
||||
|
||||
@@ -25,25 +26,32 @@ void write_json(json::JSON &jsonData, std::string filename) {
|
||||
myfile.close();
|
||||
}
|
||||
|
||||
std::mutex assetsJsonMutex;
|
||||
|
||||
void make_sure_assets_json_is_loaded() {
|
||||
if(hasLoadedAssetsJson) {
|
||||
return;
|
||||
assetsJsonMutex.lock();
|
||||
if(!hasLoadedAssetsJson) {
|
||||
if(!is_asset_folder_path_defined()) {
|
||||
display_error_and_abort("assetsFolderPath not defined! Make sure set_assets_folder_path() is called somewhere!");
|
||||
}
|
||||
std::string assetsJsonPath = get_asset_folder_path() + "/" + get_version() + "/" + ASSETS_FILENAME;
|
||||
path_must_exist(assetsJsonPath);
|
||||
std::string assetsJsonText = read_text_file(assetsJsonPath);
|
||||
assetsJson = json::JSON::Load(assetsJsonText);
|
||||
hasLoadedAssetsJson = true;
|
||||
}
|
||||
if(!is_asset_folder_path_defined()) {
|
||||
std::cout << "assetsFolderPath not defined! Make sure set_assets_folder_path() is called somewhere!" << std::endl;
|
||||
throw 1;
|
||||
}
|
||||
std::string assetsJsonPath = get_asset_folder_path() + "/" + get_version() + "/" + ASSETS_FILENAME;
|
||||
path_must_exist(assetsJsonPath);
|
||||
std::string assetsJsonText = read_text_file(assetsJsonPath);
|
||||
assetsJson = json::JSON::Load(assetsJsonText);
|
||||
hasLoadedAssetsJson = true;
|
||||
assetsJsonMutex.unlock();
|
||||
}
|
||||
|
||||
|
||||
std::mutex sectionJsonMutex;
|
||||
|
||||
json::JSON *get_section_json(std::string sectionName) {
|
||||
make_sure_assets_json_is_loaded();
|
||||
sectionJsonMutex.lock();
|
||||
std::string assetsFolderPath = get_asset_folder_path() + "/" + get_version();
|
||||
if(sectionName == lastSectionName) {
|
||||
sectionJsonMutex.unlock();
|
||||
return lastSection;
|
||||
} else if(sectionCache.find(sectionName) == sectionCache.end()) {
|
||||
// section not found in cache, so it needs to be loaded/defined!
|
||||
@@ -51,13 +59,16 @@ json::JSON *get_section_json(std::string sectionName) {
|
||||
if(assetSection.JSONType() == json::JSON::Class::String) {
|
||||
std::string sectionJsonLocalPath = assetsJson["assets"]["sections"][sectionName].ToString();
|
||||
make_lowercase(sectionJsonLocalPath);
|
||||
sectionCache[sectionName] = json::JSON::Load(read_text_file(assetsFolderPath + "/" + sectionJsonLocalPath));
|
||||
std::string jsonPath = assetsFolderPath + "/" + sectionJsonLocalPath;
|
||||
path_must_exist(jsonPath);
|
||||
sectionCache[sectionName] = json::JSON::Load(read_text_file(jsonPath));
|
||||
} else {
|
||||
sectionCache[sectionName] = assetSection;
|
||||
}
|
||||
}
|
||||
lastSectionName = sectionName;
|
||||
lastSection = §ionCache[sectionName];
|
||||
sectionJsonMutex.unlock();
|
||||
return lastSection;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,9 +7,12 @@
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include <unordered_map>
|
||||
#include <mutex>
|
||||
|
||||
#include "../../libs/json.hpp"
|
||||
|
||||
#include "fileHelper.h"
|
||||
#include "errorHelper.h"
|
||||
|
||||
void write_json(json::JSON &assetsJSON, std::string filename);
|
||||
json::JSON load_assets_json(std::string assetsFolder, std::string jsonName);
|
||||
|
||||
@@ -323,7 +323,7 @@ void write_vanilla_warning_file(std::string baseDirectory) {
|
||||
|
||||
void ExtractConfig::execute_extraction() {
|
||||
// These braces are important, because I want the ThreadPool to call its destructor by going out of scope.
|
||||
{
|
||||
//{
|
||||
ThreadPool pool(std::thread::hardware_concurrency());
|
||||
//ThreadPool pool(1);
|
||||
|
||||
@@ -363,7 +363,7 @@ void ExtractConfig::execute_extraction() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
void ExtractConfig::extract() {
|
||||
|
||||
@@ -12,20 +12,17 @@ ExtractGameText::ExtractGameText(std::string key, std::vector<uint8_t> data, std
|
||||
isDialog = (out["text-type"].ToString() == "Dialog");
|
||||
}
|
||||
|
||||
// Uncomment this when the formats have been fully figured out.
|
||||
/*
|
||||
if(isDialog) {
|
||||
extract_dialog(data, data);
|
||||
// Uncomment this when the formats have been fully figured out.
|
||||
//extract_dialog(out, data);
|
||||
// Remove this raw part when dialog has been figured out.
|
||||
out["raw"] = json::Array();
|
||||
size_t dataLength = data.size();
|
||||
for(int i = 0; i < dataLength; i++) {
|
||||
out["raw"].append(data[i]);
|
||||
}
|
||||
} else { // Textbox
|
||||
extract_textbox(data, data);
|
||||
}
|
||||
*/
|
||||
|
||||
// This is temporary
|
||||
out["raw"] = json::Array();
|
||||
size_t dataLength = data.size();
|
||||
for(int i = 0; i < dataLength; i++) {
|
||||
out["raw"].append(data[i]);
|
||||
extract_textbox(out, data);
|
||||
}
|
||||
|
||||
write_json(out, outFilepath);
|
||||
@@ -123,7 +120,7 @@ void ExtractGameText::extract_textbox(json::JSON &out, std::vector<uint8_t> &dat
|
||||
break;
|
||||
case 6:
|
||||
cmd["command"] = "SetAlignment";
|
||||
cmd["value"] = (data[offset++] == 0) ? "ALIGN_CENTER" : "ALIGN_LEFT";
|
||||
cmd["value"] = (data[offset++] == 0) ? "Center" : "Left";
|
||||
break;
|
||||
case 7:
|
||||
cmd["command"] = "Unknown"; // Doesn't seem to do anything as far as I can tell.
|
||||
|
||||
@@ -181,13 +181,19 @@ class JSON
|
||||
~JSON() {
|
||||
switch( Type ) {
|
||||
case Class::Array:
|
||||
delete Internal.List;
|
||||
if(Internal.List != nullptr) {
|
||||
delete Internal.List;
|
||||
}
|
||||
break;
|
||||
case Class::Object:
|
||||
delete Internal.Map;
|
||||
if(Internal.Map != nullptr) {
|
||||
delete Internal.Map;
|
||||
}
|
||||
break;
|
||||
case Class::String:
|
||||
delete Internal.String;
|
||||
if(Internal.String != nullptr) {
|
||||
delete Internal.String;
|
||||
}
|
||||
break;
|
||||
default:;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user