From d59553ccba9245963bba12c9d76ef457780d91b0 Mon Sep 17 00:00:00 2001 From: Nikhil Narayana Date: Fri, 3 Dec 2021 17:22:47 -0800 Subject: [PATCH] project-slippi/Ishiiruka/pull/247 removed some unnecessary logic we used to overwrite where the codehandler looks for the gecko codes... but we don't run the codehandler anymore past initializing the bootloader. Plus given the codehandler was different than in Ishii, this overwrite location was totally wrong anyway. Co-authored-by: Jas Laferriere --- Data/Sys/bootloader.gct | Bin 0 -> 1312 bytes Source/Core/Common/CommonPaths.h | 1 + Source/Core/Core/Boot/Boot.cpp | 2 + Source/Core/Core/ConfigManager.cpp | 28 ++- Source/Core/Core/ConfigManager.h | 15 ++ Source/Core/Core/GeckoCode.cpp | 187 ++++++++++++++----- Source/Core/Core/GeckoCode.h | 3 + Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp | 34 ++++ Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h | 14 +- Source/Core/Core/HW/SystemTimers.cpp | 8 +- Source/Core/DiscIO/Volume.cpp | 20 ++ Source/Core/DiscIO/Volume.h | 2 + 12 files changed, 254 insertions(+), 60 deletions(-) create mode 100644 Data/Sys/bootloader.gct diff --git a/Data/Sys/bootloader.gct b/Data/Sys/bootloader.gct new file mode 100644 index 0000000000000000000000000000000000000000..a46c729ff0ee287b10afa17311448a3260dce6e6 GIT binary patch literal 1312 zcmZR$aNr&YvzR1{crY+T95N4XU|?YIv2AEDN$hcXP}|A8%&&n(v4(?b*#t%gmMMz= z1NJpCa9GS|aHxO$y8@!l;RRe>m2^eD^W_SQ1_lL)oZSbwT%~k{MFPX+8I=qS^~$F~ zY8WjRFfiISG%zIggu>N9-DJ3IQR>jJr~jJs2h= zGFVtQGFUX!F|f!aGjP;2e-g2%WnigEk$|hQNdEt4LJ9**{o>aZApg~;-UfveLt>BN z7nqyDaw%YOqr{&60GRl$Mh1=sMusgGiVPcSI6&!!K>=N?`RxG-1_lO(LkyuG7#J9I zF#KC5T~RYvxuRyjHppI+#Gc|5nE5p|(iJ-L;BZo~NMK;7k&{?sk<6e_!~BB5vEl!h z8U~g%J`9XH76}ZD77gIEW0KgDasZ|uBo0Y8xedr-Ahi|?7+4{4X&uON^+!He)GvKq zQUC0Bg~bDynQ;loY9RXJeK5t+w_u9pm0*gco`H#h{O8cb0QH|VR{zyE!_sbX4@@6= zIQum)C_uyc4+8@OM*gj-m9D6%lCA)yFY5*dhK5=OhLB`88|wrH4r>JlP#Qbn*u>zX z!^{9mTRzY<7XeLkAezImi6H}`Pti_?nStM-i9w*DmVrfw85~D79t;dVAU-IqSto boot) PatchEngine::LoadPatches(); HLE::PatchFixedFunctions(); + Gecko::RunCodeHandler(); return true; } diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 0cebb583bd..592c6a6ad0 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -51,6 +51,7 @@ #include "VideoCommon/HiresTextures.h" #include "DiscIO/Enums.h" +#include "DiscIO/Filesystem.h" #include "DiscIO/Volume.h" #include "DiscIO/VolumeWad.h" @@ -959,7 +960,32 @@ bool SConfig::SetPathsAndGameMetadata(const BootParameters& boot) const std::string region_dir = GetDirectoryForRegion(ToGameCubeRegion(m_region)); m_strSRAM = File::GetUserPath(F_GCSRAM_IDX); m_strBootROM = GetBootROMPath(region_dir); - m_strIsoPath = (boot.parameters.index() == 0) ? std::get(boot.parameters).path : ""; + m_strIsoPath = + (boot.parameters.index() == 0) ? std::get(boot.parameters).path : ""; + + std::shared_ptr volume = DiscIO::CreateVolume(m_strIsoPath); + + if (m_game_id == "GALE01" || m_game_id == "GALJ01") + { + m_melee_version = Melee::Version::NTSC; + + if (volume->GetLongNames()[DiscIO::Language::English].find("20XX") != std::string::npos) + m_melee_version = Melee::Version::TwentyXX; + else + { + // check for m-ex based build + if (volume->FileExists("MxDt.dat")) + { + m_melee_version = Melee::Version::MEX; + } + } + } + else if (m_game_id == "GTME01") + { + m_melee_version = Melee::Version::UPTM; + } + + INFO_LOG_FMT(BOOT, "Melee Version: {}", m_melee_version); return true; } diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 4c031c805d..5d7d5a2b88 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -4,6 +4,7 @@ #pragma once +#include #include #include #include @@ -45,6 +46,18 @@ namespace SerialInterface enum SIDevices : int; } // namespace SerialInterface +namespace Melee +{ +enum class Version +{ + NTSC, + TwentyXX, + UPTM, + MEX, + OTHER, +}; +} + struct BootParameters; // DSP Backend Types @@ -67,6 +80,8 @@ enum class GPUDeterminismMode struct SConfig { + // Melee Version + Melee::Version m_melee_version; // Wii Devices bool m_WiiSDCard; bool m_WiiKeyboard; diff --git a/Source/Core/Core/GeckoCode.cpp b/Source/Core/Core/GeckoCode.cpp index 3296af2daa..8412137694 100644 --- a/Source/Core/Core/GeckoCode.cpp +++ b/Source/Core/Core/GeckoCode.cpp @@ -19,6 +19,8 @@ #include "Core/PowerPC/MMU.h" #include "Core/PowerPC/PowerPC.h" +#include "VideoCommon/OnScreenDisplay.h" + namespace Gecko { static constexpr u32 CODE_SIZE = 8; @@ -68,6 +70,8 @@ void SetActiveCodes(const std::vector& gcodes) { std::lock_guard lk(s_active_codes_lock); + DEBUG_LOG_FMT(ACTIONREPLAY, "Setting up active codes..."); + s_active_codes.clear(); if (SConfig::GetInstance().bEnableCheats) { @@ -153,69 +157,84 @@ static Installation InstallCodeHandlerLocked() } } - u32 codelist_base_address; - u32 codelist_end_address; - // Let the Gecko codehandler use free space from Melee's tournament mode region - if (SConfig::GetInstance().GetGameID() == "GALE01") + u32 codelist_base_address = INSTALLER_BASE_ADDRESS + static_cast(data.length()) - CODE_SIZE; + u32 codelist_end_address = INSTALLER_END_ADDRESS; + + // Write a magic value to 'gameid' (codehandleronly does not actually read this). + PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS); + + // Install the custom bootloader to write gecko codes to the heaps + if (SConfig::GetInstance().m_melee_version == Melee::Version::NTSC || + SConfig::GetInstance().m_melee_version == Melee::Version::MEX) { - INFO_LOG(ACTIONREPLAY, - "Detected GALE01 - using tournament mode region for Gecko codelist"); + // Write GCT loader into memory which will eventually load the real GCT into the heap + std::string bootloaderData; + std::string _bootloaderFilename = File::GetSysDirectory() + GCT_BOOTLOADER; + if (!File::ReadFileToString(_bootloaderFilename, bootloaderData)) + { + OSD::AddMessage("bootloader.gct not found in Sys folder.", 30000, 0xFFFF0000); + ERROR_LOG_FMT(ACTIONREPLAY, "Could not enable cheats because bootloader.gct was missing."); + return Installation::Failed; + } - // Set codelist base to the tournament mode region - codelist_base_address = 0x801910E0; - codelist_end_address = 0x8019AF4C; + if (bootloaderData.length() > codelist_end_address - codelist_base_address) + { + OSD::AddMessage("Gecko bootloader too large.", 30000, 0xFFFF0000); + ERROR_LOG_FMT(SLIPPI, "Gecko bootloader too large"); + return Installation::Failed; + } - // Patch codehandler to use tournament mode region - PowerPC::HostWrite_U32(0x3DE08019, 0x80001904); // lis r15, 0x8019 - PowerPC::HostWrite_U32(0x61EF10E0, 0x80001908); // ori r15, r15, 0x10e0 + // Install bootloader gct + for (size_t i = 0; i < bootloaderData.length(); ++i) + PowerPC::HostWrite_U8(bootloaderData[i], static_cast(codelist_base_address + i)); } else { - codelist_base_address = INSTALLER_BASE_ADDRESS + static_cast(data.size()) - CODE_SIZE; - codelist_end_address = INSTALLER_END_ADDRESS; - } + // Create GCT in memory + PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address); + PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4); - // Write a magic value to 'gameid' (codehandleronly does not actually read this). - // This value will be read back and modified over time by HLE_Misc::GeckoCodeHandlerICacheFlush. - PowerPC::HostWrite_U32(MAGIC_GAMEID, INSTALLER_BASE_ADDRESS); + // Each code is 8 bytes (2 words) wide. There is a starter code and an end code. + const u32 start_address = codelist_base_address + CODE_SIZE; + const u32 end_address = codelist_end_address - CODE_SIZE; + u32 next_address = start_address; - // Create GCT in memory - PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address); - PowerPC::HostWrite_U32(0x00d0c0de, codelist_base_address + 4); - - // Each code is 8 bytes (2 words) wide. There is a starter code and an end code. - const u32 start_address = codelist_base_address + CODE_SIZE; - const u32 end_address = codelist_end_address - CODE_SIZE; - u32 next_address = start_address; - - // NOTE: Only active codes are in the list - for (const GeckoCode& active_code : s_active_codes) - { - // If the code is not going to fit in the space we have left then we have to skip it - if (next_address + active_code.codes.size() * CODE_SIZE > end_address) + // NOTE: Only active codes are in the list + for (const GeckoCode& active_code : s_active_codes) { - NOTICE_LOG_FMT(ACTIONREPLAY, - "Too many GeckoCodes! Ran out of storage space in Game RAM. Could " - "not write: \"{}\". Need {} bytes, only {} remain.", - active_code.name, active_code.codes.size() * CODE_SIZE, - end_address - next_address); - continue; + // If the code is not going to fit in the space we have left then we have to skip it + if (next_address + active_code.codes.size() * CODE_SIZE > end_address) + { + OSD::AddMessage( + fmt::format("Ran out of memory applying gecko codes. Too many codes enabled. " + "Need {} bytes, only {} remain.", + active_code.codes.size() * CODE_SIZE, end_address - next_address), + 30000, 0xFFFF0000); + NOTICE_LOG_FMT(ACTIONREPLAY, + "Too many GeckoCodes! Ran out of storage space in Game RAM. Could " + "not write: \"{}\". Need {} bytes, only {} remain.", + active_code.name, active_code.codes.size() * CODE_SIZE, + end_address - next_address); + continue; + } + + for (const GeckoCode::Code& code : active_code.codes) + { + PowerPC::HostWrite_U32(code.address, next_address); + PowerPC::HostWrite_U32(code.data, next_address + 4); + next_address += CODE_SIZE; + } } - for (const GeckoCode::Code& code : active_code.codes) - { - PowerPC::HostWrite_U32(code.address, next_address); - PowerPC::HostWrite_U32(code.data, next_address + 4); - next_address += CODE_SIZE; - } + WARN_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Using {} of {} bytes", next_address - start_address, + end_address - start_address); + + // Stop code. Tells the handler that this is the end of the list. + PowerPC::HostWrite_U32(0xF0000000, next_address); + PowerPC::HostWrite_U32(0x00000000, next_address + 4); } - WARN_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Using {} of {} bytes", next_address - start_address, - end_address - start_address); - - // Stop code. Tells the handler that this is the end of the list. - PowerPC::HostWrite_U32(0xF0000000, next_address); - PowerPC::HostWrite_U32(0x00000000, next_address + 4); + // Write 0 to trampoline address, not sure why this is necessary PowerPC::HostWrite_U32(0, HLE_TRAMPOLINE_ADDRESS); // Turn on codes @@ -226,6 +245,7 @@ static Installation InstallCodeHandlerLocked() { PowerPC::ppcState.iCache.Invalidate(INSTALLER_BASE_ADDRESS + j); } + return Installation::Installed; } @@ -296,10 +316,75 @@ void RunCodeHandler() } DEBUG_LOG_FMT(ACTIONREPLAY, "GeckoCodes: Initiating phantom branch-and-link. " - "PC = {:#010x}, SP = {:#010x}, SFP = {:#010x}", + "PC = {:#010x}, SP = {:#010x}, SFP = {:#010x}\n", PC, SP, SFP); LR = HLE_TRAMPOLINE_ADDRESS; PC = NPC = ENTRY_POINT; } +u32 GetGctLength() +{ + std::lock_guard lk(s_active_codes_lock); + + int i = 0; + + for (const GeckoCode& active_code : s_active_codes) + { + if (active_code.enabled) + { + i += 8 * static_cast(active_code.codes.size()); + } + } + + return i + 0x10; // 0x10 is the fixed size of the header and terminator +} + +std::vector uint32ToVector(u32 num) +{ + u8 byte0 = num >> 24; + u8 byte1 = (num & 0xFF0000) >> 16; + u8 byte2 = (num & 0xFF00) >> 8; + u8 byte3 = num & 0xFF; + + return std::vector({byte0, byte1, byte2, byte3}); +} + +void appendWordToBuffer(std::vector* buf, u32 word) +{ + auto wordVector = uint32ToVector(word); + buf->insert(buf->end(), wordVector.begin(), wordVector.end()); +} + +std::vector GenerateGct() +{ + std::vector res; + + // Write header + appendWordToBuffer(&res, 0x00d0c0de); + appendWordToBuffer(&res, 0x00d0c0de); + + std::lock_guard lk(s_active_codes_lock); + + int i = 0; + + // Write codes + for (const GeckoCode& active_code : s_active_codes) + { + if (active_code.enabled) + { + for (const GeckoCode::Code& code : active_code.codes) + { + appendWordToBuffer(&res, code.address); + appendWordToBuffer(&res, code.data); + } + } + } + + // Write footer + appendWordToBuffer(&res, 0xff000000); + appendWordToBuffer(&res, 0x00000000); + + return res; +} + } // namespace Gecko diff --git a/Source/Core/Core/GeckoCode.h b/Source/Core/Core/GeckoCode.h index 9c6c7c7a14..9f80ba8951 100644 --- a/Source/Core/Core/GeckoCode.h +++ b/Source/Core/Core/GeckoCode.h @@ -66,4 +66,7 @@ void RunCodeHandler(); void Shutdown(); void DoState(PointerWrap&); +u32 GetGctLength(); +std::vector GenerateGct(); + } // namespace Gecko diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp index e4f91a2e0b..7f955a11db 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.cpp @@ -20,11 +20,13 @@ #include "Core/Core.h" #include "Core/CoreTiming.h" #include "Core/Debugger/Debugger_SymbolMap.h" +#include "Core/GeckoCode.h" #include "Core/HW/EXI/EXI_DeviceSlippi.h" #include "Core/HW/Memmap.h" #include "Core/HW/SystemTimers.h" #include "Core/Host.h" #include "Core/NetPlayClient.h" +#include "Core/PowerPC/PowerPC.h" #include "Core/Slippi/SlippiPlayback.h" #include "Core/Slippi/SlippiReplayComm.h" #include "Core/State.h" @@ -2316,6 +2318,32 @@ void CEXISlippi::prepareFileLoad(u8* payload) m_read_queue.insert(m_read_queue.end(), buf.begin(), buf.end()); } +void CEXISlippi::prepareGctLength() +{ + m_read_queue.clear(); + + u32 size = Gecko::GetGctLength(); + + INFO_LOG(SLIPPI, "Getting gct size: %d", size); + + // Write size to output + appendWordToBuffer(&m_read_queue, size); +} + +void CEXISlippi::prepareGctLoad(u8* payload) +{ + m_read_queue.clear(); + + auto gct = Gecko::GenerateGct(); + + // This is the address where the codes will be written to + auto address = Common::swap32(&payload[0]); + + INFO_LOG(SLIPPI, "Preparing to write gecko codes at: 0x%X", address); + + m_read_queue.insert(m_read_queue.end(), gct.begin(), gct.end()); +} + void CEXISlippi::handleChatMessage(u8* payload) { int messageId = payload[0]; @@ -2618,6 +2646,12 @@ void CEXISlippi::DMAWrite(u32 _uAddr, u32 _uSize) case CMD_REPORT_GAME: handleReportGame(&memPtr[bufLoc + 1]); break; + case CMD_GCT_LENGTH: + prepareGctLength(); + break; + case CMD_GCT_LOAD: + prepareGctLoad(&memPtr[bufLoc + 1]); + break; default: writeToFileAsync(&memPtr[bufLoc], payloadLen + 1, ""); SlippiSpectateServer::getInstance().write(&memPtr[bufLoc], payloadLen + 1); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h index c905443ff4..f172579112 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceSlippi.h @@ -10,6 +10,7 @@ #include "Common/File.h" #include "Common/FileUtil.h" #include "Core/Slippi/SlippiGameFileLoader.h" +#include "Core/Slippi/SlippiGameReporter.h" #include "Core/Slippi/SlippiMatchmaking.h" #include "Core/Slippi/SlippiNetplay.h" #include "Core/Slippi/SlippiPlayback.h" @@ -17,7 +18,6 @@ #include "Core/Slippi/SlippiSavestate.h" #include "Core/Slippi/SlippiSpectate.h" #include "Core/Slippi/SlippiUser.h" -#include "Core/Slippi/SlippiGameReporter.h" #include "EXI_Device.h" #define ROLLBACK_MAX_FRAMES 7 @@ -79,6 +79,8 @@ private: CMD_LOG_MESSAGE = 0xD0, CMD_FILE_LENGTH = 0xD1, CMD_FILE_LOAD = 0xD2, + CMD_GCT_LENGTH = 0xD3, + CMD_GCT_LOAD = 0xD4, }; enum @@ -124,6 +126,8 @@ private: {CMD_LOG_MESSAGE, 0xFFFF}, // Variable size... will only work if by itself {CMD_FILE_LENGTH, 0x40}, {CMD_FILE_LOAD, 0x40}, + {CMD_GCT_LENGTH, 0x0}, + {CMD_GCT_LOAD, 0x4}, }; struct WriteMessage @@ -174,7 +178,7 @@ private: void prepareOnlineStatus(); void handleConnectionCleanup(); void prepareNewSeed(); - void handleReportGame(u8 *payload); + void handleReportGame(u8* payload); // replay playback stuff void prepareGameInfo(u8* payload); @@ -185,10 +189,12 @@ private: void prepareIsFileReady(); // misc stuff - void handleChatMessage(u8 *payload); + void handleChatMessage(u8* payload); void logMessageFromGame(u8* payload); void prepareFileLength(u8* payload); void prepareFileLoad(u8* payload); + void prepareGctLength(); + void prepareGctLoad(u8* payload); int getCharColor(u8 charId, u8 teamId); void FileWriteThread(void); @@ -222,7 +228,7 @@ private: std::string forcedError = ""; // Used to determine when to detect when a new session has started - bool is_play_session_active = false; + bool is_play_session_active = false; // Frame skipping variables int framesToSkip = 0; diff --git a/Source/Core/Core/HW/SystemTimers.cpp b/Source/Core/Core/HW/SystemTimers.cpp index c2b05c563c..57309e8921 100644 --- a/Source/Core/Core/HW/SystemTimers.cpp +++ b/Source/Core/Core/HW/SystemTimers.cpp @@ -77,7 +77,7 @@ CoreTiming::EventType* et_AudioDMA; CoreTiming::EventType* et_DSP; CoreTiming::EventType* et_IPC_HLE; // PatchEngine updates every 1/60th of a second by default -CoreTiming::EventType* et_PatchEngine; +// CoreTiming::EventType* et_PatchEngine; CoreTiming::EventType* et_Throttle; u32 s_cpu_core_clock = 486000000u; // 486 mhz (its not 485, stop bugging me!) @@ -163,7 +163,7 @@ void PatchEngineCallback(u64 userdata, s64 cycles_late) cycles_pruned += next_schedule; } - CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned); + // CoreTiming::ScheduleEvent(next_schedule, et_PatchEngine, cycles_pruned); } void ThrottleCallback(u64 last_time, s64 cyclesLate) @@ -326,7 +326,7 @@ void Init() et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback); et_AudioDMA = CoreTiming::RegisterEvent("AudioDMACallback", AudioDMACallback); et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); - et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); + // et_PatchEngine = CoreTiming::RegisterEvent("PatchEngine", PatchEngineCallback); et_Throttle = CoreTiming::RegisterEvent("Throttle", ThrottleCallback); CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerHalfLine(), et_VI); @@ -334,7 +334,7 @@ void Init() CoreTiming::ScheduleEvent(s_audio_dma_period, et_AudioDMA); CoreTiming::ScheduleEvent(0, et_Throttle, Common::Timer::GetTimeUs()); - CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine); + // CoreTiming::ScheduleEvent(VideoInterface::GetTicksPerField(), et_PatchEngine); if (SConfig::GetInstance().bWii) CoreTiming::ScheduleEvent(s_ipc_hle_period, et_IPC_HLE); diff --git a/Source/Core/DiscIO/Volume.cpp b/Source/Core/DiscIO/Volume.cpp index ab4b0e562b..bcbc21b688 100644 --- a/Source/Core/DiscIO/Volume.cpp +++ b/Source/Core/DiscIO/Volume.cpp @@ -85,6 +85,26 @@ std::map Volume::ReadWiiNames(const std::vector return names; } +bool Volume::FileExists(std::string file_name) +{ + std::vector partitions = this->GetPartitions(); + if (partitions.empty()) + partitions.emplace_back(PARTITION_NONE); + + for (const auto& partition : partitions) + { + const DiscIO::FileInfo& root_dir = this->GetFileSystem(partition)->GetRoot(); + for (const auto& file_info : root_dir) + { + if (file_info.GetName() == file_name) + { + return true; + } + } + } + return false; +} + static std::unique_ptr CreateDisc(std::unique_ptr& reader) { // Check for Wii diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index 0673ab00af..b172bbb772 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -152,6 +152,8 @@ public: // The way the hash is calculated may change with updates to Dolphin. virtual std::array GetSyncHash() const = 0; + bool FileExists(std::string file_name); + protected: template std::string DecodeString(const char (&data)[N]) const