|
5 | 5 | #include "tools/bo6/bo6.hpp"
|
6 | 6 | #include "tools/utils/ps4_process.hpp"
|
7 | 7 | #include "tools/tools_ui.hpp"
|
| 8 | +#include "tools/tools_nui.hpp" |
8 | 9 | #include "tools/gsc.hpp"
|
9 | 10 | #include <core/config.hpp>
|
10 | 11 |
|
@@ -1072,9 +1073,195 @@ namespace {
|
1072 | 1073 |
|
1073 | 1074 | tool::ui::window().SetTitleFont(info.titleLabel);
|
1074 | 1075 | }
|
| 1076 | + |
| 1077 | + bool bo6_tools() { |
| 1078 | + static char gscFileIn[MAX_PATH + 1]{ 0 }; |
| 1079 | + static char cbuffIn[0x100]{ 0 }; |
| 1080 | + static char ps4In[0x50]{ 0 }; |
| 1081 | + static std::string notif{}; |
| 1082 | + |
| 1083 | + static std::once_flag of{}; |
| 1084 | + |
| 1085 | + bool c = false; |
| 1086 | + std::call_once(of, [&c] { |
| 1087 | + std::string injGsc = core::config::GetString("ui.bo6.path"); |
| 1088 | + std::string injCbuff = core::config::GetString("ui.bo6.cbuf"); |
| 1089 | + std::string injPs4 = core::config::GetString("ui.ps4.ipd"); |
| 1090 | + |
| 1091 | + snprintf(gscFileIn, sizeof(gscFileIn), "%s", injGsc.data()); |
| 1092 | + snprintf(cbuffIn, sizeof(cbuffIn), "%s", injCbuff.data()); |
| 1093 | + snprintf(ps4In, sizeof(ps4In), "%s", injPs4.data()); |
| 1094 | + c = true; |
| 1095 | + }); |
| 1096 | + |
| 1097 | + ImGui::SeparatorText("BO6 PS4 utilitites"); |
| 1098 | + |
| 1099 | + if (ImGui::InputText("PS4 IP", ps4In, sizeof(ps4In))) { |
| 1100 | + core::config::SetString("ui.ps4.ipd", ps4In); |
| 1101 | + c = true; |
| 1102 | + } |
| 1103 | + |
| 1104 | + ImGui::Spacing(); |
| 1105 | + ImGui::SeparatorText("cbuff"); |
| 1106 | + |
| 1107 | + if (ImGui::InputText("Command", cbuffIn, sizeof(cbuffIn))) { |
| 1108 | + core::config::SetString("ui.bo6.cbuf", cbuffIn); |
| 1109 | + c = true; |
| 1110 | + } |
| 1111 | + if (ImGui::Button("Send command")) { |
| 1112 | + try { |
| 1113 | + std::string cmd = cbuffIn; |
| 1114 | + |
| 1115 | + utils::ps4::PS4Process ps4{ ps4In }; |
| 1116 | + |
| 1117 | + uint64_t cbuf1 = ps4[0x4D6C350]; |
| 1118 | + uint64_t cbuf2 = cbuf1 + 0x10004; |
| 1119 | + |
| 1120 | + ps4.Write(cbuf1, cmd.data(), cmd.size() + 1); |
| 1121 | + ps4.Write<uint32_t>(cbuf2, (uint32_t)cmd.size()); |
| 1122 | + |
| 1123 | + |
| 1124 | + ps4.Notify(std::format("cbuf {}", cmd)); |
| 1125 | + notif = ""; |
| 1126 | + } |
| 1127 | + catch (std::exception& e) { |
| 1128 | + notif = std::format("Exception: {}", e.what()); |
| 1129 | + } |
| 1130 | + } |
| 1131 | + |
| 1132 | + ImGui::Spacing(); |
| 1133 | + ImGui::SeparatorText("GSC injection"); |
| 1134 | + |
| 1135 | + if (ImGui::InputText("GSC File", gscFileIn, sizeof(gscFileIn))) { |
| 1136 | + core::config::SetString("ui.bo6.path", gscFileIn); |
| 1137 | + c = true; |
| 1138 | + } |
| 1139 | + if (ImGui::Button("Open file...")) { |
| 1140 | + // Open file |
| 1141 | + |
| 1142 | + OPENFILENAME ofn; |
| 1143 | + TCHAR szFile[MAX_PATH + 1] = { 0 }; |
| 1144 | + |
| 1145 | + // Initialize OPENFILENAME |
| 1146 | + ZeroMemory(&ofn, sizeof(ofn)); |
| 1147 | + ofn.lStructSize = sizeof(ofn); |
| 1148 | + ofn.hwndOwner = NULL; |
| 1149 | + ofn.lpstrFile = szFile; |
| 1150 | + ofn.nMaxFile = sizeof(szFile); |
| 1151 | + ofn.lpstrFilter = L"Compiled GSC file (.gscc, .gsic, .gscobj)\0*.gscc;*.gsic;*.gscobj\0All\0*.*\0"; |
| 1152 | + ofn.lpstrTitle = L"Open GSC file"; |
| 1153 | + ofn.nFilterIndex = 1; |
| 1154 | + ofn.lpstrFileTitle = NULL; |
| 1155 | + ofn.nMaxFileTitle = 0; |
| 1156 | + ofn.lpstrInitialDir = NULL; |
| 1157 | + ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST; |
| 1158 | + |
| 1159 | + if (GetOpenFileName(&ofn) == TRUE) { |
| 1160 | + std::string injGsc = utils::WStrToStr(ofn.lpstrFile); |
| 1161 | + core::config::SetString("ui.bo6.path", injGsc); |
| 1162 | + snprintf(gscFileIn, sizeof(gscFileIn), "%s", injGsc.data()); |
| 1163 | + c = true; |
| 1164 | + } |
| 1165 | + } |
| 1166 | + |
| 1167 | + |
| 1168 | + if (ImGui::Button("Inject PS4 Script")) { |
| 1169 | + std::string file{}; |
| 1170 | + |
| 1171 | + std::string filePath = gscFileIn; |
| 1172 | + |
| 1173 | + try { |
| 1174 | + if (!utils::ReadFile(filePath, file)) { |
| 1175 | + throw std::runtime_error(std::format("Can't read '{}'", filePath)); |
| 1176 | + } |
| 1177 | + |
| 1178 | + if (file.size() >= 4 && !memcmp("GSC", file.data(), 4)) { |
| 1179 | + throw std::runtime_error("GSCBIN format not supported"); |
| 1180 | + } |
| 1181 | + |
| 1182 | + if (file.size() < 0x20) { |
| 1183 | + throw std::runtime_error(std::format("Invalid gsc file '{}'", filePath)); |
| 1184 | + } |
| 1185 | + |
| 1186 | + uint64_t magic = *reinterpret_cast<uint64_t*>(file.data()); |
| 1187 | + |
| 1188 | + tool::gsc::opcode::VmInfo* nfo{}; |
| 1189 | + if (!tool::gsc::opcode::IsValidVmMagic(magic, nfo)) { |
| 1190 | + notif = (std::format("Invalid magic: 0x{:x}", magic)); |
| 1191 | + } |
| 1192 | + else if (nfo->vm == tool::gsc::opcode::VM::VM_BO6_06) { |
| 1193 | + // bo6 injector |
| 1194 | + |
| 1195 | + try { |
| 1196 | + tool::gsc::GscObj24* script{ (tool::gsc::GscObj24*)file.data() }; |
| 1197 | + |
| 1198 | + uint64_t name = script->name; |
| 1199 | + |
| 1200 | + utils::ps4::PS4Process ps4{ ps4In }; |
| 1201 | + |
| 1202 | + auto pool = ps4.ReadObject<bo6::DB_AssetPool>(ps4[0x98FEFA0] + sizeof(bo6::DB_AssetPool) * bo6::T10_ASSET_GSCOBJ); |
| 1203 | + |
| 1204 | + |
| 1205 | + LOG_INFO("Pool: {:x}, count: {}/{}, len 0x{:x}", pool->m_entries, pool->m_loadedPoolSize, pool->m_poolSize, pool->m_elementSize); |
| 1206 | + auto objs = ps4.ReadArray<bo6::GscObjEntry>(pool->m_entries, pool->m_loadedPoolSize); |
| 1207 | + |
| 1208 | + size_t i; |
| 1209 | + for (i = 0; i < pool->m_loadedPoolSize; i++) { |
| 1210 | + auto& obj = objs[i]; |
| 1211 | + |
| 1212 | + if (obj.name != name) { |
| 1213 | + continue; |
| 1214 | + } |
| 1215 | + |
| 1216 | + if (!obj.buffer) { |
| 1217 | + throw std::runtime_error("Empty buffer"); |
| 1218 | + } |
| 1219 | + |
| 1220 | + if (obj.len < file.size()) { |
| 1221 | + throw std::runtime_error(utils::va("Buffer too small, can't remplace %llu < %llu", (size_t)obj.len, file.size())); |
| 1222 | + } |
| 1223 | + |
| 1224 | + auto scriptTarget = ps4.ReadObject<tool::gsc::GscObj24>(obj.buffer); |
| 1225 | + |
| 1226 | + if (scriptTarget->checksum != script->checksum) { |
| 1227 | + notif = ("Find target script, but the checksum doesn't match"); |
| 1228 | + return TRUE; |
| 1229 | + } |
| 1230 | + |
| 1231 | + ps4.Write(obj.buffer, file.data(), file.size()); |
| 1232 | + |
| 1233 | + notif = ("Script injected"); |
| 1234 | + ps4.Notify("Script injected"); |
| 1235 | + } |
| 1236 | + if (i == pool->m_loadedPoolSize) { |
| 1237 | + notif = ("Can't find hook script"); |
| 1238 | + } |
| 1239 | + } |
| 1240 | + catch (std::exception& e) { |
| 1241 | + notif = (std::format("Exception: {}", e.what())); |
| 1242 | + } |
| 1243 | + } |
| 1244 | + else { |
| 1245 | + notif = (std::format("PS4 injector not implemented for VM: {}", nfo->name)); |
| 1246 | + } |
| 1247 | + } |
| 1248 | + catch (std::exception& e) { |
| 1249 | + notif = std::format("Exception: {}", e.what()); |
| 1250 | + } |
| 1251 | + } |
| 1252 | + |
| 1253 | + if (!notif.empty()) { |
| 1254 | + ImGui::Separator(); |
| 1255 | + |
| 1256 | + ImGui::Text("%s", notif.data()); |
| 1257 | + } |
| 1258 | + |
| 1259 | + return c; |
| 1260 | + } |
1075 | 1261 |
|
1076 | 1262 | ADD_TOOL("ps4cbufbo6", "dev", " [ip:port] [cmd]", "", nullptr, ps4cbufbo6);
|
1077 | 1263 | ADD_TOOL("ps4dumpbo6", "dev", " [ip:port]", "", nullptr, ps4dumpbo6);
|
1078 | 1264 | ADD_TOOL("ps4repbo6", "dev", " [ip:port] [file]", "", nullptr, ps4repbo6);
|
1079 | 1265 | ADD_TOOL_UI("bo6_tools", L"BO6 PS4", Render, Update, Resize);
|
| 1266 | + ADD_TOOL_NUI("bo6_tools", "BO6 PS4", bo6_tools); |
1080 | 1267 | }
|
0 commit comments