Added support for building gametext textboxes, also fixed an issue with multithreaded extracting

This commit is contained in:
David Benepe
2022-03-30 17:16:21 -04:00
parent 7c13bdbdc8
commit ab91bcf963
12 changed files with 99 additions and 76 deletions
+4 -3
View File
@@ -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 = &sectionCache[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.
+9 -3
View File
@@ -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:;
}