From 5638ae2208ae5f5b9266b116b44bd34e5f4012a0 Mon Sep 17 00:00:00 2001 From: qaate47 Date: Mon, 9 Oct 2023 01:22:01 +0200 Subject: [PATCH] some tests and rosetta file creation --- .gitignore | 1 + .gitmodules | 8 + resolver.cfg => config/resolver.cfg | 0 config/stringtable_replace.cfg | 7 + deps/ps4debug | 1 + premake5.lua | 26 ++- scripts/setup.ps1 | 13 +- src/bo4-dll/error_handler.cpp | 22 +- src/cli/includes.hpp | 7 + src/cli/mods/custom_table_replace.cpp | 298 ++++++++++++++++++++++++++ src/cli/tools/gsc.cpp | 73 +++++++ src/cli/tools/gsc.hpp | 27 +++ src/cli/tools/gsc_opcodes_load.cpp | 158 +++++++++++++- src/cli/tools/gsc_opcodes_load.hpp | 2 + src/cli/tools/pool.cpp | 9 +- src/cli/tools/pool.hpp | 9 + src/cli/tools/process.cpp | 34 ++- src/cli/tools/ps4reader.cpp | 90 ++++++++ src/cli/tools/rosetta.cpp | 186 ++++++++++++++++ 19 files changed, 957 insertions(+), 14 deletions(-) rename resolver.cfg => config/resolver.cfg (100%) create mode 100644 config/stringtable_replace.cfg create mode 160000 deps/ps4debug create mode 100644 src/cli/mods/custom_table_replace.cpp create mode 100644 src/cli/tools/pool.hpp create mode 100644 src/cli/tools/ps4reader.cpp create mode 100644 src/cli/tools/rosetta.cpp diff --git a/.gitignore b/.gitignore index e3dc150..9317da1 100644 --- a/.gitignore +++ b/.gitignore @@ -33,6 +33,7 @@ .antlr scripts/antlr4.jar .antlr4 +src/cli/tools/ps4/* *.aps diff --git a/.gitmodules b/.gitmodules index 89dc888..873c4e4 100644 --- a/.gitmodules +++ b/.gitmodules @@ -9,3 +9,11 @@ path = deps/zlib url = https://github.com/madler/zlib branch = master +[submodule "deps/jit-lua"] + path = deps/jit-lua + url = https://github.com/Pirulax/luajit-premake + branch = master +[submodule "deps/ps4debug"] + path = deps/ps4debug + url = https://github.com/jogolden/ps4debug + branch = master diff --git a/resolver.cfg b/config/resolver.cfg similarity index 100% rename from resolver.cfg rename to config/resolver.cfg diff --git a/config/stringtable_replace.cfg b/config/stringtable_replace.cfg new file mode 100644 index 0000000..77c4c24 --- /dev/null +++ b/config/stringtable_replace.cfg @@ -0,0 +1,7 @@ +# Config file for the ctr mod + +# Target file + +target gamedata/events/schedule_pc.csv + struct string,int,int,string + diff --git a/deps/ps4debug b/deps/ps4debug new file mode 160000 index 0000000..b446dce --- /dev/null +++ b/deps/ps4debug @@ -0,0 +1 @@ +Subproject commit b446dced06009705c6f8d70e79113637d1690210 diff --git a/premake5.lua b/premake5.lua index d56af31..4946a2a 100644 --- a/premake5.lua +++ b/premake5.lua @@ -128,10 +128,10 @@ project "AtianCodTools" "./scripts/**.ps1", "./release/**", "./resources/**", + "./config/**", ".gitignore", "premake5.lua", "packages.txt", - "resolver.cfg", "gsc.conf", "LICENSE", "README.md" @@ -142,7 +142,8 @@ project "AtianCodTools" "src/shared", -- link antlr4 "deps/antlr4/runtime/Cpp/runtime/src/", - "deps/zlib/" + "deps/zlib/", + "deps/ps4debug/libdebug/cpp/include/", } vpaths { @@ -156,9 +157,11 @@ project "AtianCodTools" links { "antlr4-runtime" } links { "ACTSSharedLibrary" } links { "zlib" } + links { "libps4debug" } dependson "antlr4-runtime" dependson "ACTSSharedLibrary" dependson "zlib" + dependson "libps4debug" group "deps" project "antlr4-runtime" @@ -224,3 +227,22 @@ group "deps" "deps/zlib/*.c", "deps/zlib/*.h", } + project "libps4debug" + language "C++" + kind "StaticLib" + cppdialect "C++17" + warnings "Off" + + targetname "libps4debug" + targetdir "%{wks.location}/bin/" + objdir "%{wks.location}/obj/" + + files { + "deps/ps4debug/libdebug/cpp/source/*.cpp", + "deps/ps4debug/libdebug/cpp/include/*.hpp" + } + + includedirs { + "deps/ps4debug/libdebug/cpp/include/" + } + diff --git a/scripts/setup.ps1 b/scripts/setup.ps1 index 96eb2cf..3c72d22 100644 --- a/scripts/setup.ps1 +++ b/scripts/setup.ps1 @@ -11,11 +11,18 @@ try { $base = (Get-Item $PSScriptRoot).parent Set-Location ($base.Fullname) - # Update submodules + Write-Host "-- Update submodules" git submodule update --init --recursive - # install packages + + Write-Host "-- Install packages" vcpkg install "@.\packages.txt" - # create solution + + Write-Host "-- Create jit-lua projects" + Push-Location deps\jit-lua + premake5 vs2022 + Pop-Location + + Write-Host "-- Create solution" if ($ci) { premake5 vs2022 --ci-build } else { diff --git a/src/bo4-dll/error_handler.cpp b/src/bo4-dll/error_handler.cpp index 5ff1ac1..97d28d4 100644 --- a/src/bo4-dll/error_handler.cpp +++ b/src/bo4-dll/error_handler.cpp @@ -24,14 +24,16 @@ std::unordered_map error_handler::errors = { { 245612264, "foreach should be used with an array" }, { 647662103, "var can't be converted to string" }, { 4213634562, "precacheLeaderboards must be called before any wait statements in the gametype or level script" }, - { 3143575744, "parameter does not exist" }, // GetVector - { 2517242050, "parameter does not exist" }, // GetString - { 4196473479, "parameter does not exist" }, // GetBool - { 3699844858, "parameter does not exist" }, // GetType - { 280703902, "parameter does not exist" }, // GetPointerType + { 3143575744, "parameter does not exist" }, + { 2517242050, "parameter does not exist" }, + { 4196473479, "parameter does not exist" }, + { 3699844858, "parameter does not exist" }, + { 280703902, "parameter does not exist" }, + { 2606724305, "parameter does not exist" }, { 312545010, "not a vector" }, { 647662103, "parameter can't be cast to a string" }, { 952690413, "parameter can't be cast to a boolean" }, + { 1412130119, "parameter can't be cast to a hash" }, { 3761634992, "not a pointer" }, { 754846421, "invalid vehicle spawn origin" }, { 1909233687, "Optional argument must be a vector type" }, @@ -62,6 +64,16 @@ std::unordered_map error_handler::errors = { { 209668787, "RandomInt parm must be positive integer." }, { 753495682, "RandomIntRange's second parameter must be greater than the first." }, { 1045192683, "Scr_RandomFloatRange's second parameter must be greater than the first." }, + { 1377489376, "Gesture key can't have the higher bit set" }, + { 2355618801, "Gesture table key can't have the higher bit set" }, + { 2838301872, "Gesture table key can't have the higher bit set" }, + { 1099145600, "Can't find asset" }, + { 4088624643, "Can't find asset" }, + { 3016026156, "Can't find asset" }, + { 3251676101, "Could not load raw file" }, + { 1088278929, "Raw file is not a file of the right type" }, + { 1072585573, "Raw file is not a file of the right type" }, + { 3894031202, "Can't find gamedata/playeranim/playeranimtypes.txt" }, // messages handled by detours { 2737681163, "assert fail (with message)" }, diff --git a/src/cli/includes.hpp b/src/cli/includes.hpp index 75ee9e9..a7a1cee 100644 --- a/src/cli/includes.hpp +++ b/src/cli/includes.hpp @@ -1,8 +1,14 @@ #pragma once +#define _WINSOCKAPI_ #include #include +#pragma warning(push) +#pragma warning(disable:4455) +#include +#pragma warning(pop) + #include "memapi.hpp" #include "hashutils.hpp" @@ -12,5 +18,6 @@ #include "tools/dump.hpp" #include "tools/gsc.hpp" #include "tools/gsc_opcodes.hpp" +#include "tools/pool.hpp" extern LPCCH g_progPath; \ No newline at end of file diff --git a/src/cli/mods/custom_table_replace.cpp b/src/cli/mods/custom_table_replace.cpp new file mode 100644 index 0000000..3a8923d --- /dev/null +++ b/src/cli/mods/custom_table_replace.cpp @@ -0,0 +1,298 @@ +#include + +enum StringTableCellType : INT { + STC_TYPE_UNDEFINED = 0, + STC_TYPE_STRING = 1, + STC_TYPE_HASHED2 = 2, + STC_TYPE_INT = 4, + STC_TYPE_FLOAT = 5, + STC_TYPE_BOOL = 6, + STC_TYPE_HASHED7 = 7, + STC_TYPE_HASHED8 = 8, +}; + + +struct StringTableCell { + BYTE value[20]; + StringTableCellType type; +}; +// item size ... 40 +struct StringTableEntry { + UINT64 name; // 8 + int pad8; // 12 + int pad12; // 16 + int columnCount; // 20 + int rowCount; // 24 + int cellscount; // 28 empty? + int unk24; // 32 + uintptr_t cells; // 40 + uintptr_t values; // 48 StringTableCell + uintptr_t unk48; // 56 + uintptr_t unk56; // 64 +}; +struct XAssetPoolEntry { + uintptr_t pool; + UINT32 itemSize; + INT32 itemCount; + BYTE isSingleton; + INT32 itemAllocCount; + uintptr_t freeHead; +}; + +static int custom_table_replace(int argc, const char* argv[]) { + hashutils::ReadDefaultFile(); + Process proc{ "blackops4.exe" }; + + if (!proc || !proc.Open()) { + std::cerr << "Can't find black ops 4\n"; + return tool::BASIC_ERROR; + } + + XAssetPoolEntry entry{}; + + if (!proc.ReadMemory(&entry, proc[offset::assetPool] + sizeof(entry) * pool::ASSET_TYPE_STRINGTABLE, sizeof(entry))) { + std::cerr << "Can't read pool entry\n"; + return tool::BASIC_ERROR; + } + + auto pool = std::make_unique(entry.itemAllocCount); + + if (!proc.ReadMemory(&pool[0], entry.pool, sizeof(pool[0]) * entry.itemAllocCount)) { + std::cerr << "Can't read pool data\n"; + return tool::BASIC_ERROR; + } + + StringTableCell cell; + StringTableCell cell3[20]; + + size_t readFile = 0; + + auto target1 = 0x5c6dc30ffb97ee8bull; + auto target2 = hash::Hash64Pattern("gamedata/events/schedule_pc.csv"); + auto target3 = hash::Hash64Pattern("gamedata/loot/loot_bribes.csv"); + auto global_challenges = hash::Hash64Pattern("gamedata/stats/globalchallenges.csv"); + + UINT64 targetstats[] = { + hash::Hash64Pattern("gamedata/stats/wz/statsmilestones1.csv"), + hash::Hash64Pattern("gamedata/stats/wz/statsmilestones2.csv"), + hash::Hash64Pattern("gamedata/stats/wz/statsmilestones3.csv"), + + hash::Hash64Pattern("gamedata/stats/mp/statsmilestones1.csv"), + hash::Hash64Pattern("gamedata/stats/mp/statsmilestones2.csv"), + hash::Hash64Pattern("gamedata/stats/mp/statsmilestones3.csv"), + hash::Hash64Pattern("gamedata/stats/mp/statsmilestones4.csv"), + hash::Hash64Pattern("gamedata/stats/mp/statsmilestones5.csv"), + + hash::Hash64Pattern("gamedata/stats/zm/statsmilestones1.csv"), + hash::Hash64Pattern("gamedata/stats/zm/statsmilestones2.csv"), + hash::Hash64Pattern("gamedata/stats/zm/statsmilestones3.csv"), + hash::Hash64Pattern("gamedata/stats/zm/statsmilestones4.csv"), + hash::Hash64Pattern("gamedata/stats/zm/statsmilestones5.csv"), + }; + UINT64 targetstats2[] = { + hash::Hash64Pattern("gamedata/tables/mp/mp_combat_training_challenges.csv"), + hash::Hash64Pattern("gamedata/weapons/zm/zm_gunlevels.csv"), + hash::Hash64Pattern("gamedata/weapons/mp/mp_gunlevels.csv"), + }; + + for (size_t i = 0; i < min(entry.itemAllocCount, entry.itemCount); i++) { + const auto& e = pool[i]; + + bool isstats = std::find(&targetstats[0], std::end(targetstats), e.name) != std::end(targetstats); + bool isstats2 = std::find(&targetstats2[0], std::end(targetstats2), e.name) != std::end(targetstats2); + + if (!(e.name == target1 + || e.name == target2 + || e.name == target3 + || isstats + || isstats2 + || e.name == global_challenges + )) { + continue; // not our taget + } + + const auto size = e.columnCount * e.rowCount; + + if (!e.values || !size) { + continue; // check that we can read at least the cell + } + + std::cout << std::dec << i << ": " << hashutils::ExtractTmpScript(e.name) + << " (columns: " << e.columnCount << ", rows:" << e.rowCount << "/" << std::hex << (entry.pool + i * sizeof(entry)) << "/" << e.values << std::dec << ")\n"; + + /* + * season 2 = 0 + * season 2 bonus = 1 + * season 3 = 2 + * season 4 = 5 + * season 5 = 7 + * season 6 4 july = 9 + * season 6 = 10 + * season 7 = 11 + * season 7 bonus = 12 + * season 8 = 13 + */ + auto targetCell1 = 13 * e.columnCount + 2; // old1 + auto targetCell2 = 0 * e.columnCount + 2; // new1 + auto targetCell3 = 1 * e.columnCount + 2; // new2 + + // set season 2 + if (target2 == e.name) { + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * targetCell2, sizeof(cell))) { + std::cerr << "Can't read cell " << targetCell2 << "\n"; + continue; + } + + std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 2147364000; + + if (!proc.WriteMemory(e.values + sizeof(cell) * targetCell2, &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * targetCell3, sizeof(cell))) { + std::cerr << "Can't read cell " << targetCell3 << "\n"; + continue; + } + + std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 2147364000; + + if (!proc.WriteMemory(e.values + sizeof(cell) * targetCell3, &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * targetCell1, sizeof(cell))) { + std::cerr << "Can't read cell " << targetCell1 << "\n"; + continue; + } + + std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 1569862900; + //std::cout << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + + if (!proc.WriteMemory(e.values + sizeof(cell) * targetCell1, &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + else if (target1 == e.name) { + continue; + // set all item cost to 0 (not working) + for (size_t i = 1; i < e.rowCount; i++) { + if (!proc.ReadMemory(&cell3[0], e.values + sizeof(cell) * (i * e.columnCount + 2), sizeof(cell) * 4)) { + std::cerr << "Can't read cell " << i << "\n"; + continue; + } + + //std::cout << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell3[2].type = STC_TYPE_INT; + cell3[3].type = STC_TYPE_INT; + *reinterpret_cast(&(cell3[1].value[0])) = 1; // cp + *reinterpret_cast(&(cell3[3].value[0])) = 1; // crates + + + if (!proc.WriteMemory(e.values + sizeof(cell) * (i * e.columnCount + 2), &cell3[0], sizeof(cell) * 4)) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + } + else if (target3 == e.name) { + // set all item cost to 1 (not working) + for (size_t i = 0; i < e.rowCount; i++) { + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * (i * e.columnCount + 8), sizeof(cell))) { + std::cerr << "Can't read cell " << i << "\n"; + continue; + } + + //std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 1; + + + if (!proc.WriteMemory(e.values + sizeof(cell) * (i * e.columnCount + 8), &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + } + else if (isstats) { + // set all stats min to 0 (index = 2) + for (size_t i = 0; i < e.rowCount; i++) { + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * (i * e.columnCount + 2), sizeof(cell))) { + std::cerr << "Can't read cell " << i << "\n"; + continue; + } + + //std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 0; + + + if (!proc.WriteMemory(e.values + sizeof(cell) * (i * e.columnCount + 2), &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + } + else if (isstats2) { + // set all stats min to 0 (index = 1) + for (size_t i = 0; i < e.rowCount; i++) { + if (!proc.ReadMemory(&cell, e.values + sizeof(cell) * (i * e.columnCount + 1), sizeof(cell))) { + std::cerr << "Can't read cell " << i << "\n"; + continue; + } + + //std::cout << "old:" << cell.type << " " << *reinterpret_cast(&(cell.value[0])) << "\n"; + cell.type = STC_TYPE_INT; + *reinterpret_cast(&(cell.value[0])) = 0; + + + if (!proc.WriteMemory(e.values + sizeof(cell) * (i * e.columnCount + 1), &cell, sizeof(cell))) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + } + else if (global_challenges == e.name) { + // set all stats min to 0 (index = 1,2,3) + for (size_t i = 0; i < e.rowCount; i++) { + if (!proc.ReadMemory(&cell3[0], e.values + sizeof(cell) * (i * e.columnCount + 1), sizeof(cell) * 3)) { + std::cerr << "Can't read cell " << i << "\n"; + continue; + } + + // mp + cell3[0].type = STC_TYPE_INT; + *reinterpret_cast(&(cell3[0].value[0])) = 0; + // zm + cell3[1].type = STC_TYPE_INT; + *reinterpret_cast(&(cell3[1].value[0])) = 0; + // wz + cell3[2].type = STC_TYPE_INT; + *reinterpret_cast(&(cell3[2].value[0])) = 0; + + + if (!proc.WriteMemory(e.values + sizeof(cell) * (i * e.columnCount + 1), &cell3[0], sizeof(cell) * 2)) { + std::cerr << "Can't write cell " << i << "\n"; + continue; + } + } + } + + } + + + return tool::OK; +} + +#ifndef CI_BUILD + +ADD_MOD("ctr", "replace table data", custom_table_replace); + +#endif diff --git a/src/cli/tools/gsc.cpp b/src/cli/tools/gsc.cpp index df6559f..633052a 100644 --- a/src/cli/tools/gsc.cpp +++ b/src/cli/tools/gsc.cpp @@ -129,6 +129,14 @@ bool GscInfoOption::Compute(LPCCH* args, INT startIndex, INT endIndex) { } m_copyright = args[++i]; } + else if (!strcmp("-r", arg) || !_strcmpi("--rosetta", arg)) { + if (i + 1 == endIndex) { + std::cerr << "Missing value for param: " << arg << "!\n"; + return false; + } + m_rosetta = args[++i]; + } + else if (*arg == '-') { std::cerr << "Unknown option: " << arg << "!\n"; return false; @@ -160,10 +168,36 @@ void GscInfoOption::PrintHelp(std::ostream& out) { << "-G --gvars : Write gvars\n" << "-U --noincludes : No includes\n" << "-V --vars : Show all func vars\n" + << "-r --rosetta [f] : Create Rosetta file\n" << "-X --exptests : Enable UNK tests\n" << "-C --copyright [t] : Set a comment text to put in front of every file\n"; } +static LPCCH gRosettaOutput = NULL; +static UINT64 gRosettaCurrent = 0; +static std::map gRosettaBlocks{}; + +void tool::gsc::RosettaStartFile(T8GSCOBJ* obj) { + if (!gRosettaOutput) { + return; + } + + + auto& block = gRosettaBlocks[gRosettaCurrent = obj->name]; + // clone the header for the finder + memcpy(&block.header, obj, sizeof(*obj)); +} + +void tool::gsc::RosettaAddOpCode(UINT32 loc, UINT16 opcode) { + if (!gRosettaOutput) { + return; + } + + auto& block = gRosettaBlocks[gRosettaCurrent].blocks; + + block.push_back(tool::gsc::RosettaOpCodeBlock{ .location = loc, .opcode = opcode }); +} + int GscInfoHandleData(tool::gsc::T8GSCOBJ* data, size_t size, const char* path, const GscInfoOption& opt) { hashutils::ReadDefaultFile(); @@ -223,6 +257,8 @@ int GscInfoHandleData(tool::gsc::T8GSCOBJ* data, size_t size, const char* path, return -1; } + tool::gsc::RosettaStartFile(data); + char asmfnamebuff[1000]; if (opt.m_outputDir) { @@ -955,6 +991,8 @@ int tool::gsc::T8GSCExport::DumpAsm(std::ostream& out, BYTE* gscFile, T8GSCOBJCo << std::setfill(' ') << std::setw(25) << std::left << handler->m_name << std::right << " "; + // dump rosetta data + RosettaAddOpCode((UINT32)(reinterpret_cast(base) - reinterpret_cast(gscFile)), handler->m_id); // pass the opcode base += 2; @@ -1309,6 +1347,8 @@ int gscinfo(const Process& proc, int argc, const char* argv[]) { return 0; } + gRosettaOutput = opt.m_rosetta; + hashutils::SaveExtracted(opt.m_dump_hashmap != NULL); bool computed = false; auto ret = 0; @@ -1319,6 +1359,39 @@ int gscinfo(const Process& proc, int argc, const char* argv[]) { } } hashutils::WriteExtracted(opt.m_dump_hashmap); + + if (gRosettaOutput) { + std::ofstream os{ gRosettaOutput, std::ios::binary }; + + if (!os) { + std::cerr << "Can't open rosetta output\n"; + } + else { + os.write("ROSE", 4); + + auto len = gRosettaBlocks.size(); + os.write(reinterpret_cast(&len), sizeof(len)); + + for (const auto& [key, data] : gRosettaBlocks) { + // gsc header + os.write(reinterpret_cast(&data.header), sizeof(data.header)); + len = data.blocks.size(); + os.write(reinterpret_cast(&len), sizeof(len)); + for (const auto& block : data.blocks) { + os.write(reinterpret_cast(&block), sizeof(block)); + } + } + + // TODO: add crc + os.write("END", 3); + + + os.close(); + std::cerr << "Rosetta index created into '" << gRosettaOutput << "'\n"; + } + + + } return ret; } diff --git a/src/cli/tools/gsc.hpp b/src/cli/tools/gsc.hpp index 03c8c28..448e609 100644 --- a/src/cli/tools/gsc.hpp +++ b/src/cli/tools/gsc.hpp @@ -32,6 +32,7 @@ namespace tool::gsc { bool m_show_jump_delta = false; bool m_show_pre_dump = false; bool m_show_ref_count = false; + LPCCH m_rosetta = NULL; LPCCH m_dump_hashmap = NULL; LPCCH m_outputDir = NULL; LPCCH m_copyright = NULL; @@ -39,6 +40,7 @@ namespace tool::gsc { bool m_show_func_vars = false; UINT32 m_stepskip = 0; + std::vector m_inputFiles{}; /* * Compute options @@ -605,4 +607,29 @@ namespace tool::gsc { UINT8 type; UINT16 pad; }; + + enum RosettaBlockType : BYTE { + RBT_START = 0x50, + RBT_OPCODE = 0x51, + }; + struct RosettaOpCodeBlock { + UINT32 location; + UINT16 opcode; + }; + struct RosettaFileData { + T8GSCOBJ header; + std::vector blocks{}; + }; + + /* + * Begin rosetta file data + * @param obj script + */ + void RosettaStartFile(tool::gsc::T8GSCOBJ* obj); + /* + * Add rosetta opcode data + * @param loc opcode location + * @param opcode opcode + */ + void RosettaAddOpCode(UINT32 loc, UINT16 opcode); } \ No newline at end of file diff --git a/src/cli/tools/gsc_opcodes_load.cpp b/src/cli/tools/gsc_opcodes_load.cpp index 24b0163..8f1b3d5 100644 --- a/src/cli/tools/gsc_opcodes_load.cpp +++ b/src/cli/tools/gsc_opcodes_load.cpp @@ -354,4 +354,160 @@ void tool::gsc::opcode::RegisterOpCodesMap() { // T8-Compiler opcodes RegisterOpCode(0x36, OPCODE_T8C_GetLazyFunction, 0x16); }); -} \ No newline at end of file +} + +LPCCH tool::gsc::opcode::OpCodeName(OPCode op) { + switch (op) { + case OPCODE_Undefined: return "Undefined"; + case OPCODE_Unknown0: return "Unknown0"; + case OPCODE_Unknown1: return "Unknown1"; + case OPCODE_Unknown2: return "Unknown2"; + case OPCODE_Unknown3: return "Unknown3"; + case OPCODE_Unknown4: return "Unknown4"; + case OPCODE_Unknown5: return "Unknown5"; + case OPCODE_Unknown6: return "Unknown6"; + case OPCODE_Unknown7: return "Unknown7"; + case OPCODE_Unknowna: return "Unknowna"; + case OPCODE_Unknownb: return "Unknownb"; + case OPCODE_Unknown38: return "Unknown38"; + case OPCODE_Nop: return "Nop"; + case OPCODE_GetUndefined: return "GetUndefined"; + case OPCODE_Unknown10e: return "Unknown10e"; + case OPCODE_Unknown126: return "Unknown126"; + case OPCODE_End: return "End"; + case OPCODE_EvalLocalVariableCachedDebug: return "EvalLocalVariableCachedDebug"; + case OPCODE_EvalLocalVariableRefCachedDebug: return "EvalLocalVariableRefCachedDebug"; + case OPCODE_ClearParams: return "ClearParams"; + case OPCODE_CheckClearParams: return "CheckClearParams"; + case OPCODE_PreScriptCall: return "PreScriptCall"; + case OPCODE_CallBuiltinFunction: return "CallBuiltinFunction"; + case OPCODE_CallBuiltinMethod: return "CallBuiltinMethod"; + case OPCODE_EmptyArray: return "EmptyArray"; + case OPCODE_WaitTill: return "WaitTill"; + case OPCODE_ClearFieldVariableOnStack: return "ClearFieldVariableOnStack"; + case OPCODE_ClearArray: return "ClearArray"; + case OPCODE_ScriptMethodThreadCallPointer: return "ScriptMethodThreadCallPointer"; + case OPCODE_WaitTillMatchTimeout: return "WaitTillMatchTimeout"; + case OPCODE_GetHash: return "GetHash"; + case OPCODE_WaitFrame: return "WaitFrame"; + case OPCODE_ScriptMethodThreadCallPointerEndOn: return "ScriptMethodThreadCallPointerEndOn"; + case OPCODE_WaitTillMatch: return "WaitTillMatch"; + case OPCODE_ScriptThreadCallEndOn: return "ScriptThreadCallEndOn"; + case OPCODE_Switch: return "Switch"; + case OPCODE_ScriptFunctionCallPointer: return "ScriptFunctionCallPointer"; + case OPCODE_DevblockBegin: return "DevblockBegin"; + case OPCODE_NotEqual: return "NotEqual"; + case OPCODE_ScriptMethodCallPointer: return "ScriptMethodCallPointer"; + case OPCODE_JumpOnTrue: return "JumpOnTrue"; + case OPCODE_ScriptMethodThreadCallEndOn: return "ScriptMethodThreadCallEndOn"; + case OPCODE_SafeCreateLocalVariables: return "SafeCreateLocalVariables"; + case OPCODE_GetNegUnsignedShort: return "GetNegUnsignedShort"; + case OPCODE_ClassFunctionCall: return "ClassFunctionCall"; + case OPCODE_Return: return "Return"; + case OPCODE_EvalFieldVariableRef: return "EvalFieldVariableRef"; + case OPCODE_SafeDecTop: return "SafeDecTop"; + case OPCODE_Bit_Or: return "Bit_Or"; + case OPCODE_ScriptThreadCall: return "ScriptThreadCall"; + case OPCODE_Bit_Xor: return "Bit_Xor"; + case OPCODE_GetSelfObject: return "GetSelfObject"; + case OPCODE_GetNegByte: return "GetNegByte"; + case OPCODE_ScriptThreadCallPointerEndOn: return "ScriptThreadCallPointerEndOn"; + case OPCODE_BoolComplement: return "BoolComplement"; + case OPCODE_IsDefined: return "IsDefined"; + case OPCODE_AddToArray: return "AddToArray"; + case OPCODE_Wait: return "Wait"; + case OPCODE_SuperEqual: return "SuperEqual"; + case OPCODE_ScriptFunctionCall: return "ScriptFunctionCall"; + case OPCODE_JumpOnTrueExpr: return "JumpOnTrueExpr"; + case OPCODE_CreateArray: return "CreateArray"; + case OPCODE_Inc: return "Inc"; + case OPCODE_ShiftLeft: return "ShiftLeft"; + case OPCODE_JumpOnGreaterThan: return "JumpOnGreaterThan"; + case OPCODE_Plus: return "Plus"; + case OPCODE_CastAndEvalFieldVariable: return "CastAndEvalFieldVariable"; + case OPCODE_ShiftRight: return "ShiftRight"; + case OPCODE_CreateStruct: return "CreateStruct"; + case OPCODE_CastCanon: return "CastCanon"; + case OPCODE_GreaterThanOrEqualTo: return "GreaterThanOrEqualTo"; + case OPCODE_GetUIntPtr: return "GetUIntPtr"; + case OPCODE_GetLongInteger: return "GetLongInteger"; + case OPCODE_EvalArray: return "EvalArray"; + case OPCODE_WaitTillFrameEnd: return "WaitTillFrameEnd"; + case OPCODE_EndOnCallback: return "EndOnCallback"; + case OPCODE_EndOn: return "EndOn"; + case OPCODE_SuperNotEqual: return "SuperNotEqual"; + case OPCODE_GetFloat: return "GetFloat"; + case OPCODE_ProfileStart: return "ProfileStart"; + case OPCODE_GetString: return "GetString"; + case OPCODE_BoolNot: return "BoolNot"; + case OPCODE_CastBool: return "CastBool"; + case OPCODE_Equal: return "Equal"; + case OPCODE_GetUnsignedInteger: return "GetUnsignedInteger"; + case OPCODE_WaittillTimeout: return "WaittillTimeout"; + case OPCODE_GreaterThan: return "GreaterThan"; + case OPCODE_Jump: return "Jump"; + case OPCODE_Divide: return "Divide"; + case OPCODE_EndSwitch: return "EndSwitch"; + case OPCODE_JumpOnFalse: return "JumpOnFalse"; + case OPCODE_JumpOnFalseExpr: return "JumpOnFalseExpr"; + case OPCODE_Minus: return "Minus"; + case OPCODE_ProfileStop: return "ProfileStop"; + case OPCODE_GetInteger: return "GetInteger"; + case OPCODE_ClassFunctionThreadCall: return "ClassFunctionThreadCall"; + case OPCODE_VectorScale: return "VectorScale"; + case OPCODE_Modulus: return "Modulus"; + case OPCODE_VectorConstant: return "VectorConstant"; + case OPCODE_SizeOf: return "SizeOf"; + case OPCODE_Notify: return "Notify"; + case OPCODE_Vector: return "Vector"; + case OPCODE_Dec: return "Dec"; + case OPCODE_JumpOnLessThan: return "JumpOnLessThan"; + case OPCODE_Bit_And: return "Bit_And"; + case OPCODE_GetObjectType: return "GetObjectType"; + case OPCODE_Multiply: return "Multiply"; + case OPCODE_EvalFieldVariableOnStack: return "EvalFieldVariableOnStack"; + case OPCODE_GetTime: return "GetTime"; + case OPCODE_AddToStruct: return "AddToStruct"; + case OPCODE_ClassFunctionThreadCallEndOn: return "ClassFunctionThreadCallEndOn"; + case OPCODE_LessThanOrEqualTo: return "LessThanOrEqualTo"; + case OPCODE_GetGlobal: return "GetGlobal"; + case OPCODE_GetZero: return "GetZero"; + case OPCODE_ClearFieldVariable: return "ClearFieldVariable"; + case OPCODE_EvalFieldObjectFromRef: return "EvalFieldObjectFromRef"; + case OPCODE_GetSelf: return "GetSelf"; + case OPCODE_GetResolveFunction: return "GetResolveFunction"; + case OPCODE_GetGlobalObject: return "GetGlobalObject"; + case OPCODE_GetByte: return "GetByte"; + case OPCODE_CastFieldObject: return "CastFieldObject"; + case OPCODE_ScriptThreadCallPointer: return "ScriptThreadCallPointer"; + case OPCODE_LessThan: return "LessThan"; + case OPCODE_ScriptMethodCall: return "ScriptMethodCall"; + case OPCODE_DecTop: return "DecTop"; + case OPCODE_GetVector: return "GetVector"; + case OPCODE_ScriptMethodThreadCall: return "ScriptMethodThreadCall"; + case OPCODE_JumpPush: return "JumpPush"; + case OPCODE_GetUnsignedShort: return "GetUnsignedShort"; + case OPCODE_EvalLocalVariableCached: return "EvalLocalVariableCached"; + case OPCODE_EvalFieldVariable: return "EvalFieldVariable"; + case OPCODE_GetFunction: return "GetFunction"; + case OPCODE_EvalArrayRef: return "EvalArrayRef"; + case OPCODE_SetNextArrayKeyCached: return "SetNextArrayKeyCached"; + case OPCODE_Unknown9e: return "Unknown9e"; + case OPCODE_EvalLocalVariableCachedSafe: return "EvalLocalVariableCachedSafe"; + case OPCODE_SetLocalVariableCachedOnStack: return "SetLocalVariableCachedOnStack"; + case OPCODE_Unknownc7: return "Unknownc7"; + case OPCODE_Unknown35: return "Unknown35"; + case OPCODE_FirstArrayKey: return "FirstArrayKey"; + case OPCODE_EvalFieldVariableOnStackRef: return "EvalFieldVariableOnStackRef"; + case OPCODE_SetVariableFieldRef: return "SetVariableFieldRef"; + case OPCODE_SetVariableField: return "SetVariableField"; + case OPCODE_EvalSelfFieldVariable: return "EvalSelfFieldVariable"; + case OPCODE_SetLocalVariableCached: return "SetLocalVariableCached"; + case OPCODE_FirstArrayKeyCached: return "FirstArrayKeyCached"; + case OPCODE_EvalLocalVariableRefCached: return "EvalLocalVariableRefCached"; + case OPCODE_EvalGlobalObjectFieldVariable: return "EvalGlobalObjectFieldVariable"; + case OPCODE_EvalLocalVariableDefined: return "EvalLocalVariableDefined"; + case OPCODE_T8C_GetLazyFunction: return "T8C_GetLazyFunction"; + default: return "UNKNOWN"; + } +} diff --git a/src/cli/tools/gsc_opcodes_load.hpp b/src/cli/tools/gsc_opcodes_load.hpp index b3973cd..692fc34 100644 --- a/src/cli/tools/gsc_opcodes_load.hpp +++ b/src/cli/tools/gsc_opcodes_load.hpp @@ -163,4 +163,6 @@ namespace tool::gsc::opcode { OPCODE_T8C_GetLazyFunction, }; void RegisterOpCodesMap(); + + LPCCH OpCodeName(OPCode op); } \ No newline at end of file diff --git a/src/cli/tools/pool.cpp b/src/cli/tools/pool.cpp index 4433ab9..50cec0b 100644 --- a/src/cli/tools/pool.cpp +++ b/src/cli/tools/pool.cpp @@ -1,5 +1,7 @@ #include +using namespace tool::pool; + class PoolOption { public: bool m_help = false; @@ -135,7 +137,7 @@ inline bool HexValidString(LPCCH str) { return true; } -void WriteHex(std::ostream& out, uintptr_t base, BYTE* buff, SIZE_T size, const Process& proc) { +void tool::pool::WriteHex(std::ostream& out, uintptr_t base, BYTE* buff, SIZE_T size, const Process& proc) { CHAR strBuffer[101]; for (size_t j = 0; j < size; j++) { if (j % 8 == 0) { @@ -574,12 +576,15 @@ int pooltool(const Process& proc, int argc, const char* argv[]) { size_t readFile = 0; BYTE test[0x100]; - for (size_t i = 0; i < min(10, entry.itemAllocCount); i++) { + for (size_t i = 0; i < entry.itemAllocCount; i++) { const auto& p = pool[i]; + auto n = hashutils::ExtractPtr(p.name); std::cout << "- " << std::dec << i << " : " << hashutils::ExtractTmp("file", p.name) << ": " << std::hex << p.buffer << "\n"; + // WriteHex(std::cout, entry.pool + i * sizeof(pool[0]), (BYTE*)& p, sizeof(p), proc); // padding + if (!proc.ReadMemory(test, p.buffer, sizeof(test))) { std::cerr << "Bad read\n"; continue; diff --git a/src/cli/tools/pool.hpp b/src/cli/tools/pool.hpp new file mode 100644 index 0000000..86ce938 --- /dev/null +++ b/src/cli/tools/pool.hpp @@ -0,0 +1,9 @@ +#pragma once + +#include + +namespace tool::pool { + + void WriteHex(std::ostream& out, uintptr_t base, BYTE* buff, SIZE_T size, const Process& proc); + +} \ No newline at end of file diff --git a/src/cli/tools/process.cpp b/src/cli/tools/process.cpp index 7eb80ab..646ddca 100644 --- a/src/cli/tools/process.cpp +++ b/src/cli/tools/process.cpp @@ -3,6 +3,7 @@ int PTSearch(Process& proc, int argc, const char* argv[]); int PTAlloc(Process& proc, int argc, const char* argv[]); int PTCall(Process& proc, int argc, const char* argv[]); +int PTRead(Process& proc, int argc, const char* argv[]); int processtool(const Process& unused, int argc, const char* argv[]) { if (argc <= 3) { @@ -30,6 +31,9 @@ int processtool(const Process& unused, int argc, const char* argv[]) { else if (!_strcmpi("call", argv[3])) { func = PTCall; } + else if (!_strcmpi("r", argv[3])) { + func = PTRead; + } if (!func) { std::cerr << "Bad function: " << argv[3] << "\n"; @@ -235,4 +239,32 @@ int PTCall(Process& proc, int argc, const char* argv[]) { proc.FreeMemory(argptr, args.size()); return tool::OK; -} \ No newline at end of file +} + +int PTRead(Process& proc, int argc, const char* argv[]) { + if (argc <= 5) { + std::cerr << argv[0] << " " << argv[1] << " " << argv[2] << " " << argv[3] << " [(+)location] [size]"; + return tool::BASIC_ERROR; + } + + hashutils::ReadDefaultFile(); + + bool relative = argv[4][0] == '+'; + auto idx = std::stoull(argv[4], nullptr, 16); + if (relative) idx = proc[idx]; + auto len = std::stoull(argv[5], nullptr, 16); + + std::cout << std::hex << "reading 0x" << idx << "[0:" << len << "]...\n"; + + auto buffer = std::make_unique(len); + + if (!proc.ReadMemory(&buffer[0], (uintptr_t)idx, (size_t)len)) { + std::cerr << "Error when reading memory\n"; + return tool::BASIC_ERROR; + } + + tool::pool::WriteHex(std::cout, idx, &buffer[0], len, proc); + + return tool::OK; +} + diff --git a/src/cli/tools/ps4reader.cpp b/src/cli/tools/ps4reader.cpp new file mode 100644 index 0000000..10a1c4e --- /dev/null +++ b/src/cli/tools/ps4reader.cpp @@ -0,0 +1,90 @@ +#include + +static int ps4reader(const Process& _, int argc, const char* argv[]) { + if (argc < 5) { + return tool::BAD_USAGE; + } + + auto ipd = argv[2]; + auto loc = argv[3]; + auto size = std::strtoull(argv[4], nullptr, 16); + + if (size >= (1ull << ((sizeof(UINT32) << 3) - 1))) { + std::cerr << "Size too large: " << std::hex << size << "/" << (1ull << ((sizeof(UINT32) << 3) - 1)) << "\n"; + return tool::BASIC_ERROR; + } + + libdebug::PS4DBG ps4{ ipd }; + ps4.Connect(); + + ps4.Notify(210, std::format("Atian Tools {}", actsinfo::VERSION)); + + auto procList = ps4.GetProcessList(); + auto proc = procList.FindProcess("eboot.bin"); + + if (!proc) { + std::cerr << "Can't find eboot.bin\n"; + ps4.Disconnect(); + return tool::BASIC_ERROR; + } + + auto pid = proc->pid; + + auto entries = ps4.GetProcessMaps(proc->pid); + uint64_t base = 0; + + for (const auto& entry : entries.entries) { + if (entry->prot == 5) { + std::cout << "executable base : 0x" << std::hex << entry->start << std::endl; + base = entry->start; + break; + } + } + + if (!base) { + std::cerr << "Can't find executable base\n"; + ps4.Disconnect(); + return tool::BASIC_ERROR; + } + + uintptr_t locData; + + if (*loc == '+') { + locData = base + std::strtoull(loc + 1, nullptr, 16); + } + else { + locData = std::strtoull(loc, nullptr, 16); + } + + auto bytes = ps4.ReadMemory(pid, locData, (int)size); + + auto i = 0; + for (const auto b : bytes) { + std::cout << std::hex << std::setw(2) << std::setfill('0') << (int)b; + i++; + if (i % 16 == 0) { + std::cout << "\n"; + } + else if (i % 8 == 0) { + std::cout << " "; + } + else { + std::cout << " "; + } + } + std::cout << "\n"; + + + ps4.Disconnect(); + + + + return tool::OK; +} + + +#ifndef CI_BUILD + +ADD_TOOL("ps4r", " [ip:port] [loc] [size]", "ps4reader", false, ps4reader); + +#endif \ No newline at end of file diff --git a/src/cli/tools/rosetta.cpp b/src/cli/tools/rosetta.cpp new file mode 100644 index 0000000..6179315 --- /dev/null +++ b/src/cli/tools/rosetta.cpp @@ -0,0 +1,186 @@ +#include + + +struct T8ScriptParseTreeEntry { + UINT64 name; + UINT64 pad0; + uintptr_t buffer; + UINT32 size; + UINT32 pad02; +}; + +using tool::gsc::RosettaBlockType; +using tool::gsc::RosettaOpCodeBlock; + +static void fillmap(const std::filesystem::path& path, std::vector& paths) { + if (std::filesystem::is_directory(path)) { + for (const auto& sub : std::filesystem::directory_iterator{ path }) { + fillmap(sub, paths); + } + return; + } + + paths.push_back(path); +} + +static int rosetta(const Process& proc, int argc, const char* argv[]) { + + if (argc < 4) { + return tool::BAD_USAGE; + } + + std::ifstream rfile{ argv[2], std::ios::binary }; + + if (!rfile) { + std::cout << "Can't open file '" << argv[2] << "'\n"; + } + + std::map rosettaBlocks{}; + + CHAR checkBuff[4] = { 0 }; + + rfile.read(checkBuff, 4); + + if (memcmp(checkBuff, "ROSE", 4)) { + std::cout << "Bad magic for '" << argv[2] << "'\n"; + rfile.close(); + return tool::BASIC_ERROR; + } + + size_t len = 0; + size_t lenblk = 0; + + rfile.read(reinterpret_cast(&len), sizeof(len)); + + tool::gsc::T8GSCOBJ header{}; + + for (size_t i = 0; i < len; i++) { + + rfile.read(reinterpret_cast(&header), sizeof(header)); + auto& block = rosettaBlocks[header.name]; + memcpy(&block.header, &header, sizeof(header)); + + rfile.read(reinterpret_cast(&lenblk), sizeof(lenblk)); + for (size_t i = 0; i < lenblk; i++) { + auto& b = block.blocks.emplace_back(); + + rfile.read(reinterpret_cast(&b), sizeof(b)); + } + } + + rfile.read(checkBuff, 3); + + if (memcmp(checkBuff, "END", 3)) { + std::cout << "Bad end magic for '" << argv[2] << "'\n"; + rfile.close(); + return tool::BASIC_ERROR; + } + + + rfile.close(); + + std::cout << "Read rosetta index with " << std::dec << rosettaBlocks.size() << " script(s)\n"; + std::filesystem::path locDir = argv[3]; + + std::map> map{}; + std::map> maplookup{}; + + + if (!std::filesystem::exists(locDir)) { + std::cerr << locDir.string() << " doesn't exist!\n"; + return tool::BASIC_ERROR; + } + + std::vector files{}; + + fillmap(locDir, files); + + tool::dump::T8GSCSimpleHeader* buffer = NULL; + SIZE_T size; + + std::cout << "Find " << files.size() << " file(s)\n"; + + for (const auto& file : files) { + if (!utils::ReadFileNotAlign(file, reinterpret_cast(buffer), size, false)) { + std::cerr << "Error when reading " << file.string() << "\n"; + continue; + } + + //std::cout << "Reading data from " << file.string() << " (" << hashutils::ExtractTmpScript(buffer->name) << ")\n"; + + auto blk = rosettaBlocks.find(buffer->name); + + if (blk == rosettaBlocks.end()) { + std::cerr << "Script " << file.string() << " (" << hashutils::ExtractTmpScript(buffer->name) << ") isn't in the rosetta index\n"; + std::free(buffer); + buffer = NULL; + continue; + } + + auto& bl = blk->second; + + // clear CRC + bl.header.crc = 0; + reinterpret_cast(buffer)->crc = 0; + + if (memcmp(&bl.header, buffer, sizeof(bl.header))) { + std::cerr << "Script " << file.string() << " (" << hashutils::ExtractTmpScript(buffer->name) << ") has a different header\n"; + std::free(buffer); + buffer = NULL; + continue; + } + + for (auto opco : bl.blocks) { + if (opco.location >= size) { + std::cerr << "Bad location: " << std::hex << opco.location << " in " << file.string() << " (" << hashutils::ExtractTmpScript(buffer->name) << ")\n"; + } + + auto val = *reinterpret_cast(reinterpret_cast(buffer) + opco.location); + auto old = opco.opcode; + + map[old].insert(val); + maplookup[val].insert(old); + } + + + std::free(buffer); + buffer = NULL; + } + + std::cout << "Mapping: \n"; + int count = 0; + for (auto [key, vec] : map) { + std::cout << std::hex << tool::gsc::opcode::OpCodeName((tool::gsc::opcode::OPCode)(key)) << " = "; + for (const auto val : vec) { + std::cout << std::hex << val << " "; + count++; + } + std::cout << "\n"; + } + + std::cout << std::hex << count << " values\n"; + + std::cout << "Lookup errors: \n"; + + for (auto [key, vec] : maplookup) { + if (vec.size() == 1) { + continue; + } + std::cout << std::hex << key << " = "; + for (const auto val : vec) { + std::cout << std::hex << tool::gsc::opcode::OpCodeName((tool::gsc::opcode::OPCode)(val)) << " "; + } + std::cout << "\n"; + } + + + return tool::OK; +} + + + +#ifndef CI_BUILD + +ADD_TOOL("rosetta", " [data]", "rosetta", false, rosetta); + +#endif \ No newline at end of file