From 3bea6f1729855cf2fc77036b26aa0663153dbb5f Mon Sep 17 00:00:00 2001 From: Ludovic Marechal Date: Fri, 31 Mar 2023 21:18:22 +0200 Subject: [PATCH] Add a Logger in file and some try/catch --- archipelago-client/ArchipelagoInterface.cpp | 43 ++++---- archipelago-client/Core.cpp | 106 ++++++++++++++------ archipelago-client/Core.h | 1 + archipelago-client/GameHook.cpp | 72 ++++++++++--- 4 files changed, 156 insertions(+), 66 deletions(-) diff --git a/archipelago-client/ArchipelagoInterface.cpp b/archipelago-client/ArchipelagoInterface.cpp index 70fc20f..0bb2c28 100644 --- a/archipelago-client/ArchipelagoInterface.cpp +++ b/archipelago-client/ArchipelagoInterface.cpp @@ -17,6 +17,8 @@ APClient* ap; BOOL CArchipelago::Initialise(std::string URI) { + Core->Logger("CArchipelago::Initialise", true, false); + // read or generate uuid, required by AP std::string uuid = ap_get_uuid(UUID_FILE); if (ap != nullptr) { @@ -31,9 +33,7 @@ BOOL CArchipelago::Initialise(std::string URI) { ap->set_socket_disconnected_handler([]() { }); ap->set_slot_connected_handler([](const json& data) { - - printf("Slot connected successfully, reading slot data ... \n"); - + Core->Logger("Slot connected successfully, reading slot data ... "); // read the archipelago slot data //Mandatory values @@ -69,14 +69,12 @@ BOOL CArchipelago::Initialise(std::string URI) { }); ap->set_slot_disconnected_handler([]() { - printf("Slot disconnected\n"); + Core->Logger("Slot disconnected"); }); ap->set_slot_refused_handler([](const std::list& errors){ - printf("\n"); for (const auto& error : errors) { - printf("Connection refused : %s\n", error.c_str()); + Core->Logger("Connection refused : " + error); } - printf("\n"); }); ap->set_room_info_handler([]() { @@ -110,7 +108,7 @@ BOOL CArchipelago::Initialise(std::string URI) { //Add the item to the list of already received items, only for logging purpose Core->pReceivedItems.push_back(itemDesc); - printf(itemDesc.c_str()); + Core->Logger(itemDesc); //Determine the item address DWORD address = 0; @@ -121,8 +119,8 @@ BOOL CArchipelago::Initialise(std::string URI) { } } if (address == 0) { - std::cout << "The " << itemname.c_str() << " has not been found in the item pool. Please check your seed options" << "\n"; - return; + Core->Logger("The following item has not been found in the item pool. Please check your seed options : " + itemname); + continue; } ItemRandomiser->receivedItemsQueue.push_front((DWORD)address); @@ -134,15 +132,16 @@ BOOL CArchipelago::Initialise(std::string URI) { }); ap->set_print_handler([](const std::string& msg) { - printf("%s\n", msg.c_str()); + Core->Logger(msg); }); ap->set_print_json_handler([](const std::list& msg) { - printf("%s\n", ap->render_json(msg, APClient::RenderFormat::TEXT).c_str()); + Core->Logger(ap->render_json(msg, APClient::RenderFormat::TEXT)); }); ap->set_bounced_handler([](const json& cmd) { if (GameHook->dIsDeathLink) { + Core->Logger("Received DeathLink", true, false); auto tagsIt = cmd.find("tags"); auto dataIt = cmd.find("data"); if (tagsIt != cmd.end() && tagsIt->is_array() @@ -151,14 +150,16 @@ BOOL CArchipelago::Initialise(std::string URI) { if (dataIt != cmd.end() && dataIt->is_object()) { json data = *dataIt; if (data["source"].get() != Core->pSlotName) { - printf("Died by the hands of %s: %s\n", - data["source"].is_string() ? data["source"].get().c_str() : "???", - data["cause"].is_string() ? data["cause"].get().c_str() : "???"); + + std::string source = data["source"].is_string() ? data["source"].get().c_str() : "???"; + std::string cause = data["cause"].is_string() ? data["cause"].get().c_str() : "???"; + Core->Logger("Died by the hands of " + source + " : " + cause); + GameHook->deathLinkData = true; } } else { - printf("Bad deathlink packet!\n"); + Core->Logger("Bad deathlink packet!", true, false); } } } @@ -179,12 +180,18 @@ BOOLEAN CArchipelago::isConnected() { } VOID CArchipelago::update() { + if (ap) ap->poll(); - if (ap && !ItemRandomiser->checkedLocationsList.empty()) { + int size = ItemRandomiser->checkedLocationsList.size(); + if (ap && size > 0) { if (ap->LocationChecks(ItemRandomiser->checkedLocationsList)) { + Core->Logger(size + " checks sent successfully", true, false); ItemRandomiser->checkedLocationsList.clear(); } + else { + Core->Logger(size + " checks has not been sent and will be kept in queue"); + } } } @@ -195,7 +202,7 @@ VOID CArchipelago::gameFinished() { VOID CArchipelago::sendDeathLink() { if (!ap || !GameHook->dIsDeathLink) return; - printf("Sending deathlink...\n"); + Core->Logger("Sending deathlink"); json data{ {"time", ap->get_server_time()}, diff --git a/archipelago-client/Core.cpp b/archipelago-client/Core.cpp index bc16e42..1d1707c 100644 --- a/archipelago-client/Core.cpp +++ b/archipelago-client/Core.cpp @@ -45,9 +45,11 @@ BOOL CCore::Initialise() { SetConsoleTitleA("Dark Souls III - Archipelago Console"); freopen_s(&fp, "CONOUT$", "w", stdout); freopen_s(&fp, "CONIN$", "r", stdin); - printf_s("Archipelago client v%s \n", VERSION); - printf_s("A new version may or may not be available, please check this link for updates : %s \n\n\n", "https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/releases"); - printf_s("Type '/connect {SERVER_IP}:{SERVER_PORT} {SLOT_NAME} [password:{PASSWORD}]' to connect to the room\n\n"); + Core->Logger(std::string("Archipelago client v") + VERSION); + Core->Logger("A new version may or may not be available, please check this link for updates : https://github.com/Marechal-L/Dark-Souls-III-Archipelago-client/releases", false); + Core->Logger("Type '/connect {SERVER_IP}:{SERVER_PORT} {SLOT_NAME} [password:{PASSWORD}]' to connect to the room", false); + Core->Logger("Type '/help for more information", false); + Core->Logger("-----------------------------------------------------", false); if (!GameHook->preInitialize()) { Core->Panic("Check if the game version is 1.15 and not 1.15.1, you must use the provided DarkSoulsIII.exe", "Cannot hook the game", FE_InitFailed, 1); @@ -55,7 +57,7 @@ BOOL CCore::Initialise() { } if (CheckOldApFile()) { - printf_s("The AP.json file is not supported in this version, make sure to finish your previous seed on version 1.2 or use this version on the new Archipelago server\n\n"); + Core->Logger("The AP.json file is not supported in this version, make sure to finish your previous seed on version 1.2 or use this version on the new Archipelago server"); } //Start command prompt @@ -125,7 +127,7 @@ VOID CCore::Run() { */ VOID CCore::CleanReceivedItemsList() { if (!ItemRandomiser->receivedItemsQueue.empty()) { - + Core->Logger("Removing " + pLastReceivedIndex + std::string(" items according to the last_received_index"), true, false); for (int i = 0; i < pLastReceivedIndex; i++) { ItemRandomiser->receivedItemsQueue.pop_back(); } @@ -141,7 +143,7 @@ VOID CCore::Panic(const char* pMessage, const char* pSort, DWORD dError, DWORD d sprintf_s(pOutput, "\n%s -> %s (%i)\n", pSort, pMessage, dError); - printf("%s", pOutput); + Core->Logger(pOutput); if (dIsFatalError) { sprintf_s(pTitle, "[Archipelago client - Fatal Error]"); @@ -194,7 +196,7 @@ VOID CCore::InputCommand() { std::string param = line.substr(9); int spaceIndex = param.find(" "); if (spaceIndex == std::string::npos) { - printf("Missing parameter : Make sure to type '/connect {SERVER_IP}:{SERVER_PORT} {SLOT_NAME} [password:{PASSWORD}]' \n"); + Core->Logger("Missing parameter : Make sure to type '/connect {SERVER_IP}:{SERVER_PORT} {SLOT_NAME} [password:{PASSWORD}]'"); } else { int passwordIndex = param.find("password:"); std::string address = param.substr(0, spaceIndex); @@ -213,8 +215,7 @@ VOID CCore::InputCommand() { } } } - - if (line.find("!") == 0) { + else if (line.find("!") == 0) { ArchipelagoInterface->say(line); } } @@ -231,25 +232,29 @@ VOID CCore::ReadConfigFiles() { //Check outside the folder std::ifstream gameFile(filename); if (!gameFile.good()) { - //Missing session file, that's probably a new game + Logger("No save found, starting a new game", true, false); return; } } -#if DEBUG - printf("Reading %s ... \n", filename.c_str()); -#endif - //Read the game file + Logger("Reading " + outputFolder + "/" + filename, true, false); json k; - gameFile >> k; - k.at("last_received_index").get_to(pLastReceivedIndex); - std::map::iterator it; - for (it = ItemRandomiser->progressiveLocations.begin(); it != ItemRandomiser->progressiveLocations.end(); it++) { - char buf[10]; - sprintf(buf, "0x%x", it->first); - k.at("progressive_locations").at(buf).get_to(ItemRandomiser->progressiveLocations[it->first]); + + try { + gameFile >> k; + k.at("last_received_index").get_to(pLastReceivedIndex); + std::map::iterator it; + for (it = ItemRandomiser->progressiveLocations.begin(); it != ItemRandomiser->progressiveLocations.end(); it++) { + char buf[10]; + sprintf(buf, "0x%x", it->first); + k.at("progressive_locations").at(buf).get_to(ItemRandomiser->progressiveLocations[it->first]); + } + } catch (const std::exception&) { + Logger("Failed reading " + outputFolder + "/" + filename, true, false); + gameFile.close(); + throw; } gameFile.close(); @@ -261,15 +266,11 @@ VOID CCore::SaveConfigFiles() { return; saveConfigFiles = false; - std::string outputFolder = "archipelago"; std::string filename = Core->pSeed + "_" + Core->pSlotName + ".json"; - -#if DEBUG - printf("Writing %s ... \n", filename.c_str()); -#endif + Logger("Writing to " + outputFolder + "/" + filename, true, false); json j; j["last_received_index"] = pLastReceivedIndex; @@ -281,14 +282,53 @@ VOID CCore::SaveConfigFiles() { j["progressive_locations"][buf] = it->second; } - - if (CreateDirectory(outputFolder.c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError()) { - std::ofstream outfile(outputFolder + "\\" + filename); - outfile << std::setw(4) << j << std::endl; + try { + if (CreateDirectory(outputFolder.c_str(), NULL) || ERROR_ALREADY_EXISTS == GetLastError()) { + std::ofstream outfile(outputFolder + "\\" + filename); + outfile << std::setw(4) << j << std::endl; + outfile.close(); + } + else { + std::ofstream outfile(filename); + outfile << std::setw(4) << j << std::endl; + outfile.close(); + } } - else { - std::ofstream outfile(filename); - outfile << std::setw(4) << j << std::endl; + catch (const std::exception&) { + Logger("Failed writing " + outputFolder + "/" + filename, true, true); + } +} + + +inline std::string getCurrentDateTime(std::string s) { + time_t now = time(0); + struct tm tstruct; + char buf[80]; + tstruct = *localtime(&now); + if (s == "now") + strftime(buf, sizeof(buf), "%Y-%m-%d %X", &tstruct); + else if (s == "date") + strftime(buf, sizeof(buf), "%Y-%m-%d", &tstruct); + return std::string(buf); +}; + +VOID CCore::Logger(std::string logMessage, BOOL inFile, BOOL inConsole) { + + if(inConsole) + std::cout << logMessage << std::endl; + + if (inFile) { + try { + std::string outputFolder = "archipelago"; + std::string filename = "log_" + getCurrentDateTime("date") + ".txt"; + std::ofstream logFile(outputFolder + "\\" + filename, std::ios_base::out | std::ios_base::app); + + std::string now = getCurrentDateTime("now"); + logFile << now << '\t' << logMessage << '\n'; + logFile.close(); + } catch (const std::exception&) { + //Logging is optional and should not crash the mod + } } } diff --git a/archipelago-client/Core.h b/archipelago-client/Core.h index 839228d..b1bbf9b 100644 --- a/archipelago-client/Core.h +++ b/archipelago-client/Core.h @@ -52,6 +52,7 @@ class CCore { virtual VOID SaveConfigFiles(); virtual VOID CleanReceivedItemsList(); virtual BOOL CheckOldApFile(); + virtual VOID Logger(std::string logMessage, BOOL inFile = true, BOOL inConsole = true); std::string pSlotName; std::string pPassword; diff --git a/archipelago-client/GameHook.cpp b/archipelago-client/GameHook.cpp index 2542b30..a344831 100644 --- a/archipelago-client/GameHook.cpp +++ b/archipelago-client/GameHook.cpp @@ -17,24 +17,53 @@ extern CCore* Core; * Check if a basic hook is working on this version of the game */ BOOL CGameHook::preInitialize() { - if (MH_Initialize() != MH_OK) return false; - return Hook(0x1407BBA80, (DWORD64)&tItemRandomiser, &rItemRandomiser, 5); + Core->Logger("CGameHook::preInitialize", true, false); + + try { + if (MH_Initialize() != MH_OK) return false; + } catch (const std::exception&) { + Core->Logger("Cannot initialize MinHook"); + return false; + } + + try { + return Hook(0x1407BBA80, (DWORD64)&tItemRandomiser, &rItemRandomiser, 5); + } catch (const std::exception&) { + Core->Logger("Cannot hook the game 0x1407BBA80"); + } + return false; } BOOL CGameHook::initialize() { + Core->Logger("CGameHook::initialize", true, false); BOOL bReturn = true; //Inject ItemGibData - itemGibDataCodeCave = InjectShellCode(nullptr, ItemGibDataShellcode, 17); + try { + itemGibDataCodeCave = InjectShellCode(nullptr, ItemGibDataShellcode, 17); + } catch (const std::exception&) { + Core->Logger("Cannot inject ItemGibData"); + return false; + } //Modify ItemGibShellcode - bReturn &= replaceShellCodeAddress(ItemGibShellcode, 15, itemGibDataCodeCave, 0, sizeof(void*)); - bReturn &= replaceShellCodeAddress(ItemGibShellcode, 26, itemGibDataCodeCave, 4, 4); - bReturn &= replaceShellCodeAddress(ItemGibShellcode, 33, itemGibDataCodeCave, 8, 4); + try { + bReturn &= replaceShellCodeAddress(ItemGibShellcode, 15, itemGibDataCodeCave, 0, sizeof(void*)); + bReturn &= replaceShellCodeAddress(ItemGibShellcode, 26, itemGibDataCodeCave, 4, 4); + bReturn &= replaceShellCodeAddress(ItemGibShellcode, 33, itemGibDataCodeCave, 8, 4); + } catch (const std::exception&) { + Core->Logger("Cannot modify ItemGibShellcode"); + return false; + } //Inject ItemGibShellcode - LPVOID itemGibCodeCave = InjectShellCode((LPVOID)0x13ffe0000, ItemGibShellcode, 93); + try { + LPVOID itemGibCodeCave = InjectShellCode((LPVOID)0x13ffe0000, ItemGibShellcode, 93); + } catch (const std::exception&) { + Core->Logger("Cannot inject ItemGibShellcode"); + return false; + } if (dIsAutoEquip) { bReturn &= Hook(0x1407BBE92, (DWORD64)&tAutoEquip, &rAutoEquip, 6); } if (dIsNoWeaponRequirements) { bReturn &= Hook(0x140C073B9, (DWORD64)&tNoWeaponRequirements, &rNoWeaponRequirements, 7); } @@ -59,6 +88,7 @@ VOID CGameHook::manageDeathLink() { killThePlayer(); } else if(lastHealthPoint != 0 && healthPoint == 0) { //The player just died, ignore the deathLink if received if (deathLinkData) { + Core->Logger("The player just died, a death link has been ignored", true, false); deathLinkData = false; return; } @@ -67,6 +97,7 @@ VOID CGameHook::manageDeathLink() { } VOID CGameHook::killThePlayer() { + Core->Logger("Kill the player", true, false); DWORD processId = GetCurrentProcessId(); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, processId); std::vector hpOffsets = { 0x80, 0x1F90, 0x18, 0xD8 }; @@ -77,6 +108,7 @@ VOID CGameHook::killThePlayer() { } BOOL CGameHook::updateRuntimeValues() { + DWORD processId = GetCurrentProcessId(); HANDLE hProcess = OpenProcess(PROCESS_ALL_ACCESS, NULL, processId); @@ -109,7 +141,9 @@ BOOL CGameHook::updateRuntimeValues() { VOID CGameHook::giveItems() { //Send the next item in the list - if (!ItemRandomiser->receivedItemsQueue.empty()) { + int size = ItemRandomiser->receivedItemsQueue.size(); + if (size > 0) { + Core->Logger("Send an item from the list of " + size + std::string(" items"), true, false); itemGib(ItemRandomiser->receivedItemsQueue.back()); } } @@ -129,14 +163,22 @@ VOID CGameHook::itemGib(DWORD itemId) { char* littleEndianItemId = (char*)malloc(sizeof(DWORD)); ConvertToLittleEndianByteArray((uintptr_t)itemId, littleEndianItemId); - DWORD memory = 0; - ReadProcessMemory(hProcess, (BYTE*)gibItem, &memory, sizeof(memory), nullptr); - DWORD newMemory = itemId; - WriteProcessMemory(hProcess, (BYTE*)gibItem, &newMemory, sizeof(newMemory), nullptr); + try { + DWORD memory = 0; + ReadProcessMemory(hProcess, (BYTE*)gibItem, &memory, sizeof(memory), nullptr); + DWORD newMemory = itemId; + WriteProcessMemory(hProcess, (BYTE*)gibItem, &newMemory, sizeof(newMemory), nullptr); + } catch (const std::exception&) { + Core->Logger("Cannot write the item to the memory"); + } - typedef int func(void); - func* f = (func*)0x13ffe0000; - CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f, NULL, NULL, NULL); + try { + typedef int func(void); + func* f = (func*)0x13ffe0000; + CreateThread(NULL, NULL, (LPTHREAD_START_ROUTINE)f, NULL, NULL, NULL); + } catch (const std::exception&) { + Core->Logger("Cannot start the 0x13ffe0000 thread"); + } } BOOL CGameHook::Hook(DWORD64 qAddress, DWORD64 qDetour, DWORD64* pReturn, DWORD dByteLen) {