diff --git a/release/RELEASE.MD b/release/RELEASE.MD index c62a401..8720397 100644 --- a/release/RELEASE.MD +++ b/release/RELEASE.MD @@ -1,3 +1,10 @@ -- tools - - gsc compiler - - fix boolean and undefined node \ No newline at end of file +- ui + - replace win32 ui with ImGui+GLFW ui, same features +- compiler + - fix bo4/cw import compilers + - allow to use &"" in bo3/bo4/cw compilers to use canon ids + - fix scriptbundle dump with mwiii + - more gsc dbg options + - preprocessor options +- repl console + - add console when the user starts the cli without a console \ No newline at end of file diff --git a/release/version b/release/version index 121549c..e100a14 100644 --- a/release/version +++ b/release/version @@ -1,2 +1,2 @@ -1.13.1 -1130100 \ No newline at end of file +1.14.0 +1140000 \ No newline at end of file diff --git a/src/acts/main.cpp b/src/acts/main.cpp index f1bc52c..1744628 100644 --- a/src/acts/main.cpp +++ b/src/acts/main.cpp @@ -531,7 +531,7 @@ int MainActs(int argc, const char* _argv[], HINSTANCE hInstance, int nShowCmd) { if ( opt.type == actscli::ACTS_NUI - // || (hInstance && core::config::GetBool("nui.force", true)) // uncomment when switching to nui + || (hInstance && core::config::GetBool("nui.force", true)) ) { tool::nui::OpenNuiWindow(); return 0; diff --git a/src/acts/tools/bo6/bo6.hpp b/src/acts/tools/bo6/bo6.hpp index c63f364..e230a60 100644 --- a/src/acts/tools/bo6/bo6.hpp +++ b/src/acts/tools/bo6/bo6.hpp @@ -1,6 +1,20 @@ #pragma once namespace bo6 { + struct DB_AssetPool { + uintptr_t m_entries; // void* + uintptr_t m_freeHead; // void* + unsigned int m_poolSize; + unsigned int m_elementSize; + unsigned int m_loadedPoolSize; + char __padding[158]; + }; + struct GscObjEntry { + uint64_t name; + int len; + int padc; + uintptr_t buffer; + }; enum T10ScrVarType : uint32_t { T10VT_UNDEFINED = 0x0, diff --git a/src/acts/tools/bo6/bo6ps4.cpp b/src/acts/tools/bo6/bo6ps4.cpp index 0ada386..d407148 100644 --- a/src/acts/tools/bo6/bo6ps4.cpp +++ b/src/acts/tools/bo6/bo6ps4.cpp @@ -5,6 +5,7 @@ #include "tools/bo6/bo6.hpp" #include "tools/utils/ps4_process.hpp" #include "tools/tools_ui.hpp" +#include "tools/tools_nui.hpp" #include "tools/gsc.hpp" #include @@ -1072,9 +1073,195 @@ namespace { tool::ui::window().SetTitleFont(info.titleLabel); } + + bool bo6_tools() { + static char gscFileIn[MAX_PATH + 1]{ 0 }; + static char cbuffIn[0x100]{ 0 }; + static char ps4In[0x50]{ 0 }; + static std::string notif{}; + + static std::once_flag of{}; + + bool c = false; + std::call_once(of, [&c] { + std::string injGsc = core::config::GetString("ui.bo6.path"); + std::string injCbuff = core::config::GetString("ui.bo6.cbuf"); + std::string injPs4 = core::config::GetString("ui.ps4.ipd"); + + snprintf(gscFileIn, sizeof(gscFileIn), "%s", injGsc.data()); + snprintf(cbuffIn, sizeof(cbuffIn), "%s", injCbuff.data()); + snprintf(ps4In, sizeof(ps4In), "%s", injPs4.data()); + c = true; + }); + + ImGui::SeparatorText("BO6 PS4 utilitites"); + + if (ImGui::InputText("PS4 IP", ps4In, sizeof(ps4In))) { + core::config::SetString("ui.ps4.ipd", ps4In); + c = true; + } + + ImGui::Spacing(); + ImGui::SeparatorText("cbuff"); + + if (ImGui::InputText("Command", cbuffIn, sizeof(cbuffIn))) { + core::config::SetString("ui.bo6.cbuf", cbuffIn); + c = true; + } + if (ImGui::Button("Send command")) { + try { + std::string cmd = cbuffIn; + + utils::ps4::PS4Process ps4{ ps4In }; + + uint64_t cbuf1 = ps4[0x4D6C350]; + uint64_t cbuf2 = cbuf1 + 0x10004; + + ps4.Write(cbuf1, cmd.data(), cmd.size() + 1); + ps4.Write(cbuf2, (uint32_t)cmd.size()); + + + ps4.Notify(std::format("cbuf {}", cmd)); + notif = ""; + } + catch (std::exception& e) { + notif = std::format("Exception: {}", e.what()); + } + } + + ImGui::Spacing(); + ImGui::SeparatorText("GSC injection"); + + if (ImGui::InputText("GSC File", gscFileIn, sizeof(gscFileIn))) { + core::config::SetString("ui.bo6.path", gscFileIn); + c = true; + } + if (ImGui::Button("Open file...")) { + // Open file + + OPENFILENAME ofn; + TCHAR szFile[MAX_PATH + 1] = { 0 }; + + // Initialize OPENFILENAME + ZeroMemory(&ofn, sizeof(ofn)); + ofn.lStructSize = sizeof(ofn); + ofn.hwndOwner = NULL; + ofn.lpstrFile = szFile; + ofn.nMaxFile = sizeof(szFile); + ofn.lpstrFilter = L"Compiled GSC file (.gscc, .gsic, .gscobj)\0*.gscc;*.gsic;*.gscobj\0All\0*.*\0"; + ofn.lpstrTitle = L"Open GSC file"; + ofn.nFilterIndex = 1; + ofn.lpstrFileTitle = NULL; + ofn.nMaxFileTitle = 0; + ofn.lpstrInitialDir = NULL; + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; + + if (GetOpenFileName(&ofn) == TRUE) { + std::string injGsc = utils::WStrToStr(ofn.lpstrFile); + core::config::SetString("ui.bo6.path", injGsc); + snprintf(gscFileIn, sizeof(gscFileIn), "%s", injGsc.data()); + c = true; + } + } + + + if (ImGui::Button("Inject PS4 Script")) { + std::string file{}; + + std::string filePath = gscFileIn; + + try { + if (!utils::ReadFile(filePath, file)) { + throw std::runtime_error(std::format("Can't read '{}'", filePath)); + } + + if (file.size() >= 4 && !memcmp("GSC", file.data(), 4)) { + throw std::runtime_error("GSCBIN format not supported"); + } + + if (file.size() < 0x20) { + throw std::runtime_error(std::format("Invalid gsc file '{}'", filePath)); + } + + uint64_t magic = *reinterpret_cast(file.data()); + + tool::gsc::opcode::VmInfo* nfo{}; + if (!tool::gsc::opcode::IsValidVmMagic(magic, nfo)) { + notif = (std::format("Invalid magic: 0x{:x}", magic)); + } + else if (nfo->vm == tool::gsc::opcode::VM::VM_BO6_06) { + // bo6 injector + + try { + tool::gsc::GscObj24* script{ (tool::gsc::GscObj24*)file.data() }; + + uint64_t name = script->name; + + utils::ps4::PS4Process ps4{ ps4In }; + + auto pool = ps4.ReadObject(ps4[0x98FEFA0] + sizeof(bo6::DB_AssetPool) * bo6::T10_ASSET_GSCOBJ); + + + LOG_INFO("Pool: {:x}, count: {}/{}, len 0x{:x}", pool->m_entries, pool->m_loadedPoolSize, pool->m_poolSize, pool->m_elementSize); + auto objs = ps4.ReadArray(pool->m_entries, pool->m_loadedPoolSize); + + size_t i; + for (i = 0; i < pool->m_loadedPoolSize; i++) { + auto& obj = objs[i]; + + if (obj.name != name) { + continue; + } + + if (!obj.buffer) { + throw std::runtime_error("Empty buffer"); + } + + if (obj.len < file.size()) { + throw std::runtime_error(utils::va("Buffer too small, can't remplace %llu < %llu", (size_t)obj.len, file.size())); + } + + auto scriptTarget = ps4.ReadObject(obj.buffer); + + if (scriptTarget->checksum != script->checksum) { + notif = ("Find target script, but the checksum doesn't match"); + return TRUE; + } + + ps4.Write(obj.buffer, file.data(), file.size()); + + notif = ("Script injected"); + ps4.Notify("Script injected"); + } + if (i == pool->m_loadedPoolSize) { + notif = ("Can't find hook script"); + } + } + catch (std::exception& e) { + notif = (std::format("Exception: {}", e.what())); + } + } + else { + notif = (std::format("PS4 injector not implemented for VM: {}", nfo->name)); + } + } + catch (std::exception& e) { + notif = std::format("Exception: {}", e.what()); + } + } + + if (!notif.empty()) { + ImGui::Separator(); + + ImGui::Text("%s", notif.data()); + } + + return c; + } ADD_TOOL("ps4cbufbo6", "dev", " [ip:port] [cmd]", "", nullptr, ps4cbufbo6); ADD_TOOL("ps4dumpbo6", "dev", " [ip:port]", "", nullptr, ps4dumpbo6); ADD_TOOL("ps4repbo6", "dev", " [ip:port] [file]", "", nullptr, ps4repbo6); ADD_TOOL_UI("bo6_tools", L"BO6 PS4", Render, Update, Resize); + ADD_TOOL_NUI("bo6_tools", "BO6 PS4", bo6_tools); } diff --git a/src/acts/tools/gsc_injector_ui.cpp b/src/acts/tools/gsc_injector_ui.cpp index 0905215..b5f06b8 100644 --- a/src/acts/tools/gsc_injector_ui.cpp +++ b/src/acts/tools/gsc_injector_ui.cpp @@ -3,8 +3,10 @@ #include "tools/tools_nui.hpp" #include "tools/cw/cw.hpp" #include "tools/bo3/bo3.hpp" +#include "tools/bo6/bo6.hpp" #include "tools/gsc.hpp" #include "mods/custom_ees.hpp" +#include "tools/utils/ps4_process.hpp" #include namespace { @@ -398,6 +400,7 @@ namespace { bool gsc_inject() { static char gscFileIn[MAX_PATH + 1]{ 0 }; static char hookIn[MAX_PATH + 1]{ 0 }; + static char ps4In[0x50]{ 0 }; static std::string notif{}; static std::once_flag of{}; @@ -406,9 +409,11 @@ namespace { std::call_once(of, [&c] { std::string injGsc = core::config::GetString("ui.injector.path"); std::string injHook = core::config::GetString("ui.injector.hook", "scripts\\zm_common\\load.gsc"); + std::string injPs4 = core::config::GetString("ui.ps4.ipd"); snprintf(gscFileIn, sizeof(gscFileIn), "%s", injGsc.data()); snprintf(hookIn, sizeof(hookIn), "%s", injHook.data()); + snprintf(ps4In, sizeof(ps4In), "%s", injPs4.data()); c = true; }); @@ -445,13 +450,14 @@ namespace { c = true; } } + ImGui::Spacing(); ImGui::SeparatorText("GSC Injector (PC)"); if (ImGui::InputText("Hook", hookIn, sizeof(hookIn))) { core::config::SetString("ui.injector.hook", hookIn); c = true; } - + if (ImGui::Button("Inject PC Script")) { std::string file{}; @@ -512,12 +518,104 @@ namespace { notif = std::format("Exception: {}", e.what()); } } +#ifdef DEV_PS4_INJECTOR + ImGui::Spacing(); ImGui::SeparatorText("GSC Injector (PS4)"); + if (ImGui::InputText("PS4 IP", ps4In, sizeof(ps4In))) { + core::config::SetString("ui.ps4.ipd", ps4In); + c = true; + } + + if (ImGui::Button("Inject PS4 Script")) { - notif = "PS4 Injector not implemented"; + std::string file{}; + + std::string filePath = gscFileIn; + std::string hookPath = hookIn; + + try { + if (!utils::ReadFile(filePath, file)) { + throw std::runtime_error(std::format("Can't read '{}'", filePath)); + } + + if (file.size() >= 4 && !memcmp("GSC", file.data(), 4)) { + throw std::runtime_error("GSCBIN format not supported"); + } + + if (file.size() < 0x20) { + throw std::runtime_error(std::format("Invalid gsc file '{}'", filePath)); + } + + uint64_t magic = *reinterpret_cast(file.data()); + + tool::gsc::opcode::VmInfo* nfo{}; + if (!tool::gsc::opcode::IsValidVmMagic(magic, nfo)) { + notif = (std::format("Invalid magic: 0x{:x}", magic)); + } + else if (nfo->vm == tool::gsc::opcode::VM::VM_BO6_06) { + // bo6 injector + + try { + tool::gsc::GscObj24* script{ (tool::gsc::GscObj24*)file.data() }; + + uint64_t name = script->name; + + utils::ps4::PS4Process ps4{ ps4In }; + + auto pool = ps4.ReadObject(ps4[0x98FEFA0] + sizeof(bo6::DB_AssetPool) * bo6::T10_ASSET_GSCOBJ); + + + LOG_INFO("Pool: {:x}, count: {}/{}, len 0x{:x}", pool->m_entries, pool->m_loadedPoolSize, pool->m_poolSize, pool->m_elementSize); + auto objs = ps4.ReadArray(pool->m_entries, pool->m_loadedPoolSize); + + size_t i; + for (i = 0; i < pool->m_loadedPoolSize; i++) { + auto& obj = objs[i]; + + if (obj.name != name) { + continue; + } + + if (!obj.buffer) { + throw std::runtime_error("Empty buffer"); + } + + if (obj.len < file.size()) { + throw std::runtime_error(utils::va("Buffer too small, can't remplace %llu < %llu", (size_t)obj.len, file.size())); + } + + auto scriptTarget = ps4.ReadObject(obj.buffer); + + if (scriptTarget->checksum != script->checksum) { + notif = ("Find target script, but the checksum doesn't match"); + return TRUE; + } + + ps4.Write(obj.buffer, file.data(), file.size()); + + notif = ("Script injected"); + ps4.Notify("Script injected"); + } + if (i == pool->m_loadedPoolSize) { + notif = ("Can't find hook script"); + } + } + catch (std::exception& e) { + notif = (std::format("Exception: {}", e.what())); + } + } + else { + notif = (std::format("PS4 injector not implemented for VM: {}", nfo->name)); + } + } + catch (std::exception& e) { + notif = std::format("Exception: {}", e.what()); + } } +#endif + ImGui::Spacing(); ImGui::SeparatorText("Utilities"); if (ImGui::Button("Patch Easter Eggs (Zombies/PC)")) { @@ -591,21 +689,15 @@ namespace { notif += "\nInvalid header"; } else { - notif += - std::format( - "\nName: {}" - "\nCrc: {} (0x{:x})" - "\nExports: {} (0x{:x})" - "\nImports: {} (0x{:x})" - "\nStrings: {} (0x{:x})" - , - - hashutils::ExtractTmpScript(handler->GetName()), - handler->GetChecksum(), handler->GetChecksum(), - handler->GetExportsCount(), handler->GetExportsOffset(), - handler->GetImportsCount(), handler->GetImportsOffset(), - handler->GetStringsCount(), handler->GetStringsOffset() - ); + std::ostringstream oss{}; + oss + << "\n" + << "// " << hashutils::ExtractTmpScript(handler->GetName()) << " (" << gscFileIn << ")" << " (size: " << file.length() << " Bytes / " << std::hex << "0x" << file.length() << ")\n" + << "// magic: 0x" << std::hex << handler->Ref() + ; + + handler->DumpHeader(oss << "\n", {}); + notif += oss.str(); } } diff --git a/src/acts/tools/gsc_vm.hpp b/src/acts/tools/gsc_vm.hpp index 833cf01..61ca9f7 100644 --- a/src/acts/tools/gsc_vm.hpp +++ b/src/acts/tools/gsc_vm.hpp @@ -1043,7 +1043,7 @@ class MW23GSCOBJHandler : public GSCOBJHandler { << "// imports ... " << std::dec << std::setw(3) << data->imports_count << " (offset: 0x" << std::hex << data->import_table << ")\n" << "// animtree1 . " << std::dec << std::setw(3) << data->animtree_use_count << " (offset: 0x" << std::hex << data->animtree_use_offset << ")\n" << "// animtree2 . " << std::dec << std::setw(3) << data->animtree_count << " (offset: 0x" << std::hex << data->animtree_offset << ")\n" - << "// cseg ..... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" + << "// cseg ...... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" << std::right << std::flush; @@ -1404,7 +1404,7 @@ class MW23BGSCOBJHandler : public GSCOBJHandler { << "// imports ... " << std::dec << std::setw(3) << data->imports_count << " (offset: 0x" << std::hex << data->import_table << ")\n" << "// animtree1 . " << std::dec << std::setw(3) << data->animtree_use_count << " (offset: 0x" << std::hex << data->animtree_use_offset << ")\n" << "// animtree2 . " << std::dec << std::setw(3) << data->animtree_count << " (offset: 0x" << std::hex << data->animtree_offset << ")\n" - << "// cseg ..... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" + << "// cseg ...... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" << std::right << std::flush; @@ -2342,7 +2342,7 @@ class T10GSCOBJHandler : public GSCOBJHandler { << "// imports ... " << std::dec << std::setw(3) << data->imports_count << " (offset: 0x" << std::hex << data->import_table << ")\n" << "// animtree1 . " << std::dec << std::setw(3) << data->animtree_use_count << " (offset: 0x" << std::hex << data->animtree_use_offset << ")\n" << "// animtree2 . " << std::dec << std::setw(3) << data->animtree_count << " (offset: 0x" << std::hex << data->animtree_offset << ")\n" - << "// cseg ..... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" + << "// cseg ...... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" << std::right << std::flush; @@ -2669,7 +2669,7 @@ class T1007GSCOBJHandler : public GSCOBJHandler { << "// imports ... " << std::dec << std::setw(3) << data->imports_count << " (offset: 0x" << std::hex << data->import_table << ")\n" << "// animtree1 . " << std::dec << std::setw(3) << data->animtree_use_count << " (offset: 0x" << std::hex << data->animtree_use_offset << ")\n" << "// animtree2 . " << std::dec << std::setw(3) << data->animtree_count << " (offset: 0x" << std::hex << data->animtree_offset << ")\n" - << "// cseg ..... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" + << "// cseg ...... 0x" << std::hex << data->cseg_offset << " + 0x" << std::hex << data->cseg_size << " (0x" << (data->cseg_offset + data->cseg_size) << ")" << "\n" << std::right << std::flush; diff --git a/src/acts/tools/utils/ps4_process.hpp b/src/acts/tools/utils/ps4_process.hpp index b736ae4..f56fe6d 100644 --- a/src/acts/tools/utils/ps4_process.hpp +++ b/src/acts/tools/utils/ps4_process.hpp @@ -19,7 +19,7 @@ namespace utils::ps4 { if (!proc) { ps4.Disconnect(); - throw std::runtime_error("Can't connect to process"); + throw std::runtime_error("Can't connect to PS4 process"); } pid = proc->pid;