mirror of
https://github.com/open-goal/jak-project.git
synced 2024-11-26 16:00:52 +00:00
extractor: cleanup, support unicode properly, and add multi-game support (#1609)
* extractor: refactor and cleanup for multi-game support * deps: switch to `ghc::filesystem` as it is utf-8 everywhere by default * extractor: finally working with unicode * unicode: fix unicode cli args on windows in all `main` functions
This commit is contained in:
parent
ac1e620161
commit
6446389263
@ -1,151 +1,180 @@
|
||||
{
|
||||
"version" : "0.2.1", "defaults" : {}, "configurations" : [
|
||||
"version": "0.2.1",
|
||||
"defaults": {},
|
||||
"configurations": [
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name" : "Tests - Unit-Tests - Summary",
|
||||
"args" : ["--gtest_brief=1"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name": "Tests - Unit-Tests - Summary",
|
||||
"args": ["--gtest_brief=1"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name" : "Tests - Unit-Tests - Verbose",
|
||||
"args" : ["--gtest_brief=0"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name": "Tests - Unit-Tests - Verbose",
|
||||
"args": ["--gtest_brief=0"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name" : "Tests - Draft Tests - Verbose",
|
||||
"args" : ["--gtest_brief=0", "--gtest_filter=\"*Draft*\""]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name": "Tests - Draft Tests - Verbose",
|
||||
"args": ["--gtest_brief=0", "--gtest_filter=\"*Draft*\""]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name" : "Tests - TypeConsistency - Verbose",
|
||||
"args" : ["--gtest_brief=0", "--gtest_filter=\"*TypeConsistency*\""]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name": "Tests - TypeConsistency - Verbose",
|
||||
"args": ["--gtest_brief=0", "--gtest_filter=\"*TypeConsistency*\""]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name" : "Tests - WithGameTests - Verbose",
|
||||
"args" : ["--gtest_brief=0", "--gtest_filter=\"*WithGameTests*\""]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc-test.exe (bin\\goalc-test.exe)",
|
||||
"name": "Tests - WithGameTests - Verbose",
|
||||
"args": ["--gtest_brief=0", "--gtest_filter=\"*WithGameTests*\""]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "offline-test.exe (bin\\offline-test.exe)",
|
||||
"name" : "Tests - Offline Tests",
|
||||
"args" : ["${workspaceRoot}/iso_data/jak1"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "offline-test.exe (bin\\offline-test.exe)",
|
||||
"name": "Tests - Offline Tests",
|
||||
"args": ["${workspaceRoot}/iso_data/jak1"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "gk.exe (bin\\gk.exe)",
|
||||
"name" : "Run - Runtime (no kernel)",
|
||||
"args" : [ "-fakeiso", "-debug", "-nokernel", "-v", "-nodisplay" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run - Runtime (no kernel)",
|
||||
"args": ["-fakeiso", "-debug", "-nokernel", "-v", "-nodisplay"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "gk.exe (bin\\gk.exe)",
|
||||
"name" : "Run - Runtime (with kernel)",
|
||||
"args" : [ "-fakeiso", "-debug", "-v" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run - Runtime (with kernel)",
|
||||
"args": ["-fakeiso", "-debug", "-v"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "gk.exe (bin\\gk.exe)",
|
||||
"name" : "Run - Runtime (boot)",
|
||||
"args" : [ "-boot", "-fakeiso", "-debug", "-v" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run - Runtime (boot)",
|
||||
"args": ["-boot", "-fakeiso", "-debug", "-v"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "gk.exe (bin\\gk.exe)",
|
||||
"name" : "Run - Runtime (boot no debug)",
|
||||
"args" : [ "-boot", "-fakeiso", "-v" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "gk.exe (bin\\gk.exe)",
|
||||
"name": "Run - Runtime (boot no debug)",
|
||||
"args": ["-boot", "-fakeiso", "-v"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc.exe (bin\\goalc.exe)",
|
||||
"name" : "Run - REPL",
|
||||
"args" : [ "--user-auto" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc.exe (bin\\goalc.exe)",
|
||||
"name": "Run - REPL",
|
||||
"args": ["--user-auto"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "goalc.exe (bin\\goalc.exe)",
|
||||
"name" : "Run - REPL - Auto Listen",
|
||||
"args" : [ "--user-auto", "--auto-lt" ]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "goalc.exe (bin\\goalc.exe)",
|
||||
"name": "Run - REPL - Auto Listen",
|
||||
"args": ["--user-auto", "--auto-lt"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name" : "Run - Decompiler - Jak 1",
|
||||
"args" : [ "${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc", "${workspaceRoot}/iso_data", "${workspaceRoot}/decompiler_out"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Run - Decompiler - Jak 1",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name" : "Run - Decompiler - Jak 1 - Data Only",
|
||||
"args" : [ "${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc", "${workspaceRoot}/iso_data", "${workspaceRoot}/decompiler_out", "decompile_code=false"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Run - Decompiler - Jak 1 - Data Only",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out",
|
||||
"decompile_code=false"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name" : "Run - Decompiler - Jak 1 PAL",
|
||||
"args" : [ "${workspaceRoot}/decompiler/config/jak1_pal.jsonc", "${workspaceRoot}/iso_data", "${workspaceRoot}/decompiler_out"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Run - Decompiler - Jak 1 PAL",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_pal.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name" : "Run - Disassembler - Jak 1",
|
||||
"args" : [ "${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc", "${workspaceRoot}/iso_data", "${workspaceRoot}/decompiler_out"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Run - Disassembler - Jak 1",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak1_ntsc_black_label.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name" : "Run - Decompiler - Jak 2",
|
||||
"args" : [ "${workspaceRoot}/decompiler/config/jak2_ntsc_v1.jsonc", "${workspaceRoot}/iso_data", "${workspaceRoot}/decompiler_out"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "decompiler.exe (bin\\decompiler.exe)",
|
||||
"name": "Run - Decompiler - Jak 2",
|
||||
"args": [
|
||||
"${workspaceRoot}/decompiler/config/jak2_ntsc_v1.jsonc",
|
||||
"${workspaceRoot}/iso_data",
|
||||
"${workspaceRoot}/decompiler_out"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
|
||||
"name" : "Run - EE Memory Analyze",
|
||||
"args" : [ "${workspaceRoot}/eeMemory.bin", "${workspaceRoot}"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
|
||||
"name": "Run - EE Memory Analyze",
|
||||
"args": ["${workspaceRoot}/eeMemory.bin", "${workspaceRoot}"]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
|
||||
"name" : "Run - EE Memory Analyze - Test",
|
||||
"args" : [ "\"C:\\Users\\xtvas\\Repositories\\pcsx2\\128mb\\sstates\\SCUS-97124 (1B3976AB).00.p2s\"", "${workspaceRoot}"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "memory_dump_tool.exe (bin\\memory_dump_tool.exe)",
|
||||
"name": "Run - EE Memory Analyze - Test",
|
||||
"args": [
|
||||
"\"C:\\Users\\xtvas\\Repositories\\pcsx2\\128mb\\sstates\\SCUS-97124 (1B3976AB).00.p2s\"",
|
||||
"${workspaceRoot}"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "dgo_unpacker.exe (bin\\dgo_unpacker.exe)",
|
||||
"name" : "Run - DGO Unpacker (test)",
|
||||
"args" : [ "C:\\GameData\\Jak1\\Backup\\DGO-PAL\\GAME", "C:\\GameData\\Jak1\\Backup\\DISC-PAL\\CGO\\GAME.CGO"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "dgo_unpacker.exe (bin\\dgo_unpacker.exe)",
|
||||
"name": "Run - DGO Unpacker (test)",
|
||||
"args": [
|
||||
"C:\\GameData\\Jak1\\Backup\\DGO-PAL\\GAME",
|
||||
"C:\\GameData\\Jak1\\Backup\\DISC-PAL\\CGO\\GAME.CGO"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type" : "default",
|
||||
"project" : "CMakeLists.txt",
|
||||
"projectTarget" : "extractor.exe (bin\\extractor.exe)",
|
||||
"name" : "Run - Extractor - Extract",
|
||||
"args" : ["E:\\ISOs\\Jak\\Jak 1.iso"]
|
||||
"type": "default",
|
||||
"project": "CMakeLists.txt",
|
||||
"projectTarget": "extractor.exe (bin\\extractor.exe)",
|
||||
"name": "Run - Extractor - Extract",
|
||||
"args": ["\"E:\\ISOs\\Jak\\дWTF平仮名WTF\\Jak 1.iso\""]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ add_library(common
|
||||
util/os.cpp
|
||||
util/print_float.cpp
|
||||
util/FontUtils.cpp
|
||||
util/FrameLimiter.cpp)
|
||||
util/FrameLimiter.cpp "util/unicode_util.h" "util/unicode_util.cpp")
|
||||
|
||||
target_link_libraries(common fmt lzokay replxx libzstd_static)
|
||||
|
||||
|
@ -7,9 +7,7 @@
|
||||
/*!
|
||||
* Write a wave file from a vector of samples.
|
||||
*/
|
||||
void write_wave_file_mono(const std::vector<s16>& samples,
|
||||
s32 sample_rate,
|
||||
const std::filesystem::path& name) {
|
||||
void write_wave_file_mono(const std::vector<s16>& samples, s32 sample_rate, const fs::path& name) {
|
||||
WaveFileHeader header;
|
||||
memcpy(header.chunk_id, "RIFF", 4);
|
||||
header.chunk_size = 36 + samples.size() * sizeof(s16);
|
||||
|
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/BinaryReader.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
// The header data for a simple wave file
|
||||
struct WaveFileHeader {
|
||||
@ -29,10 +29,8 @@ struct WaveFileHeader {
|
||||
s32 subchunk2_size;
|
||||
};
|
||||
|
||||
void write_wave_file_mono(const std::vector<s16>& samples,
|
||||
s32 sample_rate,
|
||||
const std::filesystem::path& name);
|
||||
void write_wave_file_mono(const std::vector<s16>& samples, s32 sample_rate, const fs::path& name);
|
||||
|
||||
std::vector<s16> decode_adpcm(BinaryReader& reader);
|
||||
|
||||
std::vector<u8> encode_adpcm(const std::vector<s16>& samples);
|
||||
std::vector<u8> encode_adpcm(const std::vector<s16>& samples);
|
||||
|
@ -1051,7 +1051,7 @@ Object Interpreter::eval_try_load_file(const Object& form,
|
||||
vararg_check(form, args, {ObjectType::STRING}, {});
|
||||
|
||||
auto path = {args.unnamed.at(0).as_string()->data};
|
||||
if (!std::filesystem::exists(file_util::get_file_path(path))) {
|
||||
if (!fs::exists(file_util::get_file_path(path))) {
|
||||
return SymbolObject::make_new(reader.symbolTable, "#f");
|
||||
}
|
||||
|
||||
|
@ -11,8 +11,6 @@
|
||||
|
||||
#include "Reader.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "ReplUtils.h"
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
@ -57,14 +57,14 @@ void ReplWrapper::add_to_history(const std::string& line) {
|
||||
}
|
||||
|
||||
void ReplWrapper::save_history() {
|
||||
std::filesystem::path path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
||||
fs::path path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
||||
file_util::create_dir_if_needed_for_file(path.string());
|
||||
repl.history_save(path.string());
|
||||
}
|
||||
|
||||
void ReplWrapper::load_history() {
|
||||
std::filesystem::path path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
||||
if (std::filesystem::exists(path)) {
|
||||
fs::path path = file_util::get_user_config_dir() / ".opengoal.repl.history";
|
||||
if (fs::exists(path)) {
|
||||
repl.history_load(path.string());
|
||||
} else {
|
||||
fmt::print("Couldn't locate REPL history file at '{}'\n", path.string());
|
||||
|
@ -83,7 +83,7 @@ void log_message(level log_level, LogTime& now, const char* message) {
|
||||
void set_file(const std::string& filename) {
|
||||
ASSERT(!gLogger.fp);
|
||||
file_util::create_dir_if_needed_for_file(filename);
|
||||
gLogger.fp = fopen(filename.c_str(), "w");
|
||||
gLogger.fp = file_util::open_file(filename.c_str(), "w");
|
||||
ASSERT(gLogger.fp);
|
||||
}
|
||||
|
||||
|
@ -117,8 +117,7 @@ bool write_subtitle_db_to_files(const GameSubtitleDB& db) {
|
||||
}
|
||||
|
||||
// Commit it to the file
|
||||
std::string full_path =
|
||||
(file_util::get_jak_project_dir() / std::filesystem::path(bank->file_path)).string();
|
||||
std::string full_path = (file_util::get_jak_project_dir() / fs::path(bank->file_path)).string();
|
||||
file_util::write_text_file(full_path, file_contents);
|
||||
}
|
||||
|
||||
|
@ -7,11 +7,11 @@
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
struct BinaryWriterRef {
|
||||
size_t offset;
|
||||
@ -64,8 +64,8 @@ class BinaryWriter {
|
||||
|
||||
void* get_data() { return data.data(); }
|
||||
|
||||
void write_to_file(const std::filesystem::path& filename) {
|
||||
auto fp = fopen(filename.string().c_str(), "wb");
|
||||
void write_to_file(const fs::path& filename) {
|
||||
auto fp = file_util::open_file(filename.string().c_str(), "wb");
|
||||
if (!fp)
|
||||
throw std::runtime_error("failed to open " + filename.string());
|
||||
if (fwrite(get_data(), get_size(), 1, fp) != 1)
|
||||
|
@ -5,7 +5,6 @@
|
||||
* Create a DGO from existing files.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
@ -7,7 +7,6 @@
|
||||
|
||||
#include <cstdio> /* defines FILENAME_MAX */
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
@ -37,22 +36,22 @@
|
||||
#include <common/log/log.h>
|
||||
|
||||
namespace file_util {
|
||||
std::filesystem::path get_user_home_dir() {
|
||||
fs::path get_user_home_dir() {
|
||||
#ifdef _WIN32
|
||||
// NOTE - on older systems, this may case issues if it cannot be found!
|
||||
std::string home_dir = std::getenv("USERPROFILE");
|
||||
return std::filesystem::path(home_dir);
|
||||
return fs::path(home_dir);
|
||||
#else
|
||||
std::string home_dir = std::getenv("HOME");
|
||||
return std::filesystem::path(home_dir);
|
||||
return fs::path(home_dir);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::filesystem::path get_user_config_dir() {
|
||||
std::filesystem::path config_base_path;
|
||||
fs::path get_user_config_dir() {
|
||||
fs::path config_base_path;
|
||||
#ifdef _WIN32
|
||||
auto config_base_dir = std::getenv("APPDATA");
|
||||
config_base_path = std::filesystem::path(std::string(config_base_dir));
|
||||
config_base_path = fs::path(std::string(config_base_dir));
|
||||
#elif __linux
|
||||
// Docs - https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
|
||||
// Prefer XDG_CONFIG_HOME if available
|
||||
@ -66,19 +65,19 @@ std::filesystem::path get_user_config_dir() {
|
||||
return config_base_path / "OpenGOAL";
|
||||
}
|
||||
|
||||
std::filesystem::path get_user_settings_dir() {
|
||||
fs::path get_user_settings_dir() {
|
||||
// TODO - jak2
|
||||
return get_user_config_dir() / "jak1" / "settings";
|
||||
}
|
||||
|
||||
std::filesystem::path get_user_memcard_dir() {
|
||||
fs::path get_user_memcard_dir() {
|
||||
// TODO - jak2
|
||||
return get_user_config_dir() / "jak1" / "saves";
|
||||
}
|
||||
|
||||
struct {
|
||||
bool initialized = false;
|
||||
std::filesystem::path path_to_data;
|
||||
fs::path path_to_data;
|
||||
} gFilePathInfo;
|
||||
|
||||
/*!
|
||||
@ -120,17 +119,17 @@ std::optional<std::string> try_get_jak_project_path() {
|
||||
0, pos + 11)); // + 12 to include "/jak-project" in the returned filepath
|
||||
}
|
||||
|
||||
std::optional<std::filesystem::path> try_get_data_dir() {
|
||||
std::filesystem::path my_path = get_current_executable_path();
|
||||
std::optional<fs::path> try_get_data_dir() {
|
||||
fs::path my_path = get_current_executable_path();
|
||||
auto data_dir = my_path.parent_path() / "data";
|
||||
if (std::filesystem::exists(data_dir) && std::filesystem::is_directory(data_dir)) {
|
||||
if (fs::exists(data_dir) && fs::is_directory(data_dir)) {
|
||||
return std::make_optional(data_dir);
|
||||
} else {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
bool setup_project_path(std::optional<std::filesystem::path> project_path_override) {
|
||||
bool setup_project_path(std::optional<fs::path> project_path_override) {
|
||||
if (gFilePathInfo.initialized) {
|
||||
return true;
|
||||
}
|
||||
@ -162,7 +161,7 @@ bool setup_project_path(std::optional<std::filesystem::path> project_path_overri
|
||||
return false;
|
||||
}
|
||||
|
||||
std::filesystem::path get_jak_project_dir() {
|
||||
fs::path get_jak_project_dir() {
|
||||
ASSERT(gFilePathInfo.initialized);
|
||||
return gFilePathInfo.path_to_data;
|
||||
}
|
||||
@ -172,7 +171,7 @@ std::string get_file_path(const std::vector<std::string>& input) {
|
||||
// the project path should be explicitly provided by whatever if needed
|
||||
// TEMP HACK
|
||||
// - if the provided path is absolute, don't add the project path
|
||||
if (input.size() == 1 && std::filesystem::path(input.at(0)).is_absolute()) {
|
||||
if (input.size() == 1 && fs::path(input.at(0)).is_absolute()) {
|
||||
return input.at(0);
|
||||
}
|
||||
|
||||
@ -184,24 +183,24 @@ std::string get_file_path(const std::vector<std::string>& input) {
|
||||
return current_path.string();
|
||||
}
|
||||
|
||||
bool create_dir_if_needed(const std::filesystem::path& path) {
|
||||
if (!std::filesystem::is_directory(path)) {
|
||||
std::filesystem::create_directories(path);
|
||||
bool create_dir_if_needed(const fs::path& path) {
|
||||
if (!fs::is_directory(path)) {
|
||||
fs::create_directories(path);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool create_dir_if_needed_for_file(const std::string& path) {
|
||||
return create_dir_if_needed_for_file(std::filesystem::path(path));
|
||||
return create_dir_if_needed_for_file(fs::path(path));
|
||||
}
|
||||
|
||||
bool create_dir_if_needed_for_file(const std::filesystem::path& path) {
|
||||
return std::filesystem::create_directories(path.parent_path());
|
||||
bool create_dir_if_needed_for_file(const fs::path& path) {
|
||||
return fs::create_directories(path.parent_path());
|
||||
}
|
||||
|
||||
void write_binary_file(const std::filesystem::path& name, const void* data, size_t size) {
|
||||
FILE* fp = fopen(name.string().c_str(), "wb");
|
||||
void write_binary_file(const fs::path& name, const void* data, size_t size) {
|
||||
FILE* fp = file_util::open_file(name.string().c_str(), "wb");
|
||||
if (!fp) {
|
||||
throw std::runtime_error("couldn't open file " + name.string());
|
||||
}
|
||||
@ -215,10 +214,10 @@ void write_binary_file(const std::filesystem::path& name, const void* data, size
|
||||
}
|
||||
|
||||
void write_binary_file(const std::string& name, const void* data, size_t size) {
|
||||
write_binary_file(std::filesystem::path(name), data, size);
|
||||
write_binary_file(fs::path(name), data, size);
|
||||
}
|
||||
|
||||
void write_rgba_png(const std::filesystem::path& name, void* data, int w, int h) {
|
||||
void write_rgba_png(const fs::path& name, void* data, int w, int h) {
|
||||
auto flags = 0;
|
||||
|
||||
auto ok = fpng::fpng_encode_image_to_file(name.string().c_str(), data, w, h, 4, flags);
|
||||
@ -229,11 +228,11 @@ void write_rgba_png(const std::filesystem::path& name, void* data, int w, int h)
|
||||
}
|
||||
|
||||
void write_text_file(const std::string& file_name, const std::string& text) {
|
||||
write_text_file(std::filesystem::path(file_name), text);
|
||||
write_text_file(fs::path(file_name), text);
|
||||
}
|
||||
|
||||
void write_text_file(const std::filesystem::path& file_name, const std::string& text) {
|
||||
FILE* fp = fopen(file_name.string().c_str(), "w");
|
||||
void write_text_file(const fs::path& file_name, const std::string& text) {
|
||||
FILE* fp = file_util::open_file(file_name.string().c_str(), "w");
|
||||
if (!fp) {
|
||||
lg::error("Failed to fopen {}\n", file_name.string());
|
||||
throw std::runtime_error("Failed to open file");
|
||||
@ -242,26 +241,25 @@ void write_text_file(const std::filesystem::path& file_name, const std::string&
|
||||
fclose(fp);
|
||||
}
|
||||
std::vector<uint8_t> read_binary_file(const std::string& filename) {
|
||||
return read_binary_file(std::filesystem::path(filename));
|
||||
return read_binary_file(fs::path(filename));
|
||||
}
|
||||
|
||||
std::vector<uint8_t> read_binary_file(const std::filesystem::path& path) {
|
||||
std::vector<uint8_t> read_binary_file(const fs::path& path) {
|
||||
// make sure file exists and isn't a directory
|
||||
|
||||
auto status = std::filesystem::status(path);
|
||||
auto status = fs::status(path);
|
||||
|
||||
if (!std::filesystem::exists(status)) {
|
||||
if (!fs::exists(status)) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("File {} cannot be opened: does not exist.", path.string()));
|
||||
}
|
||||
|
||||
if (status.type() != std::filesystem::file_type::regular &&
|
||||
status.type() != std::filesystem::file_type::symlink) {
|
||||
if (status.type() != fs::file_type::regular && status.type() != fs::file_type::symlink) {
|
||||
throw std::runtime_error(
|
||||
fmt::format("File {} cannot be opened: not a regular file or symlink.", path.string()));
|
||||
}
|
||||
|
||||
auto fp = fopen(path.string().c_str(), "rb");
|
||||
auto fp = file_util::open_file(path.string().c_str(), "rb");
|
||||
if (!fp)
|
||||
throw std::runtime_error("File " + path.string() +
|
||||
" cannot be opened: " + std::string(strerror(errno)));
|
||||
@ -281,7 +279,7 @@ std::vector<uint8_t> read_binary_file(const std::filesystem::path& path) {
|
||||
return data;
|
||||
}
|
||||
|
||||
std::string read_text_file(const std::filesystem::path& path) {
|
||||
std::string read_text_file(const fs::path& path) {
|
||||
std::ifstream file(path.string());
|
||||
if (!file.good()) {
|
||||
throw std::runtime_error("couldn't open " + path.string());
|
||||
@ -292,7 +290,7 @@ std::string read_text_file(const std::filesystem::path& path) {
|
||||
}
|
||||
|
||||
std::string read_text_file(const std::string& path) {
|
||||
return read_text_file(std::filesystem::path(path));
|
||||
return read_text_file(fs::path(path));
|
||||
}
|
||||
|
||||
bool is_printable_char(char c) {
|
||||
@ -304,7 +302,7 @@ std::string combine_path(const std::string& parent, const std::string& child) {
|
||||
}
|
||||
|
||||
bool file_exists(const std::string& path) {
|
||||
return std::filesystem::exists(path);
|
||||
return fs::exists(path);
|
||||
}
|
||||
|
||||
std::string base_name(const std::string& filename) {
|
||||
@ -448,7 +446,7 @@ void MakeISOName(char* dst, const char* src) {
|
||||
}
|
||||
|
||||
void assert_file_exists(const char* path, const char* error_message) {
|
||||
if (!std::filesystem::exists(path)) {
|
||||
if (!fs::exists(path)) {
|
||||
ASSERT_MSG(false, fmt::format("File {} was not found: {}", path, error_message));
|
||||
}
|
||||
}
|
||||
@ -512,4 +510,12 @@ std::vector<u8> decompress_dgo(const std::vector<u8>& data_in) {
|
||||
return decompressed_data;
|
||||
}
|
||||
|
||||
FILE* open_file(const fs::path& path, std::string mode) {
|
||||
#ifdef _WIN32
|
||||
return _wfopen(path.wstring().c_str(), std::wstring(mode.begin(), mode.end()).c_str());
|
||||
#else
|
||||
return fopen(path.string().c_str(), mode.c_str());
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace file_util
|
||||
|
@ -5,36 +5,46 @@
|
||||
* Utility functions for reading and writing files.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#endif
|
||||
|
||||
#include "third-party/filesystem.hpp"
|
||||
|
||||
#ifdef _WIN32
|
||||
#undef FALSE
|
||||
#endif
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace fs = ghc::filesystem;
|
||||
|
||||
namespace file_util {
|
||||
std::filesystem::path get_user_home_dir();
|
||||
std::filesystem::path get_user_config_dir();
|
||||
std::filesystem::path get_user_settings_dir();
|
||||
std::filesystem::path get_user_memcard_dir();
|
||||
std::filesystem::path get_jak_project_dir();
|
||||
fs::path get_user_home_dir();
|
||||
fs::path get_user_config_dir();
|
||||
fs::path get_user_settings_dir();
|
||||
fs::path get_user_memcard_dir();
|
||||
fs::path get_jak_project_dir();
|
||||
|
||||
bool create_dir_if_needed(const std::filesystem::path& path);
|
||||
bool create_dir_if_needed(const fs::path& path);
|
||||
bool create_dir_if_needed_for_file(const std::string& path);
|
||||
bool create_dir_if_needed_for_file(const std::filesystem::path& path);
|
||||
bool setup_project_path(std::optional<std::filesystem::path> project_path_override);
|
||||
bool create_dir_if_needed_for_file(const fs::path& path);
|
||||
bool setup_project_path(std::optional<fs::path> project_path_override);
|
||||
std::string get_file_path(const std::vector<std::string>& path);
|
||||
void write_binary_file(const std::string& name, const void* data, size_t size);
|
||||
void write_binary_file(const std::filesystem::path& name, const void* data, size_t size);
|
||||
void write_rgba_png(const std::filesystem::path& name, void* data, int w, int h);
|
||||
void write_binary_file(const fs::path& name, const void* data, size_t size);
|
||||
void write_rgba_png(const fs::path& name, void* data, int w, int h);
|
||||
void write_text_file(const std::string& file_name, const std::string& text);
|
||||
void write_text_file(const std::filesystem::path& file_name, const std::string& text);
|
||||
void write_text_file(const fs::path& file_name, const std::string& text);
|
||||
std::vector<uint8_t> read_binary_file(const std::string& filename);
|
||||
std::vector<uint8_t> read_binary_file(const std::filesystem::path& filename);
|
||||
std::vector<uint8_t> read_binary_file(const fs::path& filename);
|
||||
std::string read_text_file(const std::string& path);
|
||||
std::string read_text_file(const std::filesystem::path& path);
|
||||
std::string read_text_file(const fs::path& path);
|
||||
bool is_printable_char(char c);
|
||||
std::string combine_path(const std::string& parent, const std::string& child);
|
||||
bool file_exists(const std::string& path);
|
||||
@ -44,4 +54,6 @@ void ISONameFromAnimationName(char* dst, const char* src);
|
||||
void assert_file_exists(const char* path, const char* error_message);
|
||||
bool dgo_header_is_compressed(const std::vector<u8>& data);
|
||||
std::vector<u8> decompress_dgo(const std::vector<u8>& data_in);
|
||||
|
||||
FILE* open_file(const fs::path& path, std::string mode);
|
||||
} // namespace file_util
|
||||
|
@ -80,11 +80,11 @@ void add_from_dir(FILE* fp, u32 sector, u32 size, IsoFile::Entry* parent) {
|
||||
void unpack_entry(FILE* fp,
|
||||
IsoFile& iso,
|
||||
const IsoFile::Entry& entry,
|
||||
const std::filesystem::path& dest,
|
||||
const fs::path& dest,
|
||||
bool print_progress) {
|
||||
std::filesystem::path path_to_entry = dest / entry.name;
|
||||
fs::path path_to_entry = dest / entry.name;
|
||||
if (entry.is_dir) {
|
||||
std::filesystem::create_directory(path_to_entry);
|
||||
fs::create_directory(path_to_entry);
|
||||
for (const auto& child : entry.children) {
|
||||
unpack_entry(fp, iso, child, path_to_entry, print_progress);
|
||||
}
|
||||
@ -118,15 +118,12 @@ IsoFile find_files_in_iso(FILE* fp) {
|
||||
return result;
|
||||
}
|
||||
|
||||
void unpack_iso_files(FILE* fp,
|
||||
IsoFile& layout,
|
||||
const std::filesystem::path& dest,
|
||||
bool print_progress) {
|
||||
void unpack_iso_files(FILE* fp, IsoFile& layout, const fs::path& dest, bool print_progress) {
|
||||
unpack_entry(fp, layout, layout.root, dest, print_progress);
|
||||
}
|
||||
|
||||
IsoFile unpack_iso_files(FILE* fp,
|
||||
const std::filesystem::path& dest,
|
||||
const fs::path& dest,
|
||||
bool print_progress,
|
||||
const bool hashFiles) {
|
||||
auto file = find_files_in_iso(fp);
|
||||
|
@ -1,9 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "third-party/xxhash.hpp"
|
||||
|
||||
struct IsoFile {
|
||||
@ -34,8 +35,8 @@ struct IsoFile {
|
||||
};
|
||||
|
||||
IsoFile find_files_in_iso(FILE* fp);
|
||||
void unpack_iso_files(FILE* fp, IsoFile& layout, const std::filesystem::path& dest);
|
||||
void unpack_iso_files(FILE* fp, IsoFile& layout, const fs::path& dest);
|
||||
IsoFile unpack_iso_files(FILE* fp,
|
||||
const std::filesystem::path& dest,
|
||||
const fs::path& dest,
|
||||
bool print_progress,
|
||||
const bool hashFiles = false);
|
||||
|
61
common/util/unicode_util.cpp
Normal file
61
common/util/unicode_util.cpp
Normal file
@ -0,0 +1,61 @@
|
||||
#include "unicode_util.h"
|
||||
|
||||
// clang-format off
|
||||
#ifdef _WIN32
|
||||
#define NOMINMAX
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
#include <Stringapiset.h>
|
||||
#include <processenv.h>
|
||||
#include <winbase.h>
|
||||
#include <shellapi.h>
|
||||
#undef FALSE
|
||||
#endif
|
||||
// clang-format on
|
||||
|
||||
std::string utf8_from_utf16(const wchar_t* utf16_string) {
|
||||
#ifdef _WIN32
|
||||
if (utf16_string == nullptr) {
|
||||
return std::string();
|
||||
}
|
||||
int target_length = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, nullptr,
|
||||
0, nullptr, nullptr);
|
||||
if (target_length == 0) {
|
||||
return std::string();
|
||||
}
|
||||
std::string utf8_string;
|
||||
utf8_string.resize(target_length);
|
||||
int converted_length = WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
|
||||
utf8_string.data(), target_length, nullptr, nullptr);
|
||||
if (converted_length == 0) {
|
||||
return std::string();
|
||||
}
|
||||
return utf8_string;
|
||||
#else
|
||||
return "don't call this on linux";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::string> get_widechar_cli_args() {
|
||||
#ifdef _WIN32
|
||||
// Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
|
||||
int argc;
|
||||
wchar_t** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
|
||||
if (argv == nullptr) {
|
||||
return {};
|
||||
}
|
||||
|
||||
std::vector<std::string> command_line_arguments;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
command_line_arguments.push_back(utf8_from_utf16(argv[i]));
|
||||
}
|
||||
|
||||
LocalFree(argv);
|
||||
|
||||
return command_line_arguments;
|
||||
#else
|
||||
return {"don't call this on linux"};
|
||||
#endif
|
||||
}
|
7
common/util/unicode_util.h
Normal file
7
common/util/unicode_util.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::string utf8_from_utf16(const wchar_t* utf16_string);
|
||||
std::vector<std::string> get_widechar_cli_args();
|
@ -100,7 +100,7 @@ target_link_libraries(decompiler
|
||||
|
||||
|
||||
add_executable(extractor
|
||||
extractor/main.cpp)
|
||||
extractor/main.cpp extractor/extractor_util.hpp )
|
||||
|
||||
target_link_libraries(extractor
|
||||
decomp
|
||||
|
@ -113,10 +113,10 @@ const ObjectFileData& ObjectFileDB::lookup_record(const ObjectFileRecord& rec) c
|
||||
/*!
|
||||
* Build an object file DB for the given list of DGOs.
|
||||
*/
|
||||
ObjectFileDB::ObjectFileDB(const std::vector<std::filesystem::path>& _dgos,
|
||||
const std::filesystem::path& obj_file_name_map_file,
|
||||
const std::vector<std::filesystem::path>& object_files,
|
||||
const std::vector<std::filesystem::path>& str_files,
|
||||
ObjectFileDB::ObjectFileDB(const std::vector<fs::path>& _dgos,
|
||||
const fs::path& obj_file_name_map_file,
|
||||
const std::vector<fs::path>& object_files,
|
||||
const std::vector<fs::path>& str_files,
|
||||
const Config& config) {
|
||||
Timer timer;
|
||||
|
||||
@ -258,7 +258,7 @@ void ObjectFileDB::load_map_file(const std::string& map_data) {
|
||||
/*!
|
||||
* Load the objects stored in the given DGO into the ObjectFileDB
|
||||
*/
|
||||
void ObjectFileDB::get_objs_from_dgo(const std::filesystem::path& filename, const Config& config) {
|
||||
void ObjectFileDB::get_objs_from_dgo(const fs::path& filename, const Config& config) {
|
||||
auto dgo_data = file_util::read_binary_file(filename);
|
||||
stats.total_dgo_bytes += dgo_data.size();
|
||||
|
||||
@ -516,7 +516,7 @@ void ObjectFileDB::process_labels() {
|
||||
/*!
|
||||
* Dump object files and their linking data to text files for debugging
|
||||
*/
|
||||
void ObjectFileDB::write_object_file_words(const std::filesystem::path& output_dir,
|
||||
void ObjectFileDB::write_object_file_words(const fs::path& output_dir,
|
||||
bool dump_data,
|
||||
bool dump_code) {
|
||||
lg::info("- Writing object file dumps (code? {} data? {})...", dump_code, dump_data);
|
||||
@ -545,7 +545,7 @@ void ObjectFileDB::write_object_file_words(const std::filesystem::path& output_d
|
||||
/*!
|
||||
* Dump disassembly for object files containing code. Data zones will also be dumped.
|
||||
*/
|
||||
void ObjectFileDB::write_disassembly(const std::filesystem::path& output_dir,
|
||||
void ObjectFileDB::write_disassembly(const fs::path& output_dir,
|
||||
bool disassemble_data,
|
||||
bool disassemble_code,
|
||||
bool print_hex) {
|
||||
@ -623,7 +623,7 @@ void ObjectFileDB::find_code(const Config& config) {
|
||||
* Finds and writes all scripts into a file named all_scripts.lisp.
|
||||
* Doesn't change any state in ObjectFileDB.
|
||||
*/
|
||||
void ObjectFileDB::find_and_write_scripts(const std::filesystem::path& output_dir) {
|
||||
void ObjectFileDB::find_and_write_scripts(const fs::path& output_dir) {
|
||||
lg::info("- Finding scripts in object files...");
|
||||
Timer timer;
|
||||
std::string all_scripts;
|
||||
@ -645,8 +645,7 @@ void ObjectFileDB::find_and_write_scripts(const std::filesystem::path& output_di
|
||||
lg::info(" Total {:.3f} ms\n", timer.getMs());
|
||||
}
|
||||
|
||||
std::string ObjectFileDB::process_tpages(TextureDB& tex_db,
|
||||
const std::filesystem::path& output_path) {
|
||||
std::string ObjectFileDB::process_tpages(TextureDB& tex_db, const fs::path& output_path) {
|
||||
lg::info("- Finding textures in tpages...");
|
||||
std::string tpage_string = "tpage-";
|
||||
int total = 0, success = 0;
|
||||
@ -796,7 +795,7 @@ void ObjectFileDB::extract_art_info() {
|
||||
/*!
|
||||
* Write out the art group information.
|
||||
*/
|
||||
void ObjectFileDB::dump_art_info(const std::filesystem::path& output_dir) {
|
||||
void ObjectFileDB::dump_art_info(const fs::path& output_dir) {
|
||||
lg::info("Writing art group info...");
|
||||
Timer timer;
|
||||
|
||||
@ -820,7 +819,7 @@ void ObjectFileDB::dump_art_info(const std::filesystem::path& output_dir) {
|
||||
lg::info("Written art group info: in {:.2f} ms\n", timer.getMs());
|
||||
}
|
||||
|
||||
void ObjectFileDB::dump_raw_objects(const std::filesystem::path& output_dir) {
|
||||
void ObjectFileDB::dump_raw_objects(const fs::path& output_dir) {
|
||||
for_each_obj([&](ObjectFileData& data) {
|
||||
auto dest = output_dir / data.to_unique_name();
|
||||
if (data.obj_version != 3) {
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "decompiler/analysis/symbol_def_map.h"
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
@ -144,30 +145,28 @@ struct LetRewriteStats {
|
||||
|
||||
class ObjectFileDB {
|
||||
public:
|
||||
ObjectFileDB(const std::vector<std::filesystem::path>& _dgos,
|
||||
const std::filesystem::path& obj_file_name_map_file,
|
||||
const std::vector<std::filesystem::path>& object_files,
|
||||
const std::vector<std::filesystem::path>& str_files,
|
||||
ObjectFileDB(const std::vector<fs::path>& _dgos,
|
||||
const fs::path& obj_file_name_map_file,
|
||||
const std::vector<fs::path>& object_files,
|
||||
const std::vector<fs::path>& str_files,
|
||||
const Config& config);
|
||||
std::string generate_dgo_listing();
|
||||
std::string generate_obj_listing(const std::unordered_set<std::string>& merged_objs);
|
||||
void process_link_data(const Config& config);
|
||||
void process_labels();
|
||||
void find_code(const Config& config);
|
||||
void find_and_write_scripts(const std::filesystem::path& output_dir);
|
||||
void find_and_write_scripts(const fs::path& output_dir);
|
||||
void extract_art_info();
|
||||
void dump_art_info(const std::filesystem::path& output_dir);
|
||||
void dump_raw_objects(const std::filesystem::path& output_dir);
|
||||
void write_object_file_words(const std::filesystem::path& output_dir,
|
||||
bool dump_data,
|
||||
bool dump_code);
|
||||
void write_disassembly(const std::filesystem::path& output_dir,
|
||||
void dump_art_info(const fs::path& output_dir);
|
||||
void dump_raw_objects(const fs::path& output_dir);
|
||||
void write_object_file_words(const fs::path& output_dir, bool dump_data, bool dump_code);
|
||||
void write_disassembly(const fs::path& output_dir,
|
||||
bool disassemble_data,
|
||||
bool disassemble_code,
|
||||
bool print_hex);
|
||||
|
||||
void analyze_functions_ir2(
|
||||
const std::filesystem::path& output_dir,
|
||||
const fs::path& output_dir,
|
||||
const Config& config,
|
||||
const std::unordered_set<std::string>& skip_functions,
|
||||
const std::unordered_map<std::string, std::unordered_set<std::string>>& skip_states = {});
|
||||
@ -185,7 +184,7 @@ class ObjectFileDB {
|
||||
void ir2_rewrite_inline_asm_instructions(int seg, ObjectFileData& data);
|
||||
void ir2_insert_anonymous_functions(int seg, ObjectFileData& data);
|
||||
void ir2_symbol_definition_map(ObjectFileData& data);
|
||||
void ir2_write_results(const std::filesystem::path& output_dir,
|
||||
void ir2_write_results(const fs::path& output_dir,
|
||||
const Config& config,
|
||||
const std::vector<std::string>& imports,
|
||||
ObjectFileData& data);
|
||||
@ -193,7 +192,7 @@ class ObjectFileDB {
|
||||
void ir2_do_segment_analysis_phase2(int seg, const Config& config, ObjectFileData& data);
|
||||
void ir2_setup_labels(const Config& config, ObjectFileData& data);
|
||||
void ir2_run_mips2c(const Config& config, ObjectFileData& data);
|
||||
void ir2_analyze_all_types(const std::filesystem::path& output_file,
|
||||
void ir2_analyze_all_types(const fs::path& output_file,
|
||||
const std::optional<std::string>& previous_game_types,
|
||||
const std::unordered_set<std::string>& bad_types);
|
||||
std::string ir2_to_file(ObjectFileData& data, const Config& config);
|
||||
@ -202,7 +201,7 @@ class ObjectFileDB {
|
||||
const std::vector<std::string>& imports,
|
||||
const std::unordered_set<std::string>& skip_functions);
|
||||
|
||||
std::string process_tpages(TextureDB& tex_db, const std::filesystem::path& output_path);
|
||||
std::string process_tpages(TextureDB& tex_db, const fs::path& output_path);
|
||||
std::string process_game_count_file();
|
||||
std::string process_game_text_files(const Config& cfg);
|
||||
|
||||
@ -216,7 +215,7 @@ class ObjectFileDB {
|
||||
|
||||
public:
|
||||
void load_map_file(const std::string& map_data);
|
||||
void get_objs_from_dgo(const std::filesystem::path& filename, const Config& config);
|
||||
void get_objs_from_dgo(const fs::path& filename, const Config& config);
|
||||
void add_obj_from_dgo(const std::string& obj_name,
|
||||
const std::string& name_in_dgo,
|
||||
const uint8_t* obj_data,
|
||||
|
@ -38,7 +38,7 @@ namespace decompiler {
|
||||
* functions, but nothing else.
|
||||
*/
|
||||
void ObjectFileDB::analyze_functions_ir2(
|
||||
const std::filesystem::path& output_dir,
|
||||
const fs::path& output_dir,
|
||||
const Config& config,
|
||||
const std::unordered_set<std::string>& skip_functions,
|
||||
const std::unordered_map<std::string, std::unordered_set<std::string>>& skip_states) {
|
||||
@ -300,7 +300,7 @@ void ObjectFileDB::ir2_top_level_pass(const Config& config) {
|
||||
lg::info("{:4d} logins {:.2f}%\n", total_top_levels, 100.f * total_top_levels / total_functions);
|
||||
}
|
||||
|
||||
void ObjectFileDB::ir2_analyze_all_types(const std::filesystem::path& output_file,
|
||||
void ObjectFileDB::ir2_analyze_all_types(const fs::path& output_file,
|
||||
const std::optional<std::string>& previous_game_types,
|
||||
const std::unordered_set<std::string>& bad_types) {
|
||||
struct PerObject {
|
||||
@ -690,7 +690,7 @@ void ObjectFileDB::ir2_insert_anonymous_functions(int seg, ObjectFileData& data)
|
||||
});
|
||||
}
|
||||
|
||||
void ObjectFileDB::ir2_write_results(const std::filesystem::path& output_dir,
|
||||
void ObjectFileDB::ir2_write_results(const fs::path& output_dir,
|
||||
const Config& config,
|
||||
const std::vector<std::string>& imports,
|
||||
ObjectFileData& obj) {
|
||||
|
@ -14,7 +14,7 @@
|
||||
#include "game/common/str_rpc_types.h"
|
||||
|
||||
namespace decompiler {
|
||||
StrFileReader::StrFileReader(const std::filesystem::path& file_path) {
|
||||
StrFileReader::StrFileReader(const fs::path& file_path) {
|
||||
auto data = file_util::read_binary_file(file_path);
|
||||
ASSERT(data.size() >= SECTOR_SIZE); // must have at least the header sector
|
||||
ASSERT(data.size() % SECTOR_SIZE == 0); // should be multiple of the sector size.
|
||||
|
@ -5,16 +5,16 @@
|
||||
* Utility class to read a .STR file and extract the full file name.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
class StrFileReader {
|
||||
public:
|
||||
explicit StrFileReader(const std::filesystem::path& file_path);
|
||||
explicit StrFileReader(const fs::path& file_path);
|
||||
int chunk_count() const;
|
||||
const std::vector<u8>& get_chunk(int idx) const;
|
||||
std::string get_full_name(const std::string& short_name) const;
|
||||
@ -22,4 +22,4 @@ class StrFileReader {
|
||||
private:
|
||||
std::vector<std::vector<u8>> m_chunks;
|
||||
};
|
||||
} // namespace decompiler
|
||||
} // namespace decompiler
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "TextureDB.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
|
||||
#include "third-party/fmt/core.h"
|
||||
@ -45,12 +43,11 @@ void TextureDB::add_texture(u32 tpage,
|
||||
}
|
||||
}
|
||||
|
||||
void TextureDB::replace_textures(const std::filesystem::path& path) {
|
||||
std::filesystem::path base_path(path);
|
||||
void TextureDB::replace_textures(const fs::path& path) {
|
||||
fs::path base_path(path);
|
||||
for (auto& tex : textures) {
|
||||
std::filesystem::path full_path =
|
||||
base_path / tpage_names.at(tex.second.page) / (tex.second.name + ".png");
|
||||
if (std::filesystem::exists(full_path)) {
|
||||
fs::path full_path = base_path / tpage_names.at(tex.second.page) / (tex.second.name + ".png");
|
||||
if (fs::exists(full_path)) {
|
||||
fmt::print("Replacing {}\n", full_path.string().c_str());
|
||||
int w, h;
|
||||
auto data = stbi_load(full_path.string().c_str(), &w, &h, 0, 4); // rgba channels
|
||||
|
@ -1,12 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
struct TextureDB {
|
||||
@ -30,6 +30,6 @@ struct TextureDB {
|
||||
const std::string& tpage_name,
|
||||
const std::vector<std::string>& level_names);
|
||||
|
||||
void replace_textures(const std::filesystem::path& path);
|
||||
void replace_textures(const fs::path& path);
|
||||
};
|
||||
} // namespace decompiler
|
||||
|
@ -89,7 +89,7 @@ struct VagFileHeader {
|
||||
/*!
|
||||
* Read the DIR file into an AudioDir
|
||||
*/
|
||||
AudioDir read_audio_dir(const std::filesystem::path& path) {
|
||||
AudioDir read_audio_dir(const fs::path& path) {
|
||||
// matches the format in file.
|
||||
struct DirEntry {
|
||||
char name[8];
|
||||
@ -145,7 +145,7 @@ struct AudioFileInfo {
|
||||
double length_seconds;
|
||||
};
|
||||
|
||||
AudioFileInfo process_audio_file(const std::filesystem::path& output_folder,
|
||||
AudioFileInfo process_audio_file(const fs::path& output_folder,
|
||||
const std::vector<u8>& data,
|
||||
const std::string& name,
|
||||
const std::string& suffix) {
|
||||
@ -182,8 +182,8 @@ AudioFileInfo process_audio_file(const std::filesystem::path& output_folder,
|
||||
return {vag_filename, (double)decoded_samples.size() / header.sample_rate};
|
||||
}
|
||||
|
||||
void process_streamed_audio(const std::filesystem::path& output_path,
|
||||
const std::filesystem::path& input_dir,
|
||||
void process_streamed_audio(const fs::path& output_path,
|
||||
const fs::path& input_dir,
|
||||
const std::vector<std::string>& audio_files) {
|
||||
auto dir_data = read_audio_dir(input_dir / "VAG" / "VAGDIR.AYB");
|
||||
double audio_len = 0.f;
|
||||
@ -201,7 +201,7 @@ void process_streamed_audio(const std::filesystem::path& output_path,
|
||||
for (size_t lang_id = 0; lang_id < audio_files.size(); lang_id++) {
|
||||
auto& file = audio_files[lang_id];
|
||||
auto wad_data = file_util::read_binary_file(input_dir / "VAG" / file);
|
||||
auto suffix = std::filesystem::path(file).extension().u8string().substr(1);
|
||||
auto suffix = fs::path(file).extension().u8string().substr(1);
|
||||
langs.push_back(suffix);
|
||||
dir_data.set_file_size(wad_data.size());
|
||||
for (int i = 0; i < dir_data.entry_count(); i++) {
|
||||
|
@ -1,11 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
namespace decompiler {
|
||||
void process_streamed_audio(const std::filesystem::path& output_path,
|
||||
const std::filesystem::path& input_dir,
|
||||
void process_streamed_audio(const fs::path& output_path,
|
||||
const fs::path& input_dir,
|
||||
const std::vector<std::string>& audio_files);
|
||||
}
|
||||
}
|
||||
|
@ -421,7 +421,7 @@ TexturePage read_texture_page(ObjectFileData& data,
|
||||
*/
|
||||
TPageResultStats process_tpage(ObjectFileData& data,
|
||||
TextureDB& texture_db,
|
||||
const std::filesystem::path& output_path) {
|
||||
const fs::path& output_path) {
|
||||
TPageResultStats stats;
|
||||
auto& words = data.linked_data.words_by_seg.at(0);
|
||||
const auto& level_names = data.dgo_names;
|
||||
|
@ -1,7 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
|
||||
namespace decompiler {
|
||||
@ -15,5 +13,5 @@ struct TPageResultStats {
|
||||
|
||||
TPageResultStats process_tpage(ObjectFileData& data,
|
||||
TextureDB& texture_db,
|
||||
const std::filesystem::path& output_path);
|
||||
} // namespace decompiler
|
||||
const fs::path& output_path);
|
||||
} // namespace decompiler
|
||||
|
251
decompiler/extractor/extractor_util.hpp
Normal file
251
decompiler/extractor/extractor_util.hpp
Normal file
@ -0,0 +1,251 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <regex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include <common/util/json_util.h>
|
||||
#include <common/util/read_iso_file.h>
|
||||
|
||||
#include <third-party/json.hpp>
|
||||
|
||||
#include "third-party/xxhash.hpp"
|
||||
|
||||
enum class ExtractorErrorCode {
|
||||
SUCCESS = 0,
|
||||
INVALID_CLI_INPUT = 3990,
|
||||
VALIDATION_CANT_LOCATE_ELF = 4000,
|
||||
VALIDATION_SERIAL_MISSING_FROM_DB = 4001,
|
||||
VALIDATION_ELF_MISSING_FROM_DB = 4002,
|
||||
VALIDATION_BAD_ISO_CONTENTS = 4010,
|
||||
VALIDATION_INCORRECT_EXTRACTION_COUNT = 4011,
|
||||
VALIDATION_FILE_CONTENTS_UNEXPECTED = 4012,
|
||||
VALIDATION_BAD_EXTRACTION = 4020,
|
||||
DECOMPILATION_GENERIC_ERROR = 4030,
|
||||
EXTRACTION_INVALID_ISO_PATH = 4040,
|
||||
EXTRACTION_ISO_UNEXPECTED_SIZE = 4041,
|
||||
COMPILATION_BAD_PROJECT_PATH = 4050,
|
||||
};
|
||||
|
||||
enum GameIsoFlags { FLAG_JAK1_BLACK_LABEL = (1 << 0) };
|
||||
|
||||
static const std::unordered_map<std::string, GameIsoFlags> sGameIsoFlagNames = {
|
||||
{"jak1-black-label", FLAG_JAK1_BLACK_LABEL}};
|
||||
|
||||
// used for - decompiler_out/<jak1> and iso_data/<jak1>
|
||||
std::unordered_map<std::string, std::string> data_subfolders = {{"jak1", "jak1"}};
|
||||
|
||||
struct ISOMetadata {
|
||||
std::string canonical_name;
|
||||
std::string region;
|
||||
int num_files;
|
||||
xxh::hash64_t contents_hash;
|
||||
std::string decomp_config;
|
||||
std::string game_name;
|
||||
std::vector<std::string> flags;
|
||||
};
|
||||
|
||||
// This is all we need to re-fetch info from the database
|
||||
// - if this changes such that we have a collision in the future,
|
||||
// then the database isn't adequate and everything must change
|
||||
struct BuildInfo {
|
||||
std::string serial = "";
|
||||
xxh::hash64_t elf_hash = 0;
|
||||
};
|
||||
|
||||
void to_json(nlohmann::json& j, const BuildInfo& info) {
|
||||
j = nlohmann::json{{"serial", info.serial}, {"elf_hash", info.elf_hash}};
|
||||
}
|
||||
|
||||
void from_json(const nlohmann::json& j, BuildInfo& info) {
|
||||
j[0].at("serial").get_to(info.serial);
|
||||
j[0].at("elf_hash").get_to(info.elf_hash);
|
||||
}
|
||||
|
||||
std::optional<BuildInfo> get_buildinfo_from_path(fs::path iso_data_path) {
|
||||
if (!fs::exists(iso_data_path / "buildinfo.json")) {
|
||||
return {};
|
||||
}
|
||||
auto buildinfo_path = (iso_data_path / "buildinfo.json").string();
|
||||
try {
|
||||
return parse_commented_json(file_util::read_text_file(buildinfo_path), buildinfo_path)
|
||||
.get<BuildInfo>();
|
||||
} catch (std::exception& e) {
|
||||
lg::error("JSON parsing error on buildinfo.json - {}", e.what());
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
static const ISOMetadata jak1_ntsc_black_label_info = {
|
||||
"Jak & Daxter™: The Precursor Legacy (Black Label)",
|
||||
"NTSC-U",
|
||||
337,
|
||||
11363853835861842434U,
|
||||
"jak1_ntsc_black_label",
|
||||
"jak1",
|
||||
{"jak1-black-label"}};
|
||||
|
||||
// { SERIAL : { ELF_HASH : ISOMetadataDatabase } }
|
||||
static const std::unordered_map<std::string, std::unordered_map<xxh::hash64_t, ISOMetadata>>
|
||||
isoDatabase{{"SCUS-97124",
|
||||
{{7280758013604870207U, jak1_ntsc_black_label_info},
|
||||
{744661860962747854,
|
||||
{"Jak & Daxter™: The Precursor Legacy",
|
||||
"NTSC-U",
|
||||
338,
|
||||
8538304367812415885U,
|
||||
"jak1_jp",
|
||||
"jak1",
|
||||
{}}}}},
|
||||
{"SCES-50361",
|
||||
{{12150718117852276522U,
|
||||
{"Jak & Daxter™: The Precursor Legacy",
|
||||
"PAL",
|
||||
338,
|
||||
16850370297611763875U,
|
||||
"jak1_pal",
|
||||
"jak1",
|
||||
{}}}}},
|
||||
{"SCPS-15021",
|
||||
{{16909372048085114219U,
|
||||
{"ジャックXダクスター ~ 旧世界の遺産",
|
||||
"NTSC-J",
|
||||
338,
|
||||
1262350561338887717,
|
||||
"jak1_jp",
|
||||
"jak1",
|
||||
{}}}}}};
|
||||
|
||||
std::optional<ISOMetadata> get_version_info_from_build_info(const BuildInfo& build_info) {
|
||||
if (build_info.serial.empty() || build_info.elf_hash == 0) {
|
||||
return {};
|
||||
}
|
||||
auto dbEntry = isoDatabase.find(build_info.serial);
|
||||
if (dbEntry == isoDatabase.end()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
auto& metaMap = dbEntry->second;
|
||||
auto meta_entry = metaMap.find(build_info.elf_hash);
|
||||
if (meta_entry == metaMap.end()) {
|
||||
return {};
|
||||
}
|
||||
return std::make_optional(meta_entry->second);
|
||||
}
|
||||
|
||||
ISOMetadata get_version_info_or_default(const fs::path& iso_data_path) {
|
||||
ISOMetadata version_info = jak1_ntsc_black_label_info;
|
||||
const auto build_info = get_buildinfo_from_path(iso_data_path);
|
||||
if (!build_info) {
|
||||
lg::warn(
|
||||
"unable locate buildinfo.json file in iso data path, defaulting to Jak 1 - NTSC "
|
||||
"Black Label");
|
||||
} else {
|
||||
auto maybe_version_info = get_version_info_from_build_info(build_info.value());
|
||||
if (!maybe_version_info) {
|
||||
lg::warn(
|
||||
"unable to determine game version from buildinfo.json file, defaulting to Jak 1 - NTSC "
|
||||
"Black Label");
|
||||
} else {
|
||||
version_info = maybe_version_info.value();
|
||||
}
|
||||
}
|
||||
return version_info;
|
||||
}
|
||||
|
||||
std::tuple<std::optional<std::string>, std::optional<xxh::hash64_t>> findElfFile(
|
||||
const fs::path& extracted_iso_path) {
|
||||
std::optional<std::string> serial = std::nullopt;
|
||||
std::optional<xxh::hash64_t> elf_hash = std::nullopt;
|
||||
for (const auto& entry : fs::directory_iterator(extracted_iso_path)) {
|
||||
auto as_str = entry.path().filename().string();
|
||||
if (std::regex_match(as_str, std::regex(".{4}_.{3}\\..{2}"))) {
|
||||
serial = std::make_optional(
|
||||
fmt::format("{}-{}", as_str.substr(0, 4), as_str.substr(5, 3) + as_str.substr(9, 2)));
|
||||
// We already found the path, so hash it while we're here
|
||||
auto fp = file_util::open_file(entry.path().string().c_str(), "rb");
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
std::vector<u8> buffer(size);
|
||||
rewind(fp);
|
||||
fread(&buffer[0], sizeof(std::vector<u8>::value_type), buffer.size(), fp);
|
||||
elf_hash = std::make_optional(xxh::xxhash<64>(buffer));
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {serial, elf_hash};
|
||||
}
|
||||
|
||||
void log_potential_new_db_entry(ExtractorErrorCode error_code,
|
||||
const std::string& serial,
|
||||
const xxh::hash64_t elf_hash,
|
||||
const int files_extracted,
|
||||
const xxh::hash64_t contents_hash) {
|
||||
// Finally, return the result
|
||||
// Generate the map entry to make things simple, just convienance
|
||||
if (error_code == ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB) {
|
||||
lg::info(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following serial entry to the database:");
|
||||
lg::info(
|
||||
"\t'{{\"{}\", {{{{{}U, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIG_FILENAME_NO_EXTENSION\", \"jak1|jak2|jak3|jakx\", {}}}}}}}}}'",
|
||||
serial, elf_hash, files_extracted, contents_hash);
|
||||
} else if (error_code == ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB) {
|
||||
lg::info(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following ELF entry to the database under the '{}' serial:",
|
||||
serial);
|
||||
lg::info(
|
||||
"\t'{{{}, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIF_FILENAME_NO_EXTENSION\", \"jak1|jak2|jak3|jakx\", {}}}}}'",
|
||||
elf_hash, files_extracted, contents_hash);
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<bool, ExtractorErrorCode> is_iso_file(fs::path path_to_supposed_iso) {
|
||||
// it's a file, normalize extension case and verify it's an ISO file
|
||||
std::string ext = path_to_supposed_iso.extension().string();
|
||||
if (!std::regex_match(ext, std::regex("\\.(iso|ISO)"))) {
|
||||
lg::error("Provided game data path contains a file that isn't a .ISO!");
|
||||
return {false, ExtractorErrorCode::EXTRACTION_INVALID_ISO_PATH};
|
||||
}
|
||||
|
||||
// make sure the .iso is greater than 1GB in size
|
||||
// to-do: verify game header data as well
|
||||
if (fs::file_size(path_to_supposed_iso) < 1000000000) {
|
||||
lg::error("Provided game data file appears to be too small or corrupted! Size is: {}",
|
||||
fs::file_size(path_to_supposed_iso));
|
||||
return {false, ExtractorErrorCode::EXTRACTION_ISO_UNEXPECTED_SIZE};
|
||||
}
|
||||
return {true, ExtractorErrorCode::SUCCESS};
|
||||
}
|
||||
|
||||
std::tuple<xxh::hash64_t, int> calculate_extraction_hash(const IsoFile& iso_file) {
|
||||
// - XOR all hashes together and hash the result. This makes the ordering of the hashes (aka
|
||||
// files) irrelevant
|
||||
xxh::hash64_t combined_hash = 0;
|
||||
for (const auto& hash : iso_file.hashes) {
|
||||
combined_hash ^= hash;
|
||||
}
|
||||
return {xxh::xxhash<64>({combined_hash}), iso_file.hashes.size()};
|
||||
}
|
||||
|
||||
std::tuple<xxh::hash64_t, int> calculate_extraction_hash(const fs::path& extracted_iso_path) {
|
||||
// - XOR all hashes together and hash the result. This makes the ordering of the hashes (aka
|
||||
// files) irrelevant
|
||||
xxh::hash64_t combined_hash = 0;
|
||||
int filec = 0;
|
||||
for (auto const& dir_entry : fs::recursive_directory_iterator(extracted_iso_path)) {
|
||||
if (dir_entry.is_regular_file()) {
|
||||
auto buffer = file_util::read_binary_file(dir_entry.path().string());
|
||||
auto hash = xxh::xxhash<64>(buffer);
|
||||
combined_hash ^= hash;
|
||||
filec++;
|
||||
}
|
||||
}
|
||||
return {xxh::xxhash<64>({combined_hash}), filec};
|
||||
}
|
@ -2,10 +2,13 @@
|
||||
#include <regex>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "extractor_util.hpp"
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include "common/util/read_iso_file.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
#include "decompiler/Disasm/OpcodeInfo.h"
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
@ -15,369 +18,111 @@
|
||||
|
||||
#include "third-party/CLI11.hpp"
|
||||
|
||||
enum class ExtractorErrorCode {
|
||||
SUCCESS = 0,
|
||||
VALIDATION_CANT_LOCATE_ELF = 4000,
|
||||
VALIDATION_SERIAL_MISSING_FROM_DB = 4001,
|
||||
VALIDATION_ELF_MISSING_FROM_DB = 4002,
|
||||
VALIDATION_BAD_ISO_CONTENTS = 4010,
|
||||
VALIDATION_INCORRECT_EXTRACTION_COUNT = 4011,
|
||||
VALIDATION_BAD_EXTRACTION = 4020,
|
||||
DECOMPILATION_GENERIC_ERROR = 4030,
|
||||
EXTRACTION_INVALID_ISO_PATH = 4040,
|
||||
EXTRACTION_ISO_UNEXPECTED_SIZE = 4041
|
||||
};
|
||||
|
||||
enum GameIsoFlags { FLAG_JAK1_BLACK_LABEL = (1 << 0) };
|
||||
|
||||
static const std::unordered_map<std::string, GameIsoFlags> sGameIsoFlagNames = {
|
||||
{"jak1-black-label", FLAG_JAK1_BLACK_LABEL}};
|
||||
|
||||
struct ISOMetadata {
|
||||
std::string canonical_name;
|
||||
std::string region;
|
||||
int num_files;
|
||||
xxh::hash64_t contents_hash;
|
||||
std::string decomp_config;
|
||||
GameIsoFlags flags = (GameIsoFlags)0;
|
||||
};
|
||||
|
||||
// TODO - when we support jak2 and beyond, add which game it's for as well
|
||||
// this will let the installer reject (or gracefully handle) jak2 isos on the jak1 page, etc.
|
||||
|
||||
// { SERIAL : { ELF_HASH : ISOMetadataDatabase } }
|
||||
static const std::map<std::string, std::map<xxh::hash64_t, ISOMetadata>> isoDatabase{
|
||||
{"SCUS-97124",
|
||||
{{7280758013604870207U,
|
||||
{"Jak & Daxter™: The Precursor Legacy (Black Label)", "NTSC-U", 337, 11363853835861842434U,
|
||||
"jak1_ntsc_black_label", FLAG_JAK1_BLACK_LABEL}},
|
||||
{744661860962747854,
|
||||
{"Jak & Daxter™: The Precursor Legacy", "NTSC-U", 338, 8538304367812415885U, "jak1_jp"}}}},
|
||||
{"SCES-50361",
|
||||
{{12150718117852276522U,
|
||||
{"Jak & Daxter™: The Precursor Legacy", "PAL", 338, 16850370297611763875U, "jak1_pal"}}}},
|
||||
{"SCPS-15021",
|
||||
{{16909372048085114219U,
|
||||
{"ジャックXダクスター ~ 旧世界の遺産", "NTSC-J", 338, 1262350561338887717,
|
||||
"jak1_jp"}}}}};
|
||||
|
||||
void setup_global_decompiler_stuff(std::optional<std::filesystem::path> project_path_override) {
|
||||
decompiler::init_opcode_info();
|
||||
file_util::setup_project_path(project_path_override);
|
||||
}
|
||||
|
||||
IsoFile extract_files(std::filesystem::path data_dir_path,
|
||||
std::filesystem::path extracted_iso_path) {
|
||||
fmt::print(
|
||||
IsoFile extract_files(fs::path input_file_path, fs::path extracted_iso_path) {
|
||||
lg::info(
|
||||
"Note: Provided game data path '{}' points to a file, not a directory. Assuming it's an ISO "
|
||||
"file and attempting to extract!\n",
|
||||
data_dir_path.string());
|
||||
"file and attempting to extract!",
|
||||
input_file_path.string());
|
||||
|
||||
std::filesystem::create_directories(extracted_iso_path);
|
||||
fs::create_directories(extracted_iso_path);
|
||||
|
||||
auto fp = fopen(data_dir_path.string().c_str(), "rb");
|
||||
ASSERT_MSG(fp, "failed to open input ISO file\n");
|
||||
auto fp = file_util::open_file(input_file_path, "rb");
|
||||
ASSERT_MSG(fp, "failed to open input ISO file");
|
||||
IsoFile iso = unpack_iso_files(fp, extracted_iso_path, true, true);
|
||||
fclose(fp);
|
||||
return iso;
|
||||
}
|
||||
|
||||
std::pair<std::optional<std::string>, std::optional<xxh::hash64_t>> findElfFile(
|
||||
const std::filesystem::path& extracted_iso_path) {
|
||||
std::optional<std::string> serial = std::nullopt;
|
||||
std::optional<xxh::hash64_t> elf_hash = std::nullopt;
|
||||
for (const auto& entry : fs::directory_iterator(extracted_iso_path)) {
|
||||
auto as_str = entry.path().filename().string();
|
||||
if (std::regex_match(as_str, std::regex(".{4}_.{3}\\..{2}"))) {
|
||||
serial = std::make_optional(
|
||||
fmt::format("{}-{}", as_str.substr(0, 4), as_str.substr(5, 3) + as_str.substr(9, 2)));
|
||||
// We already found the path, so hash it while we're here
|
||||
auto fp = fopen(entry.path().string().c_str(), "rb");
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size_t size = ftell(fp);
|
||||
std::vector<u8> buffer(size);
|
||||
rewind(fp);
|
||||
fread(&buffer[0], sizeof(std::vector<u8>::value_type), buffer.size(), fp);
|
||||
elf_hash = std::make_optional(xxh::xxhash<64>(buffer));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {serial, elf_hash};
|
||||
}
|
||||
|
||||
std::pair<ExtractorErrorCode, std::optional<ISOMetadata>> validate(
|
||||
const std::filesystem::path& extracted_iso_path) {
|
||||
if (!std::filesystem::exists(extracted_iso_path / "DGO")) {
|
||||
fmt::print(stderr, "ERROR: input folder doesn't have a DGO folder. Is this the right input?\n");
|
||||
return {ExtractorErrorCode::VALIDATION_BAD_EXTRACTION, std::nullopt};
|
||||
std::tuple<std::optional<ISOMetadata>, ExtractorErrorCode> validate(
|
||||
const fs::path& extracted_iso_path,
|
||||
const xxh::hash64_t expected_hash,
|
||||
const int expected_num_files) {
|
||||
if (!fs::exists(extracted_iso_path / "DGO")) {
|
||||
lg::error("input folder doesn't have a DGO folder. Is this the right input?");
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_BAD_EXTRACTION};
|
||||
}
|
||||
|
||||
std::optional<ExtractorErrorCode> error_code;
|
||||
std::optional<std::string> serial = std::nullopt;
|
||||
std::optional<xxh::hash64_t> elf_hash = std::nullopt;
|
||||
std::tie(serial, elf_hash) = findElfFile(extracted_iso_path);
|
||||
|
||||
// - XOR all hashes together and hash the result. This makes the ordering of the hashes (aka
|
||||
// files) irrelevant
|
||||
xxh::hash64_t combined_hash = 0;
|
||||
int filec = 0;
|
||||
for (auto const& dir_entry : std::filesystem::recursive_directory_iterator(extracted_iso_path)) {
|
||||
if (dir_entry.is_regular_file()) {
|
||||
auto buffer = file_util::read_binary_file(dir_entry.path().string());
|
||||
auto hash = xxh::xxhash<64>(buffer);
|
||||
combined_hash ^= hash;
|
||||
filec++;
|
||||
}
|
||||
}
|
||||
xxh::hash64_t contents_hash = xxh::xxhash<64>({combined_hash});
|
||||
const auto [serial, elf_hash] = findElfFile(extracted_iso_path);
|
||||
|
||||
if (!serial || !elf_hash) {
|
||||
fmt::print(stderr, "ERROR: Unable to locate a Serial/ELF file!\n");
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_CANT_LOCATE_ELF);
|
||||
}
|
||||
lg::error("Unable to locate a Serial/ELF file!");
|
||||
// No point in continuing here
|
||||
return {*error_code, std::nullopt};
|
||||
}
|
||||
|
||||
// Find the game in our tracking database
|
||||
std::optional<ISOMetadata> meta_res = std::nullopt;
|
||||
if (auto dbEntry = isoDatabase.find(serial.value()); dbEntry == isoDatabase.end()) {
|
||||
fmt::print(stderr, "ERROR: Serial '{}' not found in the validation database\n", serial.value());
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB);
|
||||
}
|
||||
} else {
|
||||
auto& metaMap = dbEntry->second;
|
||||
auto meta_entry = metaMap.find(elf_hash.value());
|
||||
if (meta_entry == metaMap.end()) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: ELF Hash '{}' not found in the validation database, is this a new or "
|
||||
"modified version of the same game?\n",
|
||||
elf_hash.value());
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB);
|
||||
}
|
||||
} else {
|
||||
meta_res = std::make_optional<ISOMetadata>(meta_entry->second);
|
||||
const auto& meta = *meta_res;
|
||||
// Print out some information
|
||||
fmt::print("Detected Game Metadata:\n");
|
||||
fmt::print("\tDetected - {}\n", meta.canonical_name);
|
||||
fmt::print("\tRegion - {}\n", meta.region);
|
||||
fmt::print("\tSerial - {}\n", dbEntry->first);
|
||||
fmt::print("\tUses Decompiler Config - {}\n", meta.decomp_config);
|
||||
|
||||
// - Number of Files
|
||||
if (meta.num_files != filec) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: Extracted an unexpected number of files. Expected '{}', Actual '{}'\n",
|
||||
meta.num_files, filec);
|
||||
if (!error_code.has_value()) {
|
||||
error_code =
|
||||
std::make_optional(ExtractorErrorCode::VALIDATION_INCORRECT_EXTRACTION_COUNT);
|
||||
}
|
||||
}
|
||||
// Check the ISO Hash
|
||||
if (meta.contents_hash != contents_hash) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: Overall ISO content's hash does not match. Expected '{}', Actual '{}'\n",
|
||||
meta.contents_hash, contents_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, return the result
|
||||
if (error_code.has_value()) {
|
||||
// Generate the map entry to make things simple, just convienance
|
||||
if (error_code.value() == ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB) {
|
||||
fmt::print(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following serial entry to the database:\n");
|
||||
fmt::print(
|
||||
"\t'{{\"{}\", {{{{{}U, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIF_FILENAME_NO_EXTENSION\"}}}}}}}}'\n",
|
||||
serial.value(), elf_hash.value(), filec, contents_hash);
|
||||
} else if (error_code.value() == ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB) {
|
||||
fmt::print(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following ELF entry to the database under the '{}' serial:\n",
|
||||
serial.value());
|
||||
fmt::print(
|
||||
"\t'{{{}, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIF_FILENAME_NO_EXTENSION\"}}}}'\n",
|
||||
elf_hash.value(), filec, contents_hash);
|
||||
} else {
|
||||
fmt::print(stderr,
|
||||
"Validation has failed to match with expected values, see the above errors for "
|
||||
"specifics. This may be an error in the validation database!\n");
|
||||
}
|
||||
return {*error_code, std::nullopt};
|
||||
}
|
||||
|
||||
return {ExtractorErrorCode::SUCCESS, meta_res};
|
||||
}
|
||||
|
||||
std::pair<ExtractorErrorCode, std::optional<ISOMetadata>> validate(
|
||||
const IsoFile& iso_file,
|
||||
const std::filesystem::path& extracted_iso_path) {
|
||||
if (!std::filesystem::exists(extracted_iso_path / "DGO")) {
|
||||
fmt::print(stderr, "ERROR: input folder doesn't have a DGO folder. Is this the right input?\n");
|
||||
return {ExtractorErrorCode::VALIDATION_BAD_EXTRACTION, std::nullopt};
|
||||
}
|
||||
|
||||
std::optional<ExtractorErrorCode> error_code;
|
||||
std::optional<std::string> serial = std::nullopt;
|
||||
std::optional<xxh::hash64_t> elf_hash = std::nullopt;
|
||||
std::tie(serial, elf_hash) = findElfFile(extracted_iso_path);
|
||||
|
||||
// - XOR all hashes together and hash the result. This makes the ordering of the hashes (aka
|
||||
// files) irrelevant
|
||||
xxh::hash64_t combined_hash = 0;
|
||||
for (const auto& hash : iso_file.hashes) {
|
||||
combined_hash ^= hash;
|
||||
}
|
||||
xxh::hash64_t contents_hash = xxh::xxhash<64>({combined_hash});
|
||||
|
||||
if (!serial || !elf_hash) {
|
||||
fmt::print(stderr, "ERROR: Unable to locate a Serial/ELF file!\n");
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_CANT_LOCATE_ELF);
|
||||
}
|
||||
// No point in continuing here
|
||||
return {*error_code, std::nullopt};
|
||||
}
|
||||
|
||||
// Find the game in our tracking database
|
||||
std::optional<ISOMetadata> meta_res = std::nullopt;
|
||||
if (auto dbEntry = isoDatabase.find(serial.value()); dbEntry == isoDatabase.end()) {
|
||||
fmt::print(stderr, "ERROR: Serial '{}' not found in the validation database\n", serial.value());
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB);
|
||||
}
|
||||
} else {
|
||||
auto& metaMap = dbEntry->second;
|
||||
auto meta_entry = metaMap.find(elf_hash.value());
|
||||
if (meta_entry == metaMap.end()) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: ELF Hash '{}' not found in the validation database, is this a new or "
|
||||
"modified version of the same game?\n",
|
||||
elf_hash.value());
|
||||
if (!error_code.has_value()) {
|
||||
error_code = std::make_optional(ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB);
|
||||
}
|
||||
} else {
|
||||
meta_res = std::make_optional<ISOMetadata>(meta_entry->second);
|
||||
const auto& meta = *meta_res;
|
||||
// Print out some information
|
||||
fmt::print("Detected Game Metadata:\n");
|
||||
fmt::print("\tDetected - {}\n", meta.canonical_name);
|
||||
fmt::print("\tRegion - {}\n", meta.region);
|
||||
fmt::print("\tSerial - {}\n", dbEntry->first);
|
||||
fmt::print("\tUses Decompiler Config - {}\n", meta.decomp_config);
|
||||
|
||||
// - Number of Files
|
||||
if (meta.num_files != iso_file.files_extracted) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: Extracted an unexpected number of files. Expected '{}', Actual '{}'\n",
|
||||
meta.num_files, iso_file.files_extracted);
|
||||
if (!error_code.has_value()) {
|
||||
error_code =
|
||||
std::make_optional(ExtractorErrorCode::VALIDATION_INCORRECT_EXTRACTION_COUNT);
|
||||
}
|
||||
}
|
||||
// Check the ISO Hash
|
||||
if (meta.contents_hash != contents_hash) {
|
||||
fmt::print(stderr,
|
||||
"ERROR: Overall ISO content's hash does not match. Expected '{}', Actual '{}'\n",
|
||||
meta.contents_hash, contents_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, return the result
|
||||
if (error_code.has_value()) {
|
||||
// Generate the map entry to make things simple, just convienance
|
||||
if (error_code.value() == ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB) {
|
||||
fmt::print(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following serial entry to the database:\n");
|
||||
fmt::print(
|
||||
"\t'{{\"{}\", {{{{{}U, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIF_FILENAME_NO_EXTENSION\"}}}}}}}}'\n",
|
||||
serial.value(), elf_hash.value(), iso_file.files_extracted, contents_hash);
|
||||
} else if (error_code.value() == ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB) {
|
||||
fmt::print(
|
||||
"If this is a new release or version that should be supported, consider adding the "
|
||||
"following ELF entry to the database under the '{}' serial:\n",
|
||||
serial.value());
|
||||
fmt::print(
|
||||
"\t'{{{}, {{\"GAME_TITLE\", \"NTSC-U/PAL/NTSC-J\", {}, {}U, "
|
||||
"\"DECOMP_CONFIF_FILENAME_NO_EXTENSION\"}}}}'\n",
|
||||
elf_hash.value(), iso_file.files_extracted, contents_hash);
|
||||
} else {
|
||||
fmt::print(stderr,
|
||||
"Validation has failed to match with expected values, see the above errors for "
|
||||
"specifics. This may be an error in the validation database!\n");
|
||||
}
|
||||
return {*error_code, std::nullopt};
|
||||
}
|
||||
|
||||
return {ExtractorErrorCode::SUCCESS, meta_res};
|
||||
}
|
||||
|
||||
std::optional<ISOMetadata> determineRelease(const std::filesystem::path& jak1_input_files) {
|
||||
std::optional<std::string> serial = std::nullopt;
|
||||
std::optional<xxh::hash64_t> elf_hash = std::nullopt;
|
||||
std::tie(serial, elf_hash) = findElfFile(jak1_input_files);
|
||||
|
||||
if (!serial || !elf_hash) {
|
||||
return std::nullopt;
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_CANT_LOCATE_ELF};
|
||||
}
|
||||
|
||||
// Find the game in our tracking database
|
||||
auto dbEntry = isoDatabase.find(serial.value());
|
||||
if (dbEntry == isoDatabase.end()) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
auto& metaMap = dbEntry->second;
|
||||
auto meta_entry = metaMap.find(elf_hash.value());
|
||||
if (meta_entry == metaMap.end()) {
|
||||
return std::nullopt;
|
||||
} else {
|
||||
return std::make_optional(meta_entry->second);
|
||||
}
|
||||
lg::error("Serial '{}' not found in the validation database", serial.value());
|
||||
log_potential_new_db_entry(ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB,
|
||||
serial.value(), elf_hash.value(), expected_num_files, expected_hash);
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_SERIAL_MISSING_FROM_DB};
|
||||
}
|
||||
|
||||
auto& metaMap = dbEntry->second;
|
||||
auto meta_entry = metaMap.find(elf_hash.value());
|
||||
if (meta_entry == metaMap.end()) {
|
||||
lg::error(
|
||||
"ELF Hash '{}' not found in the validation database, is this a new or "
|
||||
"modified version of the same game?",
|
||||
elf_hash.value());
|
||||
log_potential_new_db_entry(ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB, serial.value(),
|
||||
elf_hash.value(), expected_num_files, expected_hash);
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_ELF_MISSING_FROM_DB};
|
||||
}
|
||||
|
||||
auto version_info = meta_entry->second;
|
||||
// Print out some information
|
||||
lg::info("Detected Game Metadata:");
|
||||
lg::info("\tDetected - {}", version_info.canonical_name);
|
||||
lg::info("\tRegion - {}", version_info.region);
|
||||
lg::info("\tSerial - {}", dbEntry->first);
|
||||
lg::info("\tUses Decompiler Config - {}", version_info.decomp_config);
|
||||
|
||||
// - Number of Files
|
||||
if (version_info.num_files != expected_num_files) {
|
||||
lg::error("Extracted an unexpected number of files. Expected '{}', Actual '{}'",
|
||||
version_info.num_files, expected_num_files);
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_INCORRECT_EXTRACTION_COUNT};
|
||||
}
|
||||
// Check the ISO Hash
|
||||
if (version_info.contents_hash != expected_hash) {
|
||||
lg::error("Overall ISO content's hash does not match. Expected '{}', Actual '{}'",
|
||||
version_info.contents_hash, expected_hash);
|
||||
return {std::nullopt, ExtractorErrorCode::VALIDATION_FILE_CONTENTS_UNEXPECTED};
|
||||
}
|
||||
|
||||
return {
|
||||
std::make_optional(version_info),
|
||||
ExtractorErrorCode::SUCCESS,
|
||||
};
|
||||
}
|
||||
|
||||
void decompile(std::filesystem::path jak1_input_files) {
|
||||
void decompile(const fs::path& iso_data_path, const std::string& data_subfolder) {
|
||||
using namespace decompiler;
|
||||
|
||||
// Determine which config to use from the database
|
||||
auto meta = determineRelease(jak1_input_files);
|
||||
std::string decomp_config = "jak1_ntsc_black_label";
|
||||
if (meta.has_value()) {
|
||||
decomp_config = meta.value().decomp_config;
|
||||
fmt::print("INFO: Automatically detected decompiler config, using - {}\n", decomp_config);
|
||||
}
|
||||
const auto version_info = get_version_info_or_default(iso_data_path);
|
||||
|
||||
Config config = read_config_file((file_util::get_jak_project_dir() / "decompiler" / "config" /
|
||||
fmt::format("{}.jsonc", decomp_config))
|
||||
fmt::format("{}.jsonc", version_info.decomp_config))
|
||||
.string(),
|
||||
{});
|
||||
|
||||
std::vector<std::filesystem::path> dgos, objs;
|
||||
std::vector<fs::path> dgos, objs;
|
||||
|
||||
// grab all DGOS we need (level + common)
|
||||
// TODO - Jak 2 - jak 1 specific code?
|
||||
for (const auto& dgo_name : config.dgo_names) {
|
||||
std::string common_name = "GAME.CGO";
|
||||
if (dgo_name.length() > 3 && dgo_name.substr(dgo_name.length() - 3) == "DGO") {
|
||||
// ends in DGO, it's a level
|
||||
dgos.push_back(jak1_input_files / dgo_name);
|
||||
dgos.push_back(iso_data_path / dgo_name);
|
||||
} else if (dgo_name.length() >= common_name.length() &&
|
||||
dgo_name.substr(dgo_name.length() - common_name.length()) == common_name) {
|
||||
// it's COMMON.CGO, we need that too.
|
||||
dgos.push_back(jak1_input_files / dgo_name);
|
||||
dgos.push_back(iso_data_path / dgo_name);
|
||||
}
|
||||
}
|
||||
|
||||
@ -385,15 +130,15 @@ void decompile(std::filesystem::path jak1_input_files) {
|
||||
for (const auto& obj_name : config.object_file_names) {
|
||||
if (obj_name.length() > 3 && obj_name.substr(obj_name.length() - 3) == "TXT") {
|
||||
// ends in TXT
|
||||
objs.push_back(jak1_input_files / obj_name);
|
||||
objs.push_back(iso_data_path / obj_name);
|
||||
}
|
||||
}
|
||||
|
||||
// set up objects
|
||||
ObjectFileDB db(dgos, std::filesystem::path(config.obj_file_name_map_file), objs, {}, config);
|
||||
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, {}, config);
|
||||
|
||||
// save object files
|
||||
auto out_folder = file_util::get_jak_project_dir() / "decompiler_out" / "jak1";
|
||||
auto out_folder = file_util::get_jak_project_dir() / "decompiler_out" / data_subfolder;
|
||||
auto raw_obj_folder = out_folder / "raw_obj";
|
||||
file_util::create_dir_if_needed(raw_obj_folder);
|
||||
db.dump_raw_objects(raw_obj_folder);
|
||||
@ -422,7 +167,7 @@ void decompile(std::filesystem::path jak1_input_files) {
|
||||
db.process_tpages(tex_db, textures_out));
|
||||
// texture replacements
|
||||
auto replacements_path = file_util::get_jak_project_dir() / "texture_replacements";
|
||||
if (std::filesystem::exists(replacements_path)) {
|
||||
if (fs::exists(replacements_path)) {
|
||||
tex_db.replace_textures(replacements_path);
|
||||
}
|
||||
|
||||
@ -444,27 +189,34 @@ void decompile(std::filesystem::path jak1_input_files) {
|
||||
}
|
||||
}
|
||||
|
||||
void compile(std::filesystem::path extracted_iso_path) {
|
||||
ExtractorErrorCode compile(const fs::path& iso_data_path, const std::string& data_subfolder) {
|
||||
// Determine which config to use from the database
|
||||
const auto version_info = get_version_info_or_default(iso_data_path);
|
||||
|
||||
Compiler compiler;
|
||||
compiler.make_system().set_constant("*iso-data*", absolute(extracted_iso_path).string());
|
||||
compiler.make_system().set_constant("*iso-data*", absolute(iso_data_path).string());
|
||||
compiler.make_system().set_constant("*use-iso-data-path*", true);
|
||||
|
||||
auto buildinfo_path = (extracted_iso_path / "buildinfo.json").string();
|
||||
auto bi = parse_commented_json(file_util::read_text_file(buildinfo_path), buildinfo_path);
|
||||
auto all_flags = bi.at("flags").get<std::vector<std::string>>();
|
||||
int flags = 0;
|
||||
for (const auto& n : all_flags) {
|
||||
if (auto it = sGameIsoFlagNames.find(n); it != sGameIsoFlagNames.end()) {
|
||||
for (const auto& flag : version_info.flags) {
|
||||
if (auto it = sGameIsoFlagNames.find(flag); it != sGameIsoFlagNames.end()) {
|
||||
flags |= it->second;
|
||||
}
|
||||
}
|
||||
compiler.make_system().set_constant("*jak1-full-game*", !(flags & FLAG_JAK1_BLACK_LABEL));
|
||||
|
||||
// TODO - jak2 - BAD!
|
||||
// TODO - if this directory is failing, very bad (non-existant) error message
|
||||
compiler.make_system().load_project_file(
|
||||
(file_util::get_jak_project_dir() / "goal_src" / "jak1" / "game.gp").string());
|
||||
if (version_info.game_name == "jak1") {
|
||||
compiler.make_system().set_constant("*jak1-full-game*", !(flags & FLAG_JAK1_BLACK_LABEL));
|
||||
}
|
||||
|
||||
auto project_path = file_util::get_jak_project_dir() / "goal_src" / data_subfolder / "game.gp";
|
||||
if (!fs::exists(project_path)) {
|
||||
return ExtractorErrorCode::COMPILATION_BAD_PROJECT_PATH;
|
||||
}
|
||||
|
||||
compiler.make_system().load_project_file(project_path.string());
|
||||
compiler.run_front_end_on_string("(mi)");
|
||||
|
||||
return ExtractorErrorCode::SUCCESS;
|
||||
}
|
||||
|
||||
void launch_game() {
|
||||
@ -472,8 +224,8 @@ void launch_game() {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
std::filesystem::path data_dir_path;
|
||||
std::filesystem::path project_path_override;
|
||||
fs::path input_file_path;
|
||||
fs::path project_path_override;
|
||||
bool flag_runall = false;
|
||||
bool flag_extract = false;
|
||||
bool flag_fail_on_validation = false;
|
||||
@ -481,20 +233,30 @@ int main(int argc, char** argv) {
|
||||
bool flag_compile = false;
|
||||
bool flag_play = false;
|
||||
bool flag_folder = false;
|
||||
std::string game_name = "jak1";
|
||||
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
lg::initialize();
|
||||
|
||||
CLI::App app{"OpenGOAL Level Extraction Tool"};
|
||||
app.add_option("game-files-path", data_dir_path,
|
||||
app.add_option("game-files-path", input_file_path,
|
||||
"The path to the folder with the ISO extracted or the ISO itself")
|
||||
->check(CLI::ExistingPath)
|
||||
->required();
|
||||
app.add_option("--proj-path", project_path_override,
|
||||
"Explicitly set the location of the 'data/' folder")
|
||||
->check(CLI::ExistingPath);
|
||||
"Explicitly set the location of the 'data/' folder");
|
||||
app.add_flag("-g,--game", game_name, "Specify the game name, defaults to 'jak1'");
|
||||
app.add_flag("-a,--all", flag_runall, "Run all steps, from extraction to playing the game");
|
||||
app.add_flag("-e,--extract", flag_extract, "Extract the ISO");
|
||||
app.add_flag("-v,--validate", flag_fail_on_validation, "Fail on Validation Errors");
|
||||
app.add_flag("-v,--validate", flag_fail_on_validation,
|
||||
"Fail on validation errors during extraction");
|
||||
app.add_flag("-d,--decompile", flag_decompile, "Decompile the game data");
|
||||
app.add_flag("-c,--compile", flag_compile, "Compile the game");
|
||||
app.add_flag("-p,--play", flag_play, "Play the game");
|
||||
@ -502,7 +264,7 @@ int main(int argc, char** argv) {
|
||||
app.validate_positionals();
|
||||
CLI11_PARSE(app, argc, argv);
|
||||
|
||||
fmt::print("Working Directory - {}\n", std::filesystem::current_path().string());
|
||||
lg::info("Working Directory - {}", fs::current_path().string());
|
||||
|
||||
// If no flag is set, we default to running everything
|
||||
if (!flag_extract && !flag_decompile && !flag_compile && !flag_play) {
|
||||
@ -516,90 +278,111 @@ int main(int argc, char** argv) {
|
||||
flag_play = true;
|
||||
}
|
||||
|
||||
// todo: print revision here.
|
||||
// - SETUP
|
||||
decompiler::init_opcode_info();
|
||||
if (!project_path_override.empty()) {
|
||||
setup_global_decompiler_stuff(std::make_optional(project_path_override));
|
||||
if (!fs::exists(project_path_override)) {
|
||||
lg::error("Error: project path override '{}' does not exist", project_path_override.string());
|
||||
return static_cast<int>(ExtractorErrorCode::INVALID_CLI_INPUT);
|
||||
}
|
||||
file_util::setup_project_path(project_path_override);
|
||||
} else {
|
||||
setup_global_decompiler_stuff(std::nullopt);
|
||||
file_util::setup_project_path({});
|
||||
}
|
||||
|
||||
std::filesystem::path path_to_iso_files = file_util::get_jak_project_dir() / "iso_data" / "_temp";
|
||||
fs::path iso_data_path;
|
||||
|
||||
// make sure the input looks right
|
||||
if (!std::filesystem::exists(data_dir_path)) {
|
||||
fmt::print("Error: input data path {} does not exist\n", data_dir_path.string());
|
||||
return 1;
|
||||
// - INPUT VALIDATION
|
||||
if (!fs::exists(input_file_path)) {
|
||||
lg::error("Error: input game file path '{}' does not exist", input_file_path.string());
|
||||
return static_cast<int>(ExtractorErrorCode::INVALID_CLI_INPUT);
|
||||
}
|
||||
if (data_subfolders.count(game_name) == 0) {
|
||||
lg::error("Error: input game name '{}' is not valid", game_name);
|
||||
return static_cast<int>(ExtractorErrorCode::INVALID_CLI_INPUT);
|
||||
}
|
||||
std::string data_subfolder = data_subfolders[game_name];
|
||||
|
||||
if (flag_extract) {
|
||||
if (data_dir_path != path_to_iso_files) {
|
||||
// we extract to a temporary location because we don't know what we're extracting yet!
|
||||
fs::path temp_iso_extract_location = file_util::get_jak_project_dir() / "iso_data" / "_temp";
|
||||
if (input_file_path != temp_iso_extract_location) {
|
||||
// in case input is also output, don't just wipe everything (weird)
|
||||
std::filesystem::remove_all(path_to_iso_files);
|
||||
fs::remove_all(temp_iso_extract_location);
|
||||
}
|
||||
std::filesystem::create_directories(path_to_iso_files);
|
||||
fs::create_directories(temp_iso_extract_location);
|
||||
|
||||
int flags = 0;
|
||||
if (std::filesystem::is_regular_file(data_dir_path)) {
|
||||
// it's a file, normalize extension case and verify it's an ISO file
|
||||
std::string ext = data_dir_path.extension().string();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
|
||||
if (ext != ".iso") {
|
||||
fmt::print(stderr, "ERROR: Provided game data path contains a file that isn't a .ISO!");
|
||||
return static_cast<int>(ExtractorErrorCode::EXTRACTION_INVALID_ISO_PATH);
|
||||
if (fs::is_regular_file(input_file_path)) {
|
||||
// If it's a file, then it better be an iso file
|
||||
const auto [iso_ok, iso_code] = is_iso_file(input_file_path);
|
||||
if (!iso_ok) {
|
||||
return static_cast<int>(iso_code);
|
||||
}
|
||||
|
||||
// make sure the .iso is greater than 1GB in size
|
||||
// to-do: verify game header data as well
|
||||
if (std::filesystem::file_size(data_dir_path) < 1000000000) {
|
||||
fmt::print(
|
||||
stderr,
|
||||
"ERROR: Provided game data file appears to be too small or corrupted! Size is {}",
|
||||
std::filesystem::file_size(data_dir_path));
|
||||
return static_cast<int>(ExtractorErrorCode::EXTRACTION_ISO_UNEXPECTED_SIZE);
|
||||
// Extract to the temporary location
|
||||
const auto iso_file = extract_files(input_file_path, temp_iso_extract_location);
|
||||
// Get hash and file count
|
||||
const auto [hash, file_count] = calculate_extraction_hash(iso_file);
|
||||
// Validate the result to determine the release
|
||||
const auto [version_info, validate_code] =
|
||||
validate(temp_iso_extract_location, hash, file_count);
|
||||
if (validate_code == ExtractorErrorCode::VALIDATION_BAD_EXTRACTION ||
|
||||
(flag_fail_on_validation && validate_code != ExtractorErrorCode::SUCCESS)) {
|
||||
return static_cast<int>(validate_code);
|
||||
}
|
||||
// Finalize the folder name now that we know where it should go
|
||||
if (!version_info) {
|
||||
lg::error("could not verify release, so not finalizing iso_data, leaving in '_temp'");
|
||||
iso_data_path = temp_iso_extract_location;
|
||||
} else {
|
||||
// We know the version since we just extracted it, so the user didn't need to provide this
|
||||
// explicitly
|
||||
data_subfolder = data_subfolders[version_info->game_name];
|
||||
iso_data_path = file_util::get_jak_project_dir() / "iso_data" / data_subfolder;
|
||||
if (fs::exists(iso_data_path)) {
|
||||
fs::remove_all(iso_data_path);
|
||||
}
|
||||
|
||||
auto iso_file = extract_files(data_dir_path, path_to_iso_files);
|
||||
auto validation_res = validate(iso_file, path_to_iso_files);
|
||||
flags = validation_res.second->flags;
|
||||
if (validation_res.first == ExtractorErrorCode::VALIDATION_BAD_EXTRACTION) {
|
||||
// We fail here regardless of the flag
|
||||
return static_cast<int>(validation_res.first);
|
||||
} else if (flag_fail_on_validation && validation_res.first != ExtractorErrorCode::SUCCESS) {
|
||||
return static_cast<int>(validation_res.first);
|
||||
// std::filesystem doesn't have a rename for dirs...
|
||||
fs::copy(temp_iso_extract_location, iso_data_path, fs::copy_options::recursive);
|
||||
fs::remove_all(temp_iso_extract_location);
|
||||
}
|
||||
} else if (std::filesystem::is_directory(data_dir_path)) {
|
||||
} else if (fs::is_directory(input_file_path)) {
|
||||
if (!flag_folder) {
|
||||
// if we didn't request a folder explicitly, but we got one, assume something went wrong.
|
||||
fmt::print("Error: got a folder, but didn't get folder flag\n");
|
||||
return static_cast<int>(ExtractorErrorCode::VALIDATION_BAD_ISO_CONTENTS);
|
||||
lg::error("got a folder, but didn't get folder flag");
|
||||
return static_cast<int>(ExtractorErrorCode::INVALID_CLI_INPUT);
|
||||
}
|
||||
path_to_iso_files = data_dir_path;
|
||||
if (std::filesystem::exists(path_to_iso_files / "buildinfo.json")) {
|
||||
std::filesystem::remove(path_to_iso_files / "buildinfo.json");
|
||||
}
|
||||
auto validation_res = validate(path_to_iso_files);
|
||||
flags = validation_res.second->flags;
|
||||
iso_data_path = input_file_path;
|
||||
// Get hash and file count
|
||||
const auto [hash, file_count] = calculate_extraction_hash(iso_data_path);
|
||||
// Validate
|
||||
auto [version_info, code] = validate(iso_data_path, hash, file_count);
|
||||
}
|
||||
|
||||
// write out a json file with some metadata for the game
|
||||
nlohmann::json buildinfo_json;
|
||||
auto flags_json = nlohmann::json::array();
|
||||
for (const auto& [n, f] : sGameIsoFlagNames) {
|
||||
if (flags & f) {
|
||||
flags_json.push_back(n);
|
||||
}
|
||||
if (fs::exists(iso_data_path / "buildinfo.json")) {
|
||||
fs::remove(iso_data_path / "buildinfo.json");
|
||||
}
|
||||
buildinfo_json["flags"] = flags_json;
|
||||
// something tells me a ps2 game is unlikely to have a json file in root
|
||||
file_util::write_text_file((path_to_iso_files / "buildinfo.json").string(),
|
||||
buildinfo_json.dump(2));
|
||||
const auto [serial, elf_hash] = findElfFile(iso_data_path);
|
||||
BuildInfo build_info;
|
||||
if (serial.has_value()) {
|
||||
build_info.serial = serial.value();
|
||||
}
|
||||
if (elf_hash.has_value()) {
|
||||
build_info.elf_hash = elf_hash.value();
|
||||
}
|
||||
const nlohmann::json json_data{build_info};
|
||||
file_util::write_text_file((iso_data_path / "buildinfo.json").string(), json_data.dump(2));
|
||||
} else {
|
||||
// If we did not extract, we have no clue what game the user is trying to decompile / compile
|
||||
// this is why the user has to specify this!
|
||||
iso_data_path = file_util::get_jak_project_dir() / "iso_data" / data_subfolder;
|
||||
}
|
||||
|
||||
if (flag_decompile) {
|
||||
try {
|
||||
decompile(path_to_iso_files);
|
||||
decompile(iso_data_path, data_subfolder);
|
||||
} catch (std::exception& e) {
|
||||
lg::error("Error during decompile: {}", e.what());
|
||||
return static_cast<int>(ExtractorErrorCode::DECOMPILATION_GENERIC_ERROR);
|
||||
@ -607,7 +390,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (flag_compile) {
|
||||
compile(path_to_iso_files);
|
||||
compile(iso_data_path, data_subfolder);
|
||||
}
|
||||
|
||||
if (flag_play) {
|
||||
|
@ -203,7 +203,7 @@ void extract_common(const ObjectFileDB& db,
|
||||
const TextureDB& tex_db,
|
||||
const std::string& dgo_name,
|
||||
bool dump_levels,
|
||||
const std::filesystem::path& output_folder) {
|
||||
const fs::path& output_folder) {
|
||||
if (db.obj_files_by_dgo.count(dgo_name) == 0) {
|
||||
lg::warn("Skipping common extract for {} because the DGO was not part of the input", dgo_name);
|
||||
return;
|
||||
@ -239,7 +239,7 @@ void extract_from_level(const ObjectFileDB& db,
|
||||
const DecompileHacks& hacks,
|
||||
bool dump_level,
|
||||
bool extract_collision,
|
||||
const std::filesystem::path& output_folder) {
|
||||
const fs::path& output_folder) {
|
||||
if (db.obj_files_by_dgo.count(dgo_name) == 0) {
|
||||
lg::warn("Skipping extract for {} because the DGO was not part of the input", dgo_name);
|
||||
return;
|
||||
@ -272,7 +272,7 @@ void extract_all_levels(const ObjectFileDB& db,
|
||||
const DecompileHacks& hacks,
|
||||
bool debug_dump_level,
|
||||
bool extract_collision,
|
||||
const std::filesystem::path& output_path) {
|
||||
const fs::path& output_path) {
|
||||
extract_common(db, tex_db, common_name, debug_dump_level, output_path);
|
||||
SimpleThreadGroup threads;
|
||||
threads.run(
|
||||
|
@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "common/math/Vector.h"
|
||||
@ -17,5 +16,5 @@ void extract_all_levels(const ObjectFileDB& db,
|
||||
const DecompileHacks& hacks,
|
||||
bool debug_dump_level,
|
||||
bool extract_collision,
|
||||
const std::filesystem::path& path);
|
||||
const fs::path& path);
|
||||
} // namespace decompiler
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include "common/util/diff.h"
|
||||
#include "common/util/os.h"
|
||||
#include "common/versions.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
#include "ObjectFile/ObjectFileDB.h"
|
||||
#include "decompiler/data/TextureDB.h"
|
||||
@ -17,6 +18,15 @@
|
||||
#include "decompiler/level_extractor/extract_level.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
Timer decomp_timer;
|
||||
|
||||
fmt::print("[Mem] Top of main: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
@ -87,8 +97,8 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// std::string in_folder = file_util::combine_path(argv[2], config.game_name);
|
||||
std::filesystem::path in_folder = std::filesystem::path(argv[2]) / config.game_name;
|
||||
std::filesystem::path out_folder = std::filesystem::path(argv[3]) / config.game_name;
|
||||
fs::path in_folder = fs::path(argv[2]) / config.game_name;
|
||||
fs::path out_folder = fs::path(argv[3]) / config.game_name;
|
||||
|
||||
// Verify the in_folder is correct
|
||||
if (!exists(in_folder)) {
|
||||
@ -105,7 +115,7 @@ int main(int argc, char** argv) {
|
||||
in_folder.string(), config.expected_elf_name);
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> dgos, objs, strs;
|
||||
std::vector<fs::path> dgos, objs, strs;
|
||||
for (const auto& dgo_name : config.dgo_names) {
|
||||
dgos.push_back(in_folder / dgo_name);
|
||||
}
|
||||
@ -129,7 +139,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
// build file database
|
||||
lg::info("Setting up object file DB...");
|
||||
ObjectFileDB db(dgos, std::filesystem::path(config.obj_file_name_map_file), objs, strs, config);
|
||||
ObjectFileDB db(dgos, fs::path(config.obj_file_name_map_file), objs, strs, config);
|
||||
|
||||
fmt::print("[Mem] After DB setup: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
|
||||
@ -220,7 +230,7 @@ int main(int argc, char** argv) {
|
||||
|
||||
fmt::print("[Mem] After textures: {} MB\n", get_peak_rss() / (1024 * 1024));
|
||||
auto replacements_path = file_util::get_jak_project_dir() / "texture_replacements";
|
||||
if (std::filesystem::exists(replacements_path)) {
|
||||
if (fs::exists(replacements_path)) {
|
||||
tex_db.replace_textures(replacements_path);
|
||||
}
|
||||
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include "gfx.h"
|
||||
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
|
||||
#include "display.h"
|
||||
@ -57,9 +56,9 @@ GfxSettings g_settings;
|
||||
// TODO serialize
|
||||
void LoadSettings() {
|
||||
const auto filename = file_util::get_file_path({GAME_CONFIG_DIR_NAME, SETTINGS_GFX_FILE_NAME});
|
||||
if (std::filesystem::exists(filename)) {
|
||||
if (fs::exists(filename)) {
|
||||
// this is just wrong LOL
|
||||
FILE* fp = fopen(filename.c_str(), "rb");
|
||||
FILE* fp = file_util::open_file(filename.c_str(), "rb");
|
||||
lg::info("Found graphics configuration file. Checking version.");
|
||||
u64 version;
|
||||
fread(&version, sizeof(u64), 1, fp);
|
||||
@ -78,7 +77,7 @@ void LoadSettings() {
|
||||
void SaveSettings() {
|
||||
const auto filename = file_util::get_file_path({GAME_CONFIG_DIR_NAME, SETTINGS_GFX_FILE_NAME});
|
||||
file_util::create_dir_if_needed(file_util::get_file_path({GAME_CONFIG_DIR_NAME}));
|
||||
FILE* fp = fopen(filename.c_str(), "wb");
|
||||
FILE* fp = file_util::open_file(filename.c_str(), "wb");
|
||||
fwrite(&g_settings, sizeof(GfxSettings), 1, fp);
|
||||
fclose(fp);
|
||||
lg::info("Saved graphics configuration file.");
|
||||
|
@ -16,7 +16,7 @@ std::string uppercase_string(const std::string& s) {
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Loader::Loader(const std::filesystem::path& base_path) : m_base_path(base_path) {
|
||||
Loader::Loader(const fs::path& base_path) : m_base_path(base_path) {
|
||||
m_loader_thread = std::thread(&Loader::loader_thread, this);
|
||||
m_loader_stages = make_loader_stages();
|
||||
}
|
||||
@ -408,4 +408,4 @@ std::optional<MercRef> Loader::get_merc_model(const char* model_name) {
|
||||
} else {
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <condition_variable>
|
||||
#include <filesystem>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "common/custom_data/Tfrag3Data.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/Timer.h"
|
||||
|
||||
#include "game/graphics/opengl_renderer/loader/common.h"
|
||||
@ -16,7 +16,7 @@ class Loader {
|
||||
public:
|
||||
static constexpr float TIE_LOAD_BUDGET = 1.5f;
|
||||
static constexpr float SHARED_TEXTURE_LOAD_BUDGET = 3.f;
|
||||
Loader(const std::filesystem::path& base_path);
|
||||
Loader(const fs::path& base_path);
|
||||
~Loader();
|
||||
void update(TexturePool& tex_pool);
|
||||
void update_blocking(TexturePool& tex_pool);
|
||||
@ -52,5 +52,5 @@ class Loader {
|
||||
std::vector<std::string> m_desired_levels;
|
||||
std::vector<std::unique_ptr<LoaderStage>> m_loader_stages;
|
||||
|
||||
std::filesystem::path m_base_path;
|
||||
fs::path m_base_path;
|
||||
};
|
||||
|
@ -411,7 +411,7 @@ void mkdir_path(u32 filepath) {
|
||||
|
||||
u64 filepath_exists(u32 filepath) {
|
||||
auto filepath_str = std::string(Ptr<String>(filepath).c()->data());
|
||||
if (std::filesystem::exists(filepath_str)) {
|
||||
if (fs::exists(filepath_str)) {
|
||||
return s7.offset + true_symbol_offset(g_game_version);
|
||||
}
|
||||
return s7.offset;
|
||||
@ -470,4 +470,4 @@ void vif_interrupt_callback() {
|
||||
*/
|
||||
u32 offset_of_s7() {
|
||||
return s7.offset;
|
||||
}
|
||||
}
|
||||
|
@ -117,8 +117,7 @@ u32 mc_checksum(Ptr<u8> data, s32 size) {
|
||||
*/
|
||||
bool file_is_present(int id, int bank = 0) {
|
||||
auto bankname = file_util::get_user_memcard_dir() / filename[4 + id * 2 + bank];
|
||||
if (!std::filesystem::exists(bankname) ||
|
||||
std::filesystem::file_size(bankname) < BANK_TOTAL_SIZE) {
|
||||
if (!fs::exists(bankname) || fs::file_size(bankname) < BANK_TOTAL_SIZE) {
|
||||
// file doesn't exist, or size is bad. we do not want to open files that will crash on read!
|
||||
return false;
|
||||
}
|
||||
@ -130,7 +129,7 @@ bool file_is_present(int id, int bank = 0) {
|
||||
// file exists. but let's see if it's an empty one.
|
||||
// this prevents the game from reading a bank but classifying it as corrupt data.
|
||||
// which a file full of zeros logically is.
|
||||
auto fp = fopen(bankname.c_str(), "rb");
|
||||
auto fp = file_util::open_file(bankname.c_str(), "rb");
|
||||
|
||||
// we can actually just check if the save count is over zero...
|
||||
u32 savecount = 0;
|
||||
@ -214,7 +213,7 @@ void pc_game_save_synch() {
|
||||
mc_print("open {} for saving", filename[op.param2 * 2 + 4 + p4]);
|
||||
auto save_path = file_util::get_user_memcard_dir() / filename[op.param2 * 2 + 4 + p4];
|
||||
file_util::create_dir_if_needed_for_file(save_path.string());
|
||||
auto fd = fopen(save_path.string().c_str(), "wb");
|
||||
auto fd = file_util::open_file(save_path.string().c_str(), "wb");
|
||||
mc_print("synchronous save file open took {:.2f}ms\n", mc_timer.getMs());
|
||||
if (fd) {
|
||||
// cb_openedsave //
|
||||
@ -282,12 +281,12 @@ void pc_game_load_open_file(FILE* fd) {
|
||||
if (fclose(fd) == 0) {
|
||||
// cb_closedload //
|
||||
// added : check if aux bank exists
|
||||
if (p2 < 1 && std::filesystem::exists(file_util::get_user_memcard_dir() /
|
||||
filename[op.param2 * 2 + 4 + p2 + 1])) {
|
||||
if (p2 < 1 &&
|
||||
fs::exists(file_util::get_user_memcard_dir() / filename[op.param2 * 2 + 4 + p2 + 1])) {
|
||||
p2++;
|
||||
mc_print("reading next save bank {}", filename[op.param2 * 2 + 4 + p2]);
|
||||
auto new_bankname = file_util::get_user_memcard_dir() / filename[op.param2 * 2 + 4 + p2];
|
||||
auto new_fd = fopen(new_bankname.string().c_str(), "rb");
|
||||
auto new_fd = file_util::open_file(new_bankname.string().c_str(), "rb");
|
||||
pc_game_load_open_file(new_fd);
|
||||
} else {
|
||||
// let's verify the data.
|
||||
@ -404,7 +403,7 @@ void pc_game_load_synch() {
|
||||
mc_print("opening save file {}", filename[op.param2 * 2 + 4]);
|
||||
|
||||
auto path = file_util::get_user_memcard_dir() / filename[op.param2 * 2 + 4];
|
||||
auto fd = fopen(path.string().c_str(), "rb");
|
||||
auto fd = file_util::open_file(path.string().c_str(), "rb");
|
||||
pc_game_load_open_file(fd);
|
||||
|
||||
mc_print("synchronous load took {:.2f}ms\n", mc_timer.getMs());
|
||||
|
@ -8,7 +8,6 @@
|
||||
|
||||
#include "kmachine.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "common/log/log.h"
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/os.h"
|
||||
#include "common/versions.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
#include "game/discord.h"
|
||||
|
||||
@ -39,6 +40,15 @@ void setup_logging(bool verbose) {
|
||||
* Entry point for the game.
|
||||
*/
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
// Figure out if the CPU has AVX2 to enable higher performance AVX2 versions of functions.
|
||||
setup_cpu_info();
|
||||
// If the CPU doesn't have AVX, GOAL code won't work and we exit.
|
||||
@ -50,7 +60,7 @@ int main(int argc, char** argv) {
|
||||
// parse arguments
|
||||
bool verbose = false;
|
||||
bool disable_avx2 = false;
|
||||
std::optional<std::filesystem::path> project_path_override = std::nullopt;
|
||||
std::optional<fs::path> project_path_override = std::nullopt;
|
||||
for (int i = 1; i < argc; i++) {
|
||||
if (std::string("-v") == argv[i]) {
|
||||
verbose = true;
|
||||
@ -62,7 +72,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
if (std::string("-proj-path") == argv[i] && i + 1 < argc) {
|
||||
project_path_override = std::make_optional(std::filesystem::path(argv[i + 1]));
|
||||
project_path_override = std::make_optional(fs::path(argv[i + 1]));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,7 +20,6 @@
|
||||
#include "fake_iso.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
|
||||
#include "isocommon.h"
|
||||
#include "overlord.h"
|
||||
@ -98,8 +97,8 @@ void fake_iso_init_globals() {
|
||||
int FS_Init(u8* buffer) {
|
||||
(void)buffer;
|
||||
|
||||
for (const auto& f : std::filesystem::directory_iterator(
|
||||
file_util::get_jak_project_dir() / "out" / game_version_names[g_game_version] / "iso")) {
|
||||
for (const auto& f : fs::directory_iterator(file_util::get_jak_project_dir() / "out" /
|
||||
game_version_names[g_game_version] / "iso")) {
|
||||
if (f.is_regular_file()) {
|
||||
ASSERT(fake_iso_entry_count < MAX_ISO_FILES);
|
||||
FakeIsoEntry* e = &fake_iso_entries[fake_iso_entry_count];
|
||||
@ -175,7 +174,7 @@ static const char* get_file_path(FileRecord* fr) {
|
||||
uint32_t FS_GetLength(FileRecord* fr) {
|
||||
const char* path = get_file_path(fr);
|
||||
file_util::assert_file_exists(path, "fake_iso FS_GetLength");
|
||||
FILE* fp = fopen(path, "rb");
|
||||
FILE* fp = file_util::open_file(path, "rb");
|
||||
ASSERT(fp);
|
||||
fseek(fp, 0, SEEK_END);
|
||||
uint32_t len = ftell(fp);
|
||||
@ -265,7 +264,7 @@ uint32_t FS_BeginRead(LoadStackEntry* fd, void* buffer, int32_t len) {
|
||||
u32 offset_into_file = SECTOR_SIZE * fd->location;
|
||||
|
||||
const char* path = get_file_path(fd->fr);
|
||||
FILE* fp = fopen(path, "rb");
|
||||
FILE* fp = file_util::open_file(path, "rb");
|
||||
if (!fp) {
|
||||
lg::error("[OVERLORD] fake iso could not open the file \"{}\"", path);
|
||||
}
|
||||
@ -354,7 +353,7 @@ uint32_t FS_LoadSoundBank(char* name, void* buffer) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
auto fp = fopen(get_file_path(file), "rb");
|
||||
auto fp = file_util::open_file(get_file_path(file), "rb");
|
||||
fread(buffer, offset, 1, fp);
|
||||
fclose(fp);
|
||||
|
||||
@ -370,7 +369,7 @@ void LoadMusicTweaks() {
|
||||
MakeISOName(tweakname, "TWEAKVAL.MUS");
|
||||
auto file = FS_FindIN(tweakname);
|
||||
if (file) {
|
||||
auto fp = fopen(get_file_path(file), "rb");
|
||||
auto fp = file_util::open_file(get_file_path(file), "rb");
|
||||
fread(&gMusicTweakInfo, sizeof(gMusicTweakInfo), 1, fp);
|
||||
fclose(fp);
|
||||
} else {
|
||||
|
@ -105,11 +105,11 @@ s32 sceOpen(const char* filename, s32 flag) {
|
||||
auto name = file_util::get_file_path({filename});
|
||||
switch (flag) {
|
||||
case SCE_RDONLY: {
|
||||
fp = fopen(name.c_str(), "rb");
|
||||
fp = file_util::open_file(name.c_str(), "rb");
|
||||
} break;
|
||||
|
||||
default: {
|
||||
fp = fopen(name.c_str(), "w");
|
||||
fp = file_util::open_file(name.c_str(), "w");
|
||||
} break;
|
||||
}
|
||||
if (!fp) {
|
||||
|
@ -300,7 +300,7 @@ void flush_memory_card_to_file() {
|
||||
}
|
||||
|
||||
void read_memory_card_from_file() {
|
||||
if (std::filesystem::exists(get_memory_card_path())) {
|
||||
if (fs::exists(get_memory_card_path())) {
|
||||
g_mc_state.data.load_from_file(get_memory_card_path());
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright: 2021 - 2022, Ziemas
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <third-party/fmt/core.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <combaseapi.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
@ -193,7 +194,7 @@ void player::set_master_volume(u32 group, s32 volume) {
|
||||
}
|
||||
}
|
||||
|
||||
u32 player::load_bank(std::filesystem::path& filepath, size_t offset) {
|
||||
u32 player::load_bank(fs::path& filepath, size_t offset) {
|
||||
std::scoped_lock lock(m_ticklock);
|
||||
fmt::print("Loading bank {}\n", filepath.string());
|
||||
std::fstream in(filepath, std::fstream::binary | std::fstream::in);
|
||||
|
@ -1,7 +1,7 @@
|
||||
// Copyright: 2021 - 2022, Ziemas
|
||||
// SPDX-License-Identifier: ISC
|
||||
#pragma once
|
||||
#include <filesystem>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
@ -14,6 +14,7 @@
|
||||
#include "sound_handler.h"
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
#include "../common/synth.h"
|
||||
#include "game/sound/989snd/vagvoice.h"
|
||||
@ -32,7 +33,7 @@ class player {
|
||||
// player(player&& other) noexcept = default;
|
||||
// player& operator=(player&& other) noexcept = default;
|
||||
|
||||
u32 load_bank(std::filesystem::path& path, size_t offset);
|
||||
u32 load_bank(fs::path& path, size_t offset);
|
||||
|
||||
u32 play_sound(u32 bank, u32 sound, s32 vol, s32 pan, s32 pm, s32 pb);
|
||||
void set_midi_reg(u32 sound_id, u8 reg, u8 value);
|
||||
|
@ -1,12 +1,10 @@
|
||||
#include <filesystem>
|
||||
|
||||
#include "player.h"
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
snd::player player;
|
||||
unsigned bankid = 0;
|
||||
|
||||
std::filesystem::path file = argv[1];
|
||||
fs::path file = argv[1];
|
||||
|
||||
if (argc > 2) {
|
||||
bankid = player.load_bank(file, 0);
|
||||
|
@ -178,7 +178,7 @@ void snd_AutoPitchBend(s32, s32, s32, s32) {
|
||||
s32 snd_BankLoadEx(const char* filename, s32 offset, s32, s32) {
|
||||
// printf("snd_BankLoadEx\n");
|
||||
if (player) {
|
||||
std::filesystem::path path = filename;
|
||||
fs::path path = filename;
|
||||
return player->load_bank(path, offset);
|
||||
} else {
|
||||
return 0;
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include <cstring>
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
#include <common/util/FileUtil.h>
|
||||
|
||||
#include "game/sce/iop.h"
|
||||
|
||||
@ -299,7 +300,7 @@ void IOP_Kernel::rpc_loop(iop::sceSifQueueData* qd) {
|
||||
|
||||
void IOP_Kernel::read_disc_sectors(u32 sector, u32 sectors, void* buffer) {
|
||||
if (!iso_disc_file) {
|
||||
iso_disc_file = fopen("./disc.iso", "rb");
|
||||
iso_disc_file = file_util::open_file("./disc.iso", "rb");
|
||||
}
|
||||
|
||||
ASSERT(iso_disc_file);
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
void save_pc_data(const std::string& nickname,
|
||||
tfrag3::Level& data,
|
||||
const std::filesystem::path& fr3_output_dir) {
|
||||
const fs::path& fr3_output_dir) {
|
||||
Serializer ser;
|
||||
data.serialize(ser);
|
||||
auto compressed =
|
||||
@ -55,7 +55,7 @@ bool run_build_level(const std::string& input_file,
|
||||
gltf_mesh_extract::extract(mesh_extract_in, mesh_extract_out);
|
||||
|
||||
// add stuff to the GOAL level structure
|
||||
file.info = make_file_info_for_level(std::filesystem::path(input_file).filename().string());
|
||||
file.info = make_file_info_for_level(fs::path(input_file).filename().string());
|
||||
// all vis
|
||||
// drawable trees
|
||||
// pat
|
||||
|
@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
bool run_build_level(const std::string& input_file,
|
||||
const std::string& bsp_output_file,
|
||||
const std::string& output_prefix);
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file);
|
||||
std::vector<std::string> get_build_level_deps(const std::string& input_file);
|
||||
|
@ -37,9 +37,8 @@ Compiler::Compiler(const std::string& user_profile, std::unique_ptr<ReplWrapper>
|
||||
compile_object_file("goal-lib", library_code, false);
|
||||
|
||||
// user profile stuff
|
||||
if (user_profile != "#f" &&
|
||||
std::filesystem::exists(file_util::get_jak_project_dir() / "goal_src" / "user" /
|
||||
user_profile / "user.gc")) {
|
||||
if (user_profile != "#f" && fs::exists(file_util::get_jak_project_dir() / "goal_src" / "user" /
|
||||
user_profile / "user.gc")) {
|
||||
try {
|
||||
Object user_code =
|
||||
m_goos.reader.read_from_file({"goal_src", "user", user_profile, "user.gc"});
|
||||
|
@ -3,7 +3,6 @@
|
||||
* Compiler implementation for forms which actually control the compiler.
|
||||
*/
|
||||
|
||||
#include <filesystem>
|
||||
#include <regex>
|
||||
#include <stack>
|
||||
|
||||
@ -308,7 +307,7 @@ Val* Compiler::compile_build_dgo(const goos::Object& form, const goos::Object& r
|
||||
desc.entries.push_back(o);
|
||||
} else {
|
||||
// allow data objects to be missing.
|
||||
if (std::filesystem::exists(file_util::get_file_path(
|
||||
if (fs::exists(file_util::get_file_path(
|
||||
{"out", m_make.compiler_output_prefix(), "obj", o.file_name}))) {
|
||||
desc.entries.push_back(o);
|
||||
}
|
||||
|
@ -87,6 +87,7 @@ int main(int argc, char** argv) {
|
||||
// the compiler may throw an exception if it fails to load its standard library.
|
||||
try {
|
||||
std::unique_ptr<Compiler> compiler;
|
||||
// TODO - allow passing in an iso_data override
|
||||
std::mutex compiler_mutex;
|
||||
// if a command is provided on the command line, no REPL just run the compiler on it
|
||||
if (!cmd.empty()) {
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "MakeSystem.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/Timer.h"
|
||||
@ -179,7 +177,7 @@ goos::Object MakeSystem::handle_basename(const goos::Object& form,
|
||||
const std::shared_ptr<goos::EnvironmentObject>& env) {
|
||||
m_goos.eval_args(&args, env);
|
||||
va_check(form, args, {goos::ObjectType::STRING}, {});
|
||||
std::filesystem::path input(args.unnamed.at(0).as_string()->data);
|
||||
fs::path input(args.unnamed.at(0).as_string()->data);
|
||||
|
||||
return goos::StringObject::make_new(input.filename().u8string());
|
||||
}
|
||||
@ -189,7 +187,7 @@ goos::Object MakeSystem::handle_stem(const goos::Object& form,
|
||||
const std::shared_ptr<goos::EnvironmentObject>& env) {
|
||||
m_goos.eval_args(&args, env);
|
||||
va_check(form, args, {goos::ObjectType::STRING}, {});
|
||||
std::filesystem::path input(args.unnamed.at(0).as_string()->data);
|
||||
fs::path input(args.unnamed.at(0).as_string()->data);
|
||||
|
||||
return goos::StringObject::make_new(input.stem().u8string());
|
||||
}
|
||||
|
@ -2,7 +2,6 @@
|
||||
#include "Tool.h"
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/util/FileUtil.h"
|
||||
|
||||
@ -14,17 +13,17 @@ bool Tool::needs_run(const ToolInput& task, const PathMap& path_map) {
|
||||
// for this to return false, all outputs need to be newer than all inputs.
|
||||
|
||||
for (auto& in : task.input) {
|
||||
auto in_file = std::filesystem::path(file_util::get_file_path({in}));
|
||||
auto in_file = fs::path(file_util::get_file_path({in}));
|
||||
|
||||
if (!std::filesystem::exists(in_file)) {
|
||||
if (!fs::exists(in_file)) {
|
||||
throw std::runtime_error(fmt::format("Input file {} does not exist.", in));
|
||||
}
|
||||
|
||||
auto newest_input = std::filesystem::last_write_time(in_file);
|
||||
auto newest_input = fs::last_write_time(in_file);
|
||||
for (auto& dep : task.deps) {
|
||||
auto dep_path = std::filesystem::path(file_util::get_file_path({dep}));
|
||||
if (std::filesystem::exists(dep_path)) {
|
||||
auto dep_time = std::filesystem::last_write_time(dep_path);
|
||||
auto dep_path = fs::path(file_util::get_file_path({dep}));
|
||||
if (fs::exists(dep_path)) {
|
||||
auto dep_time = fs::last_write_time(dep_path);
|
||||
if (dep_time > newest_input) {
|
||||
newest_input = dep_time;
|
||||
}
|
||||
@ -34,9 +33,9 @@ bool Tool::needs_run(const ToolInput& task, const PathMap& path_map) {
|
||||
}
|
||||
|
||||
for (auto& dep : get_additional_dependencies(task, path_map)) {
|
||||
auto dep_path = std::filesystem::path(file_util::get_file_path({dep}));
|
||||
if (std::filesystem::exists(dep_path)) {
|
||||
auto dep_time = std::filesystem::last_write_time(dep_path);
|
||||
auto dep_path = fs::path(file_util::get_file_path({dep}));
|
||||
if (fs::exists(dep_path)) {
|
||||
auto dep_time = fs::last_write_time(dep_path);
|
||||
if (dep_time > newest_input) {
|
||||
newest_input = dep_time;
|
||||
}
|
||||
@ -46,9 +45,9 @@ bool Tool::needs_run(const ToolInput& task, const PathMap& path_map) {
|
||||
}
|
||||
|
||||
for (auto& out : task.output) {
|
||||
auto out_path = std::filesystem::path(file_util::get_file_path({out}));
|
||||
if (std::filesystem::exists(out_path)) {
|
||||
auto out_time = std::filesystem::last_write_time(out_path);
|
||||
auto out_path = fs::path(file_util::get_file_path({out}));
|
||||
if (fs::exists(out_path)) {
|
||||
auto out_time = fs::last_write_time(out_path);
|
||||
if (out_time < newest_input) {
|
||||
return true;
|
||||
}
|
||||
@ -87,4 +86,4 @@ std::string PathMap::apply_remaps(const std::string& input) const {
|
||||
} else {
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,5 @@
|
||||
#include "Tools.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
#include "common/goos/ParseHelpers.h"
|
||||
#include "common/util/DgoWriter.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
@ -21,7 +19,7 @@ bool CompilerTool::needs_run(const ToolInput& task, const PathMap& path_map) {
|
||||
throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name()));
|
||||
}
|
||||
|
||||
if (!m_compiler->knows_object_file(std::filesystem::path(task.input.at(0)).stem().u8string())) {
|
||||
if (!m_compiler->knows_object_file(fs::path(task.input.at(0)).stem().u8string())) {
|
||||
return true;
|
||||
}
|
||||
return Tool::needs_run(task, path_map);
|
||||
@ -113,9 +111,8 @@ bool CopyTool::run(const ToolInput& task, const PathMap& /*path_map*/) {
|
||||
throw std::runtime_error(fmt::format("Invalid amount of inputs to {} tool", name()));
|
||||
}
|
||||
for (auto& out : task.output) {
|
||||
std::filesystem::copy(std::filesystem::path(file_util::get_file_path({task.input.at(0)})),
|
||||
std::filesystem::path(file_util::get_file_path({out})),
|
||||
std::filesystem::copy_options::overwrite_existing);
|
||||
fs::copy(fs::path(file_util::get_file_path({task.input.at(0)})),
|
||||
fs::path(file_util::get_file_path({out})), fs::copy_options::overwrite_existing);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
|
||||
#include "test_runner.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include "inja.hpp"
|
||||
@ -118,8 +117,8 @@ void runtime_with_kernel_no_debug_segment() {
|
||||
}
|
||||
|
||||
void createDirIfAbsent(const std::string& path) {
|
||||
if (!std::filesystem::is_directory(path) || !std::filesystem::exists(path)) {
|
||||
std::filesystem::create_directory(path);
|
||||
if (!fs::is_directory(path) || !fs::exists(path)) {
|
||||
fs::create_directory(path);
|
||||
}
|
||||
}
|
||||
std::string getTemplateDir(const std::string& category) {
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
@ -1,6 +1,5 @@
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <regex>
|
||||
@ -380,8 +379,8 @@ TEST_F(WithGameTests, GameCount) {
|
||||
shared_compiler->runner.run_static_test(env, testCategory, "test-game-count.gc",
|
||||
get_test_pass_string("game-count", 4));
|
||||
// don't leave behind a weird version of the game-count file.
|
||||
std::filesystem::remove(file_util::get_file_path({"out", "jak1", "iso", "ENGINE.CGO"}));
|
||||
std::filesystem::remove(file_util::get_file_path({"out", "jak1", "obj", "game-cnt.go"}));
|
||||
fs::remove(file_util::get_file_path({"out", "jak1", "iso", "ENGINE.CGO"}));
|
||||
fs::remove(file_util::get_file_path({"out", "jak1", "obj", "game-cnt.go"}));
|
||||
}
|
||||
|
||||
TEST_F(WithGameTests, BitFieldAccess) {
|
||||
|
@ -9,13 +9,14 @@
|
||||
#include "common/util/Timer.h"
|
||||
#include "common/util/diff.h"
|
||||
#include "common/util/json_util.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
#include "decompiler/ObjectFile/ObjectFileDB.h"
|
||||
#include "goalc/compiler/Compiler.h"
|
||||
|
||||
#include "third-party/fmt/format.h"
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace fs = fs;
|
||||
|
||||
// command line arguments
|
||||
struct OfflineTestArgs {
|
||||
@ -82,7 +83,7 @@ OfflineTestConfig parse_config() {
|
||||
}
|
||||
|
||||
struct DecompilerFile {
|
||||
std::filesystem::path path;
|
||||
fs::path path;
|
||||
std::string name_in_dgo;
|
||||
std::string unique_name;
|
||||
std::string reference;
|
||||
@ -264,20 +265,20 @@ Decompiler setup_decompiler(const std::vector<DecompilerFile>& files,
|
||||
// don't try to do this because we can't write the file
|
||||
dc.config->generate_symbol_definition_map = false;
|
||||
|
||||
std::vector<std::filesystem::path> dgo_paths;
|
||||
std::vector<fs::path> dgo_paths;
|
||||
if (args.iso_data_path.empty()) {
|
||||
for (auto& x : offline_config.dgos) {
|
||||
dgo_paths.push_back(file_util::get_jak_project_dir() / "iso_data" / "jak1" / x);
|
||||
}
|
||||
} else {
|
||||
for (auto& x : offline_config.dgos) {
|
||||
dgo_paths.push_back(std::filesystem::path(args.iso_data_path) / x);
|
||||
dgo_paths.push_back(fs::path(args.iso_data_path) / x);
|
||||
}
|
||||
}
|
||||
|
||||
dc.db = std::make_unique<decompiler::ObjectFileDB>(
|
||||
dgo_paths, dc.config->obj_file_name_map_file, std::vector<std::filesystem::path>{},
|
||||
std::vector<std::filesystem::path>{}, *dc.config);
|
||||
dc.db = std::make_unique<decompiler::ObjectFileDB>(dgo_paths, dc.config->obj_file_name_map_file,
|
||||
std::vector<fs::path>{},
|
||||
std::vector<fs::path>{}, *dc.config);
|
||||
|
||||
std::unordered_set<std::string> db_files;
|
||||
for (auto& files_by_name : dc.db->obj_files_by_name) {
|
||||
@ -420,6 +421,15 @@ bool compile(Decompiler& dc,
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
#ifdef _WIN32
|
||||
auto utf8_args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : utf8_args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
fmt::print("Offline Decompiler Test 2\n");
|
||||
lg::initialize();
|
||||
if (!file_util::setup_project_path(std::nullopt)) {
|
||||
|
@ -1,8 +1,9 @@
|
||||
#include <filesystem>
|
||||
|
||||
|
||||
#include "common/log/log.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/os.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
@ -18,6 +19,15 @@
|
||||
// to make it easier to test a subset of tests
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
// hopefully get a debug print on github actions
|
||||
setup_cpu_info();
|
||||
file_util::setup_project_path(std::nullopt);
|
||||
@ -27,10 +37,10 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Re-init failed folder
|
||||
std::string failedFolder = file_util::get_file_path({"test/goalc/source_generated/failed/"});
|
||||
if (std::filesystem::exists(failedFolder)) {
|
||||
std::filesystem::remove_all(failedFolder);
|
||||
if (fs::exists(failedFolder)) {
|
||||
fs::remove_all(failedFolder);
|
||||
}
|
||||
std::filesystem::create_directory(failedFolder);
|
||||
fs::create_directory(failedFolder);
|
||||
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
|
6049
third-party/filesystem.hpp
generated
vendored
Normal file
6049
third-party/filesystem.hpp
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@ -12,8 +12,9 @@
|
||||
|
||||
#include "decompiler/util/DecompilerTypeSystem.h"
|
||||
#include "common/util/Assert.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
namespace fs = fs;
|
||||
|
||||
struct Ram {
|
||||
const u8* data = nullptr;
|
||||
@ -554,6 +555,15 @@ void inspect_symbols(const Ram& ram,
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
fmt::print("MemoryDumpTool\n");
|
||||
|
||||
if (argc != 2 && argc != 3) {
|
||||
|
@ -3,8 +3,18 @@
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/BinaryWriter.h"
|
||||
#include "third-party/json.hpp"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
printf("OpenGOAL version %d.%d\n", versions::GOAL_VERSION_MAJOR, versions::GOAL_VERSION_MINOR);
|
||||
printf("DGO Packing Tool\n");
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "common/versions.h"
|
||||
#include "common/util/FileUtil.h"
|
||||
#include "common/util/DgoReader.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
namespace {
|
||||
int run(int argc, char** argv) {
|
||||
@ -48,6 +49,15 @@ int run(int argc, char** argv) {
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
try {
|
||||
return run(argc, argv);
|
||||
} catch (const std::exception& e) {
|
||||
|
@ -8,6 +8,7 @@
|
||||
#include "decompiler/level_extractor/BspHeader.h"
|
||||
|
||||
#include "common/util/Assert.h"
|
||||
#include <common/util/unicode_util.h>
|
||||
|
||||
constexpr GameVersion kGameVersion = GameVersion::Jak1;
|
||||
|
||||
@ -55,6 +56,15 @@ bool is_valid_bsp(const decompiler::LinkedObjectFile& file) {
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef _WIN32
|
||||
auto args = get_widechar_cli_args();
|
||||
std::vector<char*> string_ptrs;
|
||||
for (auto& str : args) {
|
||||
string_ptrs.push_back(str.data());
|
||||
}
|
||||
argv = string_ptrs.data();
|
||||
#endif
|
||||
|
||||
try {
|
||||
fmt::print("Level Dump Tool\n");
|
||||
|
||||
@ -92,4 +102,4 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user