From 45b137e50508f2bceec2fff17e4c59ed7b546de5 Mon Sep 17 00:00:00 2001 From: crediar Date: Wed, 16 Jul 2025 19:55:59 +0200 Subject: [PATCH 01/18] Added Triforce support --- Source/Core/Common/CommonPaths.h | 2 + Source/Core/Common/FileUtil.cpp | 1 + Source/Core/Common/FileUtil.h | 1 + Source/Core/Common/Logging/Log.h | 4 + Source/Core/Common/Logging/LogManager.cpp | 4 + Source/Core/Core/Boot/Boot_BS2Emu.cpp | 18 +- Source/Core/Core/CMakeLists.txt | 4 + Source/Core/Core/Core.cpp | 8 + Source/Core/Core/HW/DVD/AMMediaboard.cpp | 1863 ++++++++++++++ Source/Core/Core/HW/DVD/AMMediaboard.h | 216 ++ Source/Core/Core/HW/DVD/DVDInterface.cpp | 30 + Source/Core/Core/HW/DVD/DVDInterface.h | 3 + Source/Core/Core/HW/EXI/EXI_Device.cpp | 1 + .../Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp | 318 ++- Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h | 61 +- Source/Core/Core/HW/Memmap.cpp | 19 + Source/Core/Core/HW/Memmap.h | 2 +- Source/Core/Core/HW/SI/SI.h | 1 + Source/Core/Core/HW/SI/SI_Device.cpp | 3 + Source/Core/Core/HW/SI/SI_Device.h | 2 - .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 2192 +++++++++++++++++ Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h | 254 ++ Source/Core/DolphinLib.vcxproj | 26 +- .../Config/GamecubeControllersWidget.cpp | 2 + .../Core/DolphinQt/Settings/GameCubePane.cpp | 5 + Source/Core/UICommon/UICommon.cpp | 1 + 26 files changed, 4995 insertions(+), 46 deletions(-) create mode 100644 Source/Core/Core/HW/DVD/AMMediaboard.cpp create mode 100644 Source/Core/Core/HW/DVD/AMMediaboard.h create mode 100644 Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp create mode 100644 Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 0f6b7adfb6..4f556e6e41 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -46,6 +46,7 @@ #define GC_USER_DIR "GC" #define GBA_USER_DIR "GBA" #define WII_USER_DIR "Wii" +#define TRI_USER_DIR "Triforce" #define CONFIG_DIR "Config" #define GAMESETTINGS_DIR "GameSettings" #define MAPS_DIR "Maps" @@ -154,6 +155,7 @@ // Subdirs in Sys #define GC_SYS_DIR "GC" #define WII_SYS_DIR "Wii" +#define TRI_SYS_DIR "Triforce" // Subdirs in Config #define GRAPHICSMOD_CONFIG_DIR "GraphicMods" diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index 51a841ddec..af67e4b327 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -844,6 +844,7 @@ static void RebuildUserDirectories(unsigned int dir_index) case D_USER_IDX: s_user_paths[D_GCUSER_IDX] = s_user_paths[D_USER_IDX] + GC_USER_DIR DIR_SEP; s_user_paths[D_WIIROOT_IDX] = s_user_paths[D_USER_IDX] + WII_USER_DIR DIR_SEP; + s_user_paths[D_TRIUSER_IDX] = s_user_paths[D_USER_IDX] + TRI_USER_DIR DIR_SEP; s_user_paths[D_CONFIG_IDX] = s_user_paths[D_USER_IDX] + CONFIG_DIR DIR_SEP; s_user_paths[D_GAMESETTINGS_IDX] = s_user_paths[D_USER_IDX] + GAMESETTINGS_DIR DIR_SEP; s_user_paths[D_MAPS_IDX] = s_user_paths[D_USER_IDX] + MAPS_DIR DIR_SEP; diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index a887ffd8f3..dc33aed974 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -29,6 +29,7 @@ enum D_USER_IDX, D_GCUSER_IDX, D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory + D_TRIUSER_IDX, D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism D_CONFIG_IDX, // global settings D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default diff --git a/Source/Core/Common/Logging/Log.h b/Source/Core/Common/Logging/Log.h index 4b40297836..157d40ea43 100644 --- a/Source/Core/Common/Logging/Log.h +++ b/Source/Core/Common/Logging/Log.h @@ -28,6 +28,7 @@ enum class LogType : int DSP_MAIL, DSPINTERFACE, DVDINTERFACE, + DVDINTERFACE_AMMB, DYNA_REC, EXPANSIONINTERFACE, FILEMON, @@ -59,6 +60,9 @@ enum class LogType : int PROCESSORINTERFACE, POWERPC, SERIALINTERFACE, + SERIALINTERFACE_AMBB, + SERIALINTERFACE_CARD, + SERIALINTERFACE_JVSIO, SP1, SYMBOLS, VIDEO, diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index d0117ad07f..2f67a22afd 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -113,6 +113,7 @@ LogManager::LogManager() m_log[LogType::DSP_MAIL] = {"DSPMails", "DSP Mails"}; m_log[LogType::DSPINTERFACE] = {"DSP", "DSP Interface"}; m_log[LogType::DVDINTERFACE] = {"DVD", "DVD Interface"}; + m_log[LogType::DVDINTERFACE_AMMB] = { "DVD", "AMMB Interface" }; m_log[LogType::DYNA_REC] = {"JIT", "JIT Dynamic Recompiler"}; m_log[LogType::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"}; m_log[LogType::FILEMON] = {"FileMon", "File Monitor"}; @@ -144,6 +145,9 @@ LogManager::LogManager() m_log[LogType::PROCESSORINTERFACE] = {"PI", "Processor Interface"}; m_log[LogType::POWERPC] = {"PowerPC", "PowerPC IBM CPU"}; m_log[LogType::SERIALINTERFACE] = {"SI", "Serial Interface"}; + m_log[LogType::SERIALINTERFACE_AMBB] = { "SI", "AMBB Interface" }; + m_log[LogType::SERIALINTERFACE_CARD] = { "SI", "CARD Interface" }; + m_log[LogType::SERIALINTERFACE_JVSIO] = { "SI", "JVS-I/O" }; m_log[LogType::SP1] = {"SP1", "Serial Port 1"}; m_log[LogType::SYMBOLS] = {"SYMBOLS", "Symbols"}; m_log[LogType::VIDEO] = {"Video", "Video Backend"}; diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 884a11bd87..12f76d43ab 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -22,6 +22,7 @@ #include "Core/Debugger/BranchWatch.h" #include "Core/HLE/HLE.h" #include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/Memmap.h" #include "Core/IOS/DI/DI.h" @@ -227,12 +228,23 @@ bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard ppc_state.pc = ppc_state.gpr[3]; branch_watch.SetRecordingActive(guard, resume_branch_watch); - // Blank out session key (https://debugmo.de/2008/05/part-2-dumping-the-media-board/) - if (volume.GetVolumeType() == DiscIO::Platform::Triforce) + + // Check for Triforce board being connected + const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); + bool enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0; + if (enable_gcam) { auto& memory = system.GetMemory(); + u32 dsize = volume.GetDataSize(); - memory.Memset(0, 0, 12); + // Load game into RAM, like on the actual Triforce + u8* dimm_disc = AMMediaboard::InitDIMM(dsize); + + volume.Read(0, dsize, dimm_disc, DiscIO::PARTITION_NONE); + + // Triforce disc register obfucation + AMMediaboard::InitKeys(memory.Read_U32(0), memory.Read_U32(4), memory.Read_U32(8)); + AMMediaboard::FirmwareMap(false); } return true; diff --git a/Source/Core/Core/CMakeLists.txt b/Source/Core/Core/CMakeLists.txt index 79aab432cd..b68f239a54 100644 --- a/Source/Core/Core/CMakeLists.txt +++ b/Source/Core/Core/CMakeLists.txt @@ -183,6 +183,8 @@ add_library(core HW/DSPLLE/DSPSymbols.h HW/DVD/DVDInterface.cpp HW/DVD/DVDInterface.h + HW/DVD/AMMediaboard.cpp + HW/DVD/AMMediaboard.h HW/DVD/DVDMath.cpp HW/DVD/DVDMath.h HW/DVD/DVDThread.cpp @@ -276,6 +278,8 @@ add_library(core HW/SI/SI_DeviceGCSteeringWheel.h HW/SI/SI_DeviceKeyboard.cpp HW/SI/SI_DeviceKeyboard.h + HW/SI/SI_DeviceAMBaseboard.cpp + HW/SI/SI_DeviceAMBaseboard.h HW/SI/SI_DeviceNull.cpp HW/SI/SI_DeviceNull.h HW/SI/SI.cpp diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index a122a467e9..134aee38e8 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -52,6 +52,7 @@ #include "Core/HLE/HLE.h" #include "Core/HW/CPU.h" #include "Core/HW/DSP.h" +#include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/EXI/EXI.h" #include "Core/HW/GBAPad.h" #include "Core/HW/GCKeyboard.h" @@ -301,6 +302,13 @@ void Stop(Core::System& system) // - Hammertime! system.GetFifo().ExitGpuLoop(); } + + const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); + + if ((Type == ExpansionInterface::EXIDeviceType::Baseboard)) + { + AMMediaboard::Shutdown(); + } } void DeclareAsCPUThread() diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp new file mode 100644 index 0000000000..9507688a50 --- /dev/null +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -0,0 +1,1863 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#include "Core/HW/DVD/AMMediaboard.h" + +#include +#include +#include +#include +#include + +#include + +#include "Common/CommonPaths.h" +#include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IOFile.h" +#include "Common/IniFile.h" +#include "Common/Logging/Log.h" +#include "Core/Boot/Boot.h" +#include "Core/BootManager.h" +#include "Core/Config/MainSettings.h" +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigLoaders/BaseConfigLoader.h" +#include "Core/ConfigLoaders/NetPlayConfigLoader.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/HLE/HLE.h" +#include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/DVD/DVDThread.h" +#include "Core/HW/EXI/EXI.h" +#include "Core/HW/EXI/EXI_DeviceBaseboard.h" +#include "Core/HW/MMIO.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_Device.h" +#include "Core/HW/Sram.h" +#include "Core/HW/WiimoteReal/WiimoteReal.h" +#include "Core/IOS/Network/Socket.h" +#include "Core/Movie.h" +#include "Core/NetPlayProto.h" +#include "Core/PowerPC/PPCSymbolDB.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" +#include "Core/WiiRoot.h" +#include "DiscIO/DirectoryBlob.h" +#include "DiscIO/Enums.h" +#include "DiscIO/VolumeDisc.h" + +#if defined(__linux__) or defined(__APPLE__) or defined(__FreeBSD__) or defined(__NetBSD__) or \ + defined(__HAIKU__) + +#include + +#define closesocket close +#define ioctlsocket ioctl + +#define WSAEWOULDBLOCK 10035L +#define SOCKET_ERROR (-1) + +typedef int SOCKET; + +#define INVALID_SOCKET (SOCKET)(~0) + +static int WSAGetLastError(void) +{ + switch (errno) + { + case EWOULDBLOCK: + return WSAEWOULDBLOCK; + default: + break; + } + + return errno; +} + +#endif + +namespace AMMediaboard +{ + +static bool s_firmwaremap = false; +static bool s_segaboot = false; +static SOCKET s_namco_cam = 0; +static u32 s_timeouts[3] = {20000, 20000, 20000}; +static u32 s_last_error = SSC_SUCCESS; + +static u32 s_GCAM_key_a = 0; +static u32 s_GCAM_key_b = 0; +static u32 s_GCAM_key_c = 0; + +static File::IOFile* s_netcfg = nullptr; +static File::IOFile* s_netctrl = nullptr; +static File::IOFile* s_extra = nullptr; +static File::IOFile* s_backup = nullptr; +static File::IOFile* s_dimm = nullptr; + +static u8* s_dimm_disc = nullptr; + +static u8 s_firmware[2 * 1024 * 1024]; +static u8 s_media_buffer[0x300]; +static u8 s_network_command_buffer[0x4FFE00]; +static u8 s_network_buffer[256 * 1024]; + +/* Sockets FDs are required to go from 0 to 63. + Games use the FD as indexes so we have to workaround it. + */ +static SOCKET s_sockets[64]; + +static SOCKET socket_(int af, int type, int protocol) +{ + for (u32 i = 1; i < 64; ++i) + { + if (s_sockets[i] == SOCKET_ERROR) + { + s_sockets[i] = socket(af, type, protocol); + return i; + } + } + + // Out of sockets + return SOCKET_ERROR; +} + +static SOCKET accept_(int fd, struct sockaddr* addr, int* len) +{ + for (u32 i = 1; i < 64; ++i) + { + if (s_sockets[i] == SOCKET_ERROR) + { + s_sockets[i] = accept(fd, addr, (socklen_t*)len); + if (s_sockets[i] == SOCKET_ERROR) + return SOCKET_ERROR; + return i; + } + } + + // Out of sockets + return SOCKET_ERROR; +} + +static inline void PrintMBBuffer(u32 address, u32 length) +{ + auto& system = Core::System::GetInstance(); + auto& memory = system.GetMemory(); + + for (u32 i = 0; i < length; i += 0x10) + { + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: {:08x} {:08x} {:08x} {:08x}", + memory.Read_U32(address + i), memory.Read_U32(address + i + 4), + memory.Read_U32(address + i + 8), memory.Read_U32(address + i + 12)); + } +} + +void FirmwareMap(bool on) +{ + s_firmwaremap = on; +} + +void InitKeys(u32 key_a, u32 key_b, u32 key_c) +{ + s_GCAM_key_a = key_a; + s_GCAM_key_b = key_b; + s_GCAM_key_c = key_c; +} + +static File::IOFile* OpenOrCreateFile(const std::string& filename) +{ + // Try opening for read/write first + if (File::Exists(filename)) + return new File::IOFile(filename, "rb+"); + + // Create new file + return new File::IOFile(filename, "wb+"); +} + +void Init(void) +{ + memset(s_media_buffer, 0, sizeof(s_media_buffer)); + memset(s_network_buffer, 0, sizeof(s_network_buffer)); + memset(s_network_command_buffer, 0, sizeof(s_network_command_buffer)); + memset(s_firmware, -1, sizeof(s_firmware)); + memset(s_sockets, SOCKET_ERROR, sizeof(s_sockets)); + + s_segaboot = false; + s_firmwaremap = false; + + s_last_error = SSC_SUCCESS; + + s_GCAM_key_a = 0; + s_GCAM_key_b = 0; + s_GCAM_key_c = 0; + + std::string base_path = File::GetUserPath(D_TRIUSER_IDX); + + s_netcfg = OpenOrCreateFile(base_path + "trinetcfg.bin"); + s_netctrl = OpenOrCreateFile(base_path + "trinetctrl.bin"); + s_extra = OpenOrCreateFile(base_path + "triextra.bin"); + s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetTriforceID() + ".bin"); + s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetTriforceID() + ".bin"); + + if (!s_netcfg) + PanicAlertFmt("Failed to open/create: {}", base_path + "s_netcfg.bin"); + if (!s_netctrl) + PanicAlertFmt("Failed to open/create: {}", base_path + "s_netctrl.bin"); + if (!s_extra) + PanicAlertFmt("Failed to open/create: {}", base_path + "s_extra.bin"); + if (!s_dimm) + PanicAlertFmt("Failed to open/create: {}", base_path + "s_dimm.bin"); + if (!s_backup) + PanicAlertFmt("Failed to open/create: {}", base_path + "s_backup.bin"); + + // This is the firmware for the Triforce + const std::string sega_boot_filename = + File::GetSysDirectory() + TRI_SYS_DIR + DIR_SEP + "segaboot.gcm"; + + if (!File::Exists(sega_boot_filename)) + { + PanicAlertFmt("Failed to open segaboot.gcm({}), which is required for test menus.", sega_boot_filename.c_str()); + return; + } + + File::IOFile sega_boot(sega_boot_filename, "rb+"); + if (!sega_boot.IsOpen()) + { + PanicAlertFmt("Failed to read: {}", sega_boot_filename); + return; + } + + u64 length = std::min(sega_boot.GetSize(), sizeof(s_firmware)); + sega_boot.ReadBytes(s_firmware, length); +} + +u8* InitDIMM(u32 size) +{ + if (size == 0) + return nullptr; + + if (!s_dimm_disc) + { + s_dimm_disc = new (std::nothrow) u8[size]; + if (!s_dimm_disc) + { + PanicAlertFmt("Failed to allocate DIMM memory."); + return nullptr; + } + } + + s_firmwaremap = 0; + return s_dimm_disc; +} + +static s32 NetDIMMAccept(int fd, struct sockaddr* addr, int* len) +{ + SOCKET clientSock = INVALID_SOCKET; + fd_set readfds; + + FD_ZERO(&readfds); + FD_SET(fd, &readfds); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 20000; // 20 milliseconds + + int result = select(0, &readfds, NULL, NULL, &timeout); + if (result > 0 && FD_ISSET(fd, &readfds)) + { + clientSock = accept_(fd, addr, len); + if (clientSock != INVALID_SOCKET) + { + s_last_error = SSC_SUCCESS; + return clientSock; + } + else + { + s_last_error = SOCKET_ERROR; + return SOCKET_ERROR; + } + } + else if (result == 0) + { + // Timeout + s_last_error = SSC_EWOULDBLOCK; + } + else + { + // select() failed + s_last_error = SOCKET_ERROR; + } + + return SOCKET_ERROR; +} + +static s32 NetDIMMConnect(int fd, struct sockaddr_in* addr, int len) +{ + // CyCraft Connect IP + if (addr->sin_addr.s_addr == inet_addr("192.168.11.111")) + { + addr->sin_addr.s_addr = inet_addr("127.0.0.1"); + } + + // NAMCO Camera ( IPs are: 192.168.29.104-108 ) + if ((addr->sin_addr.s_addr & 0xFFFFFF00) == 0xC0A81D00) + { + addr->sin_addr.s_addr = inet_addr("127.0.0.1"); + /* + BUG: An invalid family value is used + */ + addr->sin_family = htons(AF_INET); + s_namco_cam = fd; + } + + // Key of Avalon Client + if (addr->sin_addr.s_addr == inet_addr("192.168.13.1")) + { + addr->sin_addr.s_addr = inet_addr("10.0.0.45"); + } + + addr->sin_family = Common::swap16(addr->sin_family); + //*(u32*)(&addr.sin_addr) = Common::swap32(*(u32*)(&addr.sin_addr)); + + int ret = 0; + int err = 0; + u_long val = 1; + + // Set socket to non-blocking + ioctlsocket(fd, FIONBIO, &val); + + ret = connect(fd, (const sockaddr*)addr, len); + err = WSAGetLastError(); + + if (ret == SOCKET_ERROR && err == WSAEWOULDBLOCK) + { + fd_set writefds; + FD_ZERO(&writefds); + FD_SET(fd, &writefds); + + timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = s_timeouts[0]; + + ret = select(0, NULL, &writefds, NULL, &timeout); + if (ret > 0 && FD_ISSET(fd, &writefds)) + { + int so_error = 0; + socklen_t optlen = sizeof(so_error); + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char*)&so_error, &optlen) == 0 && so_error == 0) + { + s_last_error = SSC_SUCCESS; + ret = 0; + } + else + { + s_last_error = SOCKET_ERROR; + ret = SOCKET_ERROR; + } + } + else if (ret == 0) + { + // Timeout + s_last_error = SSC_EWOULDBLOCK; + ret = SOCKET_ERROR; + } + else + { + // select() failed + s_last_error = SOCKET_ERROR; + ret = SOCKET_ERROR; + } + } + else if (ret == SOCKET_ERROR) + { + // Immediate failure (e.g. WSAECONNREFUSED) + s_last_error = ret; + } + else + { + s_last_error = SSC_SUCCESS; + } + + // Restore blocking mode + val = 0; + ioctlsocket(fd, FIONBIO, &val); + + return ret; +} +static void FileWriteData(File::IOFile* file, u32 seek_pos, const u8* data, size_t length) +{ + file->Seek(seek_pos, File::SeekOrigin::Begin); + file->WriteBytes(data, length); + file->Flush(); +} +u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) +{ + auto& system = Core::System::GetInstance(); + auto& memory = system.GetMemory(); + auto& ppc_state = system.GetPPCState(); + auto& jit_interface = system.GetJitInterface(); + + /* + The triforce IPL sends these commands first + 01010000 00000101 00000000 + 01010000 00000000 0000ffff + */ + if (s_GCAM_key_a == 0) + { + /* + Since it is currently unknown how the seed is created + we have to patch out the crypto. + */ + if (memory.Read_U32(0x8131ecf4)) + { + memory.Write_U32(0, 0x8131ecf4); + memory.Write_U32(0, 0x8131ecf8); + memory.Write_U32(0, 0x8131ecfC); + memory.Write_U32(0, 0x8131ebe0); + memory.Write_U32(0, 0x8131ed6c); + memory.Write_U32(0, 0x8131ed70); + memory.Write_U32(0, 0x8131ed74); + + memory.Write_U32(0x4E800020, 0x813025C8); + memory.Write_U32(0x4E800020, 0x81302674); + + ppc_state.iCache.Invalidate(memory, jit_interface, 0x813025C8); + ppc_state.iCache.Invalidate(memory, jit_interface, 0x81302674); + + HLE::Patch(system, 0x813048B8, "OSReport"); + HLE::Patch(system, 0x8130095C, "OSReport"); // Apploader + } + } + + DICMDBUF[0] ^= s_GCAM_key_a; + DICMDBUF[1] ^= s_GCAM_key_b; + // length ^= s_GCAM_key_c; // DMA length is always plain + + u32 seed = DICMDBUF[0] >> 16; + + s_GCAM_key_a *= seed; + s_GCAM_key_b *= seed; + s_GCAM_key_c *= seed; + + DICMDBUF[0] <<= 24; + DICMDBUF[1] <<= 2; + + // SegaBoot adds bits for some reason to offset/length + // also adds 0x20 to offset + if (DICMDBUF[1] == 0x00100440) + { + s_segaboot = true; + } + + u32 command = DICMDBUF[0]; + u32 offset = DICMDBUF[1]; + + INFO_LOG_FMT(DVDINTERFACE_AMMB, + "GC-AM: {:08x} {:08x} DMA=addr:{:08x},len:{:08x} Keys: {:08x} {:08x} {:08x}", + command, offset, address, length, s_GCAM_key_a, s_GCAM_key_b, s_GCAM_key_c); + + // Test mode + if (offset == 0x0002440) + { + // Set by OSResetSystem + if (memory.Read_U32(0x811FFF00) == 1) + { + // Don't map firmware while in SegaBoot + if (memory.Read_U32(0x8006BF70) != 0x0A536567) + { + s_firmwaremap = 1; + } + } + } + + switch (AMMBCommand(command >> 24)) + { + case AMMBCommand::Inquiry: + if (s_firmwaremap) + { + s_firmwaremap = false; + s_segaboot = false; + } + + // Returned value is used to set the protocol version. + switch (GetGameType()) + { + default: + return Version1; + case KeyOfAvalon: + case MarioKartGP: + case MarioKartGP2: + case FirmwareUpdate: + return Version2; + } + break; + case AMMBCommand::Read: + if ((offset & 0x8FFF0000) == 0x80000000) + { + switch (offset) + { + case MediaBoardStatus1: + memory.Write_U16(Common::swap16(0x0100), address); + break; + case MediaBoardStatus2: + memset(memory.GetPointer(address), 0, length); + break; + case MediaBoardStatus3: + memset(memory.GetPointer(address), 0xFF, length); + // DIMM size (512MB) + memory.Write_U32(Common::swap32(0x20000000), address); + // GCAM signature + memory.Write_U32(0x4743414D, address + 4); + break; + case 0x80000100: + memory.Write_U32(Common::swap32((u32)0x001F1F1F), address); + break; + case FirmwareStatus1: + memory.Write_U32(Common::swap32((u32)0x01FA), address); + break; + case FirmwareStatus2: + memory.Write_U32(Common::swap32((u32)1), address); + break; + case 0x80000160: + memory.Write_U32(0x00001E00, address); + break; + case 0x80000180: + memory.Write_U32(0, address); + break; + case 0x800001A0: + memory.Write_U32(0xFFFFFFFF, address); + break; + default: + PrintMBBuffer(address, length); + PanicAlertFmtT("Unhandled Media Board Read:{0:08x}", offset); + break; + } + return 0; + } + + // Network configuration + if (offset == 0x00000000 && length == 0x80) + { + s_netcfg->Seek(0, File::SeekOrigin::Begin); + s_netcfg->ReadBytes(memory.GetPointer(address), length); + return 0; + } + + // media crc check on/off + if (offset == DIMMExtraSettings && length == 0x20) + { + s_extra->Seek(0, File::SeekOrigin::Begin); + s_extra->ReadBytes(memory.GetPointer(address), length); + return 0; + } + + // DIMM memory (8MB) + if (offset >= DIMMMemory && offset <= 0x1F800000) + { + u32 dimmoffset = offset - DIMMMemory; + s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); + s_dimm->ReadBytes(memory.GetPointer(address), length); + return 0; + } + + if (offset >= DIMMCommandVersion1 && offset < 0x1F900040) + { + u32 dimmoffset = offset - DIMMCommandVersion1; + memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (1) ({:08x},{})", offset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if (offset >= NetworkBufferAddress4 && offset < 0x891C0000) + { + u32 dimmoffset = offset - NetworkBufferAddress4; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (4) ({:08x},{})", offset, length); + memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkBufferAddress5 && offset < 0x1FB10000) + { + u32 dimmoffset = offset - NetworkBufferAddress5; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (5) ({:08x},{})", offset, length); + memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkCommandAddress && offset < 0x1FD00000) + { + u32 dimmoffset = offset - NetworkCommandAddress; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK COMMAND BUFFER ({:08x},{})", offset, + length); + memcpy(memory.GetPointer(address), s_network_command_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkCommandAddress2 && offset < 0x89060200) + { + u32 dimmoffset = offset - NetworkCommandAddress2; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK COMMAND BUFFER (2) ({:08x},{})", offset, + length); + memcpy(memory.GetPointer(address), s_network_command_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkBufferAddress1 && offset < 0x1FA10000) + { + u32 dimmoffset = offset - NetworkBufferAddress1; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (1) ({:08x},{})", offset, length); + memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkBufferAddress2 && offset < 0x1FD10000) + { + u32 dimmoffset = offset - NetworkBufferAddress2; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (2) ({:08x},{})", offset, length); + memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + return 0; + } + + if (offset >= NetworkBufferAddress3 && offset < 0x89110000) + { + u32 dimmoffset = offset - NetworkBufferAddress3; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (3) ({:08x},{})", offset, length); + memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + return 0; + } + + if (offset >= DIMMCommandVersion2 && offset < 0x84000060) + { + u32 dimmoffset = offset - DIMMCommandVersion2; + memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (2) ({:08x},{})", offset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if (offset == DIMMCommandExecute2) + { + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: EXECUTE MEDIA BOARD COMMAND"); + + memcpy(s_media_buffer, s_media_buffer + 0x200, 0x20); + memset(s_media_buffer + 0x200, 0, 0x20); + s_media_buffer[0x204] = 1; + + // Recast for easier access + u32* media_buffer_32 = (u32*)(s_media_buffer); + + switch (AMMBCommand(*(u16*)(s_media_buffer + 2))) + { + case AMMBCommand::Unknown_001: + media_buffer_32[1] = 1; + break; + case AMMBCommand::GetNetworkFirmVersion: + media_buffer_32[1] = 0x1305; // Version: 13.05 + s_media_buffer[6] = 1; // Type: VxWorks + break; + case AMMBCommand::GetSystemFlags: + s_media_buffer[4] = 1; + s_media_buffer[6] = NANDMaskBoardNAND; + s_media_buffer[7] = 1; + break; + // Empty reply + case AMMBCommand::Unknown_103: + break; + // Network Commands + case AMMBCommand::Accept: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + int ret = -1; + + // Handle optional paramters + if (media_buffer_32[3] == 0 || media_buffer_32[4] == 0) + { + ret = NetDIMMAccept(fd, nullptr, nullptr); + } + else + { + u32 addr_off = media_buffer_32[3] - NetworkCommandAddress2; + u32 len_off = media_buffer_32[4] - NetworkCommandAddress2; + + struct sockaddr* addr = (struct sockaddr*)(s_network_command_buffer + addr_off); + int* len = (int*)(s_network_command_buffer + len_off); + + ret = NetDIMMAccept(fd, addr, len); + } + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: accept( {}({}) ):{}\n", fd, media_buffer_32[2], + ret); + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::Bind: + { + struct sockaddr_in addr; + + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 off = media_buffer_32[3] - NetworkCommandAddress2; + u32 len = media_buffer_32[4]; + + memcpy((void*)&addr, s_network_command_buffer + off, sizeof(struct sockaddr_in)); + + addr.sin_family = Common::swap16(addr.sin_family); + *(u32*)(&addr.sin_addr) = Common::swap32(*(u32*)(&addr.sin_addr)); + + /* + Triforce games usually use hardcoded IPs + This is replaced to listen to the ANY address instead + */ + addr.sin_addr.s_addr = INADDR_ANY; + + int ret = bind(fd, (const sockaddr*)&addr, len); + int err = WSAGetLastError(); + + if (ret < 0) + PanicAlertFmt("Socket Bind Failed with{0}", err); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: bind( {}, ({},{:08x}:{}), {} ):{} ({})\n", fd, + addr.sin_family, addr.sin_addr.s_addr, Common::swap16(addr.sin_port), len, + ret, err); + + media_buffer_32[1] = ret; + s_last_error = SSC_SUCCESS; + break; + } + case AMMBCommand::Closesocket: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + + int ret = closesocket(fd); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: closesocket( {}({}) ):{}\n", fd, + media_buffer_32[2], ret); + + s_sockets[media_buffer_32[2]] = SOCKET_ERROR; + + media_buffer_32[1] = ret; + s_last_error = SSC_SUCCESS; + break; + } + case AMMBCommand::Connect: + { + struct sockaddr_in addr; + + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 off = media_buffer_32[3] - NetworkCommandAddress2; + u32 len = media_buffer_32[4]; + + int ret = 0; + int err = 0; + + memcpy((void*)&addr, s_network_command_buffer + off, sizeof(struct sockaddr_in)); + + ret = NetDIMMConnect(fd, &addr, len); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: connect( {}({}), ({},{}:{}), {} ):{} ({})\n", fd, + media_buffer_32[2], addr.sin_family, inet_ntoa(addr.sin_addr), + Common::swap16(addr.sin_port), len, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::InetAddr: + { + u32 ip = inet_addr((char*)s_network_command_buffer); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: InetAddr( {} )\n", + (char*)s_network_command_buffer); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = Common::swap32(ip); + break; + } + case AMMBCommand::Listen: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 backlog = media_buffer_32[3]; + + int ret = listen(fd, backlog); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: listen( {}, {} ):{:d}\n", fd, backlog, ret); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::Recv: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 off = media_buffer_32[3]; + u32 len = media_buffer_32[4]; + + if (len >= sizeof(s_network_buffer)) + { + len = sizeof(s_network_buffer); + } + + int ret = 0; + char* buffer = (char*)(s_network_buffer + off); + + if (off >= NetworkBufferAddress4 && off < NetworkBufferAddress4 + sizeof(s_network_buffer)) + { + buffer = (char*)(s_network_buffer + off - NetworkBufferAddress4); + } + else + { + PanicAlertFmt("RECV: Buffer overrun:{0} {1} ", off, len); + } + + int err = 0; + + ret = recv(fd, buffer, len, 0); + err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: recv( {}, 0x{:08x}, {} ):{} {}\n", fd, off, len, + ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::Send: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 off = media_buffer_32[3]; + u32 len = media_buffer_32[4]; + + int ret = 0; + + if (off >= NetworkBufferAddress3 && off < NetworkBufferAddress3 + sizeof(s_network_buffer)) + { + off -= NetworkBufferAddress3; + } + else + { + ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: send(error) unhandled destination:{:08x}\n", + off); + } + + ret = send(fd, (char*)(s_network_buffer + off), len, 0); + int err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: send( {}({}), 0x{:08x}, {} ): {} {}\n", fd, + media_buffer_32[2], off, len, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::Socket: + { + // Protocol is not sent + u32 af = media_buffer_32[2]; + u32 type = media_buffer_32[3]; + + SOCKET fd = socket_(af, type, IPPROTO_TCP); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: socket( {}, {}, IPPROTO_TCP ):{}\n", af, type, + fd); + + s_media_buffer[1] = 0; + media_buffer_32[1] = fd; + break; + } + case AMMBCommand::Select: + { + u32 nfds = s_sockets[SocketCheck(media_buffer_32[2] - 1)]; + + /* + BUG: NAMCAM is hardcoded to call this with socket ID 0x100 which might be some magic + thing? Winsocks expects a valid socket so we take the socket from the connect. + */ + if (AMMediaboard::GetGameType() == MarioKartGP2) + { + if (media_buffer_32[2] == 256) + { + nfds = s_namco_cam; + } + } + + fd_set* readfds = nullptr; + fd_set* writefds = nullptr; + fd_set* exceptfds = nullptr; + timeval* timeout = nullptr; + + // Only one of 3, 4, 5 is ever set alongside 6 + if (media_buffer_32[3] && media_buffer_32[6]) + { + u32 ROffset = media_buffer_32[6] - NetworkCommandAddress2; + readfds = (fd_set*)(s_network_command_buffer + ROffset); + FD_ZERO(readfds); + FD_SET(nfds, readfds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_32[3] - NetworkCommandAddress2); + } + else if (media_buffer_32[4] && media_buffer_32[6]) + { + u32 WOffset = media_buffer_32[6] - NetworkCommandAddress2; + writefds = (fd_set*)(s_network_command_buffer + WOffset); + FD_ZERO(writefds); + FD_SET(nfds, writefds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_32[4] - NetworkCommandAddress2); + } + else if (media_buffer_32[5] && media_buffer_32[6]) + { + u32 EOffset = media_buffer_32[6] - NetworkCommandAddress2; + exceptfds = (fd_set*)(s_network_command_buffer + EOffset); + FD_ZERO(exceptfds); + FD_SET(nfds, exceptfds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_32[5] - NetworkCommandAddress2); + } + + if (AMMediaboard::GetGameType() == KeyOfAvalon) + { + timeout->tv_sec = 0; + timeout->tv_usec = 1800; + } + + int ret = select(nfds + 1, readfds, writefds, exceptfds, timeout); + + int err = WSAGetLastError(); + + NOTICE_LOG_FMT( + DVDINTERFACE_AMMB, + "GC-AM: select( {}({}), 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x} ):{} {} {}:{} \n", nfds, + media_buffer_32[2], media_buffer_32[3], media_buffer_32[4], media_buffer_32[5], + media_buffer_32[6], ret, err, timeout->tv_sec, timeout->tv_usec); + // hexdump( s_network_command_buffer, 0x40 ); + + s_media_buffer[1] = 0; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::SetSockOpt: + { + SOCKET fd = (SOCKET)(s_sockets[SocketCheck(media_buffer_32[2])]); + int level = (int)(media_buffer_32[3]); + int optname = (int)(media_buffer_32[4]); + const char* optval = + (char*)(s_network_command_buffer + media_buffer_32[5] - NetworkCommandAddress2); + int optlen = (int)(media_buffer_32[6]); + + int ret = setsockopt(fd, level, optname, optval, optlen); + + int err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, + "GC-AM: setsockopt( {:d}, {:04x}, {}, {:p}, {} ):{:d} ({})\n", fd, level, + optname, optval, optlen, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::SetTimeOuts: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + u32 timeoutA = media_buffer_32[3]; + u32 timeoutB = media_buffer_32[4]; + u32 timeoutC = media_buffer_32[5]; + + s_timeouts[0] = timeoutA; + s_timeouts[1] = timeoutB; + s_timeouts[2] = timeoutC; + + int ret = 0; + + if (fd != INVALID_SOCKET) + { + ret = setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeoutB, sizeof(int)); + if (ret < 0) + { + ret = WSAGetLastError(); + } + else + { + ret = setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeoutC, sizeof(int)); + if (ret < 0) + ret = WSAGetLastError(); + } + } + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: SetTimeOuts( {}, {}, {}, {} ):{}\n", fd, timeoutA, + timeoutB, timeoutC, ret); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = ret; + break; + } + case AMMBCommand::GetParambyDHCPExec: + { + u32 value = media_buffer_32[2]; + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: GetParambyDHCPExec({})\n", value); + + s_media_buffer[1] = 0; + media_buffer_32[1] = 0; + break; + } + case AMMBCommand::ModifyMyIPaddr: + { + u32 NetBufferOffset = *(u32*)(s_media_buffer + 8) - NetworkCommandAddress2; + + char* IP = (char*)(s_network_command_buffer + NetBufferOffset); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: modifyMyIPaddr({})\n", IP); + break; + } + case AMMBCommand::GetLastError: + { + u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: GetLastError( {}({}) ):{}\n", fd, + media_buffer_32[2], s_last_error); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_32[1] = s_last_error; + } + break; + case AMMBCommand::InitLink: + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: InitLink"); + break; + default: + ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Command:{:03X}", *(u16*)(s_media_buffer + 2)); + ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Command Unhandled!"); + break; + } + + s_media_buffer[3] |= 0x80; // Command complete flag + + memset(memory.GetPointer(address), 0, length); + + ExpansionInterface::GenerateInterrupt(0x10); + return 0; + } + + if (offset >= DIMMCommandVersion2_2 && offset <= 0x89000200) + { + u32 dimmoffset = offset - DIMMCommandVersion2_2; + memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (3) ({:08x})", dimmoffset); + PrintMBBuffer(address, length); + return 0; + } + + // DIMM memory (8MB) + if (offset >= DIMMMemory2 && offset <= 0xFF800000) + { + u32 dimmoffset = offset - DIMMMemory2; + s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); + s_dimm->ReadBytes(memory.GetPointer(address), length); + return 0; + } + + if (offset == NetworkControl && length == 0x20) + { + s_netctrl->Seek(0, File::SeekOrigin::Begin); + s_netctrl->ReadBytes(memory.GetPointer(address), length); + return 0; + } + + // Max GC disc offset + if (offset >= 0x57058000) + { + PanicAlertFmtT("Unhandled Media Board Read:{0:08x}", offset); + return 0; + } + + if (s_firmwaremap) + { + if (s_segaboot) + { + DICMDBUF[1] &= ~0x00100000; + DICMDBUF[1] -= 0x20; + } + memcpy(memory.GetPointer(address), s_firmware + offset, length); + return 0; + } + + if (s_dimm_disc) + { + memcpy(memory.GetPointer(address), s_dimm_disc + offset, length); + return 0; + } + + return 1; + break; + case AMMBCommand::Write: + /* + These two magic writes allow a new firmware to be programmed + */ + if ((offset == FirmwareMagicWrite1) && (length == 0x20)) + { + s_firmwaremap = true; + return 0; + } + + if ((offset == FirmwareMagicWrite2) && (length == 0x20)) + { + s_firmwaremap = true; + return 0; + } + + if (s_firmwaremap) + { + // Firmware memory (2MB) + if ((offset >= 0x00400000) && (offset <= 0x600000)) + { + u32 fwoffset = offset - 0x00400000; + memcpy(s_firmware + fwoffset, memory.GetPointer(address), length); + return 0; + } + } + + // Network configuration + if ((offset == 0x00000000) && (length == 0x80)) + { + FileWriteData(s_netcfg, 0, memory.GetPointer(address), length); + return 0; + } + + // media crc check on/off + if ((offset == DIMMExtraSettings) && (length == 0x20)) + { + FileWriteData(s_extra, 0, memory.GetPointer(address), length); + return 0; + } + + // Backup memory (8MB) + if ((offset >= BackupMemory) && (offset <= 0x00800000)) + { + FileWriteData(s_backup, 0, memory.GetPointer(address), length); + return 0; + } + + // DIMM memory (8MB) + if ((offset >= DIMMMemory) && (offset <= 0x1F800000)) + { + u32 dimmoffset = offset - DIMMMemory; + FileWriteData(s_dimm, dimmoffset, memory.GetPointer(address), length); + return 0; + } + + if ((offset >= NetworkCommandAddress) && (offset < 0x1F801240)) + { + u32 dimmoffset = offset - NetworkCommandAddress; + + memcpy(s_network_command_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK COMMAND BUFFER ({:08x},{})", dimmoffset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= NetworkCommandAddress2) && (offset <= 0x890601FF)) + { + u32 dimmoffset = offset - NetworkCommandAddress2; + + memcpy(s_network_command_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK COMMAND BUFFER (2) ({:08x},{})", + dimmoffset, length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= NetworkBufferAddress1) && (offset <= 0x1FA1FFFF)) + { + u32 dimmoffset = offset - 0x1FA00000; + + memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (1) ({:08x},{})", dimmoffset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= NetworkBufferAddress2) && (offset <= 0x1FD0FFFF)) + { + u32 dimmoffset = offset - 0x1FD00000; + + memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (2) ({:08x},{})", dimmoffset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= NetworkBufferAddress3) && (offset <= 0x8910FFFF)) + { + u32 dimmoffset = offset - 0x89100000; + + memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (3) ({:08x},{})", dimmoffset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= DIMMCommandVersion1) && (offset <= 0x1F90003F)) + { + u32 dimmoffset = offset - 0x1F900000; + memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write MEDIA BOARD COMM AREA (1) ({:08x},{})", offset, + length); + PrintMBBuffer(address, length); + return 0; + } + + if ((offset >= DIMMCommandVersion2) && (offset <= 0x8400005F)) + { + u32 dimmoffset = offset - 0x84000000; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write MEDIA BOARD COMM AREA (2) ({:08x},{})", offset, + length); + PrintMBBuffer(address, length); + + u8 cmd_flag = memory.Read_U8(address); + + if (dimmoffset == 0x40 && cmd_flag == 1) + { + // Recast for easier access + u32* media_buffer_in_32 = (u32*)(s_media_buffer + 0x20); + u16* media_buffer_in_16 = (u16*)(s_media_buffer + 0x20); + u32* media_buffer_out_32 = (u32*)(s_media_buffer); + u16* media_buffer_out_16 = (u16*)(s_media_buffer); + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Execute command:{:03X}", media_buffer_in_16[1]); + + memset(s_media_buffer, 0, 0x20); + + media_buffer_out_32[0] = media_buffer_in_32[0] | 0x80000000; // Set command okay flag + + memcpy(s_media_buffer + 0x40, s_media_buffer, 0x20); + + switch (static_cast(media_buffer_in_16[1])) + { + case AMMBCommand::Unknown_000: + media_buffer_out_32[1] = 1; + break; + case AMMBCommand::GetDIMMSize: + media_buffer_out_32[1] = 0x1FFF8000; + break; + case AMMBCommand::GetMediaBoardStatus: + // Status + media_buffer_out_32[1] = LoadedGameProgram; + // Progress in % + media_buffer_out_32[2] = 100; + break; + // SegaBoot version: 3.09 + case AMMBCommand::GetSegaBootVersion: + // Version + media_buffer_out_16[2] = Common::swap16(0x0309); + // Unknown + media_buffer_out_16[3] = 2; + media_buffer_out_32[2] = 0x4746; // "GF" + media_buffer_out_32[4] = 0xFF; + break; + case AMMBCommand::GetSystemFlags: + s_media_buffer[4] = 0; + s_media_buffer[5] = GDROM; + + // Enable development mode (Sega Boot) + // This also allows region free booting + s_media_buffer[6] = 1; + media_buffer_out_16[4] = 0; // Access Count + s_media_buffer[7] = 1; + break; + case AMMBCommand::GetMediaBoardSerial: + memcpy(s_media_buffer + 4, "A85E-01A62204904", 16); + break; + case AMMBCommand::Unknown_104: + s_media_buffer[4] = 1; + break; + default: + PanicAlertFmtT("Unhandled Media Board Command:{0:02x}", media_buffer_in_16[1]); + break; + } + + memcpy(memory.GetPointer(address), s_media_buffer, length); + + memset(s_media_buffer + 0x20, 0, 0x20); + + ExpansionInterface::GenerateInterrupt(0x04); + return 0; + } + else + { + memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + } + return 0; + } + + if ((offset >= DIMMCommandVersion2_2) && (offset <= 0x89000200)) + { + u32 dimmoffset = offset - 0x89000000; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write MEDIA BOARD COMM AREA (3) ({:08x})", + dimmoffset); + PrintMBBuffer(address, length); + + memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + + return 0; + } + + // Firmware Write + if ((offset >= FirmwareAddress) && (offset <= 0x84818000)) + { + u32 dimmoffset = offset - 0x84800000; + + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write Firmware ({:08x})", dimmoffset); + PrintMBBuffer(address, length); + return 0; + } + + // DIMM memory (8MB) + if ((offset >= DIMMMemory2) && (offset <= 0xFF800000)) + { + u32 dimmoffset = offset - 0xFF000000; + FileWriteData(s_dimm, dimmoffset, memory.GetPointer(address), length); + return 0; + } + + if ((offset == NetworkControl) && (length == 0x20)) + { + FileWriteData(s_netctrl, 0, memory.GetPointer(address), length); + return 0; + } + + // Max GC disc offset + if (offset >= 0x57058000) + { + PrintMBBuffer(address, length); + PanicAlertFmtT("Unhandled Media Board Write:{0:08x}", offset); + } + break; + case AMMBCommand::Execute: + if ((offset == 0) && (length == 0)) + { + // Recast for easier access + u32* media_buffer_in_32 = (u32*)(s_media_buffer + 0x20); + u16* media_buffer_in_16 = (u16*)(s_media_buffer + 0x20); + u32* media_buffer_out_32 = (u32*)(s_media_buffer); + u16* media_buffer_out_16 = (u16*)(s_media_buffer); + + memset(s_media_buffer, 0, 0x20); + + media_buffer_out_16[0] = media_buffer_in_16[0]; + + // Command + media_buffer_out_16[1] = media_buffer_in_16[1] | 0x8000; // Set command okay flag + + if (media_buffer_in_16[1]) + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Execute command:{:03X}", media_buffer_in_16[1]); + + switch (static_cast(media_buffer_in_16[1])) + { + case AMMBCommand::Unknown_000: + media_buffer_out_32[1] = 1; + break; + case AMMBCommand::GetDIMMSize: + media_buffer_out_32[1] = 0x20000000; + break; + case AMMBCommand::GetMediaBoardStatus: + { + // Fake loading the game to have a chance to enter test mode + static u32 status = LoadingGameProgram; + static u32 progress = 80; + + media_buffer_out_32[1] = status; + media_buffer_out_32[2] = progress; + if (progress < 100) + { + progress++; + } + else + { + status = LoadedGameProgram; + } + } + break; + // SegaBoot version: 3.11 + case AMMBCommand::GetSegaBootVersion: + // Version + media_buffer_out_16[2] = Common::swap16(0x1103); + // Unknown + media_buffer_out_16[3] = 1; + media_buffer_out_32[2] = 1; + media_buffer_out_32[4] = 0xFF; + break; + case AMMBCommand::GetSystemFlags: + // 1: GD-ROM + s_media_buffer[4] = 1; + s_media_buffer[5] = 1; + // Enable development mode (Sega Boot) + // This also allows region free booting + s_media_buffer[6] = 1; + media_buffer_out_16[4] = 0; // Access Count + break; + case AMMBCommand::GetMediaBoardSerial: + memcpy(s_media_buffer + 4, "A89E-27A50364511", 16); + break; + case AMMBCommand::Unknown_104: + s_media_buffer[4] = 1; + break; + case AMMBCommand::NetworkReInit: + break; + case AMMBCommand::TestHardware: + // Test type + + // 0x01: Media board + // 0x04: Network + + // ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x301: ({:08x})", *(u32*)(s_media_buffer+0x24) + // ); + + // Pointer to a memory address that is directly displayed on screen as a string + // ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:08x})", *(u32*)(s_media_buffer+0x28) + // ); + + // On real system it shows the status about the DIMM/GD-ROM here + // We just show "TEST OK" + memory.Write_U32(0x54534554, media_buffer_in_32[4]); + memory.Write_U32(0x004B4F20, media_buffer_in_32[4] + 4); + + media_buffer_out_32[1] = media_buffer_in_32[1]; + break; + case AMMBCommand::Closesocket: + { + u32 fd = s_sockets[SocketCheck(media_buffer_in_32[2])]; + + int ret = closesocket(fd); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: closesocket( {}({}) ):{}\n", fd, + media_buffer_in_32[2], ret); + + s_sockets[media_buffer_in_32[2]] = SOCKET_ERROR; + + media_buffer_out_32[1] = ret; + s_last_error = SSC_SUCCESS; + } + break; + case AMMBCommand::Connect: + { + struct sockaddr_in addr; + + u32 fd = s_sockets[SocketCheck(media_buffer_in_32[2])]; + u32 off = media_buffer_in_32[3] - NetworkCommandAddress; + u32 len = media_buffer_in_32[4]; + + int ret = 0; + int err = 0; + + memcpy((void*)&addr, s_network_command_buffer + off, sizeof(struct sockaddr_in)); + + ret = NetDIMMConnect(fd, &addr, len); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: connect( {}({}), ({},{}:{}), {} ):{} ({})\n", fd, + media_buffer_in_32[2], addr.sin_family, inet_ntoa(addr.sin_addr), + Common::swap16(addr.sin_port), len, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_out_32[1] = ret; + } + break; + case AMMBCommand::Recv: + { + u32 fd = s_sockets[SocketCheck(media_buffer_in_32[2])]; + u32 off = media_buffer_in_32[3]; + u32 len = media_buffer_in_32[4]; + + if (len >= sizeof(s_network_buffer)) + { + len = sizeof(s_network_buffer); + } + + int ret = 0; + char* buffer = (char*)(s_network_buffer + off); + + if (off >= NetworkBufferAddress5 && off < NetworkBufferAddress5 + sizeof(s_network_buffer)) + { + buffer = (char*)(s_network_buffer + off - NetworkBufferAddress5); + } + else + { + PanicAlertFmt("RECV: Buffer overrun:{0} {1} ", off, len); + } + + int err = 0; + + ret = recv(fd, buffer, len, 0); + err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: recv( {}, 0x{:08x}, {} ):{} {}\n", fd, off, len, + ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_out_32[1] = ret; + } + break; + case AMMBCommand::Send: + { + u32 fd = s_sockets[SocketCheck(media_buffer_in_32[2])]; + u32 off = media_buffer_in_32[3]; + u32 len = media_buffer_in_32[4]; + + int ret = 0; + + if (off >= NetworkBufferAddress1 && off < NetworkBufferAddress1 + sizeof(s_network_buffer)) + { + off -= NetworkBufferAddress1; + } + else + { + ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: send(error) unhandled destination:{:08x}\n", + off); + } + + ret = send(fd, (char*)(s_network_buffer + off), len, 0); + int err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: send( {}({}), 0x{:08x}, {} ): {} {}\n", fd, + media_buffer_in_32[2], off, len, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_out_32[1] = ret; + } + break; + case AMMBCommand::Socket: + { + // Protocol is not sent + u32 af = media_buffer_in_32[2]; + u32 type = media_buffer_in_32[3]; + + SOCKET fd = socket_(af, type, IPPROTO_TCP); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: socket( {}, {}, 6 ):{}\n", af, type, fd); + + s_media_buffer[1] = 0; + media_buffer_out_32[1] = fd; + } + break; + case AMMBCommand::Select: + { + u32 nfds = s_sockets[SocketCheck(media_buffer_in_32[2] - 1)]; + + fd_set* readfds = nullptr; + fd_set* writefds = nullptr; + fd_set* exceptfds = nullptr; + timeval* timeout = nullptr; + + // Only one of 3, 4, 5 is ever set alongside 6 + if (media_buffer_in_32[3] && media_buffer_in_32[6]) + { + u32 ROffset = media_buffer_in_32[6] - NetworkCommandAddress; + + readfds = (fd_set*)(s_network_command_buffer + ROffset); + FD_ZERO(readfds); + FD_SET(nfds, readfds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_in_32[3] - NetworkCommandAddress); + } + else if (media_buffer_in_32[4] && media_buffer_in_32[6]) + { + u32 WOffset = media_buffer_in_32[6] - NetworkCommandAddress; + writefds = (fd_set*)(s_network_command_buffer + WOffset); + FD_ZERO(writefds); + FD_SET(nfds, writefds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_in_32[4] - NetworkCommandAddress); + } + else if (media_buffer_in_32[5] && media_buffer_in_32[6]) + { + u32 EOffset = media_buffer_in_32[6] - NetworkCommandAddress; + exceptfds = (fd_set*)(s_network_command_buffer + EOffset); + FD_ZERO(exceptfds); + FD_SET(nfds, exceptfds); + + timeout = + (timeval*)(s_network_command_buffer + media_buffer_in_32[5] - NetworkCommandAddress); + } + + /* + BUG?: F-Zero AX Monster calls select with a two second timeout for unknown reasons, which + slows down the game a lot + */ + if (AMMediaboard::GetGameType() == FZeroAXMonster) + { + timeout->tv_sec = 0; + timeout->tv_usec = 1800; + } + + int ret = select(nfds + 1, readfds, writefds, exceptfds, timeout); + + int err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, + "GC-AM: select( {}({}), 0x{:08x} 0x{:08x} 0x{:08x} 0x{:08x} ):{} {} \n", + nfds, media_buffer_in_32[2], media_buffer_in_32[3], media_buffer_in_32[4], + media_buffer_in_32[5], media_buffer_in_32[6], ret, err); + // hexdump( NetworkCMDBuffer, 0x40 ); + + s_media_buffer[1] = 0; + media_buffer_out_32[1] = ret; + } + break; + case AMMBCommand::SetSockOpt: + { + SOCKET fd = (SOCKET)(s_sockets[SocketCheck(media_buffer_in_32[2])]); + int level = (int)(media_buffer_in_32[3]); + int optname = (int)(media_buffer_in_32[4]); + const char* optval = + (char*)(s_network_command_buffer + media_buffer_in_32[5] - NetworkCommandAddress); + int optlen = (int)(media_buffer_in_32[6]); + + int ret = setsockopt(fd, level, optname, optval, optlen); + + int err = WSAGetLastError(); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, + "GC-AM: setsockopt( {:d}, {:04x}, {}, {:p}, {} ):{:d} ({})\n", fd, level, + optname, optval, optlen, ret, err); + + s_media_buffer[1] = s_media_buffer[8]; + media_buffer_out_32[1] = ret; + } + break; + case AMMBCommand::ModifyMyIPaddr: + { + u32 NetBufferOffset = media_buffer_in_32[2] - NetworkCommandAddress; + + char* IP = (char*)(s_network_command_buffer + NetBufferOffset); + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: modifyMyIPaddr({})\n", IP); + } + break; + // Empty reply + case AMMBCommand::InitLink: + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x601"); + break; + case AMMBCommand::Unknown_605: + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x605"); + break; + case AMMBCommand::SetupLink: + { + struct sockaddr_in addra, addrb; + addra.sin_addr.s_addr = media_buffer_in_32[4]; + addrb.sin_addr.s_addr = media_buffer_in_32[5]; + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x606:"); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Size: ({}) ", media_buffer_in_16[2]); // size + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Port: ({})", + Common::swap16(media_buffer_in_16[3])); // port + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM:LinkNum:({:02x})", + s_media_buffer[0x28]); // linknum + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:02x})", s_media_buffer[0x29]); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:04x})", media_buffer_in_16[5]); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: IP: ({})", inet_ntoa(addra.sin_addr)); // IP + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: IP: ({})", + inet_ntoa(addrb.sin_addr)); // Target IP + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:08x})", + Common::swap32(media_buffer_in_32[6])); // some RAM address + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:08x})", + Common::swap32(media_buffer_in_32[7])); // some RAM address + + media_buffer_out_32[1] = 0; + } + break; + // This sends a UDP packet to previously defined Target IP/Port + case AMMBCommand::SearchDevices: + { + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x607: ({})", media_buffer_in_16[2]); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({})", media_buffer_in_16[3]); + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: ({:08x})", media_buffer_in_32[2]); + + u8* Data = (u8*)(s_network_buffer + media_buffer_in_32[2] - 0x1FD00000); + + for (u32 i = 0; i < 0x20; i += 0x10) + { + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: {:08x} {:08x} {:08x} {:08x}", *(u32*)(Data + i), + *(u32*)(Data + i + 4), *(u32*)(Data + i + 8), *(u32*)(Data + i + 12)); + } + + media_buffer_out_32[1] = 0; + } + break; + case AMMBCommand::Unknown_608: + { + u32 IP = media_buffer_in_32[2]; + u16 Port = media_buffer_in_16[4]; + u16 Flag = media_buffer_in_16[5]; + + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x608( {} {} {} )", IP, Port, Flag); + } + break; + case AMMBCommand::Unknown_614: + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: 0x614"); + break; + default: + ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: execute buffer UNKNOWN:{:03x}", + *(u16*)(s_media_buffer + 0x22)); + break; + } + + memset(s_media_buffer + 0x20, 0, 0x20); + return 0; + } + + PanicAlertFmtT("Unhandled Media Board Execute:{0:08x}", *(u16*)(s_media_buffer + 0x22)); + break; + default: + PanicAlertFmtT("Unhandled Media Board Command:{0:02x}", command ); + break; + } + + return 0; +} + +u32 GetMediaType(void) +{ + switch (GetGameType()) + { + default: + case FZeroAX: + case VirtuaStriker3: + case VirtuaStriker4: + case GekitouProYakyuu: + case KeyOfAvalon: + return GDROM; + break; + + case MarioKartGP: + case MarioKartGP2: + case FZeroAXMonster: + return NAND; + break; + } + // Never reached +} + +u32 GetGameType(void) +{ + u64 game_id = 0; + + // Convert game ID into hex + if (strlen(SConfig::GetInstance().GetTriforceID().c_str()) > 4) + { + game_id = 0x30303030; + } + else + { + sscanf(SConfig::GetInstance().GetTriforceID().c_str(), "%s", (char*)&game_id); + } + + // This is checking for the real game IDs (See boot.id within the game) + switch (Common::swap32((u32)game_id)) + { + // SBGG - F-ZERO AX + case 0x53424747: + return FZeroAX; + // SBHA - F-ZERO AX (Monster) + case 0x53424841: + return FZeroAXMonster; + // SBKJ/SBKP - MARIOKART ARCADE GP + case 0x53424B50: + case 0x53424B5A: + return MarioKartGP; + // SBNJ/SBNL - MARIOKART ARCADE GP2 + case 0x53424E4A: + case 0x53424E4C: + return MarioKartGP2; + // SBEJ/SBEY - Virtua Striker 2002 + case 0x5342454A: + case 0x53424559: + return VirtuaStriker3; + // SBLJ/SBLK/SBLL - VIRTUA STRIKER 4 Ver.2006 + case 0x53424C4A: + case 0x53424C4B: + case 0x53424C4C: + // SBHJ/SBHN/SBHZ - VIRTUA STRIKER 4 VER.A + case 0x5342484A: + case 0x5342484E: + case 0x5342485A: + // SBJA/SBJJ - VIRTUA STRIKER 4 + case 0x53424A41: + case 0x53424A4A: + return VirtuaStriker4; + // SBFX/SBJN - Key of Avalon + case 0x53424658: + case 0x53424A4E: + return KeyOfAvalon; + // SBGX - Gekitou Pro Yakyuu (DIMM Upgrade 3.17) + case 0x53424758: + return GekitouProYakyuu; + default: + PanicAlertFmtT("Unknown game ID:{0:08x}, using default controls.", game_id); + // GSBJ/G12U - VIRTUA STRIKER 3 + // RELS/RELJ - SegaBoot (does not have a boot.id) + case 0x4753424A: + case 0x47313255: + case 0x52454C53: + case 0x52454c4a: + return VirtuaStriker3; + // S000 - Firmware update + case 0x53303030: + return FirmwareUpdate; + } + // never reached +} + +void Shutdown(void) +{ + if (s_netcfg) + s_netcfg->Close(); + + if (s_netctrl) + s_netctrl->Close(); + + if (s_extra) + s_extra->Close(); + + if (s_backup) + s_backup->Close(); + + if (s_dimm) + s_dimm->Close(); + + if (s_dimm_disc) + { + delete[] s_dimm_disc; + s_dimm_disc = nullptr; + } + + // close all sockets + for (u32 i = 1; i < 64; ++i) + { + if (s_sockets[i] != SOCKET_ERROR) + { + closesocket(s_sockets[i]); + } + } +} + +} // namespace AMMediaboard diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h new file mode 100644 index 0000000000..dd32a5e0c0 --- /dev/null +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -0,0 +1,216 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include + +#include "Common/CommonTypes.h" + +class PointerWrap; + +namespace Core +{ +class System; +} + +namespace File +{ +class IOFile; +} + +enum GameType +{ + FZeroAX = 1, + FZeroAXMonster, + MarioKartGP, + MarioKartGP2, + VirtuaStriker3, + VirtuaStriker4, + GekitouProYakyuu, + KeyOfAvalon, + FirmwareUpdate +}; +enum MediaType +{ + GDROM = 1, + NAND, +}; +enum MediaBoardType +{ + NANDMaskBoardHDD = 0, + NANDMaskBoardMask = 1, + NANDMaskBoardNAND = 2, + DIMMBoardType3 = 4, +}; +enum MediaBoardStatus +{ + Initializing = 0, + CheckingNetwork = 1, + SystemDisc = 2, + TestingGameProgram = 3, + LoadingGameProgram = 4, + LoadedGameProgram = 5, + Error = 6 +}; +enum InquiryType +{ + Version1 = 0x21484100, + Version2 = 0x29484100 +}; + +#define SocketCheck(x) (x <= 0x3F ? x : 0) + +namespace AMMediaboard +{ +enum class AMMBCommand : u16 +{ + Unknown_000 = 0x000, + GetDIMMSize = 0x001, + + Inquiry = 0x12, + Read = 0xa8, + Write = 0xaa, + Execute = 0xab, + + GetMediaBoardStatus = 0x100, + GetSegaBootVersion = 0x101, + GetSystemFlags = 0x102, + GetMediaBoardSerial = 0x103, + Unknown_104 = 0x104, + + NetworkReInit = 0x204, + + TestHardware = 0x301, + + // Network used by Mario Kart GPs + Accept = 0x401, + Bind = 0x402, + Closesocket = 0x403, + Connect = 0x404, + GetIPbyDNS = 0x405, + InetAddr = 0x406, + Ioctl = 0x407, // Unused + Listen = 0x408, + Recv = 0x409, + Send = 0x40A, + Socket = 0x40B, + Select = 0x40C, + Shutdown = 0x40D, // Unused + SetSockOpt = 0x40E, + GetSockOpt = 0x40F, // Unused + SetTimeOuts = 0x410, + GetLastError = 0x411, + RouteAdd = 0x412, // Unused + RouteDelete = 0x413, // Unused + GetParambyDHCPExec = 0x414, + ModifyMyIPaddr = 0x415, + Recvfrom = 0x416, // Unused + Sendto = 0x417, // Unused + RecvDimmImage = 0x418, // Unused + SendDimmImage = 0x419, // Unused + + // Network used by F-Zero AX + InitLink = 0x601, + Unknown_605 = 0x605, + SetupLink = 0x606, + SearchDevices = 0x607, + Unknown_608 = 0x608, + Unknown_614 = 0x614, + + // NETDIMM Commands + Unknown_001 = 0x001, + GetNetworkFirmVersion = 0x101, + Unknown_103 = 0x103, +}; + +enum MediaBoardAddress : u32 +{ + MediaBoardStatus1 = 0x80000000, + MediaBoardStatus2 = 0x80000020, + MediaBoardStatus3 = 0x80000040, + + FirmwareStatus1 = 0x80000120, + FirmwareStatus2 = 0x80000140, + + BackupMemory = 0x000006A0, + + DIMMMemory = 0x1F000000, + DIMMMemory2 = 0xFF000000, + + DIMMExtraSettings = 0x1FFEFFE0, + + NetworkControl = 0xFFFF0000, + + DIMMCommandVersion1 = 0x1F900000, + DIMMCommandVersion2 = 0x84000000, + DIMMCommandVersion2_2 = 0x89000000, + DIMMCommandExecute2 = 0x88000000, + + NetworkCommandAddress = 0x1F800200, + NetworkCommandAddress2 = 0x89040200, + + NetworkBufferAddress1 = 0x1FA00000, + NetworkBufferAddress2 = 0x1FD00000, + NetworkBufferAddress3 = 0x89100000, + NetworkBufferAddress4 = 0x89180000, + NetworkBufferAddress5 = 0x1FB00000, + + FirmwareAddress = 0x84800000, + + FirmwareMagicWrite1 = 0x00600000, + FirmwareMagicWrite2 = 0x00700000, +}; + +// Mario Kart GP2 has a complete list of them +// but in japanese +// They somewhat match WSA errors codes +enum SocketStatusCodes +{ + SSC_E_4 = -4, // Failure (abnormal argument) + SSC_E_3 = -3, // Success (unsupported command) + SSC_E_2 = + -2, // Failure (failed to send, abnormal argument, or communication condition violation) + SSC_E_1 = -1, // Failure (error termination) + + SSC_EINTR = 4, // An interrupt occurred before data reception was completed + SSC_EBADF = 9, // Invalid descriptor + SSC_E_11 = 11, // Send operation was blocked on a non-blocking mode socket + SSC_EACCES = 13, // The socket does not support broadcast addresses, but the destination address + // is a broadcast address + SSC_EFAULT = + 14, // The name argument specifies a location other than an address used by the process. + SSC_E_23 = 23, // System file table is full. + SSC_AEMFILE = 24, // Process descriptor table is full. + SSC_EMSGSIZE = + 36, // Socket tried to send message without splitting, but message size is too large. + SSC_EAFNOSUPPORT = 47, // Address prohibited for use on this socket. + SSC_EADDRINUSE = 48, // Address already in use. + SSC_EADDRNOTAVAIL = 49, // Prohibited address. + SSC_E_50 = 50, // Non-socket descriptor. + SSC_ENETUNREACH = 51, // Cannot access specified network. + SSC_ENOBUFS = 55, // Insufficient buffer + SSC_EISCONN = 56, // Already connected socket + SSC_ENOTCONN = 57, // No connection for connection-type socket + SSC_ETIMEDOUT = 60, // Timeout + SSC_ECONNREFUSED = 61, // Connection request forcibly rejected + SSC_EHOSTUNREACH = 65, // Remote host cannot be reached + SSC_EHOSTDOWN = 67, // Remote host is down + SSC_EWOULDBLOCK = 70, // Socket is in non-blocking mode and connection has not been completed + SSC_E_69 = 69, // Socket is in non-blocking mode and a previously issued Connect command has not + // been completed + SSC_SUCCESS = 70, +}; + +void Init(void); +void FirmwareMap(bool on); +u8* InitDIMM(u32 size); +void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC); +u32 ExecuteCommand(std::array& DICMDBUF, u32 Address, u32 Length); +u32 GetGameType(void); +u32 GetMediaType(void); +void Shutdown(void); +}; // namespace AMMediaboard diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 1c6ea07aa0..0d43dcbc67 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -28,6 +28,7 @@ #include "Core/DolphinAnalytics.h" #include "Core/HW/AudioInterface.h" #include "Core/HW/DVD/DVDMath.h" +#include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/DVD/DVDThread.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/MMIO.h" @@ -269,6 +270,8 @@ void DVDInterface::Init() m_DIIMMBUF = 0; m_DICFG.Hex = 0; m_DICFG.CONFIG = 1; // Disable bootrom descrambler + m_DICFG.Hex |= 8; /* The Triforce IPL checks this bit + to set the physical memory to either 50MB(unset) or 24MB(set) */ ResetDrive(false); @@ -282,6 +285,13 @@ void DVDInterface::Init() u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT); core_timing.ScheduleEvent(0, m_finish_executing_command, userdata); + + const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); + m_enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0; + if (m_enable_gcam) + { + AMMediaboard::Init(); + } } // Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the @@ -753,6 +763,26 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) DIInterruptType interrupt_type = DIInterruptType::TCINT; bool command_handled_by_thread = false; + if (m_enable_gcam) + { + u32 ret = AMMediaboard::ExecuteCommand(m_DICMDBUF, m_DIMAR, m_DILENGTH); + if (ret != 1) + { + if (m_DICMDBUF[0] == 0x12000000) + m_DIIMMBUF = ret; + + // Transfer is done + m_DICR.TSTART = 0; + m_DIMAR += m_DILENGTH; + m_DILENGTH = 0; + GenerateDIInterrupt(DIInterruptType::TCINT); + m_error_code = DriveError::None; + return; + } + m_DICMDBUF[1] >>= 2; + // Normal read command pass on to normal handling + } + // Swaps endian of Triforce DI commands, and zeroes out random bytes to prevent unknown read // subcommand errors auto& dvd_thread = m_system.GetDVDThread(); diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index 600edc5050..a2b8f9776a 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -279,6 +279,9 @@ private: bool m_enable_dtk = false; u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer + // Triforce + bool m_enable_gcam = false; + // Disc drive state DriveState m_drive_state = DriveState::Ready; DriveError m_error_code = DriveError::None; diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index ac92c7b723..328c9fa529 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -16,6 +16,7 @@ #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/EXI/EXI_DeviceMemoryCard.h" #include "Core/HW/EXI/EXI_DeviceModem.h" +#include "Core/HW/EXI/EXI_DeviceBaseboard.h" #include "Core/HW/Memmap.h" #include "Core/System.h" diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp index a9200ac85a..864214aa86 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp @@ -1,21 +1,139 @@ -// Copyright 2013 Dolphin Emulator Project +// Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "Core/HW/EXI/EXI_DeviceBaseboard.h" +#include "Core/HW/EXI/EXI_DeviceBaseboard.h" +#include +#include +#include +#include +#include + +#include -#include "Common/Assert.h" -#include "Common/ChunkFile.h" #include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/IOFile.h" +#include "Common/IniFile.h" #include "Common/Logging/Log.h" +#include "Core/Boot/Boot.h" +#include "Core/BootManager.h" +#include "Core/Config/MainSettings.h" +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigLoaders/BaseConfigLoader.h" +#include "Core/ConfigLoaders/NetPlayConfigLoader.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/HW/DVD/AMMediaboard.h" +#include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/EXI/EXI.h" +#include "Core/HW/EXI/EXI_Device.h" +#include "Core/HW/MMIO.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_Device.h" +#include "Core/HW/Sram.h" +#include "Core/HW/WiimoteReal/WiimoteReal.h" +#include "Core/Movie.h" +#include "Core/NetPlayProto.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" +#include "Core/WiiRoot.h" +#include "DiscIO/Enums.h" + +bool g_interrupt_set = false; +u32 g_irq_timer = 0; +u32 g_irq_status = 0; + +static u16 CheckSum(u8* Data, u32 Length) +{ + u16 check = 0; + + for (u32 i = 0; i < Length; i++) + { + check += Data[i]; + } + + return check; +} namespace ExpansionInterface { -CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system) + +void GenerateInterrupt(int flag) { + auto& system = Core::System::GetInstance(); + + g_interrupt_set = true; + g_irq_timer = 0; + g_irq_status = flag; + + system.GetExpansionInterface().UpdateInterrupts(); +} + +CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_position(0) +{ + std::string backup_Filename(File::GetUserPath(D_TRIUSER_IDX) + "tribackup_" + + SConfig::GetInstance().GetTriforceID().c_str() + ".bin"); + + if (File::Exists(backup_Filename)) + { + m_backup = new File::IOFile(backup_Filename, "rb+"); + } + else + { + m_backup = new File::IOFile(backup_Filename, "wb+"); + } + + // Some games share the same ID Client/Server + if (!m_backup->IsGood()) + { + PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str() ); + + std::srand(static_cast(std::time(nullptr))); + + backup_Filename = File::GetUserPath(D_TRIUSER_IDX) + "tribackup_tmp_" + std::to_string(rand()) + + SConfig::GetInstance().GetTriforceID().c_str() + ".bin"; + + m_backup = new File::IOFile(backup_Filename, "wb+"); + } + + // Virtua Striker 4 and Gekitou Pro Yakyuu need a higher FIRM version + // Which is read from the backup data?! + if (AMMediaboard::GetGameType() == VirtuaStriker4 || + AMMediaboard::GetGameType() == GekitouProYakyuu) + { + if (m_backup->GetSize() != 0) + { + u8* data = new u8[m_backup->GetSize()]; + + m_backup->ReadBytes(data, m_backup->GetSize()); + + // Set FIRM version + *(u16*)(data + 0x12) = 0x1703; + *(u16*)(data + 0x212) = 0x1703; + + // Update checksum + *(u16*)(data + 0x0A) = Common::swap16(CheckSum(data + 0xC, 0x1F4)); + *(u16*)(data + 0x20A) = Common::swap16(CheckSum(data + 0x20C, 0x1F4)); + + m_backup->Seek(0, File::SeekOrigin::Begin); + m_backup->WriteBytes(data, m_backup->GetSize()); + m_backup->Flush(); + + delete[] data; + } + } +} +CEXIBaseboard::~CEXIBaseboard() +{ + m_backup->Close(); + delete m_backup; } void CEXIBaseboard::SetCS(int cs) { + DEBUG_LOG_FMT(SP1, "AM-BB: ChipSelect={}", cs); if (cs) m_position = 0; } @@ -25,33 +143,199 @@ bool CEXIBaseboard::IsPresent() const return true; } -void CEXIBaseboard::TransferByte(u8& byte) +bool CEXIBaseboard::IsInterruptSet() { - if (m_position == 0) + if (g_interrupt_set) { - m_command = byte; + DEBUG_LOG_FMT(SP1, "AM-BB: IRQ"); + if (++g_irq_timer > 12) + g_interrupt_set = false; + return 1; } else { - switch (m_command) + return 0; + } +} + +void CEXIBaseboard::DMAWrite(u32 addr, u32 size) +{ + auto& system = Core::System::GetInstance(); + auto& memory = system.GetMemory(); + + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Write: {:08x} {:x}", addr, size); + + m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + + m_backup->WriteBytes(memory.GetPointer(addr), size); + + m_backup->Flush(); +} + +void CEXIBaseboard::DMARead(u32 addr, u32 size) +{ + auto& system = Core::System::GetInstance(); + auto& memory = system.GetMemory(); + + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Read: {:08x} {:x}", addr, size); + + m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + + m_backup->Flush(); + + m_backup->ReadBytes(memory.GetPointer(addr), size); +} + +void CEXIBaseboard::TransferByte(u8& _byte) +{ + DEBUG_LOG_FMT(SP1, "AM-BB: > {:02x}", _byte); + if (m_position < 4) + { + m_command[m_position] = _byte; + _byte = 0xFF; + } + + if ((m_position >= 2) && (m_command[0] == 0 && m_command[1] == 0)) + { + // Read serial ID + _byte = "\x06\x04\x10\x00"[(m_position - 2) & 3]; + } + else if (m_position == 3) + { + u32 checksum = (m_command[0] << 24) | (m_command[1] << 16) | (m_command[2] << 8); + u32 bit = 0x80000000UL; + u32 check = 0x8D800000UL; + while (bit >= 0x100) { - case 0x00: - { - static constexpr std::array ID = {0x06, 0x04, 0x10, 0x00}; - byte = ID[(m_position - 2) & 3]; - break; + if (checksum & bit) + checksum ^= check; + check >>= 1; + bit >>= 1; } - default: - ERROR_LOG_FMT(EXPANSIONINTERFACE, "EXI BASEBOARD: Unhandled command {:#x} {:#x}", m_command, - m_position); + + if (m_command[3] != (checksum & 0xFF)) + DEBUG_LOG_FMT(SP1, "AM-BB: cs: {:02x}, w: {:02x}", m_command[3], checksum & 0xFF); + } + else + { + if (m_position == 4) + { + switch (m_command[0]) + { + case BackupOffsetSet: + m_backoffset = (m_command[1] << 8) | m_command[2]; + DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupOffsetSet:{:04x}", m_backoffset); + m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + _byte = 0x01; + break; + case BackupWrite: + DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupWrite:{:04x}-{:02x}", m_backoffset, m_command[1]); + m_backup->WriteBytes(&m_command[1], 1); + m_backup->Flush(); + _byte = 0x01; + break; + case BackupRead: + DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupRead :{:04x}", m_backoffset); + _byte = 0x01; + break; + case DMAOffsetLengthSet: + m_backup_dma_offset = (m_command[1] << 8) | m_command[2]; + m_backup_dma_length = m_command[3]; + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: DMAOffsetLengthSet :{:04x} {:02x}", m_backup_dma_offset, + m_backup_dma_length); + _byte = 0x01; + break; + case ReadISR: + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: ReadISR :{:02x} {:02x}:{:02x} {:02x}", m_command[1], + m_command[2], 4, g_irq_status); + _byte = 0x04; + break; + case WriteISR: + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: WriteISR :{:02x} {:02x}", m_command[1], m_command[2]); + g_irq_status &= ~(m_command[2]); + _byte = 0x04; + break; + // 2 byte out + case ReadIMR: + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: ReadIMR :{:02x} {:02x}", m_command[1], m_command[2]); + _byte = 0x04; + break; + case WriteIMR: + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: WriteIMR :{:02x} {:02x}", m_command[1], m_command[2]); + _byte = 0x04; + break; + case WriteLANCNT: + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: WriteLANCNT :{:02x} {:02x}", m_command[1], + m_command[2]); + if ((m_command[1] == 0) && (m_command[2] == 0)) + { + g_interrupt_set = true; + g_irq_timer = 0; + g_irq_status = 0x02; + } + if ((m_command[1] == 2) && (m_command[2] == 1)) + { + g_irq_status = 0; + } + _byte = 0x08; + break; + default: + _byte = 4; + ERROR_LOG_FMT(SP1, "AM-BB: COMMAND: {:02x} {:02x} {:02x}", m_command[0], m_command[1], + m_command[2]); + break; + } + } + else if (m_position > 4) + { + switch (m_command[0]) + { + // 1 byte out + case BackupRead: + m_backup->Flush(); + m_backup->ReadBytes(&_byte, 1); + break; + case DMAOffsetLengthSet: + _byte = 0x01; + break; + // 2 byte out + case ReadISR: + if (m_position == 6) + { + _byte = g_irq_status; + g_interrupt_set = false; + } + else + { + _byte = 0x04; + } + break; + // 2 byte out + case ReadIMR: + if (m_position == 5) + _byte = 0xFF; + if (m_position == 6) + _byte = 0x81; + break; + default: + ERROR_LOG_FMT(SP1, "Unknown AM-BB command: {:02x}", m_command[0]); + break; + } + } + else + { + _byte = 0xFF; } } + DEBUG_LOG_FMT(SP1, "AM-BB < {:02x}", _byte); m_position++; } void CEXIBaseboard::DoState(PointerWrap& p) { p.Do(m_position); + p.Do(g_interrupt_set); p.Do(m_command); } + } // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h index 4c534428c9..a0632fb098 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h @@ -1,27 +1,68 @@ -// Copyright 2013 Dolphin Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later #pragma once -#include "Core/HW/EXI/EXI_Device.h" +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Flag.h" +#include "Core/HW/EXI/EXI_Device.h" +#include "Common/IOFile.h" + +namespace Core +{ +class System; +} -class PointerWrap; namespace ExpansionInterface { +void GenerateInterrupt(int flag); + class CEXIBaseboard : public IEXIDevice { public: - CEXIBaseboard(Core::System& system); - void SetCS(int cs) override; + explicit CEXIBaseboard(Core::System& system); + virtual ~CEXIBaseboard(); + + void SetCS(int _iCS) override; + bool IsInterruptSet() override; bool IsPresent() const override; void DoState(PointerWrap& p) override; + void DMAWrite(u32 addr, u32 size) override; + void DMARead(u32 addr, u32 size) override; + private: - // STATE_TO_SAVE - u32 m_position = 0; - u32 m_command = 0; - void TransferByte(u8& byte) override; +enum Command +{ + BackupOffsetSet = 0x01, + BackupWrite = 0x02, + BackupRead = 0x03, + + DMAOffsetLengthSet = 0x05, + + ReadISR = 0x82, + WriteISR = 0x83, + ReadIMR = 0x86, + WriteIMR = 0x87, + + WriteLANCNT = 0xFF, +}; + + u32 m_position; + u32 m_backup_dma_offset; + u32 m_backup_dma_length; + u8 m_command[4]; + u16 m_backoffset; + File::IOFile* m_backup; + +protected: + void TransferByte(u8& _uByte) override; }; } // namespace ExpansionInterface diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 675e942009..61d5e50464 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -526,6 +526,25 @@ std::span MemoryManager::GetSpanForAddress(u32 address) const LR(ppc_state)); return {}; } +u8* MemoryManager::GetPointer(u32 address) const +{ + // TODO: Should we be masking off more bits here? Can all devices access + // EXRAM? + address &= 0x3FFFFFFF; + if (address < GetRamSizeReal()) + return m_ram + address; + + if (m_exram) + { + if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal()) + return m_exram + (address & GetExRamMask()); + } + + auto& ppc_state = m_system.GetPPCState(); + PanicAlertFmt("Unknown Pointer {:#010x} PC {:#010x} LR {:#010x}", address, ppc_state.pc, + LR(ppc_state)); + return nullptr; +} u8 MemoryManager::Read_U8(u32 address) const { diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index e0708605db..48c63e2dd4 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -115,7 +115,7 @@ public: // If the specified range is within a single valid memory region, returns a pointer to the start // of the corresponding range in host memory. Otherwise, returns nullptr. u8* GetPointerForRange(u32 address, size_t size) const; - + u8* GetPointer(u32 address) const; void CopyFromEmu(void* data, u32 address, size_t size) const; void CopyToEmu(u32 address, const void* data, size_t size); void Memset(u32 address, u8 value, size_t size); diff --git a/Source/Core/Core/HW/SI/SI.h b/Source/Core/Core/HW/SI/SI.h index 731b9b5c19..0a10b54a6a 100644 --- a/Source/Core/Core/HW/SI/SI.h +++ b/Source/Core/Core/HW/SI/SI.h @@ -56,6 +56,7 @@ public: void RemoveEvent(int device_number); void UpdateDevices(); + u32 GetInLength(void) const { return m_com_csr.INLNGTH; } void RemoveDevice(int device_number); void AddDevice(SIDevices device, int device_number); diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp index 8ea3e75d02..9d3f2cea15 100644 --- a/Source/Core/Core/HW/SI/SI_Device.cpp +++ b/Source/Core/Core/HW/SI/SI_Device.cpp @@ -20,6 +20,7 @@ #include "Core/HW/SI/SI_DeviceGCController.h" #include "Core/HW/SI/SI_DeviceGCSteeringWheel.h" #include "Core/HW/SI/SI_DeviceKeyboard.h" +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" #include "Core/HW/SI/SI_DeviceNull.h" #include "Core/HW/SystemTimers.h" #include "Core/System.h" @@ -181,6 +182,8 @@ std::unique_ptr SIDevice_Create(Core::System& system, const SIDevices return std::make_unique(system, device, port_number); case SIDEVICE_AM_BASEBOARD: + return std::make_unique(system, device, port_number); + case SIDEVICE_NONE: default: return std::make_unique(system, device, port_number); diff --git a/Source/Core/Core/HW/SI/SI_Device.h b/Source/Core/Core/HW/SI/SI_Device.h index ebab7e3235..ea022fa5fa 100644 --- a/Source/Core/Core/HW/SI/SI_Device.h +++ b/Source/Core/Core/HW/SI/SI_Device.h @@ -97,8 +97,6 @@ enum SIDevices : int SIDEVICE_GC_STEERING, SIDEVICE_DANCEMAT, SIDEVICE_GC_TARUKONGA, - // Was used for Triforce in the past, but the implementation is no longer in Dolphin. - // It's kept here so that values below will stay constant. SIDEVICE_AM_BASEBOARD, SIDEVICE_WIIU_ADAPTER, SIDEVICE_GC_GBA_EMULATED, diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp new file mode 100644 index 0000000000..c82d70b730 --- /dev/null +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -0,0 +1,2192 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +// #pragma warning(disable : 4189) + +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" + +#include +#include +#include +#include +#include + +#include + +#include "Common/CommonTypes.h" +#include "Common/Config/Config.h" +#include "Common/FileUtil.h" +#include "Common/Hash.h" +#include "Common/IOFile.h" +#include "Common/IniFile.h" +#include "Common/Logging/Log.h" +#include "Core/Boot/Boot.h" +#include "Core/BootManager.h" +#include "Core/Config/MainSettings.h" +#include "Core/Config/SYSCONFSettings.h" +#include "Core/ConfigLoaders/BaseConfigLoader.h" +#include "Core/ConfigLoaders/NetPlayConfigLoader.h" +#include "Core/ConfigManager.h" +#include "Core/Core.h" +#include "Core/HW/DVD/AMMediaboard.h" +#include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/EXI/EXI.h" +#include "Core/HW/MMIO.h" +#include "Core/HW/Memmap.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_Device.h" +#include "Core/HW/SI/SI_DeviceGCController.h" +#include "Core/HW/Sram.h" +#include "Core/HW/WiimoteReal/WiimoteReal.h" +#include "Core/Movie.h" +#include "Core/NetPlayProto.h" +#include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" +#include "Core/WiiRoot.h" +#include "DiscIO/Enums.h" + +namespace SerialInterface +{ + +JVSIOMessage::JVSIOMessage() +{ + m_ptr = 0; + m_last_start = 0; +} + +void JVSIOMessage::start(int node) +{ + m_last_start = m_ptr; + u8 hdr[3] = {0xE0, (u8)node, 0}; + m_csum = 0; + addData(hdr, 3, 1); +} + +void JVSIOMessage::addData(const u8* dst, size_t len, int sync = 0) +{ + while (len--) + { + int c = *dst++; + if (!sync && ((c == 0xE0) || (c == 0xD0))) + { + m_msg[m_ptr++] = 0xD0; + m_msg[m_ptr++] = c - 1; + } + else + { + m_msg[m_ptr++] = c; + } + + if (!sync) + m_csum += c; + sync = 0; + if (m_ptr >= 0x80) + PanicAlertFmt("JVSIOMessage overrun!"); + } +} + +void JVSIOMessage::addData(const void* data, size_t len) +{ + addData((const u8*)data, len); +} + +void JVSIOMessage::addData(const char* data) +{ + addData(data, strlen(data)); +} + +void JVSIOMessage::addData(u32 n) +{ + u8 cs = n; + addData(&cs, 1); +} + +void JVSIOMessage::end() +{ + u32 len = m_ptr - m_last_start; + m_msg[m_last_start + 2] = len - 2; // assuming len <0xD0 + addData(m_csum + len - 2); +} + +static u8 CheckSumXOR(u8* Data, u32 Length) +{ + u8 check = 0; + + for (u32 i = 0; i < Length; i++) + { + check ^= Data[i]; + } + + return check; +} + +static u8 last[2][0x80]; +static u32 lastptr[2]; +/* + Reply has to be delayed due a bug in the parser +*/ +static void swap_buffers(u8* buffer, u32* buffer_length) +{ + memcpy(last[1], buffer, 0x80); // Save current buffer + memcpy(buffer, last[0], 0x80); // Load previous buffer + memcpy(last[0], last[1], 0x80); // Update history + + lastptr[1] = *buffer_length; // Swap lengths + *buffer_length = lastptr[0]; + lastptr[0] = lastptr[1]; +} + +static const char s_cdr_program_version[] = {" Version 1.22,2003/09/19,171-8213B"}; +static const char s_cdr_boot_version[] = {" Version 1.04,2003/06/17,171-8213B"}; +static const u8 s_cdr_card_data[] = { + 0x00, 0x6E, 0x00, 0x00, 0x01, 0x00, 0x00, 0x06, 0x00, 0x00, 0x07, 0x00, 0x00, 0x0B, + 0x00, 0x00, 0x0E, 0x00, 0x00, 0x10, 0x00, 0x00, 0x17, 0x00, 0x00, 0x19, 0x00, 0x00, + 0x1A, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x1D, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x20, 0x00, + 0x00, 0x22, 0x00, 0x00, 0x23, 0x00, 0x00, 0x24, 0x00, 0x00, 0x27, 0x00, 0x00, 0x28, + 0x00, 0x00, 0x2C, 0x00, 0x00, 0x2F, 0x00, 0x00, 0x34, 0x00, 0x00, 0x35, 0x00, 0x00, + 0x37, 0x00, 0x00, 0x38, 0x00, 0x00, 0x39, 0x00, 0x00, 0x3D, 0x00}; + +// AM-Baseboard device on SI +CSIDevice_AMBaseboard::CSIDevice_AMBaseboard(Core::System& system, SIDevices device, + int device_number) + : ISIDevice(system, device, device_number) +{ + memset(m_coin, 0, sizeof(m_coin)); + + // Setup IC-card + m_ic_card_state = 0x20; + m_ic_card_status = 0; + m_ic_card_session = 0x23; + + m_ic_write_size = 0; + m_ic_write_offset = 0; + + memset(m_ic_write_buffer, 0, sizeof(m_ic_write_buffer)); + memset(m_ic_card_data, 0, sizeof(m_ic_card_data)); + + // Card ID + m_ic_card_data[0x20] = 0x95; + m_ic_card_data[0x21] = 0x71; + + if (AMMediaboard::GetGameType() == KeyOfAvalon) + { + m_ic_card_data[0x22] = 0x26; + m_ic_card_data[0x23] = 0x40; + } + else if (AMMediaboard::GetGameType() == VirtuaStriker4) + { + m_ic_card_data[0x22] = 0x44; + m_ic_card_data[0x23] = 0x00; + } + + // Use count + m_ic_card_data[0x28] = 0xFF; + m_ic_card_data[0x29] = 0xFF; + + // Setup CARD + m_card_memory_size = 0; + m_card_is_inserted = 0; + + m_card_offset = 0; + m_card_command = 0; + m_card_clean = 0; + + m_card_write_length = 0; + m_card_wrote = 0; + + m_card_read_length = 0; + m_card_read = 0; + + m_card_bit = 0; + m_card_shutter = 1; // Open + m_card_state_call_count = 0; + + // Serial + m_wheelinit = 0; + + m_motorinit = 0; + m_motorforce_x = 0; + + m_fzdx_seatbelt = 1; + m_fzdx_motion_stop = 0; + m_fzdx_sensor_right = 0; + m_fzdx_sensor_left = 0; + + m_rx_reply = 0xF0; + + m_fzcc_seatbelt = 1; + m_fzcc_sensor = 0; + m_fzcc_emergency = 0; + m_fzcc_service = 0; + + memset(m_motorreply, 0, sizeof(m_motorreply)); +} + +constexpr u32 SI_XFER_LENGTH_MASK = 0x7f; + +// Translate [0,1,2,...,126,127] to [128,1,2,...,126,127] +constexpr s32 ConvertSILengthField(u32 field) +{ + return ((field - 1) & SI_XFER_LENGTH_MASK) + 1; +} + +void CSIDevice_AMBaseboard::ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length) +{ + iccommand->status = Common::swap16(iccommand->status); + + u16 crc = CheckSumXOR(iccommand->data + 2, iccommand->pktlen - 1); + + for (u32 i = 0; i < iccommand->pktlen + 1; ++i) + { + buffer[(*length)++] = iccommand->data[i]; + } + + buffer[(*length)++] = crc; +} + +int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) +{ + const auto& serial_interface = m_system.GetSerialInterface(); + u32 buffer_length = ConvertSILengthField(serial_interface.GetInLength()); + + // Debug logging + ISIDevice::RunBuffer(buffer, buffer_length); + + u32 buffer_position = 0; + while (buffer_position < buffer_length) + { + BaseBoardCommand command = static_cast(buffer[buffer_position]); + buffer_position++; + + switch (command) + { + case BaseBoardCommand::GCAM_Reset: // Returns ID and dip switches + { + u32 id = Common::swap32(SI_AM_BASEBOARD | 0x100); + std::memcpy(buffer, &id, sizeof(id)); + return sizeof(id); + } + break; + case BaseBoardCommand::GCAM_Command: + { + u32 checksum = 0; + for (u32 i = 0; i < buffer_length; ++i) + checksum += buffer[i]; + + u8 data_out[0x80]; + u32 data_offset = 0; + + static u32 dip_switch_1 = 0xFE; + static u32 dip_switch_0 = 0xFF; + + memset(data_out, 0, sizeof(data_out)); + data_out[data_offset++] = 1; + data_out[data_offset++] = 1; + + u8* data_in = buffer + 2; + u8* data_in_end = buffer + buffer[buffer_position] + 2; + + while (data_in < data_in_end) + { + u32 gcam_command = *data_in++; + switch (GCAMCommand(gcam_command)) + { + case GCAMCommand::StatusSwitches: + { + u8 status = *data_in++; + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x10, {:02x} (READ STATUS&SWITCHES)", + status); + + GCPadStatus PadStatus; + PadStatus = Pad::GetStatus(ISIDevice::m_device_number); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x2; + + /* baseboard test/service switches + if (PadStatus.button & PAD_BUTTON_Y) // Test + dip_switch_0 &= ~0x80; + if (PadStatus.button & PAD_BUTTON_X) // Service + dip_switch_0 &= ~0x40; + */ + + // Horizontal Scanning Frequency switch + // Required for F-Zero AX booting via Sega Boot + if (AMMediaboard::GetGameType() == FZeroAX || + AMMediaboard::GetGameType() == FZeroAXMonster) + { + dip_switch_0 &= ~0x20; + } + + // Disable camera in MKGP1/2 + if (AMMediaboard::GetGameType() == MarioKartGP || + AMMediaboard::GetGameType() == MarioKartGP2) + { + dip_switch_0 &= ~0x10; + } + + data_out[data_offset++] = dip_switch_0; + data_out[data_offset++] = dip_switch_1; + break; + } + case GCAMCommand::SerialNumber: + { + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x11, {:02x} (READ SERIAL NR)", + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 16; + memcpy(data_out + data_offset, "AADE-01B98394904", 16); + data_offset += 16; + break; + } + case GCAMCommand::Unknown_12: + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x12, {:02x} {:02x}", *data_in++, + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; + break; + case GCAMCommand::Unknown_14: + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x14, {:02x} {:02x}", *data_in++, + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; + break; + case GCAMCommand::FirmVersion: + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x15, {:02x} (READ FIRM VERSION)", + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x02; + // 00.26 + data_out[data_offset++] = 0x00; + data_out[data_offset++] = 0x26; + break; + case GCAMCommand::FPGAVersion: + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x16, {:02x} (READ FPGA VERSION)", + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x02; + // 07.06 + data_out[data_offset++] = 0x07; + data_out[data_offset++] = 0x06; + break; + case GCAMCommand::RegionSettings: + { + // Used by SegaBoot for region checks (dev mode skips this check) + // In some games this also controls the displayed language + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command 0x1F, {:02x} {:02x} {:02x} {:02x} {:02x} (REGION)", + *data_in++, *data_in++, *data_in++, *data_in++, *data_in++); + u8 string[] = "\x00\x00\x30\x00" + // "\x01\xfe\x00\x00" // JAPAN + "\x02\xfd\x00\x00" // USA + // "\x03\xfc\x00\x00" // export + "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x14; + + for (int i = 0; i < 0x14; ++i) + data_out[data_offset++] = string[i]; + } + break; + /* No reply + Note: Always sends three bytes even though size is set to two + */ + case GCAMCommand::Unknown_21: + { + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x21, {:02x}, {:02x}, {:02x}, {:02x}", + data_in[0], data_in[1], data_in[2], data_in[3]); + data_in += 4; + } + break; + /* No reply + Note: Always sends six bytes + */ + case GCAMCommand::Unknown_22: + { + DEBUG_LOG_FMT( + SERIALINTERFACE_AMBB, + "GC-AM: Command 0x22, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}, {:02x}", + data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], data_in[6]); + data_in += data_in[0] + 1; + } + break; + case GCAMCommand::Unknown_23: + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x23, {:02x} {:02x}", *data_in++, + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; + break; + case GCAMCommand::Unknown_24: + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x24, {:02x} {:02x}", *data_in++, + *data_in++); + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; + break; + case GCAMCommand::SerialA: + { + u32 length = *data_in++; + if (length) + { + INFO_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command 0x31, {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} " + "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + length, data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], + data_in[5], data_in[6], data_in[7], data_in[8], data_in[9], data_in[10], + data_in[11], data_in[12]); + + // Serial - Wheel + if (AMMediaboard::GetGameType() == MarioKartGP || + AMMediaboard::GetGameType() == MarioKartGP2) + { + INFO_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command 0x31, (WHEEL) {:02x}{:02x} {:02x}{:02x} {:02x} {:02x} " + "{:02x} {:02x} {:02x} {:02x}", + data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], + data_in[6], data_in[7], data_in[8], data_in[9]); + + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x03; + + switch (m_wheelinit) + { + case 0: + data_out[data_offset++] = 'E'; // Error + data_out[data_offset++] = '0'; + data_out[data_offset++] = '0'; + m_wheelinit++; + break; + case 1: + data_out[data_offset++] = 'C'; // Power Off + data_out[data_offset++] = '0'; + data_out[data_offset++] = '6'; + // Only turn on when a wheel is connected + if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING) + { + m_wheelinit++; + } + break; + case 2: + data_out[data_offset++] = 'C'; // Power On + data_out[data_offset++] = '0'; + data_out[data_offset++] = '1'; + break; + default: + break; + } + /* + u16 CenteringForce= ptr(6); + u16 FrictionForce = ptr(8); + u16 Roll = ptr(10); + */ + + data_in += length; + break; + } + + // Serial - Unknown + if (AMMediaboard::GetGameType() == GekitouProYakyuu) + { + u32 serial_command = *(u32*)(data_in); + + if (serial_command == 0x00001000) + { + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x03; + data_out[data_offset++] = 1; + data_out[data_offset++] = 2; + data_out[data_offset++] = 3; + } + + data_in += length; + break; + } + + // Serial IC-CARD / Serial Deck Reader + if (AMMediaboard::GetGameType() == VirtuaStriker4 || + AMMediaboard::GetGameType() == KeyOfAvalon) + { + u32 serial_command = data_in[1]; + + ICCommand icco; + + // Set default reply + icco.pktcmd = gcam_command; + icco.pktlen = 7; + icco.fixed = 0x10; + icco.command = serial_command; + icco.flag = 0; + icco.length = 2; + icco.status = 0; + icco.extlen = 0; + + // Check for rest of data from the write pages command + if (m_ic_write_size && m_ic_write_offset) + { + u32 size = data_in[1]; + + char logptr[1024]; + char* log = logptr; + + for (u32 i = 0; i < (u32)(data_in[1] + 2); ++i) + { + log += sprintf(log, "%02X ", data_in[i]); + } + + INFO_LOG_FMT(SERIALINTERFACE_CARD, "Command: {}", logptr); + + INFO_LOG_FMT( + SERIALINTERFACE_CARD, + "GC-AM: Command 25 (IC-CARD) Write Pages: Off:{:x} Size:{:x} PSize:{:x}", + m_ic_write_offset, m_ic_write_size, size); + + memcpy(m_ic_write_buffer + m_ic_write_offset, data_in + 2, size); + + m_ic_write_offset += size; + + if (m_ic_write_offset > m_ic_write_size) + { + m_ic_write_offset = 0; + + u16 page = m_ic_write_buffer[5]; + u16 count = m_ic_write_buffer[7]; + + memcpy(m_ic_card_data + page * 8, m_ic_write_buffer + 10, count * 8); + + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 25 (IC-CARD) Write Pages:{} Count:{}({:x})", page, + count, size); + + icco.command = WritePages; + + ICCardSendReply(&icco, data_out, &data_offset); + } + data_in += length; + break; + } + + switch (ICCARDCommand(serial_command)) + { + case ICCARDCommand::GetStatus: + icco.status = m_ic_card_state; + + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (IC-CARD) Get Status:{:02x}", m_ic_card_state); + break; + case ICCARDCommand::SetBaudrate: + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Set Baudrate"); + break; + case ICCARDCommand::FieldOn: + m_ic_card_state |= 0x10; + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Field On"); + break; + case ICCARDCommand::InsertCheck: + icco.status = m_ic_card_status; + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (IC-CARD) Insert Check:{:02x}", m_ic_card_status); + break; + case ICCARDCommand::AntiCollision: + icco.extlen = 8; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + // Card ID + icco.extdata[0] = 0x00; + icco.extdata[1] = 0x00; + icco.extdata[2] = 0x54; + icco.extdata[3] = 0x4D; + icco.extdata[4] = 0x50; + icco.extdata[5] = 0x00; + icco.extdata[6] = 0x00; + icco.extdata[7] = 0x00; + + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Anti Collision"); + break; + case ICCARDCommand::SelectCard: + icco.extlen = 8; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + // Session + icco.extdata[0] = 0x00; + icco.extdata[1] = m_ic_card_session; + icco.extdata[2] = 0x00; + icco.extdata[3] = 0x00; + icco.extdata[4] = 0x00; + icco.extdata[5] = 0x00; + icco.extdata[6] = 0x00; + icco.extdata[7] = 0x00; + + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Select Card:{}", + m_ic_card_session); + break; + case ICCARDCommand::ReadPage: + case ICCARDCommand::ReadUseCount: + { + u16 page = Common::swap16(*(u16*)(data_in + 6)); + + icco.extlen = 8; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + memcpy(icco.extdata, m_ic_card_data + page * 8, 8); + + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 31 (IC-CARD) Read Page:{}", + page); + break; + } + case ICCARDCommand::WritePage: + { + u16 page = Common::swap16(*(u16*)(data_in + 8)); + + // Write only one page + if (page == 4) + { + icco.status = 0x80; + } + else + { + memcpy(m_ic_card_data + page * 8, data_in + 10, 8); + } + + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (IC-CARD) Write Page:{}", + page); + break; + } + case ICCARDCommand::DecreaseUseCount: + { + u16 page = Common::swap16(*(u16*)(data_in + 6)); + + icco.extlen = 2; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + *(u16*)(m_ic_card_data + 0x28) = *(u16*)(m_ic_card_data + 0x28) - 1; + + // Counter + icco.extdata[0] = m_ic_card_data[0x28]; + icco.extdata[1] = m_ic_card_data[0x29]; + + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 31 (IC-CARD) Decrease Use Count:{}", page); + break; + } + case ICCARDCommand::ReadPages: + { + u16 page = Common::swap16(*(u16*)(data_in + 6)); + u16 count = Common::swap16(*(u16*)(data_in + 8)); + + u32 offs = page * 8; + u32 cnt = count * 8; + + // Limit read size to not overwrite the reply buffer + if (cnt > (u32)0x50 - data_offset) + { + cnt = 5 * 8; + } + + icco.extlen = cnt; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + memcpy(icco.extdata, m_ic_card_data + offs, cnt); + + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 31 (IC-CARD) Read Pages:{} Count:{}", page, count); + break; + } + case ICCARDCommand::WritePages: + { + u16 pksize = length; + u16 size = Common::swap16(*(u16*)(data_in + 2)); + u16 page = Common::swap16(*(u16*)(data_in + 6)); + u16 count = Common::swap16(*(u16*)(data_in + 8)); + + // We got a complete packet + if (pksize - 5 == size) + { + if (page == 4) // Read Only Page, must return error + { + icco.status = 0x80; + } + else + { + memcpy(m_ic_card_data + page * 8, data_in + 13, count * 8); + } + + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (IC-CARD) Write Pages:{} Count:{}({:x})", page, + count, size); + } + // VirtuaStriker 4 splits the writes over multiple packets + else + { + memcpy(m_ic_write_buffer, data_in + 2, pksize); + m_ic_write_offset += pksize; + m_ic_write_size = size; + } + break; + } + default: + // Handle Deck Reader commands + serial_command = data_in[0]; + icco.command = serial_command; + icco.flag = 0; + switch (CDReaderCommand(serial_command)) + { + case CDReaderCommand::ProgramVersion: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Program Version"); + + icco.extlen = (u32)strlen(s_cdr_program_version); + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + memcpy(icco.extdata, s_cdr_program_version, icco.extlen); + break; + case CDReaderCommand::BootVersion: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Boot Version"); + + icco.extlen = (u32)strlen(s_cdr_boot_version); + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + memcpy(icco.extdata, s_cdr_boot_version, icco.extlen); + break; + case CDReaderCommand::ShutterGet: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Shutter Get"); + + icco.extlen = 4; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + icco.extdata[0] = 0; + icco.extdata[1] = 0; + icco.extdata[2] = 0; + icco.extdata[3] = 0; + break; + case CDReaderCommand::CameraCheck: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Camera Check"); + + icco.extlen = 6; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + icco.extdata[0] = 0x23; + icco.extdata[1] = 0x28; + icco.extdata[2] = 0x45; + icco.extdata[3] = 0x29; + icco.extdata[4] = 0x45; + icco.extdata[5] = 0x29; + break; + case CDReaderCommand::ProgramChecksum: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Program Checksum"); + + icco.extlen = 4; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + icco.extdata[0] = 0x23; + icco.extdata[1] = 0x28; + icco.extdata[2] = 0x45; + icco.extdata[3] = 0x29; + break; + case CDReaderCommand::BootChecksum: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Boot Checksum"); + + icco.extlen = 4; + icco.length += icco.extlen; + icco.pktlen += icco.extlen; + + icco.extdata[0] = 0x23; + icco.extdata[1] = 0x28; + icco.extdata[2] = 0x45; + icco.extdata[3] = 0x29; + break; + case CDReaderCommand::SelfTest: + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Self Test"); + icco.flag = 0x00; + break; + case CDReaderCommand::SensLock: + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Lock"); + icco.flag = 0x01; + break; + case CDReaderCommand::SensCard: + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Sens Card"); + break; + case CDReaderCommand::ShutterCard: + INFO_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (DECK READER) Shutter Card"); + break; + case CDReaderCommand::ReadCard: + INFO_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command 0x31 (DECK READER) Read Card"); + + icco.fixed = 0xAA; + icco.flag = 0xAA; + icco.extlen = sizeof(s_cdr_card_data); + icco.length = 0x72; + icco.status = Common::swap16(icco.extlen); + + icco.pktlen += icco.extlen; + + memcpy(icco.extdata, s_cdr_card_data, sizeof(s_cdr_card_data)); + + break; + default: + WARN_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command 0x31 (IC-Card) {:02x} {:02x} {:02x} {:02x} {:02x} " + "{:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x}", + data_in[2], data_in[3], data_in[4], data_in[5], data_in[6], + data_in[7], data_in[8], data_in[9], data_in[10], data_in[11], + data_in[12], data_in[13]); + break; + } + break; + } + + ICCardSendReply(&icco, data_out, &data_offset); + + data_in += length; + break; + } + } + + u32 command_offset = 0; + while (command_offset < length) + { + // All commands are OR'd with 0x80 + // Last byte is checksum which we don't care about + u32 serial_command = Common::swap32(*(u32*)(data_in + command_offset)); + serial_command ^= 0x80000000; + if (AMMediaboard::GetGameType() == FZeroAX || + AMMediaboard::GetGameType() == FZeroAXMonster) + { + INFO_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command 0x31 (MOTOR) Length:{:02x} Command:{:06x}({:02x})", + length, serial_command >> 8, serial_command & 0xFF); + } + else + { + INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x31 (SERIAL) Command:{:06x}", + serial_command); + + if (/*command == 0xf43200 || */ serial_command == 0x801000) + { + // u32 PC = m_system.GetPowerPC().GetPPCState().pc; + + // INFO_LOG_FMT(SERIALINTERFACE_AMBB, "GCAM: PC:{:08x}", PC); + + // m_system.GetPowerPC().GetBreakPoints().Add(PC + 8, true, true, std::nullopt); + + data_out[data_offset++] = 0x31; + data_out[data_offset++] = 0x02; + data_out[data_offset++] = 0xFF; + data_out[data_offset++] = 0x01; + } + } + + command_offset += 4; + + if (AMMediaboard::GetGameType() == FZeroAX || + AMMediaboard::GetGameType() == FZeroAXMonster) + { + // Status + m_motorreply[command_offset + 2] = 0; + m_motorreply[command_offset + 3] = 0; + + // Error + m_motorreply[command_offset + 4] = 0; + + switch (serial_command >> 24) + { + case 0: + break; + case 1: // Set Maximum? + break; + case 2: + break; + /* + 0x00-0x40: left + 0x40-0x80: right + */ + case 4: // Move Steering Wheel + // Left + if (serial_command & 0x010000) + { + m_motorforce_x = -((s16)serial_command & 0xFF00); + } + else // Right + { + m_motorforce_x = (serial_command - 0x4000) & 0xFF00; + } + + m_motorforce_x *= 2; + + // FFB + if (m_motorinit == 2) + { + if (serial_interface.GetDeviceType(1) == SerialInterface::SIDEVICE_GC_STEERING) + { + GCPadStatus PadStatus; + PadStatus = Pad::GetStatus(1); + if (PadStatus.isConnected) + { + ControlState mapped_strength = (double)(m_motorforce_x >> 8); + mapped_strength /= 127.f; + Pad::Rumble(1, mapped_strength); + INFO_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command 0x31 (MOTOR) mapped_strength:{}", + mapped_strength); + } + } + } + break; + case 6: // nice + case 9: + default: + break; + // Switch back to normal controls + case 7: + m_motorinit = 2; + break; + // Reset + case 0x7F: + m_motorinit = 1; + memset(m_motorreply, 0, sizeof(m_motorreply)); + break; + } + + // Checksum + m_motorreply[command_offset + 5] = m_motorreply[command_offset + 2] ^ + m_motorreply[command_offset + 3] ^ + m_motorreply[command_offset + 4]; + } + } + + if (length == 0) + { + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; + } + else + { + if (m_motorinit) + { + // Motor + m_motorreply[0] = gcam_command; + m_motorreply[1] = length; // Same out as in size + + memcpy(data_out + data_offset, m_motorreply, m_motorreply[1] + 2); + data_offset += m_motorreply[1] + 2; + } + } + + data_in += length; + break; + } + case GCAMCommand::SerialB: + { + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 32 (CARD-Interface)"); + u32 length = *data_in++; + if (length) + { + /* Send Card Reply */ + if (length == 1 && data_in[0] == 0x05) + { + if (m_card_read_length) + { + data_out[data_offset++] = gcam_command; + u32 ReadLength = m_card_read_length - m_card_read; + + if (AMMediaboard::GetGameType() == FZeroAX) + { + if (ReadLength > 0x2F) + ReadLength = 0x2F; + } + + data_out[data_offset++] = ReadLength; // 0x2F (max size per packet) + + memcpy(data_out + data_offset, m_card_read_packet + m_card_read, ReadLength); + + data_offset += ReadLength; + m_card_read += ReadLength; + + if (m_card_read >= m_card_read_length) + m_card_read_length = 0; + + data_in += length; + break; + } + + data_out[data_offset++] = gcam_command; + u32 command_length_offset = data_offset; + data_out[data_offset++] = 0x00; // len + + data_out[data_offset++] = 0x02; // + u32 checksum_start = data_offset; + + data_out[data_offset++] = 0x00; // 0x00 len + + data_out[data_offset++] = m_card_command; // 0x01 command + + switch (CARDCommand(m_card_command)) + { + case CARDCommand::Init: + data_out[data_offset++] = 0x00; // 0x02 + data_out[data_offset++] = 0x30; // 0x03 + break; + case CARDCommand::GetState: + data_out[data_offset++] = 0x20 | m_card_bit; // 0x02 + /* + bit 0: Please take your card + bit 1: endless waiting causes UNK_E to be called + */ + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::Read: + data_out[data_offset++] = 0x02; // 0x02 + data_out[data_offset++] = 0x53; // 0x03 + break; + case CARDCommand::IsPresent: + data_out[data_offset++] = 0x22; // 0x02 + data_out[data_offset++] = 0x30; // 0x03 + break; + case CARDCommand::Write: + data_out[data_offset++] = 0x02; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::SetPrintParam: + data_out[data_offset++] = 0x00; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::RegisterFont: + data_out[data_offset++] = 0x00; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::WriteInfo: + data_out[data_offset++] = 0x02; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::Eject: + if (AMMediaboard::GetGameType() == FZeroAX) + { + data_out[data_offset++] = 0x01; // 0x02 + } + else + { + data_out[data_offset++] = 0x31; // 0x02 + } + data_out[data_offset++] = 0x30; // 0x03 + break; + case CARDCommand::Clean: + data_out[data_offset++] = 0x02; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + case CARDCommand::Load: + data_out[data_offset++] = 0x02; // 0x02 + data_out[data_offset++] = 0x30; // 0x03 + break; + case CARDCommand::SetShutter: + data_out[data_offset++] = 0x00; // 0x02 + data_out[data_offset++] = 0x00; // 0x03 + break; + } + + data_out[data_offset++] = 0x30; // 0x04 + data_out[data_offset++] = 0x00; // 0x05 + + data_out[data_offset++] = 0x03; // 0x06 + + data_out[checksum_start] = data_offset - checksum_start; // 0x00 len + + u32 i; + data_out[data_offset] = 0; // 0x07 + for (i = 0; i < data_out[checksum_start]; ++i) + data_out[data_offset] ^= data_out[checksum_start + i]; + + data_offset++; + + data_out[command_length_offset] = data_out[checksum_start] + 2; + } + else + { + for (u32 i = 0; i < length; ++i) + m_card_buffer[m_card_offset + i] = data_in[i]; + + m_card_offset += length; + + // Check if we got a complete command + + if (m_card_buffer[0] == 0x02) + if (m_card_buffer[1] == m_card_offset - 2) + { + if (m_card_buffer[m_card_offset - 2] == 0x03) + { + m_card_command = m_card_buffer[2]; + + switch (CARDCommand(m_card_command)) + { + case CARDCommand::Init: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Init"); + + m_card_write_length = 0; + m_card_bit = 0; + m_card_memory_size = 0; + m_card_state_call_count = 0; + break; + case CARDCommand::GetState: + { + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD GetState({:02X})", + m_card_bit); + + if (m_card_memory_size == 0) + { + std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + + SConfig::GetInstance().GetTriforceID().c_str() + + ".bin"); + + if (File::Exists(card_filename)) + { + File::IOFile card = File::IOFile(card_filename, "rb+"); + m_card_memory_size = (u32)card.GetSize(); + + card.ReadBytes(m_card_memory, m_card_memory_size); + card.Close(); + + m_card_is_inserted = 1; + } + } + + if (AMMediaboard::GetGameType() == FZeroAX && m_card_memory_size) + { + m_card_state_call_count++; + if (m_card_state_call_count > 10) + { + if (m_card_bit & 2) + m_card_bit &= ~2; + else + m_card_bit |= 2; + + m_card_state_call_count = 0; + } + } + + if (m_card_clean == 1) + { + m_card_clean = 2; + } + else if (m_card_clean == 2) + { + std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + + SConfig::GetInstance().GetTriforceID().c_str() + + ".bin"); + + if (File::Exists(card_filename)) + { + m_card_memory_size = (u32)File::GetSize(card_filename); + if (m_card_memory_size) + { + if (AMMediaboard::GetGameType() == FZeroAX) + { + m_card_bit = 2; + } + else + { + m_card_bit = 1; + } + } + } + m_card_clean = 0; + } + break; + } + case CARDCommand::IsPresent: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD IsPresent"); + break; + case CARDCommand::RegisterFont: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD RegisterFont"); + break; + case CARDCommand::Load: + { + u8 mode = m_card_buffer[6]; + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Load({:02X})", + mode); + break; + } + case CARDCommand::Clean: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Clean"); + m_card_clean = 1; + break; + case CARDCommand::Read: + { + u8 mode = m_card_buffer[6]; + u8 bitmode = m_card_buffer[7]; + u8 track = m_card_buffer[8]; + + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command CARD Read({:02X},{:02X},{:02X})", mode, + bitmode, track); + + // Prepare read packet + memset(m_card_read_packet, 0, 0xDB); + u32 packet_offset = 0; + + std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + + SConfig::GetInstance().GetTriforceID().c_str() + + ".bin"); + + if (File::Exists(card_filename)) + { + File::IOFile card = File::IOFile(card_filename, "rb+"); + if (m_card_memory_size == 0) + { + m_card_memory_size = (u32)card.GetSize(); + } + + card.ReadBytes(m_card_memory, m_card_memory_size); + card.Close(); + + m_card_is_inserted = 1; + } + + m_card_read_packet[packet_offset++] = 0x02; // SUB CMD + m_card_read_packet[packet_offset++] = 0x00; // SUB CMDLen + + m_card_read_packet[packet_offset++] = 0x33; // CARD CMD + + if (m_card_is_inserted) // CARD Status + { + m_card_read_packet[packet_offset++] = 0x31; + } + else + { + m_card_read_packet[packet_offset++] = 0x30; + } + + m_card_read_packet[packet_offset++] = 0x30; // + m_card_read_packet[packet_offset++] = 0x30; // + + // Data reply + memcpy(m_card_read_packet + packet_offset, m_card_memory, m_card_memory_size); + packet_offset += m_card_memory_size; + + m_card_read_packet[packet_offset++] = 0x03; + + m_card_read_packet[1] = packet_offset - 1; // SUB CMDLen + + u32 i; + for (i = 0; i < packet_offset - 1; ++i) + m_card_read_packet[packet_offset] ^= m_card_read_packet[1 + i]; + + packet_offset++; + + m_card_read_length = packet_offset; + m_card_read = 0; + break; + } + case CARDCommand::Write: + { + u8 mode = m_card_buffer[6]; + u8 bitmode = m_card_buffer[7]; + u8 track = m_card_buffer[8]; + + m_card_memory_size = m_card_buffer[1] - 9; + + memcpy(m_card_memory, m_card_buffer + 9, m_card_memory_size); + + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, + "GC-AM: Command CARD Write: {:02X} {:02X} {:02X} {}", mode, + bitmode, track, m_card_memory_size); + + std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + + SConfig::GetInstance().GetTriforceID().c_str() + + ".bin"); + + File::IOFile card = File::IOFile(card_filename, "wb+"); + card.WriteBytes(m_card_memory, m_card_memory_size); + card.Close(); + + m_card_bit = 2; + + m_card_state_call_count = 0; + break; + } + case CARDCommand::SetPrintParam: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetPrintParam"); + break; + case CARDCommand::WriteInfo: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD WriteInfo"); + break; + case CARDCommand::Erase: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Erase"); + break; + case CARDCommand::Eject: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD Eject"); + if (AMMediaboard::GetGameType() != FZeroAX) + { + m_card_bit = 0; + } + break; + case CARDCommand::SetShutter: + NOTICE_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: Command CARD SetShutter"); + if (AMMediaboard::GetGameType() != FZeroAX) + { + m_card_bit = 0; + } + // Close + if (m_card_buffer[6] == 0x30) + { + m_card_shutter = 0; + } + // Open + else if (m_card_buffer[6] == 0x31) + { + m_card_shutter = 1; + } + break; + default: + ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:Unhandled command!"); + ERROR_LOG_FMT(SERIALINTERFACE_CARD, "GC-AM: CARD:[{:08X}]", m_card_command); + // hexdump( m_card_buffer, m_card_offset ); + break; + } + m_card_offset = 0; + } + } + + data_out[data_offset++] = 0x32; + data_out[data_offset++] = 0x01; // len + data_out[data_offset++] = 0x06; // OK + } + } + else + { + data_out[data_offset++] = gcam_command; + data_out[data_offset++] = 0x00; // len + } + data_in += length; + break; + } + case GCAMCommand::JVSIOA: + case GCAMCommand::JVSIOB: + { + DEBUG_LOG_FMT( + SERIALINTERFACE_JVSIO, + "GC-AM: Command {:02x}, {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} {:02x} (JVS IO)", + gcam_command, data_in[0], data_in[1], data_in[2], data_in[3], data_in[4], data_in[5], + data_in[6]); + JVSIOMessage message; + + static int delay = 0; + + u8* frame = &data_in[0]; + u8 nr_bytes = frame[3]; // Byte after E0 xx + u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum + + u8 jvs_buf[0x80]; + memcpy(jvs_buf, frame, frame_len); + + // Extract node and payload pointers + u8 node = jvs_buf[2]; + u8* jvs_io = jvs_buf + 4; // First payload byte + u8* jvs_end = jvs_buf + frame_len; // One byte before checksum + + message.start(0); + message.addData(1); + + // Now iterate over the payload + while (jvs_io < jvs_end) + { + int jvsio_command = *jvs_io++; + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:node={}, command={:02x}", node, + jvsio_command); + + switch (JVSIOCommand(jvsio_command)) + { + case JVSIOCommand::IOID: + message.addData(StatusOkay); + switch (AMMediaboard::GetGameType()) + { + case FZeroAX: + // Specific version that enables DX mode on AX machines, all this does is enable the + // motion of a chair + message.addData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;"); + break; + case FZeroAXMonster: + case MarioKartGP: + case MarioKartGP2: + default: + message.addData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"); + break; + case VirtuaStriker3: + case VirtuaStriker4: + message.addData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00"); + break; + } + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID"); + message.addData((u32)0); + break; + case JVSIOCommand::CommandRevision: + message.addData(StatusOkay); + message.addData(0x11); + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision"); + break; + case JVSIOCommand::JVRevision: + message.addData(StatusOkay); + message.addData(0x20); + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision"); + break; + case JVSIOCommand::CommunicationVersion: + message.addData(StatusOkay); + message.addData(0x10); + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion"); + break; + /* + Slave features: + + Inputs: + 0x01: Switchinput: players, buttons + 0x02: Coininput: slots + 0x03: Analoginput: channels, bits + 0x04: Rotary input: channels + 0x05: Keycode input: 0,0,0 ? + 0x06: Screen position input: Xbits, Ybits, channels + + Outputs: + 0x10: Card system: slots + 0x11: Medal hopper: channels + 0x12: GPO-out: slots + 0x13: Analog output: channels + 0x14: Character output: width, height, type + 0x15: Backup + */ + case JVSIOCommand::CheckFunctionality: + message.addData(StatusOkay); + switch (AMMediaboard::GetGameType()) + { + case FZeroAX: + case FZeroAXMonster: + // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in + // message.addData((void *)"\x01\x02\x0C\x00", 4); + // message.addData((void *)"\x02\x01\x00\x00", 4); + // message.addData((void *)"\x03\x06\x00\x00", 4); + // message.addData((void *)"\x00\x00\x00\x00", 4); + // + // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in, + // 22 Driver-out + message.addData((void*)"\x01\x02\x12\x00", 4); + message.addData((void*)"\x02\x02\x00\x00", 4); + message.addData((void*)"\x03\x08\x0A\x00", 4); + message.addData((void*)"\x12\x16\x00\x00", 4); + message.addData((void*)"\x00\x00\x00\x00", 4); + break; + case VirtuaStriker3: + case GekitouProYakyuu: + // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out + message.addData((void*)"\x01\x02\x0D\x00", 4); + message.addData((void*)"\x02\x02\x00\x00", 4); + message.addData((void*)"\x03\x04\x00\x00", 4); + message.addData((void*)"\x10\x01\x00\x00", 4); + message.addData((void*)"\x12\x08\x00\x00", 4); + message.addData((void*)"\x00\x00\x00\x00", 4); + break; + case VirtuaStriker4: + // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD + message.addData((void*)"\x01\x02\x0D\x00", 4); + message.addData((void*)"\x02\x01\x00\x00", 4); + message.addData((void*)"\x03\x04\x00\x00", 4); + message.addData((void*)"\x10\x01\x00\x00", 4); + message.addData((void*)"\x00\x00\x00\x00", 4); + break; + case KeyOfAvalon: + // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out + // (Unconfirmed) + message.addData((void*)"\x01\x01\x0F\x00", 4); + message.addData((void*)"\x02\x01\x00\x00", 4); + message.addData((void*)"\x03\x03\x00\x00", 4); + message.addData((void*)"\x06\x10\x10\x01", 4); + message.addData((void*)"\x10\x01\x00\x00", 4); + message.addData((void*)"\x12\x01\x00\x00", 4); + message.addData((void*)"\x00\x00\x00\x00", 4); + break; + case MarioKartGP: + case MarioKartGP2: + default: + // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out + message.addData((void*)"\x01\x01\x0F\x00", 4); + message.addData((void*)"\x02\x01\x00\x00", 4); + message.addData((void*)"\x03\x03\x00\x00", 4); + message.addData((void*)"\x10\x01\x00\x00", 4); + message.addData((void*)"\x12\x01\x00\x00", 4); + message.addData((void*)"\x00\x00\x00\x00", 4); + break; + } + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality"); + break; + case JVSIOCommand::MainID: + while (*jvs_io++) + { + }; + message.addData(StatusOkay); + break; + case JVSIOCommand::SwitchesInput: + { + int player_count = *jvs_io++; + int player_byte_count = *jvs_io++; + + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}", + player_count, player_byte_count); + + message.addData(StatusOkay); + + GCPadStatus PadStatus; + PadStatus = Pad::GetStatus(0); + + // Test button + if (PadStatus.button & PAD_TRIGGER_Z) + message.addData(0x80); + else + message.addData((u32)0x00); + + for (int i = 0; i < player_count; ++i) + { + unsigned char player_data[3] = {0, 0, 0}; + + switch (AMMediaboard::GetGameType()) + { + // Controller configuration for F-Zero AX (DX) + case FZeroAX: + PadStatus = Pad::GetStatus(0); + if (i == 0) + { + if (m_fzdx_seatbelt) + { + player_data[0] |= 0x01; + } + + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Boost + if (PadStatus.button & PAD_BUTTON_Y) + player_data[0] |= 0x02; + // View Change 1 + if (PadStatus.button & PAD_BUTTON_RIGHT) + player_data[0] |= 0x20; + // View Change 2 + if (PadStatus.button & PAD_BUTTON_LEFT) + player_data[0] |= 0x10; + // View Change 3 + if (PadStatus.button & PAD_BUTTON_UP) + player_data[0] |= 0x08; + // View Change 4 + if (PadStatus.button & PAD_BUTTON_DOWN) + player_data[0] |= 0x04; + player_data[1] = m_rx_reply & 0xF0; + } + else if (i == 1) + { + // Paddle left + if (PadStatus.button & PAD_BUTTON_A) + player_data[0] |= 0x20; + // Paddle right + if (PadStatus.button & PAD_BUTTON_B) + player_data[0] |= 0x10; + + if (m_fzdx_motion_stop) + { + player_data[0] |= 2; + } + if (m_fzdx_sensor_right) + { + player_data[0] |= 4; + } + if (m_fzdx_sensor_left) + { + player_data[0] |= 8; + } + + player_data[1] = m_rx_reply << 4; + } + break; + // Controller configuration for F-Zero AX MonsterRide + case FZeroAXMonster: + PadStatus = Pad::GetStatus(0); + if (i == 0) + { + if (m_fzcc_sensor) + { + player_data[0] |= 0x01; + } + + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Boost + if (PadStatus.button & PAD_BUTTON_Y) + player_data[0] |= 0x02; + // View Change 1 + if (PadStatus.button & PAD_BUTTON_RIGHT) + player_data[0] |= 0x20; + // View Change 2 + if (PadStatus.button & PAD_BUTTON_LEFT) + player_data[0] |= 0x10; + // View Change 3 + if (PadStatus.button & PAD_BUTTON_UP) + player_data[0] |= 0x08; + // View Change 4 + if (PadStatus.button & PAD_BUTTON_DOWN) + player_data[0] |= 0x04; + + player_data[1] = m_rx_reply & 0xF0; + } + else if (i == 1) + { + // Paddle left + if (PadStatus.button & PAD_BUTTON_A) + player_data[0] |= 0x20; + // Paddle right + if (PadStatus.button & PAD_BUTTON_B) + player_data[0] |= 0x10; + + if (m_fzcc_seatbelt) + { + player_data[0] |= 2; + } + if (m_fzcc_service) + { + player_data[0] |= 4; + } + if (m_fzcc_emergency) + { + player_data[0] |= 8; + } + } + break; + // Controller configuration for Virtua Striker 3 games + case VirtuaStriker3: + PadStatus = Pad::GetStatus(i); + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Long Pass + if (PadStatus.button & PAD_TRIGGER_L) + player_data[0] |= 0x01; + // Short Pass + if (PadStatus.button & PAD_TRIGGER_R) + player_data[1] |= 0x80; + // Shoot + if (PadStatus.button & PAD_BUTTON_A) + player_data[0] |= 0x02; + // Left + if (PadStatus.button & PAD_BUTTON_LEFT) + player_data[0] |= 0x08; + // Up + if (PadStatus.button & PAD_BUTTON_UP) + player_data[0] |= 0x20; + // Right + if (PadStatus.button & PAD_BUTTON_RIGHT) + player_data[0] |= 0x04; + // Down + if (PadStatus.button & PAD_BUTTON_DOWN) + player_data[0] |= 0x10; + break; + // Controller configuration for Virtua Striker 4 games + case VirtuaStriker4: + { + PadStatus = Pad::GetStatus(i); + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Long Pass + if (PadStatus.button & PAD_TRIGGER_L) + player_data[0] |= 0x01; + // Short Pass + if (PadStatus.button & PAD_TRIGGER_R) + player_data[0] |= 0x02; + // Shoot + if (PadStatus.button & PAD_BUTTON_A) + player_data[1] |= 0x80; + // Dash + if (PadStatus.button & PAD_BUTTON_B) + player_data[1] |= 0x40; + // Tactics (U) + if (PadStatus.button & PAD_BUTTON_LEFT) + player_data[0] |= 0x20; + // Tactics (M) + if (PadStatus.button & PAD_BUTTON_UP) + player_data[0] |= 0x08; + // Tactics (D) + if (PadStatus.button & PAD_BUTTON_RIGHT) + player_data[0] |= 0x04; + + if (i == 0) + { + player_data[0] |= 0x10; // IC-Card Switch ON + + // IC-Card Lock + if (PadStatus.button & PAD_BUTTON_DOWN) + player_data[1] |= 0x20; + } + } + break; + // Controller configuration for Gekitou Pro Yakyuu + case GekitouProYakyuu: + PadStatus = Pad::GetStatus(i); + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // A + if (PadStatus.button & PAD_BUTTON_B) + player_data[0] |= 0x01; + // B + if (PadStatus.button & PAD_BUTTON_A) + player_data[0] |= 0x02; + // Gekitou + if (PadStatus.button & PAD_TRIGGER_L) + player_data[1] |= 0x80; + // Left + if (PadStatus.button & PAD_BUTTON_LEFT) + player_data[0] |= 0x08; + // Up + if (PadStatus.button & PAD_BUTTON_UP) + player_data[0] |= 0x20; + // Right + if (PadStatus.button & PAD_BUTTON_RIGHT) + player_data[0] |= 0x04; + // Down + if (PadStatus.button & PAD_BUTTON_DOWN) + player_data[0] |= 0x10; + break; + // Controller configuration for Mario Kart and other games + default: + case MarioKartGP: + case MarioKartGP2: + { + PadStatus = Pad::GetStatus(0); + // Start + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Item button + if (PadStatus.button & PAD_BUTTON_A) + player_data[1] |= 0x20; + // VS-Cancel button + if (PadStatus.button & PAD_BUTTON_B) + player_data[1] |= 0x02; + } + break; + case KeyOfAvalon: + { + PadStatus = Pad::GetStatus(0); + // Debug On + if (PadStatus.button & PAD_BUTTON_START) + player_data[0] |= 0x80; + // Service button + if (PadStatus.button & PAD_BUTTON_X) + player_data[0] |= 0x40; + // Switch 1 + if (PadStatus.button & PAD_BUTTON_A) + player_data[0] |= 0x04; + // Switch 2 + if (PadStatus.button & PAD_BUTTON_B) + player_data[0] |= 0x08; + // Toggle inserted card + if (PadStatus.button & PAD_TRIGGER_L) + { + m_ic_card_status ^= 0x8000; + } + } + break; + } + + for (int j = 0; j < player_byte_count; ++j) + message.addData(player_data[j]); + } + break; + } + case JVSIOCommand::CoinInput: + { + int slots = *jvs_io++; + message.addData(StatusOkay); + for (int i = 0; i < slots; i++) + { + GCPadStatus PadStatus; + PadStatus = Pad::GetStatus(i); + if ((PadStatus.button & PAD_TRIGGER_Z) && !m_coin_pressed[i]) + { + m_coin[i]++; + } + m_coin_pressed[i] = PadStatus.button & PAD_TRIGGER_Z; + message.addData((m_coin[i] >> 8) & 0x3f); + message.addData(m_coin[i] & 0xff); + } + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x21, CoinInput: {}", slots); + break; + } + case JVSIOCommand::AnalogInput: + { + message.addData(StatusOkay); + + int analogs = *jvs_io++; + GCPadStatus PadStatus; + GCPadStatus PadStatus2; + PadStatus = Pad::GetStatus(0); + + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x22, AnalogInput: {}", + analogs); + + switch (AMMediaboard::GetGameType()) + { + case FZeroAX: + case FZeroAXMonster: + // Steering + if (m_motorinit == 1) + { + if (m_motorforce_x > 0) + { + message.addData(0x80 - (m_motorforce_x >> 8)); + } + else + { + message.addData((m_motorforce_x >> 8)); + } + message.addData((u8)0); + + message.addData(PadStatus.stickY); + message.addData((u8)0); + } + else + { + message.addData(PadStatus.stickX); + message.addData((u8)0); + + message.addData(PadStatus.stickY); + message.addData((u8)0); + } + + // Unused + message.addData((u8)0); + message.addData((u8)0); + message.addData((u8)0); + message.addData((u8)0); + + // Gas + message.addData(PadStatus.triggerRight); + message.addData((u8)0); + + // Brake + message.addData(PadStatus.triggerLeft); + message.addData((u8)0); + + message.addData((u8)0x80); // Motion Stop + message.addData((u8)0); + + message.addData((u8)0); + message.addData((u8)0); + + break; + case VirtuaStriker3: + case VirtuaStriker4: + { + PadStatus2 = Pad::GetStatus(1); + + message.addData(PadStatus.stickX); + message.addData((u8)0); + message.addData(PadStatus.stickY); + message.addData((u8)0); + + message.addData(PadStatus2.stickX); + message.addData((u8)0); + message.addData(PadStatus2.stickY); + message.addData((u8)0); + } + break; + default: + case MarioKartGP: + case MarioKartGP2: + // Steering + message.addData(PadStatus.stickX); + message.addData((u8)0); + + // Gas + message.addData(PadStatus.triggerRight); + message.addData((u8)0); + + // Brake + message.addData(PadStatus.triggerLeft); + message.addData((u8)0); + break; + } + break; + } + case JVSIOCommand::PositionInput: + { + int channel = *jvs_io++; + + GCPadStatus PadStatus; + PadStatus = Pad::GetStatus(0); + + if (PadStatus.button & PAD_TRIGGER_R) + { + // Tap at center of screen (~320,240) + message.addData((void*)"\x01\x00\x8C\x01\x95", + 5); // X=320 (0x0140), Y=240 (0x00F0) + } + else + { + message.addData((void*)"\x01\xFF\xFF\xFF\xFF", 5); + } + + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x25, PositionInput:{}", + channel); + break; + } + case JVSIOCommand::CoinSubOutput: + { + u32 slot = *jvs_io++; + m_coin[slot] -= (*jvs_io++ << 8) | *jvs_io++; + message.addData(StatusOkay); + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x30, CoinSubOutput: {}", slot); + break; + } + case JVSIOCommand::GeneralDriverOutput: + { + u32 bytes = *jvs_io++; + + if (bytes) + { + message.addData(StatusOkay); + + // The lamps are controlled via this + if (AMMediaboard::GetGameType() == MarioKartGP) + { + u32 status = *jvs_io++; + if (status & 4) + { + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button ON"); + } + else + { + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Item Button OFF"); + } + if (status & 8) + { + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button ON"); + } + else + { + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 32, Cancel Button OFF"); + } + break; + } + + u8* buf = new u8[bytes]; + + for (u32 i = 0; i < bytes; ++i) + { + buf[i] = *jvs_io++; + } + + INFO_LOG_FMT( + SERIALINTERFACE_JVSIO, + "JVS-IO: Command 0x32, GPO: {:02x} {:02x} {} {:02x}{:02x}{:02x} ({:02x})", + delay, m_rx_reply, bytes, buf[0], buf[1], buf[2], + Common::swap16(*(u16*)(buf + 1)) >> 2); + + // TODO: figure this out + + u8 trepl[] = { + 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, + 0xD0, 0xE0, 0xF0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, + 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, + 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, 0x04, 0x14, 0x24, 0x34, + 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, 0x05, + 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, + 0xE5, 0xF5, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, + 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, + 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x09, 0x19, 0x29, 0x39, 0x49, + 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0A, 0x1A, + 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, + 0xFA, 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, + 0xCC, 0xDC, 0xEC, 0xFC, 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, + 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, + 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE}; + + static u32 off = 0; + if (off > sizeof(trepl)) + off = 0; + + switch (Common::swap16(*(u16*)(buf + 1)) >> 2) + { + case 0x70: + delay++; + if ((delay % 10) == 0) + { + m_rx_reply = 0xFB; + } + break; + case 0xF0: + m_rx_reply = 0xF0; + break; + default: + case 0xA0: + case 0x60: + break; + } + ////if( buf[1] == 1 && buf[2] == 0x80 ) + ////{ + //// INFO_LOG_FMT(DVDINTERFACE, "GCAM: PC:{:08x}", PC); + //// PowerPC::breakpoints.Add( PC+8, false ); + ////} + + delete[] buf; + } + break; + } + case JVSIOCommand::CoinAddOutput: + { + int slot = *jvs_io++; + m_coin[slot] += (*jvs_io++ << 8) | *jvs_io++; + message.addData(StatusOkay); + DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x35, CoinAddOutput: {}", slot); + break; + } + case JVSIOCommand::NAMCOCommand: + { + int cmd_ = *jvs_io++; + if (cmd_ == 0x18) + { // id check + jvs_io += 4; + message.addData(StatusOkay); + message.addData(0xff); + } + else + { + message.addData(StatusOkay); + ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:Unknown:{:02x}", cmd_); + } + break; + } + case JVSIOCommand::Reset: + if (*jvs_io++ == 0xD9) + { + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF0, Reset"); + delay = 0; + m_wheelinit = 0; + m_ic_card_state = 0x20; + } + message.addData(StatusOkay); + + dip_switch_1 |= 1; + break; + case JVSIOCommand::SetAddress: + node = *jvs_io++; + NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF1, SetAddress: node={}", + node); + message.addData(node == 1); + dip_switch_1 &= ~1; + break; + default: + ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unhandled: node={}, command={:02x}", + node, jvsio_command); + break; + } + } + + message.end(); + + data_out[data_offset++] = gcam_command; + + u8* buf = message.m_msg; + u32 len = message.m_ptr; + data_out[data_offset++] = len; + + for (u32 i = 0; i < len; ++i) + data_out[data_offset++] = buf[i]; + + data_in += frame[0] + 1; + break; + } + case GCAMCommand::Unknown_60: + NOTICE_LOG_FMT(SERIALINTERFACE_AMBB, "GC-AM: Command 0x60, {:02x} {:02x} {:02x}", + data_in[0], data_in[1], data_in[2]); + data_in += data_in[0] + 1; + break; + default: + ERROR_LOG_FMT(SERIALINTERFACE_AMBB, + "GC-AM: Command {:02x} (unknown) {:02x} {:02x} {:02x} {:02x} {:02x}", + gcam_command, data_in[0], data_in[1], data_in[2], data_in[3], data_in[4]); + break; + } + } + memset(buffer, 0, buffer_length); + + data_in = buffer; + data_out[1] = data_offset - 2; + checksum = 0; + char logptr[1024]; + char* log = logptr; + + for (int i = 0; i < 0x7F; ++i) + { + checksum += data_in[i] = data_out[i]; + log += sprintf(log, "%02X", data_in[i]); + } + data_in[0x7f] = ~checksum; + DEBUG_LOG_FMT(SERIALINTERFACE_AMBB, "Command send back: {}", logptr); + + swap_buffers(buffer, &buffer_length); + + buffer_position = buffer_length; + break; + } + default: + { + ERROR_LOG_FMT(SERIALINTERFACE, "Unknown SI command (0x{:08x})", (u32)command); + PanicAlertFmt("SI: Unknown command"); + buffer_position = buffer_length; + } + break; + } + } + + return buffer_position; +} + +// Unused +DataResponse CSIDevice_AMBaseboard::GetData(u32& _Hi, u32& _Low) +{ + _Low = 0; + _Hi = 0x00800000; + + return DataResponse::Success; +} + +void CSIDevice_AMBaseboard::SendCommand(u32 _Cmd, u8 _Poll) +{ + ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command (0x{})", _Cmd); + PanicAlertFmt("SI: (GCAM) Unknown direct command"); +} + +} // namespace SerialInterface diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h new file mode 100644 index 0000000000..c4f21bc321 --- /dev/null +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h @@ -0,0 +1,254 @@ +// Copyright 2017 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "Common/CommonTypes.h" +#include "Common/Flag.h" +#include "Core/HW/SI/SI_Device.h" + +namespace SerialInterface +{ + +// "JAMMA Video Standard" I/O +class JVSIOMessage +{ +public: + u32 m_ptr, m_last_start, m_csum; + u8 m_msg[0x80]; + + JVSIOMessage(); + void start(int node); + void addData(const u8* dst, size_t len, int sync); + void addData(const void* data, size_t len); + void addData(const char* data); + void addData(u32 n); + void end(); +}; // end class JVSIOMessage + +// triforce (GC-AM) baseboard +class CSIDevice_AMBaseboard : public ISIDevice +{ +private: + enum BaseBoardCommand + { + GCAM_Reset = 0x00, + GCAM_Command = 0x70, + }; + + enum GCAMCommand + { + StatusSwitches = 0x10, + SerialNumber = 0x11, + Unknown_12 = 0x12, + Unknown_14 = 0x14, + FirmVersion= 0x15, + FPGAVersion= 0x16, + RegionSettings = 0x1F, + + Unknown_21 = 0x21, + Unknown_22 = 0x22, + Unknown_23 = 0x23, + Unknown_24 = 0x24, + + SerialA = 0x31, + SerialB = 0x32, + + JVSIOA = 0x40, + JVSIOB = 0x41, + + Unknown_60 = 0x60, + }; + + enum JVSIOCommand + { + IOID = 0x10, + CommandRevision = 0x11, + JVRevision = 0x12, + CommunicationVersion = 0x13, + CheckFunctionality = 0x14, + MainID = 0x15, + + SwitchesInput = 0x20, + CoinInput = 0x21, + AnalogInput = 0x22, + RotaryInput = 0x23, + KeyCodeInput = 0x24, + PositionInput = 0x25, + GeneralSwitchInput = 0x26, + + PayoutRemain = 0x2E, + Retrans = 0x2F, + CoinSubOutput = 0x30, + PayoutAddOutput = 0x31, + GeneralDriverOutput = 0x32, + AnalogOutput = 0x33, + CharacterOutput = 0x34, + CoinAddOutput = 0x35, + PayoutSubOutput = 0x36, + GeneralDriverOutput2 = 0x37, + GeneralDriverOutput3 = 0x38, + + NAMCOCommand = 0x70, + + Reset = 0xF0, + SetAddress = 0xF1, + ChangeComm = 0xF2, + }; + + enum JVSIOStatusCode + { + StatusOkay = 1, + UnsupportedCommand = 2, + ChecksumError = 3, + AcknowledgeOverflow = 4, + }; + + enum CARDCommand + { + Init = 0x10, + GetState = 0x20, + Read = 0x33, + IsPresent = 0x40, + Write = 0x53, + SetPrintParam = 0x78, + RegisterFont = 0x7A, + WriteInfo = 0x7C, + Erase = 0x7D, + Eject = 0x80, + Clean = 0xA0, + Load = 0xB0, + SetShutter = 0xD0, + }; + + enum ICCARDCommand + { + GetStatus = 0x10, + SetBaudrate = 0x11, + FieldOn = 0x14, + FieldOff = 0x15, + InsertCheck = 0x20, + AntiCollision = 0x21, + SelectCard = 0x22, + ReadPage = 0x24, + WritePage = 0x25, + DecreaseUseCount = 0x26, + ReadUseCount = 0x33, + ReadPages = 0x34, + WritePages = 0x35, + }; + + enum CDReaderCommand + { + ShutterAuto = 0x61, + BootVersion = 0x62, + SensLock = 0x63, + SensCard = 0x65, + FirmwareUpdate =0x66, + ShutterGet = 0x67, + CameraCheck = 0x68, + ShutterCard = 0x69, + ProgramChecksum = 0x6B, + BootChecksum = 0x6D, + ShutterLoad = 0x6F, + ReadCard = 0x72, + ShutterSave = 0x73, + SelfTest = 0x74, + ProgramVersion = 0x76, + }; + + union ICCommand + { + u8 data[81 + 4 + 4 + 4]; + + struct + { + u32 pktcmd : 8; + u32 pktlen : 8; + u32 fixed : 8; + u32 command : 8; + u32 flag : 8; + u32 length : 8; + u32 status : 16; + + u8 extdata[81]; + u32 extlen; + }; + }; + + u16 m_coin[2]; + u32 m_coin_pressed[2]; + + u8 m_ic_card_data[2048]; + u16 m_ic_card_state; + /* + 0 - OK + 8000 - no card + 800E - ??? + other- bad card + */ + u16 m_ic_card_status; + u16 m_ic_card_session; + u8 m_ic_write_buffer[512]; + u32 m_ic_write_offset; + u32 m_ic_write_size; + + u8 m_card_memory[0xD0]; + u8 m_card_read_packet[0xDB]; + u8 m_card_buffer[0x100]; + u32 m_card_memory_size; + u32 m_card_is_inserted; + u32 m_card_command; + u32 m_card_clean; + u32 m_card_write_length; + u32 m_card_wrote; + u32 m_card_read_length; + u32 m_card_read; + u32 m_card_bit; + u32 m_card_shutter; + u32 m_card_state_call_count; + u8 m_card_offset; + + u32 m_wheelinit; + + u32 m_motorinit; + u8 m_motorreply[64]; + s16 m_motorforce_x; + + // F-Zero AX (DX) + u32 m_fzdx_seatbelt; + u32 m_fzdx_motion_stop; + u32 m_fzdx_sensor_right; + u32 m_fzdx_sensor_left; + u8 m_rx_reply; + + // F-Zero AX (CryCraft) + u32 m_fzcc_seatbelt; + u32 m_fzcc_sensor; + u32 m_fzcc_emergency; + u32 m_fzcc_service; + + void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length); + +public: + // constructor + CSIDevice_AMBaseboard(Core::System& system, SIDevices device, int device_number); + + // run the SI Buffer + int RunBuffer(u8* buffer, int request_length) override; + + // return true on new data + DataResponse GetData(u32& hi, u32& low) override; + + // send a command directly + void SendCommand(u32 command, u8 poll) override; +}; + +} // namespace SerialInterface diff --git a/Source/Core/DolphinLib.vcxproj b/Source/Core/DolphinLib.vcxproj index 0d4925c47e..4d9ba4b553 100644 --- a/Source/Core/DolphinLib.vcxproj +++ b/Source/Core/DolphinLib.vcxproj @@ -70,20 +70,24 @@ - + + + + + + + + + - - - + - + ]]> @@ -91,10 +95,6 @@ - + \ No newline at end of file diff --git a/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp b/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp index f45b51e9ee..2401c63cdc 100644 --- a/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp +++ b/Source/Core/DolphinQt/Config/GamecubeControllersWidget.cpp @@ -43,6 +43,7 @@ static constexpr std::array s_gc_types = { #endif SIDeviceName{SerialInterface::SIDEVICE_GC_GBA, _trans("GBA (TCP)")}, SIDeviceName{SerialInterface::SIDEVICE_GC_KEYBOARD, _trans("Keyboard Controller")}, + SIDeviceName{SerialInterface::SIDEVICE_AM_BASEBOARD, _trans("Triforce Baseboard")}, }; static std::optional ToGCMenuIndex(const SerialInterface::SIDevices sidevice) @@ -133,6 +134,7 @@ void GamecubeControllersWidget::OnGCPadConfigure(size_t index) case SerialInterface::SIDEVICE_NONE: case SerialInterface::SIDEVICE_GC_GBA: return; + case SerialInterface::SIDEVICE_AM_BASEBOARD: case SerialInterface::SIDEVICE_GC_CONTROLLER: type = MappingWindow::Type::MAPPING_GCPAD; break; diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index 8c5d306988..7667136c05 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -146,6 +146,7 @@ void GameCubePane::CreateWidgets() EXIDeviceType::EthernetTapServer, EXIDeviceType::EthernetBuiltIn, EXIDeviceType::ModemTapServer, + EXIDeviceType::Baseboard }) { m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()), @@ -414,6 +415,10 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot) dialog.exec(); return; } + case ExpansionInterface::EXIDeviceType::Baseboard: + { + return; + } default: PanicAlertFmt("Unknown settings pressed for {}", device); return; diff --git a/Source/Core/UICommon/UICommon.cpp b/Source/Core/UICommon/UICommon.cpp index 89912c802c..5198a2c70c 100644 --- a/Source/Core/UICommon/UICommon.cpp +++ b/Source/Core/UICommon/UICommon.cpp @@ -269,6 +269,7 @@ void CreateDirectories() File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP); File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + EUR_DIR DIR_SEP); File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_DIR DIR_SEP); + File::CreateFullPath(File::GetUserPath(D_TRIUSER_IDX)); File::CreateFullPath(File::GetUserPath(D_HIRESTEXTURES_IDX)); File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX)); File::CreateFullPath(File::GetUserPath(D_MAILLOGS_IDX)); From 83985cbec426c4c0910a577a6ee4e293f8455cc4 Mon Sep 17 00:00:00 2001 From: crediar Date: Wed, 16 Jul 2025 23:31:58 +0200 Subject: [PATCH 02/18] Changed segaboot.gcm to be loaded from user dir instead Changed accept timeout to 10ms --- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 9507688a50..f9c704c468 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -213,8 +213,7 @@ void Init(void) PanicAlertFmt("Failed to open/create: {}", base_path + "s_backup.bin"); // This is the firmware for the Triforce - const std::string sega_boot_filename = - File::GetSysDirectory() + TRI_SYS_DIR + DIR_SEP + "segaboot.gcm"; + const std::string sega_boot_filename = base_path + "segaboot.gcm"; if (!File::Exists(sega_boot_filename)) { @@ -262,7 +261,7 @@ static s32 NetDIMMAccept(int fd, struct sockaddr* addr, int* len) timeval timeout; timeout.tv_sec = 0; - timeout.tv_usec = 20000; // 20 milliseconds + timeout.tv_usec = 10000; // 10 milliseconds int result = select(0, &readfds, NULL, NULL, &timeout); if (result > 0 && FD_ISSET(fd, &readfds)) From 001b94a6e27778ea15008520ade2043a4c12f544 Mon Sep 17 00:00:00 2001 From: crediar Date: Thu, 17 Jul 2025 14:36:20 +0200 Subject: [PATCH 03/18] Prevent misconfigured Triforce launches and warn on mismatched hardware --- Source/Core/DolphinQt/MainWindow.cpp | 363 +++++++++++++++------------ 1 file changed, 198 insertions(+), 165 deletions(-) diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 488ca8ecd6..9303842737 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -178,8 +178,8 @@ static WindowSystemType GetWindowSystemType() return WindowSystemType::Haiku; ModalMessageBox::critical( - nullptr, QStringLiteral("Error"), - QString::asprintf("Unknown Qt platform: %s", platform_name.toStdString().c_str())); + nullptr, QStringLiteral("Error"), + QString::asprintf("Unknown Qt platform: %s", platform_name.toStdString().c_str())); return WindowSystemType::Headless; } @@ -218,8 +218,8 @@ static std::vector StringListToStdVector(QStringList list) } MainWindow::MainWindow(Core::System& system, std::unique_ptr boot_parameters, - const std::string& movie_path) - : QMainWindow(nullptr), m_system(system) + const std::string& movie_path) + : QMainWindow(nullptr), m_system(system) { setWindowTitle(QString::fromStdString(Common::GetScmRevStr())); setWindowIcon(Resources::GetAppIcon()); @@ -249,21 +249,21 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, - [this](Qt::ColorScheme colorScheme) { - Settings::Instance().ApplyStyle(); - if (m_skylander_window) - m_skylander_window->RefreshList(); - }); + [this](Qt::ColorScheme colorScheme) { + Settings::Instance().ApplyStyle(); + if (m_skylander_window) + m_skylander_window->RefreshList(); + }); #endif connect(m_cheats_manager, &CheatsManager::OpenGeneralSettings, this, - &MainWindow::ShowGeneralWindow); + &MainWindow::ShowGeneralWindow); #ifdef USE_RETRO_ACHIEVEMENTS connect(m_cheats_manager, &CheatsManager::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); connect(m_game_list, &GameList::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS InitCoreCallbacks(); @@ -276,7 +276,7 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo Settings::Instance().SetDebugModeEnabled(false); // This needs to trigger on both RA_HARDCORE_ENABLED and RA_ENABLED m_config_changed_callback_id = Config::AddConfigChangedCallback( - [this] { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); }); + [this] { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); }); // If hardcore is enabled when the emulator starts, make sure it turns off what it needs to if (Config::Get(Config::RA_HARDCORE_ENABLED)) OnHardcoreChanged(); @@ -300,14 +300,14 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (m_system.GetMovie().PlayInput(movie_path, &savestate_path)) { m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path), - DeleteSavestateAfterBoot::No); + DeleteSavestateAfterBoot::No); emit RecordingStatusChanged(true); } } } m_state_slot = - std::clamp(Settings::Instance().GetStateSlot(), 1, static_cast(State::NUM_STATES)); + std::clamp(Settings::Instance().GetStateSlot(), 1, static_cast(State::NUM_STATES)); m_render_widget_geometry = settings.value(QStringLiteral("renderwidget/geometry")).toByteArray(); @@ -318,7 +318,7 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (!ResourcePack::Init()) { ModalMessageBox::critical(this, tr("Error"), - tr("Error occurred while loading some texture packs")); + tr("Error occurred while loading some texture packs")); } for (auto& pack : ResourcePack::GetPacks()) @@ -326,9 +326,9 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (!pack.IsValid()) { ModalMessageBox::critical(this, tr("Error"), - tr("Invalid Pack %1 provided: %2") - .arg(QString::fromStdString(pack.GetPath())) - .arg(QString::fromStdString(pack.GetError()))); + tr("Invalid Pack %1 provided: %2") + .arg(QString::fromStdString(pack.GetPath())) + .arg(QString::fromStdString(pack.GetError()))); return; } } @@ -433,7 +433,7 @@ void MainWindow::InitCoreCallbacks() FullScreen(); m_fullscreen_requested = false; } - }); + }); installEventFilter(this); m_render_widget->installEventFilter(this); @@ -441,7 +441,7 @@ void MainWindow::InitCoreCallbacks() auto* filter = new FileOpenEventFilter(QGuiApplication::instance()); connect(filter, &FileOpenEventFilter::fileOpened, this, [this](const QString& file_name) { StartGame(BootParameters::GenerateFromFile(file_name.toStdString())); - }); + }); } static void InstallHotkeyFilter(QWidget* dialog) @@ -450,9 +450,9 @@ static void InstallHotkeyFilter(QWidget* dialog) dialog->installEventFilter(filter); filter->connect(filter, &WindowActivationEventFilter::windowDeactivated, - [] { HotkeyManagerEmu::Enable(true); }); + [] { HotkeyManagerEmu::Enable(true); }); filter->connect(filter, &WindowActivationEventFilter::windowActivated, - [] { HotkeyManagerEmu::Enable(false); }); + [] { HotkeyManagerEmu::Enable(false); }); } void MainWindow::CreateComponents() @@ -486,15 +486,15 @@ void MainWindow::CreateComponents() const auto request_watch = [this](QString name, u32 addr) { m_watch_widget->AddWatch(name, addr); - }; + }; const auto request_breakpoint = [this](u32 addr) { m_breakpoint_widget->AddBP(addr); }; const auto request_memory_breakpoint = [this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); - }; + }; const auto request_view_in_memory = [this](u32 addr) { m_memory_widget->SetAddress(addr); }; const auto request_view_in_code = [this](u32 addr) { m_code_widget->SetAddress(addr, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }; + }; connect(m_jit_widget, &JITWidget::SetCodeAddress, m_code_widget, &CodeWidget::OnSetCodeAddress); connect(m_watch_widget, &WatchWidget::RequestMemoryBreakpoint, request_memory_breakpoint); @@ -510,19 +510,19 @@ void MainWindow::CreateComponents() connect(m_thread_widget, &ThreadWidget::RequestViewInCode, request_view_in_code); connect(m_code_widget, &CodeWidget::RequestPPCComparison, m_jit_widget, - &JITWidget::OnRequestPPCComparison); + &JITWidget::OnRequestPPCComparison); connect(m_code_widget, &CodeWidget::ShowMemory, m_memory_widget, &MemoryWidget::SetAddress); connect(m_memory_widget, &MemoryWidget::ShowCode, m_code_widget, [this](u32 address) { m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }); + }); connect(m_memory_widget, &MemoryWidget::RequestWatch, request_watch); connect(m_breakpoint_widget, &BreakpointWidget::ShowCode, [this](u32 address) { if (Core::GetState(m_system) == Core::State::Paused) m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }); + }); connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget, - &MemoryWidget::SetAddress); + &MemoryWidget::SetAddress); connect(m_cheats_manager, &CheatsManager::ShowMemory, m_memory_widget, &MemoryWidget::SetAddress); connect(m_cheats_manager, &CheatsManager::RequestWatch, request_watch); } @@ -567,7 +567,7 @@ void MainWindow::ConnectMenuBar() // Tools connect(m_menu_bar, &MenuBar::ShowMemcardManager, this, &MainWindow::ShowMemcardManager); connect(m_menu_bar, &MenuBar::ShowResourcePackManager, this, - &MainWindow::ShowResourcePackManager); + &MainWindow::ShowResourcePackManager); connect(m_menu_bar, &MenuBar::ShowCheatsManager, this, &MainWindow::ShowCheatsManager); connect(m_menu_bar, &MenuBar::BootGameCubeIPL, this, &MainWindow::OnBootGameCubeIPL); connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup); @@ -599,12 +599,12 @@ void MainWindow::ConnectMenuBar() connect(m_menu_bar, &MenuBar::ShowSearch, m_search_bar, &SearchBar::Show); connect(m_menu_bar, &MenuBar::ColumnVisibilityToggled, m_game_list, - &GameList::OnColumnVisibilityToggled); + &GameList::OnColumnVisibilityToggled); connect(m_menu_bar, &MenuBar::GameListPlatformVisibilityToggled, m_game_list, - &GameList::OnGameListVisibilityChanged); + &GameList::OnGameListVisibilityChanged); connect(m_menu_bar, &MenuBar::GameListRegionVisibilityToggled, m_game_list, - &GameList::OnGameListVisibilityChanged); + &GameList::OnGameListVisibilityChanged); connect(m_menu_bar, &MenuBar::ShowAboutDialog, this, &MainWindow::ShowAboutDialog); @@ -623,9 +623,9 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::TogglePauseHotkey, this, &MainWindow::TogglePause); connect(m_hotkey_scheduler, &HotkeyScheduler::ActivateChat, this, &MainWindow::OnActivateChat); connect(m_hotkey_scheduler, &HotkeyScheduler::RequestGolfControl, this, - &MainWindow::OnRequestGolfControl); + &MainWindow::OnRequestGolfControl); connect(m_hotkey_scheduler, &HotkeyScheduler::RefreshGameListHotkey, this, - &MainWindow::RefreshGameList); + &MainWindow::RefreshGameList); connect(m_hotkey_scheduler, &HotkeyScheduler::StopHotkey, this, &MainWindow::RequestStop); connect(m_hotkey_scheduler, &HotkeyScheduler::ResetHotkey, this, &MainWindow::Reset); connect(m_hotkey_scheduler, &HotkeyScheduler::ScreenShotHotkey, this, &MainWindow::ScreenShot); @@ -634,40 +634,40 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadSlot, this, &MainWindow::StateLoadSlotAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveSlot, this, &MainWindow::StateSaveSlotAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadLastSaved, this, - &MainWindow::StateLoadLastSavedAt); + &MainWindow::StateLoadLastSavedAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadUndo, this, &MainWindow::StateLoadUndo); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveUndo, this, &MainWindow::StateSaveUndo); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveOldest, this, - &MainWindow::StateSaveOldest); + &MainWindow::StateSaveOldest); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveFile, this, &MainWindow::StateSave); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadFile, this, &MainWindow::StateLoad); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadSlotHotkey, this, - &MainWindow::StateLoadSlot); + &MainWindow::StateLoadSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveSlotHotkey, this, - &MainWindow::StateSaveSlot); + &MainWindow::StateSaveSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::SetStateSlotHotkey, this, - &MainWindow::SetStateSlot); + &MainWindow::SetStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::IncrementSelectedStateSlotHotkey, this, - &MainWindow::IncrementSelectedStateSlot); + &MainWindow::IncrementSelectedStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::DecrementSelectedStateSlotHotkey, this, - &MainWindow::DecrementSelectedStateSlot); + &MainWindow::DecrementSelectedStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::StartRecording, this, - &MainWindow::OnStartRecording); + &MainWindow::OnStartRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::PlayRecording, this, &MainWindow::OnPlayRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::ExportRecording, this, - &MainWindow::OnExportRecording); + &MainWindow::OnExportRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::ConnectWiiRemote, this, - &MainWindow::OnConnectWiiRemote); + &MainWindow::OnConnectWiiRemote); connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] { auto& movie = m_system.GetMovie(); bool read_only = !movie.IsReadOnly(); movie.SetReadOnly(read_only); emit ReadOnlyModeChanged(read_only); - }); + }); #ifdef USE_RETRO_ACHIEVEMENTS connect(m_hotkey_scheduler, &HotkeyScheduler::OpenAchievements, this, - &MainWindow::ShowAchievementsWindow, Qt::QueuedConnection); + &MainWindow::ShowAchievementsWindow, Qt::QueuedConnection); #endif // USE_RETRO_ACHIEVEMENTS connect(m_hotkey_scheduler, &HotkeyScheduler::Step, m_code_widget, &CodeWidget::Step); @@ -679,14 +679,14 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::SetPC, m_code_widget, &CodeWidget::SetPC); connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleBreakpoint, m_code_widget, - &CodeWidget::ToggleBreakpoint); + &CodeWidget::ToggleBreakpoint); connect(m_hotkey_scheduler, &HotkeyScheduler::AddBreakpoint, m_code_widget, - &CodeWidget::AddBreakpoint); + &CodeWidget::AddBreakpoint); connect(m_hotkey_scheduler, &HotkeyScheduler::SkylandersPortalHotkey, this, - &MainWindow::ShowSkylanderPortal); + &MainWindow::ShowSkylanderPortal); connect(m_hotkey_scheduler, &HotkeyScheduler::InfinityBaseHotkey, this, - &MainWindow::ShowInfinityBase); + &MainWindow::ShowInfinityBase); } void MainWindow::ConnectToolBar() @@ -718,7 +718,7 @@ void MainWindow::ConnectGameList() connect(m_game_list, &GameList::GameSelected, this, [this] { Play(); }); connect(m_game_list, &GameList::NetPlayHost, this, &MainWindow::NetPlayHost); connect(m_game_list, &GameList::OnStartWithRiivolution, this, - &MainWindow::ShowRiivolutionBootWidget); + &MainWindow::ShowRiivolutionBootWidget); connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow); connect(m_game_list, &GameList::OpenGraphicsSettings, this, &MainWindow::ShowGraphicsWindow); @@ -732,7 +732,7 @@ void MainWindow::ConnectRenderWidget() connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) { if (m_render_widget->isFullScreen()) SetFullScreenResolution(focus); - }); + }); } void MainWindow::ConnectHost() @@ -792,17 +792,17 @@ QStringList MainWindow::PromptFileNames() { auto& settings = Settings::Instance().GetQSettings(); QStringList paths = DolphinFileDialog::getOpenFileNames( - this, tr("Select a File"), - settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(), - QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz " - "hif_000000.nfs *.wad *.dff *.m3u *.json);;%2 (*)") - .arg(tr("All GC/Wii files")) - .arg(tr("All Files"))); + this, tr("Select a File"), + settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(), + QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz " + "hif_000000.nfs *.wad *.dff *.m3u *.json);;%2 (*)") + .arg(tr("All GC/Wii files")) + .arg(tr("All Files"))); if (!paths.isEmpty()) { settings.setValue(QStringLiteral("mainwindow/lastdir"), - QFileInfo(paths.front()).absoluteDir().absolutePath()); + QFileInfo(paths.front()).absoluteDir().absolutePath()); } return paths; @@ -815,12 +815,12 @@ void MainWindow::ChangeDisc() if (paths.empty()) return; - m_system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{m_system}, paths); + m_system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{ m_system }, paths); } void MainWindow::EjectDisc() { - m_system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{m_system}, DVD::EjectCause::User); + m_system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{ m_system }, DVD::EjectCause::User); } void MainWindow::OpenUserFolder() @@ -855,7 +855,7 @@ void MainWindow::Play(const std::optional& savestate_path) if (selection) { StartGame(selection->GetFilePath(), ScanForSecondDisc::Yes, - std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); + std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); } else { @@ -863,7 +863,7 @@ void MainWindow::Play(const std::optional& savestate_path) if (!default_path.isEmpty() && QFile::exists(default_path)) { StartGame(default_path, ScanForSecondDisc::Yes, - std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); + std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); } else { @@ -930,11 +930,11 @@ bool MainWindow::RequestStop() } const bool rendered_widget_was_active = - Settings::Instance().IsKeepWindowOnTopEnabled() || - (m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen()); + Settings::Instance().IsKeepWindowOnTopEnabled() || + (m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen()); QWidget* confirm_parent = (!m_rendering_to_main && rendered_widget_was_active) ? - m_render_widget : - static_cast(this); + m_render_widget : + static_cast(this); const bool was_cursor_locked = m_render_widget->IsCursorLocked(); if (!m_render_widget->isFullScreen()) @@ -975,14 +975,14 @@ bool MainWindow::RequestStop() if (m_stop_requested) { message = tr("A shutdown is already in progress. Unsaved data " - "may be lost if you stop the current emulation " - "before it completes. Force stop?"); + "may be lost if you stop the current emulation " + "before it completes. Force stop?"); } #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION else if (AchievementManager::GetInstance().CheckForModifications()) { message = tr( - "Do you want to stop the current emulation? Unsaved achievement modifications detected."); + "Do you want to stop the current emulation? Unsaved achievement modifications detected."); } #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION else @@ -990,8 +990,8 @@ bool MainWindow::RequestStop() message = tr("Do you want to stop the current emulation?"); } auto confirm = ModalMessageBox::question(confirm_parent, tr("Confirm"), message, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::NoButton, Qt::ApplicationModal); + QMessageBox::Yes | QMessageBox::No, + QMessageBox::NoButton, Qt::ApplicationModal); // If a user confirmed stopping the emulation, we do not capture the cursor again, // even if the render widget will stay alive for a while. @@ -1095,11 +1095,11 @@ void MainWindow::ScreenShot() } void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { auto second_game = m_game_list->FindSecondDisc(game); - std::vector paths = {game.GetFilePath()}; + std::vector paths = { game.GetFilePath() }; if (second_game != nullptr) paths.push_back(second_game->GetFilePath()); @@ -1107,13 +1107,13 @@ void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game, } void MainWindow::StartGame(const QString& path, ScanForSecondDisc scan, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { StartGame(path.toStdString(), scan, std::move(boot_session_data)); } void MainWindow::StartGame(const std::string& path, ScanForSecondDisc scan, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { if (scan == ScanForSecondDisc::Yes) { @@ -1126,14 +1126,14 @@ void MainWindow::StartGame(const std::string& path, ScanForSecondDisc scan, } StartGame(BootParameters::GenerateFromFile( - path, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); + path, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); } void MainWindow::StartGame(const std::vector& paths, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { StartGame(BootParameters::GenerateFromFile( - paths, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); + paths, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); } void MainWindow::StartGame(std::unique_ptr&& parameters) @@ -1145,6 +1145,39 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) if (!NKitWarningDialog::ShowUnlessDisabled()) return; } + } + + /* + When booting Triforce games, we need to ensure that the hardware is set up correctly. + */ + const auto volume_type = + std::get(parameters->parameters).volume->GetVolumeType(); + + const bool triforce_hardware_sp1 = Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard; + const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD; + + if (volume_type == DiscIO::Platform::Triforce) + { + if (!triforce_hardware_sp1 || !triforce_hardware_port_1) + { + ModalMessageBox::critical( + this, tr("Error"), tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."), + QMessageBox::Ok); + HideRenderWidget(); + return; + } + } + else + { + /* + Some Triforce tools don't include a boot.id file, but they can still be launched. + */ + if (triforce_hardware_sp1 || triforce_hardware_port_1) + { + ModalMessageBox::warning( + this, tr("Warning"), tr("Non-Triforce games cannot be booted with Triforce hardware attached."), + QMessageBox::Ok); + } } // If we're running, only start a new game once we've stopped the last. @@ -1163,7 +1196,7 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) // Boot up, show an error if it fails to load the game. if (!BootManager::BootCore(m_system, std::move(parameters), - ::GetWindowSystemInfo(m_render_widget->windowHandle()))) + ::GetWindowSystemInfo(m_render_widget->windowHandle()))) { ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); HideRenderWidget(); @@ -1195,7 +1228,7 @@ void MainWindow::SetFullScreenResolution(bool fullscreen) memset(&screen_settings, 0, sizeof(screen_settings)); screen_settings.dmSize = sizeof(screen_settings); sscanf(Config::Get(Config::MAIN_FULLSCREEN_DISPLAY_RES).c_str(), "%dx%d", - &screen_settings.dmPelsWidth, &screen_settings.dmPelsHeight); + &screen_settings.dmPelsWidth, &screen_settings.dmPelsHeight); screen_settings.dmBitsPerPel = 32; screen_settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; @@ -1266,14 +1299,14 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) { if (m_render_widget->isFullScreen()) SetFullScreenResolution(focus); - }); + }); // The controller interface will still be registered to the old render widget, if the core // has booted. Therefore, we should re-bind it to the main window for now. When the core // is next started, it will be swapped back to the new render widget. g_controller_interface.ChangeWindow(::GetWindowSystemInfo(windowHandle()).render_window, - is_exit ? ControllerInterface::WindowChangeReason::Exit : - ControllerInterface::WindowChangeReason::Other); + is_exit ? ControllerInterface::WindowChangeReason::Exit : + ControllerInterface::WindowChangeReason::Other); } } @@ -1292,7 +1325,7 @@ void MainWindow::ShowFreeLookWindow() #ifdef USE_RETRO_ACHIEVEMENTS connect(m_freelook_window, &FreeLookWindow::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS } @@ -1309,9 +1342,9 @@ void MainWindow::ShowSettingsWindow() if (GetWindowSystemType() == WindowSystemType::X11) { m_xrr_config = std::make_unique( - static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow( - "display", windowHandle())), - winId()); + static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow( + "display", windowHandle())), + winId()); } #endif m_settings_window = new SettingsWindow(this); @@ -1337,7 +1370,7 @@ void MainWindow::ShowGeneralWindow() void MainWindow::ShowAboutDialog() { - AboutDialog about{this}; + AboutDialog about{ this }; about.exec(); } @@ -1381,7 +1414,7 @@ void MainWindow::ShowFIFOPlayer() { m_fifo_window = new FIFOPlayerWindow(m_system.GetFifoPlayer(), m_system.GetFifoRecorder()); connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this, - [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); + [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); } m_fifo_window->show(); @@ -1428,10 +1461,10 @@ void MainWindow::ShowWiiSpeakWindow() void MainWindow::StateLoad() { QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ? - QDir::currentPath() : - QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); + QDir::currentPath() : + QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); QString path = DolphinFileDialog::getOpenFileName( - this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); + this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); if (!path.isEmpty()) State::LoadAs(m_system, path.toStdString()); @@ -1440,10 +1473,10 @@ void MainWindow::StateLoad() void MainWindow::StateSave() { QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ? - QDir::currentPath() : - QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); + QDir::currentPath() : + QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); QString path = DolphinFileDialog::getSaveFileName( - this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); + this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); if (!path.isEmpty()) State::SaveAs(m_system, path.toStdString()); @@ -1495,8 +1528,8 @@ void MainWindow::SetStateSlot(int slot) m_state_slot = slot; Core::DisplayMessage(fmt::format("Selected slot {} - {}", m_state_slot, - State::GetInfoStringOfSlot(m_state_slot, false)), - 2500); + State::GetInfoStringOfSlot(m_state_slot, false)), + 2500); } void MainWindow::IncrementSelectedStateSlot() @@ -1524,7 +1557,7 @@ void MainWindow::PerformOnlineUpdate(const std::string& region) void MainWindow::BootWiiSystemMenu() { - StartGame(std::make_unique(BootParameters::NANDTitle{Titles::SYSTEM_MENU})); + StartGame(std::make_unique(BootParameters::NANDTitle{ Titles::SYSTEM_MENU })); } void MainWindow::NetPlayInit() @@ -1532,10 +1565,10 @@ void MainWindow::NetPlayInit() const auto& game_list_model = m_game_list->GetGameListModel(); m_netplay_setup_dialog = new NetPlaySetupDialog(game_list_model, this); m_netplay_dialog = new NetPlayDialog( - game_list_model, - [this](const std::string& path, std::unique_ptr boot_session_data) { - StartGame(path, ScanForSecondDisc::Yes, std::move(boot_session_data)); - }); + game_list_model, + [this](const std::string& path, std::unique_ptr boot_session_data) { + StartGame(path, ScanForSecondDisc::Yes, std::move(boot_session_data)); + }); #ifdef USE_DISCORD_PRESENCE m_netplay_discord = new DiscordHandler(this); #endif @@ -1551,9 +1584,9 @@ void MainWindow::NetPlayInit() m_netplay_discord->Start(); #endif connect(&Settings::Instance(), &Settings::ConfigChanged, this, - &MainWindow::UpdateScreenSaverInhibition); + &MainWindow::UpdateScreenSaverInhibition); connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, - &MainWindow::UpdateScreenSaverInhibition); + &MainWindow::UpdateScreenSaverInhibition); } bool MainWindow::NetPlayJoin() @@ -1561,14 +1594,14 @@ bool MainWindow::NetPlayJoin() if (!Core::IsUninitialized(m_system)) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("Can't start a NetPlay Session while a game is still running!")); + tr("Can't start a NetPlay Session while a game is still running!")); return false; } if (m_netplay_dialog->isVisible()) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("A NetPlay Session is already in progress!")); + tr("A NetPlay Session is already in progress!")); return false; } @@ -1588,7 +1621,7 @@ bool MainWindow::NetPlayJoin() else { host_ip = is_traversal ? Config::Get(Config::NETPLAY_HOST_CODE) : - Config::Get(Config::NETPLAY_ADDRESS); + Config::Get(Config::NETPLAY_ADDRESS); host_port = Config::Get(Config::NETPLAY_CONNECT_PORT); } @@ -1607,9 +1640,9 @@ bool MainWindow::NetPlayJoin() // Create Client const bool is_hosting_netplay = server != nullptr; Settings::Instance().ResetNetPlayClient(new NetPlay::NetPlayClient( - host_ip, host_port, m_netplay_dialog, nickname, - NetPlay::NetTraversalConfig{is_hosting_netplay ? false : is_traversal, traversal_host, - traversal_port})); + host_ip, host_port, m_netplay_dialog, nickname, + NetPlay::NetTraversalConfig{ is_hosting_netplay ? false : is_traversal, traversal_host, + traversal_port })); if (!Settings::Instance().GetNetPlayClient()->IsConnected()) { @@ -1628,14 +1661,14 @@ bool MainWindow::NetPlayHost(const UICommon::GameFile& game) if (!Core::IsUninitialized(m_system)) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("Can't start a NetPlay Session while a game is still running!")); + tr("Can't start a NetPlay Session while a game is still running!")); return false; } if (m_netplay_dialog->isVisible()) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("A NetPlay Session is already in progress!")); + tr("A NetPlay Session is already in progress!")); return false; } @@ -1654,22 +1687,22 @@ bool MainWindow::NetPlayHost(const UICommon::GameFile& game) // Create Server Settings::Instance().ResetNetPlayServer( - new NetPlay::NetPlayServer(host_port, use_upnp, m_netplay_dialog, - NetPlay::NetTraversalConfig{is_traversal, traversal_host, - traversal_port, traversal_port_alt})); + new NetPlay::NetPlayServer(host_port, use_upnp, m_netplay_dialog, + NetPlay::NetTraversalConfig{ is_traversal, traversal_host, + traversal_port, traversal_port_alt })); if (!Settings::Instance().GetNetPlayServer()->is_connected) { ModalMessageBox::critical( - nullptr, tr("Failed to open server"), - tr("Failed to listen on port %1. Is another instance of the NetPlay server running?") - .arg(host_port)); + nullptr, tr("Failed to open server"), + tr("Failed to listen on port %1. Is another instance of the NetPlay server running?") + .arg(host_port)); NetPlayQuit(); return false; } Settings::Instance().GetNetPlayServer()->ChangeGame(game.GetSyncIdentifier(), - m_game_list->GetNetPlayName(game)); + m_game_list->GetNetPlayName(game)); // Join our local server return NetPlayJoin(); @@ -1687,7 +1720,7 @@ void MainWindow::NetPlayQuit() void MainWindow::UpdateScreenSaverInhibition() { const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) && - (Core::GetState(m_system) == Core::State::Running); + (Core::GetState(m_system) == Core::State::Running); if (inhibit == m_is_screensaver_inhibited) return; @@ -1761,9 +1794,9 @@ void MainWindow::dropEvent(QDropEvent* event) if (show_confirm) { if (ModalMessageBox::question( - this, tr("Confirm"), - tr("Do you want to add \"%1\" to the list of Game Paths?").arg(folder)) != - QMessageBox::Yes) + this, tr("Confirm"), + tr("Do you want to add \"%1\" to the list of Game Paths?").arg(folder)) != + QMessageBox::Yes) return; } settings.AddPath(folder); @@ -1778,25 +1811,25 @@ QSize MainWindow::sizeHint() const void MainWindow::OnBootGameCubeIPL(DiscIO::Region region) { - StartGame(std::make_unique(BootParameters::IPL{region})); + StartGame(std::make_unique(BootParameters::IPL{ region })); } void MainWindow::OnImportNANDBackup() { auto response = ModalMessageBox::question( - this, tr("Question"), - tr("Merging a new NAND over your currently selected NAND will overwrite any channels " - "and savegames that already exist. This process is not reversible, so it is " - "recommended that you keep backups of both NANDs. Are you sure you want to " - "continue?")); + this, tr("Question"), + tr("Merging a new NAND over your currently selected NAND will overwrite any channels " + "and savegames that already exist. This process is not reversible, so it is " + "recommended that you keep backups of both NANDs. Are you sure you want to " + "continue?")); if (response == QMessageBox::No) return; QString file = - DolphinFileDialog::getOpenFileName(this, tr("Select NAND Backup"), QDir::currentPath(), - tr("BootMii NAND backup file (*.bin);;" - "All Files (*)")); + DolphinFileDialog::getOpenFileName(this, tr("Select NAND Backup"), QDir::currentPath(), + tr("BootMii NAND backup file (*.bin);;" + "All Files (*)")); if (file.isEmpty()) return; @@ -1811,26 +1844,26 @@ void MainWindow::OnImportNANDBackup() std::future result = std::async(std::launch::async, [&] { DiscIO::NANDImporter().ImportNANDBin( - file.toStdString(), - [&dialog, beginning] { - dialog.SetLabelText( - tr("Importing NAND backup\n Time elapsed: %1s") - .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); - }, - [this] { - std::optional keys_file = RunOnObject(this, [this] { - return DolphinFileDialog::getOpenFileName( - this, tr("Select Keys File (OTP/SEEPROM Dump)"), QDir::currentPath(), - tr("BootMii keys file (*.bin);;" - "All Files (*)")) - .toStdString(); + file.toStdString(), + [&dialog, beginning] { + dialog.SetLabelText( + tr("Importing NAND backup\n Time elapsed: %1s") + .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); + }, + [this] { + std::optional keys_file = RunOnObject(this, [this] { + return DolphinFileDialog::getOpenFileName( + this, tr("Select Keys File (OTP/SEEPROM Dump)"), QDir::currentPath(), + tr("BootMii keys file (*.bin);;" + "All Files (*)")) + .toStdString(); }); - if (keys_file) - return *keys_file; - return std::string(""); - }); + if (keys_file) + return *keys_file; + return std::string(""); + }); dialog.Reset(); - }); + }); dialog.GetRaw()->exec(); @@ -1844,13 +1877,13 @@ void MainWindow::OnPlayRecording() if (AchievementManager::GetInstance().IsHardcoreModeActive()) { ModalMessageBox::critical( - this, tr("Error"), - tr("Playback of input recordings is disabled in RetroAchievements hardcore mode.")); + this, tr("Error"), + tr("Playback of input recordings is disabled in RetroAchievements hardcore mode.")); return; } QString dtm_file = DolphinFileDialog::getOpenFileName( - this, tr("Select the Recording File to Play"), QString(), tr("Dolphin TAS Movies (*.dtm)")); + this, tr("Select the Recording File to Play"), QString(), tr("Dolphin TAS Movies (*.dtm)")); if (dtm_file.isEmpty()) return; @@ -1876,8 +1909,8 @@ void MainWindow::OnStartRecording() { auto& movie = m_system.GetMovie(); if (Core::GetState(m_system) == Core::State::Starting || - Core::GetState(m_system) == Core::State::Stopping || movie.IsRecordingInput() || - movie.IsPlayingInput()) + Core::GetState(m_system) == Core::State::Stopping || movie.IsRecordingInput() || + movie.IsPlayingInput()) { return; } @@ -1928,7 +1961,7 @@ void MainWindow::OnExportRecording() const Core::CPUThreadGuard guard(m_system); QString dtm_file = DolphinFileDialog::getSaveFileName( - this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); + this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); if (!dtm_file.isEmpty()) m_system.GetMovie().SaveRecording(dtm_file.toStdString()); } @@ -1958,7 +1991,7 @@ void MainWindow::ShowTASInput() m_gba_tas_input_windows[i]->activateWindow(); } else if (si_device != SerialInterface::SIDEVICE_NONE && - si_device != SerialInterface::SIDEVICE_GC_GBA) + si_device != SerialInterface::SIDEVICE_GC_GBA) { m_gc_tas_input_windows[i]->show(); m_gc_tas_input_windows[i]->raise(); @@ -1969,7 +2002,7 @@ void MainWindow::ShowTASInput() for (int i = 0; i < num_wii_controllers; i++) { if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated && - (!Core::IsRunning(m_system) || m_system.IsWii())) + (!Core::IsRunning(m_system) || m_system.IsWii())) { m_wii_tas_input_windows[i]->show(); m_wii_tas_input_windows[i]->raise(); @@ -1999,7 +2032,7 @@ void MainWindow::ShowAchievementsWindow() m_achievements_window->show(); m_achievements_window->raise(); m_achievements_window->activateWindow(); - m_achievements_window->UpdateData(AchievementManager::UpdatedItems{.all = true}); + m_achievements_window->UpdateData(AchievementManager::UpdatedItems{ .all = true }); } void MainWindow::ShowAchievementSettings() @@ -2043,7 +2076,7 @@ void MainWindow::ShowCheatsManager() void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game) { auto second_game = m_game_list->FindSecondDisc(game); - std::vector paths = {game.GetFilePath()}; + std::vector paths = { game.GetFilePath() }; if (second_game != nullptr) paths.push_back(second_game->GetFilePath()); std::unique_ptr boot_params = BootParameters::GenerateFromFile(paths); @@ -2054,11 +2087,11 @@ void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game) auto& disc = std::get(boot_params->parameters); RiivolutionBootWidget w(disc.volume->GetGameID(), disc.volume->GetRevision(), - disc.volume->GetDiscNumber(), game.GetFilePath(), this); + disc.volume->GetDiscNumber(), game.GetFilePath(), this); #ifdef USE_RETRO_ACHIEVEMENTS connect(&w, &RiivolutionBootWidget::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS w.exec(); From 49541626ff85439c3cc34e65dd745a84f438db6e Mon Sep 17 00:00:00 2001 From: crediar Date: Thu, 17 Jul 2025 23:38:08 +0200 Subject: [PATCH 04/18] Added support to boot the Triforce IPL Added handling of dev region for Triforce IPL --- Source/Core/Common/CommonPaths.h | 1 + Source/Core/Core/Boot/Boot.cpp | 10 ++++ Source/Core/Core/Config/MainSettings.cpp | 3 ++ Source/Core/DiscIO/Enums.h | 3 +- Source/Core/DolphinQt/MainWindow.cpp | 58 ++++++++++++------------ Source/Core/DolphinQt/MenuBar.cpp | 3 ++ Source/Core/DolphinQt/MenuBar.h | 1 + 7 files changed, 49 insertions(+), 30 deletions(-) diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 4f556e6e41..c92b0a477a 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -41,6 +41,7 @@ #define USA_DIR "USA" #define JAP_DIR "JAP" #define JPN_DIR "JPN" +#define DEV_DIR "DEV" // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) #define GC_USER_DIR "GC" diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 1b796952f8..3f7bf3a140 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -38,6 +38,7 @@ #include "Core/FifoPlayer/FifoPlayer.h" #include "Core/HLE/HLE.h" #include "Core/HW/DVD/DVDInterface.h" +#include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/Memmap.h" #include "Core/HW/VideoInterface.h" @@ -408,6 +409,7 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename) break; case Triforce: known_ipl = true; + break; default: PanicAlertFmtT("The IPL file is not a known good dump. (CRC32: {0:x})", ipl_hash); break; @@ -458,6 +460,14 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename) ppc_state.spr[SPR_DBAT3L] = 0xfff00001; SetupBAT(system, /*is_wii*/ false); + if (ipl_hash == Triforce) + { + HLE::Patch(system, 0x813048B8, "OSReport"); + HLE::Patch(system, 0x8130095C, "OSReport"); // Apploader + + AMMediaboard::FirmwareMap(true); + } + ppc_state.pc = 0x81200150; PowerPC::MSRUpdated(ppc_state); diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index dc6a48aa7f..90ccc15a65 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -640,6 +640,9 @@ const char* GetDirectoryForRegion(DiscIO::Region region, RegionDirectoryStyle st ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region"); return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR; + case DiscIO::Region::DEV: + return DEV_DIR; + default: ASSERT_MSG(BOOT, false, "Default case should not be reached"); return EUR_DIR; diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h index 9b5e274de9..966b5bfc15 100644 --- a/Source/Core/DiscIO/Enums.h +++ b/Source/Core/DiscIO/Enums.h @@ -48,7 +48,8 @@ enum class Region NTSC_U = 1, // Mainly North America PAL = 2, // Mainly Europe and Oceania Unknown = 3, // Nintendo uses this to mean region free, but we also use it for unknown regions - NTSC_K = 4 // South Korea (Wii only) + NTSC_K = 4, // South Korea (Wii only) + DEV = 5, // Workaround for Triforce IPL }; // Languages 0 - 9 match Nintendo's Wii language numbering. diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 9303842737..1059f89a1f 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -1145,40 +1145,40 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) if (!NKitWarningDialog::ShowUnlessDisabled()) return; } - } - /* - When booting Triforce games, we need to ensure that the hardware is set up correctly. - */ - const auto volume_type = - std::get(parameters->parameters).volume->GetVolumeType(); - - const bool triforce_hardware_sp1 = Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard; - const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD; - - if (volume_type == DiscIO::Platform::Triforce) - { - if (!triforce_hardware_sp1 || !triforce_hardware_port_1) - { - ModalMessageBox::critical( - this, tr("Error"), tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."), - QMessageBox::Ok); - HideRenderWidget(); - return; - } - } - else - { /* - Some Triforce tools don't include a boot.id file, but they can still be launched. + When booting Triforce games, we need to ensure that the hardware is set up correctly. */ - if (triforce_hardware_sp1 || triforce_hardware_port_1) + const auto volume_type = + std::get(parameters->parameters).volume->GetVolumeType(); + + const bool triforce_hardware_sp1 = Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard; + const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD; + + if (volume_type == DiscIO::Platform::Triforce) { - ModalMessageBox::warning( - this, tr("Warning"), tr("Non-Triforce games cannot be booted with Triforce hardware attached."), - QMessageBox::Ok); + if (!triforce_hardware_sp1 || !triforce_hardware_port_1) + { + ModalMessageBox::critical( + this, tr("Error"), tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."), + QMessageBox::Ok); + HideRenderWidget(); + return; + } } - } + else + { + /* + Some Triforce tools don't include a boot.id file, but they can still be launched. + */ + if (triforce_hardware_sp1 || triforce_hardware_port_1) + { + ModalMessageBox::warning( + this, tr("Warning"), tr("Non-Triforce games cannot be booted with Triforce hardware attached."), + QMessageBox::Ok); + } + } + } // If we're running, only start a new game once we've stopped the last. if (!Core::IsUninitialized(m_system)) diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 6f21030294..9710ac760d 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -310,6 +310,9 @@ void MenuBar::AddToolsMenu() m_pal_ipl = gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); }); + m_dev_ipl = + gc_ipl->addAction(tr("Triforce"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::DEV); }); + tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); }); tools_menu->addSeparator(); diff --git a/Source/Core/DolphinQt/MenuBar.h b/Source/Core/DolphinQt/MenuBar.h index 934772e72c..92836179ba 100644 --- a/Source/Core/DolphinQt/MenuBar.h +++ b/Source/Core/DolphinQt/MenuBar.h @@ -217,6 +217,7 @@ private: QAction* m_ntscj_ipl; QAction* m_ntscu_ipl; QAction* m_pal_ipl; + QAction* m_dev_ipl; QMenu* m_manage_nand_menu; QAction* m_import_backup; QAction* m_check_nand; From 994fd7d90036ff3e76469eacbd0a65674c867ec3 Mon Sep 17 00:00:00 2001 From: crediar Date: Fri, 18 Jul 2025 19:56:36 +0200 Subject: [PATCH 05/18] Improved Triforce IPL compatibility and simplify logic changes Refactored AMMediaboard.cpp to remove legacy memory patching, introducing cryptographic key handling for Triforce IPL commands Enhanced DVDInterface.cpp to ensure cover closure during initialization for Triforce IPL Adjusted VideoInterface.cpp region flags for Triforce IPL compatibility Added detailed comments to clarify new logic and removed outdated code Added loading of region for Triforce games from boot.id Prevent test menu access without SegaBoot present Changed insert coin button to X --- Source/Core/Core/Boot/Boot.cpp | 3 +- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 68 ++++++++----------- Source/Core/Core/HW/DVD/AMMediaboard.h | 1 + Source/Core/Core/HW/DVD/DVDInterface.cpp | 3 + .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 18 +++-- Source/Core/Core/HW/VideoInterface.cpp | 11 ++- Source/Core/DiscIO/VolumeGC.cpp | 32 +++++++++ 7 files changed, 90 insertions(+), 46 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 3f7bf3a140..31fb207f61 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -375,7 +375,8 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename) constexpr u32 PAL_v1_0 = 0x4F319F43; // DOL-101(EUR) (PAL Revision 1.2) constexpr u32 PAL_v1_2 = 0xAD1B7F16; - constexpr u32 Triforce = 0xD1883221; // The Triforce's special IPL + // Triforce Arcade IPL (DEV Revision 1.0) + constexpr u32 Triforce = 0xD1883221; // Load the IPL ROM dump, limited to 2MiB which is the size of the official IPLs. constexpr size_t max_ipl_size = 2 * 1024 * 1024; diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index f9c704c468..2cab83379f 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -83,6 +83,7 @@ namespace AMMediaboard static bool s_firmwaremap = false; static bool s_segaboot = false; +static bool s_test_menu = false; static SOCKET s_namco_cam = 0; static u32 s_timeouts[3] = {20000, 20000, 20000}; static u32 s_last_error = SSC_SUCCESS; @@ -186,6 +187,7 @@ void Init(void) s_segaboot = false; s_firmwaremap = false; + s_test_menu = false; s_last_error = SSC_SUCCESS; @@ -230,6 +232,8 @@ void Init(void) u64 length = std::min(sega_boot.GetSize(), sizeof(s_firmware)); sega_boot.ReadBytes(s_firmware, length); + + s_test_menu = true; } u8* InitDIMM(u32 size) @@ -305,7 +309,7 @@ static s32 NetDIMMConnect(int fd, struct sockaddr_in* addr, int len) { addr->sin_addr.s_addr = inet_addr("127.0.0.1"); /* - BUG: An invalid family value is used + BUG: An invalid family value is being used */ addr->sin_family = htons(AF_INET); s_namco_cam = fd; @@ -395,44 +399,10 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); - auto& ppc_state = system.GetPPCState(); - auto& jit_interface = system.GetJitInterface(); - - /* - The triforce IPL sends these commands first - 01010000 00000101 00000000 - 01010000 00000000 0000ffff - */ - if (s_GCAM_key_a == 0) - { - /* - Since it is currently unknown how the seed is created - we have to patch out the crypto. - */ - if (memory.Read_U32(0x8131ecf4)) - { - memory.Write_U32(0, 0x8131ecf4); - memory.Write_U32(0, 0x8131ecf8); - memory.Write_U32(0, 0x8131ecfC); - memory.Write_U32(0, 0x8131ebe0); - memory.Write_U32(0, 0x8131ed6c); - memory.Write_U32(0, 0x8131ed70); - memory.Write_U32(0, 0x8131ed74); - - memory.Write_U32(0x4E800020, 0x813025C8); - memory.Write_U32(0x4E800020, 0x81302674); - - ppc_state.iCache.Invalidate(memory, jit_interface, 0x813025C8); - ppc_state.iCache.Invalidate(memory, jit_interface, 0x81302674); - - HLE::Patch(system, 0x813048B8, "OSReport"); - HLE::Patch(system, 0x8130095C, "OSReport"); // Apploader - } - } DICMDBUF[0] ^= s_GCAM_key_a; DICMDBUF[1] ^= s_GCAM_key_b; - // length ^= s_GCAM_key_c; // DMA length is always plain + DICMDBUF[2] ^= s_GCAM_key_c; u32 seed = DICMDBUF[0] >> 16; @@ -440,6 +410,22 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) s_GCAM_key_b *= seed; s_GCAM_key_c *= seed; + /* + Key setup for Triforce IPL: + These RAM offset always hold the keys for the next command and since it sends two dummy + commands before a real read we can just use the key from RAM without missing any real commands. + */ + if (s_GCAM_key_a == 0) + { + if (memory.Read_U32(0)) + { + HLE::Patch(system, 0x813048B8, "OSReport"); + HLE::Patch(system, 0x8130095C, "OSReport"); // Apploader + + InitKeys(memory.Read_U32(0), memory.Read_U32(4), memory.Read_U32(8)); + } + } + DICMDBUF[0] <<= 24; DICMDBUF[1] <<= 2; @@ -669,7 +655,6 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Empty reply case AMMBCommand::Unknown_103: break; - // Network Commands case AMMBCommand::Accept: { u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; @@ -710,8 +695,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) *(u32*)(&addr.sin_addr) = Common::swap32(*(u32*)(&addr.sin_addr)); /* - Triforce games usually use hardcoded IPs - This is replaced to listen to the ANY address instead + Triforce titles typically rely on hardcoded IP addresses. + This behavior has been modified to bind to the wildcard address instead. */ addr.sin_addr.s_addr = INADDR_ANY; @@ -1825,7 +1810,10 @@ u32 GetGameType(void) } // never reached } - +bool GetTestMenu(void) +{ + return s_test_menu; +} void Shutdown(void) { if (s_netcfg) diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h index dd32a5e0c0..192a551331 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.h +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -212,5 +212,6 @@ void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC); u32 ExecuteCommand(std::array& DICMDBUF, u32 Address, u32 Length); u32 GetGameType(void); u32 GetMediaType(void); +bool GetTestMenu(void); void Shutdown(void); }; // namespace AMMediaboard diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 0d43dcbc67..2ffe2855e2 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -291,6 +291,9 @@ void DVDInterface::Init() if (m_enable_gcam) { AMMediaboard::Init(); + + // The Trifoce IPL expects the cover to be closed + m_DICVR.Hex = 0; } } diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index c82d70b730..97306c212c 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1544,10 +1544,20 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) GCPadStatus PadStatus; PadStatus = Pad::GetStatus(0); - + // Test button if (PadStatus.button & PAD_TRIGGER_Z) - message.addData(0x80); + { + // Trying to access the test menu without SegaBoot present will cause a crash + if (AMMediaboard::GetTestMenu()) + { + message.addData(0x80); + } + else + { + PanicAlertFmt("Test menu is disabled due missing SegaBoot"); + } + } else message.addData((u32)0x00); @@ -1833,11 +1843,11 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { GCPadStatus PadStatus; PadStatus = Pad::GetStatus(i); - if ((PadStatus.button & PAD_TRIGGER_Z) && !m_coin_pressed[i]) + if ((PadStatus.button & PAD_BUTTON_X) && !m_coin_pressed[i]) { m_coin[i]++; } - m_coin_pressed[i] = PadStatus.button & PAD_TRIGGER_Z; + m_coin_pressed[i] = PadStatus.button & PAD_BUTTON_X; message.addData((m_coin[i] >> 8) & 0x3f); message.addData(m_coin[i] & 0xff); } diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index e1e822f08e..1cdfe6279d 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -163,7 +163,16 @@ void VideoInterfaceManager::Preset(bool _bNTSC) // Say component cable is plugged m_dtv_status.component_plugged = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN); - m_dtv_status.ntsc_j = region == DiscIO::Region::NTSC_J; + + // Triforce IPL requires this to be set + if (region == DiscIO::Region::DEV || region == DiscIO::Region::NTSC_J) + { + m_dtv_status.ntsc_j = true; + } + else + { + m_dtv_status.ntsc_j = false; + } m_fb_width.Hex = 0; m_border_hblank.Hex = 0; diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 8cc255570a..a557ced9f1 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -29,6 +29,8 @@ namespace DiscIO { + Region g_triforce_region; + VolumeGC::VolumeGC(std::unique_ptr reader) : m_reader(std::move(reader)), m_is_triforce(false) { @@ -40,6 +42,7 @@ VolumeGC::VolumeGC(std::unique_ptr reader) }; m_converted_banner = [this] { return LoadBannerFile(); }; + g_triforce_region = Region::Unknown; constexpr u32 BTID_MAGIC = 0x44495442; auto tmp_fs = GetFileSystem(PARTITION_NONE); @@ -103,6 +106,9 @@ std::string VolumeGC::GetTriforceID() const Region VolumeGC::GetRegion() const { + if (g_triforce_region != Region::Unknown) + return g_triforce_region; + return RegionCodeToRegion(m_reader->ReadSwapped(0x458)); } @@ -187,6 +193,32 @@ std::array VolumeGC::GetSyncHash() const VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const { + /* + There is at least one Triforce game that has an opening.bnr file but from a different game. + Check for boot.id and then just skip loading the banner. + */ + u8 bootid[0x1E0]; + const u64 bootid_size = ReadFile(*this, PARTITION_NONE, "boot.id", reinterpret_cast(&bootid), sizeof(bootid)); + if (bootid_size) + { + // Load region from the file + switch (bootid[0x38]) + { + default: + case 0x02: // JAPAN + case 0x08: // ASIA + g_triforce_region = Region::NTSC_J; + break; + case 0x0E: // USA + g_triforce_region = Region::NTSC_U; + break; + case 0x0C: // EXPORT + g_triforce_region = Region::PAL; + break; + } + + return {}; + } GCBanner banner_file; const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr", reinterpret_cast(&banner_file), sizeof(GCBanner)); From 50d52fd0c9337311253e3c2d7f2698775adc60ef Mon Sep 17 00:00:00 2001 From: crediar Date: Sat, 19 Jul 2025 00:23:40 +0200 Subject: [PATCH 06/18] Fixed a bug that always connected the Triforce Baseboard by default --- Source/Core/Core/HW/DVD/DVDInterface.cpp | 2 +- Source/Core/Core/HW/EXI/EXI.cpp | 11 ++--------- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 2ffe2855e2..d82f544773 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -292,7 +292,7 @@ void DVDInterface::Init() { AMMediaboard::Init(); - // The Trifoce IPL expects the cover to be closed + // The Triforce IPL expects the cover to be closed m_DICVR.Hex = 0; } } diff --git a/Source/Core/Core/HW/EXI/EXI.cpp b/Source/Core/Core/HW/EXI/EXI.cpp index d676dc4845..6f56e8cc76 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -67,15 +67,8 @@ void ExpansionInterfaceManager::AddMemoryCard(Slot slot) } void ExpansionInterfaceManager::AddSP1Device() -{ - EXIDeviceType sp1_device = EXIDeviceType::Baseboard; - auto& system = Core::System::GetInstance(); - if (system.IsTriforce()) - { - sp1_device = Config::Get(Config::MAIN_SERIAL_PORT_1); - } - - m_channels[0]->AddDevice(sp1_device, SlotToEXIDevice(Slot::SP1)); +{ + m_channels[0]->AddDevice( Config::Get(Config::MAIN_SERIAL_PORT_1), SlotToEXIDevice(Slot::SP1) ); } u8 SlotToEXIChannel(Slot slot) From 2ca74be32029630aa52d5102a29f0c3c8732ba93 Mon Sep 17 00:00:00 2001 From: crediar Date: Sun, 20 Jul 2025 23:22:48 +0200 Subject: [PATCH 07/18] Fixed not booting the Triforce IPL when loading a Triforce game --- Source/Core/Core/ConfigManager.cpp | 6 ++++++ Source/Core/DolphinQt/MenuBar.cpp | 1 + 2 files changed, 7 insertions(+) diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 4e0b7dd3ac..8927b8c2f9 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -433,6 +433,12 @@ bool SConfig::SetPathsAndGameMetadata(Core::System& system, const BootParameters if (m_region == DiscIO::Region::Unknown) m_region = Config::Get(Config::MAIN_FALLBACK_REGION); + // Triforce IPL + if (m_triforce_id.length()) + { + m_region = DiscIO::Region::DEV; + } + // Set up paths const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region)); m_strSRAM = File::GetUserPath(F_GCSRAM_IDX); diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 9710ac760d..16ceefb489 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -1108,6 +1108,7 @@ void MenuBar::UpdateToolsMenu(const Core::State state) m_ntscj_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(JAP_DIR))); m_ntscu_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(USA_DIR))); m_pal_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(EUR_DIR))); + m_dev_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(DEV_DIR))); m_wad_install_action->setEnabled(is_uninitialized); m_import_backup->setEnabled(is_uninitialized); m_check_nand->setEnabled(is_uninitialized); From 62988f388890e54d4de354ddd15651743978668a Mon Sep 17 00:00:00 2001 From: crediar Date: Tue, 22 Jul 2025 15:24:32 +0200 Subject: [PATCH 08/18] Removed m_enable_gcam variable and changed code to use IsTriforce() instead Fixed a bug that connected the SP1 device twice Removed AddSP1Device function and fixed the code to automatically connect the Triforce Baseboard when a Triforce game is detected Updated BootID structure Changed how the region and maker for Triforce games are loaded --- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 2 +- Source/Core/Core/HW/DVD/DVDInterface.cpp | 8 ++-- Source/Core/Core/HW/DVD/DVDInterface.h | 3 -- Source/Core/Core/HW/EXI/EXI.cpp | 19 ++++++---- Source/Core/Core/HW/EXI/EXI.h | 1 - .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 8 ++-- Source/Core/DiscIO/VolumeDisc.cpp | 22 +++++++++++ Source/Core/DiscIO/VolumeGC.cpp | 38 +++++++++---------- Source/Core/DiscIO/VolumeGC.h | 7 ++++ Source/Core/UICommon/GameFile.cpp | 9 +++++ 10 files changed, 75 insertions(+), 42 deletions(-) diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 2cab83379f..c626aadba7 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -660,7 +660,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; int ret = -1; - // Handle optional paramters + // Handle optional parameters if (media_buffer_32[3] == 0 || media_buffer_32[4] == 0) { ret = NetDIMMAccept(fd, nullptr, nullptr); diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index d82f544773..054ea25a12 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -286,9 +286,7 @@ void DVDInterface::Init() u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT); core_timing.ScheduleEvent(0, m_finish_executing_command, userdata); - const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); - m_enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0; - if (m_enable_gcam) + if (m_system.IsTriforce()) { AMMediaboard::Init(); @@ -324,7 +322,7 @@ void DVDInterface::ResetDrive(bool spinup) else if (!spinup) { // Wii hardware tests indicate that this is used when ejecting and inserting a new disc, or - // performing a reset without spinup. + // performing a reset without spin up. SetDriveState(DriveState::DiscChangeDetected); } else @@ -766,7 +764,7 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) DIInterruptType interrupt_type = DIInterruptType::TCINT; bool command_handled_by_thread = false; - if (m_enable_gcam) + if (m_system.IsTriforce()) { u32 ret = AMMediaboard::ExecuteCommand(m_DICMDBUF, m_DIMAR, m_DILENGTH); if (ret != 1) diff --git a/Source/Core/Core/HW/DVD/DVDInterface.h b/Source/Core/Core/HW/DVD/DVDInterface.h index a2b8f9776a..600edc5050 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.h +++ b/Source/Core/Core/HW/DVD/DVDInterface.h @@ -279,9 +279,6 @@ private: bool m_enable_dtk = false; u8 m_dtk_buffer_length = 0; // TODO: figure out how this affects the regular buffer - // Triforce - bool m_enable_gcam = false; - // Disc drive state DriveState m_drive_state = DriveState::Ready; DriveError m_error_code = DriveError::None; diff --git a/Source/Core/Core/HW/EXI/EXI.cpp b/Source/Core/Core/HW/EXI/EXI.cpp index 6f56e8cc76..515cae925f 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -66,11 +66,6 @@ void ExpansionInterfaceManager::AddMemoryCard(Slot slot) m_channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(slot)); } -void ExpansionInterfaceManager::AddSP1Device() -{ - m_channels[0]->AddDevice( Config::Get(Config::MAIN_SERIAL_PORT_1), SlotToEXIDevice(Slot::SP1) ); -} - u8 SlotToEXIChannel(Slot slot) { switch (slot) @@ -150,11 +145,19 @@ void ExpansionInterfaceManager::Init(const Sram* override_sram) AddMemoryCard(slot); m_channels[0]->AddDevice(EXIDeviceType::MaskROM, 1); - AddSP1Device(); - m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_1), - SlotToEXIDevice(Slot::SP1)); + + // Automatically connect the Triforce Baseboard in Triforce mode + EXIDeviceType sp1_device = Config::Get(Config::MAIN_SERIAL_PORT_1); + if (m_system.IsTriforce()) + { + sp1_device = EXIDeviceType::Baseboard; + } + + m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(sp1_device, SlotToEXIDevice(Slot::SP1)); + m_channels[SlotToEXIChannel(Slot::SP2)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_2), SlotToEXIDevice(Slot::SP2)); + m_event_type_change_device = core_timing.RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback); m_event_type_update_interrupts = diff --git a/Source/Core/Core/HW/EXI/EXI.h b/Source/Core/Core/HW/EXI/EXI.h index f0ea5fbfb5..7ded905b98 100644 --- a/Source/Core/Core/HW/EXI/EXI.h +++ b/Source/Core/Core/HW/EXI/EXI.h @@ -89,7 +89,6 @@ public: private: void AddMemoryCard(Slot slot); - void AddSP1Device(); static void ChangeDeviceCallback(Core::System& system, u64 userdata, s64 cycles_late); static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late); diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index 97306c212c..f23815a08b 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1448,12 +1448,12 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) Slave features: Inputs: - 0x01: Switchinput: players, buttons - 0x02: Coininput: slots - 0x03: Analoginput: channels, bits + 0x01: Switch input: players, buttons + 0x02: Coin input: slots + 0x03: Analog input: channels, bits 0x04: Rotary input: channels 0x05: Keycode input: 0,0,0 ? - 0x06: Screen position input: Xbits, Ybits, channels + 0x06: Screen position input: X bits, Y bits, channels Outputs: 0x10: Card system: slots diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index b32ddc6d17..5d3cd54129 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -67,6 +67,28 @@ std::optional VolumeDisc::GetRevision(const Partition& partition) const std::string VolumeDisc::GetInternalName(const Partition& partition) const { char name[0x60]; + + // Triforce games have their Title stored in the boot.id file + const FileSystem* file_system = GetFileSystem(partition); + if (file_system) + { + std::unique_ptr file_info = file_system->FindFileInfo("boot.id"); + if (file_info && !file_info->IsDirectory()) + { + u8* bootid_buffer = new u8[file_info->GetTotalSize()]; + if (Read(file_info->GetOffset(), file_info->GetTotalSize(), bootid_buffer, partition)) + { + memcpy(name, bootid_buffer + 0x80, 0x20); + + delete[] bootid_buffer; + + return DecodeString(name); + } + // Fall back to normal title from header + delete[] bootid_buffer; + } + } + if (!Read(0x20, sizeof(name), reinterpret_cast(&name), partition)) return std::string(); diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index a557ced9f1..406461cb58 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -58,6 +58,22 @@ VolumeGC::VolumeGC(std::unique_ptr reader) { m_is_triforce = true; m_triforce_id = triforce_header.id; + + // Load region from the file + switch (triforce_header.region) + { + default: + case 0x02: // JAPAN + case 0x08: // ASIA + g_triforce_region = Region::NTSC_J; + break; + case 0x0E: // USA + g_triforce_region = Region::NTSC_U; + break; + case 0x0C: // EXPORT + g_triforce_region = Region::PAL; + break; + } } } } @@ -195,30 +211,12 @@ VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const { /* There is at least one Triforce game that has an opening.bnr file but from a different game. - Check for boot.id and then just skip loading the banner. */ - u8 bootid[0x1E0]; - const u64 bootid_size = ReadFile(*this, PARTITION_NONE, "boot.id", reinterpret_cast(&bootid), sizeof(bootid)); - if (bootid_size) + if (m_is_triforce) { - // Load region from the file - switch (bootid[0x38]) - { - default: - case 0x02: // JAPAN - case 0x08: // ASIA - g_triforce_region = Region::NTSC_J; - break; - case 0x0E: // USA - g_triforce_region = Region::NTSC_U; - break; - case 0x0C: // EXPORT - g_triforce_region = Region::PAL; - break; - } - return {}; } + GCBanner banner_file; const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr", reinterpret_cast(&banner_file), sizeof(GCBanner)); diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index ff768c2e2a..b9aeb35b10 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -82,6 +82,13 @@ private: u32 magic; // "BTID" u32 padding[11]; std::array id; + u32 padding_b; + u8 region; + u8 padding_c[0x27]; + std::array maker; + std::array name; + u32 padding_d[0x10]; + char credits_text[8][32]; }; struct ConvertedGCBanner diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index d548c55fbb..69777aaa9f 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -529,6 +529,15 @@ const std::string& GameFile::GetMaker(Variant variant) const if (!maker.empty()) return maker; + // Triforce games use a default header, so all have RELSAB as the ID + // The actual information is stored within the boot.id file + + // TODO: use maker name to set company IDs + if (m_triforce_id[0] == 'S' && m_triforce_id[1] == 'B') + { + return DiscIO::GetCompanyFromID("6E"); // SEGA + } + if (m_game_id.size() >= 6) return DiscIO::GetCompanyFromID(m_maker_id); From 062677017a1a46f17f6de1d842e978af53d0bb6b Mon Sep 17 00:00:00 2001 From: crediar Date: Wed, 23 Jul 2025 19:30:53 +0200 Subject: [PATCH 09/18] Removed Triforce ID and updated code --- Source/Core/Core/ConfigManager.cpp | 8 +----- Source/Core/Core/ConfigManager.h | 1 - Source/Core/Core/HW/DVD/AMMediaboard.cpp | 8 +++--- .../Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp | 4 +-- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 8 +++--- Source/Core/Core/TitleDatabase.cpp | 28 ++----------------- Source/Core/Core/TitleDatabase.h | 3 +- Source/Core/DiscIO/VolumeDisc.cpp | 21 ++++++++++++++ Source/Core/DiscIO/VolumeGC.cpp | 9 ------ Source/Core/DiscIO/VolumeGC.h | 2 -- Source/Core/DolphinQt/Config/InfoWidget.cpp | 6 ---- Source/Core/UICommon/GameFile.cpp | 10 +++---- Source/Core/UICommon/GameFile.h | 4 +-- 13 files changed, 40 insertions(+), 72 deletions(-) diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 8927b8c2f9..6617e443f6 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -126,12 +126,6 @@ const std::string SConfig::GetTitleDescription() const return m_title_description; } -std::string SConfig::GetTriforceID() const -{ - std::lock_guard lock(m_metadata_lock); - return m_triforce_id; -} - u64 SConfig::GetTitleID() const { std::lock_guard lock(m_metadata_lock); @@ -235,7 +229,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri const Core::TitleDatabase title_database; auto& system = Core::System::GetInstance(); const DiscIO::Language language = GetLanguageAdjustedForRegion(system.IsWii(), region); - m_title_name = title_database.GetTitleName(m_gametdb_id, m_triforce_id, language); + m_title_name = title_database.GetTitleName(m_gametdb_id, language); m_title_description = title_database.Describe(m_gametdb_id, language); NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description); Host_TitleChanged(); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index f441cd888b..5aa1f30917 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -67,7 +67,6 @@ struct SConfig const std::string GetGameTDBID() const; const std::string GetTitleName() const; const std::string GetTitleDescription() const; - std::string GetTriforceID() const; u64 GetTitleID() const; u16 GetRevision() const; void ResetRunningGameMetadata(); diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index c626aadba7..2f4f4adfe4 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -200,8 +200,8 @@ void Init(void) s_netcfg = OpenOrCreateFile(base_path + "trinetcfg.bin"); s_netctrl = OpenOrCreateFile(base_path + "trinetctrl.bin"); s_extra = OpenOrCreateFile(base_path + "triextra.bin"); - s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetTriforceID() + ".bin"); - s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetTriforceID() + ".bin"); + s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetGameID() + ".bin"); + s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetGameID() + ".bin"); if (!s_netcfg) PanicAlertFmt("Failed to open/create: {}", base_path + "s_netcfg.bin"); @@ -1746,13 +1746,13 @@ u32 GetGameType(void) u64 game_id = 0; // Convert game ID into hex - if (strlen(SConfig::GetInstance().GetTriforceID().c_str()) > 4) + if (strlen(SConfig::GetInstance().GetGameID().c_str()) > 4) { game_id = 0x30303030; } else { - sscanf(SConfig::GetInstance().GetTriforceID().c_str(), "%s", (char*)&game_id); + sscanf(SConfig::GetInstance().GetGameID().c_str(), "%s", (char*)&game_id); } // This is checking for the real game IDs (See boot.id within the game) diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp index 864214aa86..694ef3524f 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp @@ -74,7 +74,7 @@ void GenerateInterrupt(int flag) CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_position(0) { std::string backup_Filename(File::GetUserPath(D_TRIUSER_IDX) + "tribackup_" + - SConfig::GetInstance().GetTriforceID().c_str() + ".bin"); + SConfig::GetInstance().GetGameID().c_str() + ".bin"); if (File::Exists(backup_Filename)) { @@ -93,7 +93,7 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit std::srand(static_cast(std::time(nullptr))); backup_Filename = File::GetUserPath(D_TRIUSER_IDX) + "tribackup_tmp_" + std::to_string(rand()) + - SConfig::GetInstance().GetTriforceID().c_str() + ".bin"; + SConfig::GetInstance().GetGameID().c_str() + ".bin"; m_backup = new File::IOFile(backup_Filename, "wb+"); } diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index f23815a08b..f30f01dcc1 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1145,7 +1145,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (m_card_memory_size == 0) { std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + - SConfig::GetInstance().GetTriforceID().c_str() + + SConfig::GetInstance().GetGameID().c_str() + ".bin"); if (File::Exists(card_filename)) @@ -1181,7 +1181,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) else if (m_card_clean == 2) { std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + - SConfig::GetInstance().GetTriforceID().c_str() + + SConfig::GetInstance().GetGameID().c_str() + ".bin"); if (File::Exists(card_filename)) @@ -1235,7 +1235,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) u32 packet_offset = 0; std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + - SConfig::GetInstance().GetTriforceID().c_str() + + SConfig::GetInstance().GetGameID().c_str() + ".bin"); if (File::Exists(card_filename)) @@ -1302,7 +1302,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) bitmode, track, m_card_memory_size); std::string card_filename(File::GetUserPath(D_TRIUSER_IDX) + "tricard_" + - SConfig::GetInstance().GetTriforceID().c_str() + + SConfig::GetInstance().GetGameID().c_str() + ".bin"); File::IOFile card = File::IOFile(card_filename, "wb+"); diff --git a/Source/Core/Core/TitleDatabase.cpp b/Source/Core/Core/TitleDatabase.cpp index 4609cfddb1..5cc521995d 100644 --- a/Source/Core/Core/TitleDatabase.cpp +++ b/Source/Core/Core/TitleDatabase.cpp @@ -94,32 +94,8 @@ TitleDatabase::TitleDatabase() TitleDatabase::~TitleDatabase() = default; const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id, - const std::string& triforce_id, DiscIO::Language language) const { - if (triforce_id != "") - { - const Map& map = *m_triforce_title_maps.at(DiscIO::Language::English); - auto it = map.find(triforce_id); - if (it != map.end()) - return it->second; - - // This code has been commented out as there is currently only a english title map, and all - // Triforce games are detected as Japanese. - - // if (language != DiscIO::Language::English) - //{ - // const Map& english_triforce_map = *m_triforce_title_maps.at(DiscIO::Language::English); - // it = english_triforce_map.find(triforce_id); - // if (it != english_triforce_map.end()) - // return it->second; - //} - - // it = m_base_map.find(triforce_id); - // if (it != m_base_map.end()) - // return it->second; - } - auto it = m_user_title_map.find(gametdb_id); if (it != m_user_title_map.end()) return it->second; @@ -152,12 +128,12 @@ const std::string& TitleDatabase::GetChannelName(u64 title_id, DiscIO::Language const std::string id{ {static_cast((title_id >> 24) & 0xff), static_cast((title_id >> 16) & 0xff), static_cast((title_id >> 8) & 0xff), static_cast(title_id & 0xff)}}; - return GetTitleName(id, "", language); + return GetTitleName(id, language); } std::string TitleDatabase::Describe(const std::string& gametdb_id, DiscIO::Language language) const { - const std::string& title_name = GetTitleName(gametdb_id, "", language); + const std::string& title_name = GetTitleName(gametdb_id, language); if (title_name.empty()) return gametdb_id; return fmt::format("{} ({})", title_name, gametdb_id); diff --git a/Source/Core/Core/TitleDatabase.h b/Source/Core/Core/TitleDatabase.h index dff9a56dfe..d1163d80a6 100644 --- a/Source/Core/Core/TitleDatabase.h +++ b/Source/Core/Core/TitleDatabase.h @@ -25,8 +25,7 @@ public: // Get a user friendly title name for a GameTDB ID. // This falls back to returning an empty string if none could be found. - const std::string& GetTitleName(const std::string& gametdb_id, const std::string& triforce_id, - DiscIO::Language language) const; + const std::string& GetTitleName(const std::string& gametdb_id, DiscIO::Language language) const; // Same as above, but takes a title ID instead of a GameTDB ID, and only works for channels. const std::string& GetChannelName(u64 title_id, DiscIO::Language language) const; diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index 5d3cd54129..35a99feace 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -20,6 +20,27 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const { char id[6]; + // Triforce games have their Game ID stored in the boot.id file + const FileSystem* file_system = GetFileSystem(partition); + if (file_system) + { + std::unique_ptr file_info = file_system->FindFileInfo("boot.id"); + if (file_info && !file_info->IsDirectory()) + { + u8* bootid_buffer = new u8[file_info->GetTotalSize()]; + if (Read(file_info->GetOffset(), file_info->GetTotalSize(), bootid_buffer, partition)) + { + memcpy(id, bootid_buffer + 0x30, sizeof(id)); + + delete[] bootid_buffer; + + return DecodeString(id); + } + // Fall back to normal ID from header + delete[] bootid_buffer; + } + } + if (!Read(0, sizeof(id), reinterpret_cast(id), partition)) return std::string(); diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index 406461cb58..e370a40fb6 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -57,7 +57,6 @@ VolumeGC::VolumeGC(std::unique_ptr reader) if (file_size >= 4 && triforce_header.magic == BTID_MAGIC) { m_is_triforce = true; - m_triforce_id = triforce_header.id; // Load region from the file switch (triforce_header.region) @@ -112,14 +111,6 @@ std::string VolumeGC::GetGameTDBID(const Partition& partition) const return GetGameID(partition); } -std::string VolumeGC::GetTriforceID() const -{ - if (m_is_triforce) - return (std::string(m_triforce_id.data(), m_triforce_id.size())); - else - return ""; -} - Region VolumeGC::GetRegion() const { if (g_triforce_region != Region::Unknown) diff --git a/Source/Core/DiscIO/VolumeGC.h b/Source/Core/DiscIO/VolumeGC.h index b9aeb35b10..bd4d64fd86 100644 --- a/Source/Core/DiscIO/VolumeGC.h +++ b/Source/Core/DiscIO/VolumeGC.h @@ -34,7 +34,6 @@ public: const Partition& partition = PARTITION_NONE) const override; const FileSystem* GetFileSystem(const Partition& partition = PARTITION_NONE) const override; std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const override; - std::string GetTriforceID() const override; std::map GetShortNames() const override; std::map GetLongNames() const override; std::map GetShortMakers() const override; @@ -120,7 +119,6 @@ private: std::unique_ptr m_reader; bool m_is_triforce; - std::array m_triforce_id; }; } // namespace DiscIO diff --git a/Source/Core/DolphinQt/Config/InfoWidget.cpp b/Source/Core/DolphinQt/Config/InfoWidget.cpp index 53785d3cd0..2d9f32a130 100644 --- a/Source/Core/DolphinQt/Config/InfoWidget.cpp +++ b/Source/Core/DolphinQt/Config/InfoWidget.cpp @@ -123,12 +123,6 @@ QGroupBox* InfoWidget::CreateGameDetails() m_game.GetMakerID() + ")"); layout->addRow(tr("Name:"), internal_name); - if (m_game.GetPlatform() == DiscIO::Platform::Triforce) - { - const auto triforce_id_string = QString::fromStdString(m_game.GetTriforceID()); - auto* const triforce_id = CreateValueDisplay(triforce_id_string); - layout->addRow(tr("Triforce ID:"), triforce_id); - } layout->addRow(tr("Game ID:"), game_id); layout->addRow(tr("Country:"), country); layout->addRow(tr("Maker:"), maker); diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 69777aaa9f..8883994926 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -130,8 +130,7 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path)) m_internal_name = volume->GetInternalName(); m_game_id = volume->GetGameID(); - m_gametdb_id = volume->GetGameTDBID(); - m_triforce_id = volume->GetTriforceID(); + m_gametdb_id = volume->GetGameTDBID(); m_title_id = volume->GetTitleID().value_or(0); m_maker_id = volume->GetMakerID(); m_revision = volume->GetRevision().value_or(0); @@ -311,8 +310,7 @@ void GameFile::DoState(PointerWrap& p) p.Do(m_descriptions); p.Do(m_internal_name); p.Do(m_game_id); - p.Do(m_gametdb_id); - p.Do(m_triforce_id); + p.Do(m_gametdb_id); p.Do(m_title_id); p.Do(m_maker_id); @@ -502,7 +500,7 @@ const std::string& GameFile::GetName(const Core::TitleDatabase& title_database) return GetName(Variant::LongAndPossiblyCustom); const std::string& database_name = - title_database.GetTitleName(m_gametdb_id, m_triforce_id, GetConfigLanguage()); + title_database.GetTitleName(m_gametdb_id, GetConfigLanguage()); return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name; } @@ -533,7 +531,7 @@ const std::string& GameFile::GetMaker(Variant variant) const // The actual information is stored within the boot.id file // TODO: use maker name to set company IDs - if (m_triforce_id[0] == 'S' && m_triforce_id[1] == 'B') + if (m_game_id[0] == 'S' && m_game_id[1] == 'B') { return DiscIO::GetCompanyFromID("6E"); // SEGA } diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h index f015fa9725..b56ca9ba57 100644 --- a/Source/Core/UICommon/GameFile.h +++ b/Source/Core/UICommon/GameFile.h @@ -75,8 +75,7 @@ public: std::vector GetLanguages() const; const std::string& GetInternalName() const { return m_internal_name; } const std::string& GetGameID() const { return m_game_id; } - const std::string& GetGameTDBID() const { return m_gametdb_id; } - std::string GetTriforceID() const { return m_triforce_id; } + const std::string& GetGameTDBID() const { return m_gametdb_id; } u64 GetTitleID() const { return m_title_id; } const std::string& GetMakerID() const { return m_maker_id; } u16 GetRevision() const { return m_revision; } @@ -160,7 +159,6 @@ private: std::string m_internal_name; std::string m_game_id; std::string m_gametdb_id; - std::string m_triforce_id; u64 m_title_id{}; std::string m_maker_id; From 64d7386c8fff37a74a87fc99edfc14ec4b4db756 Mon Sep 17 00:00:00 2001 From: crediar Date: Wed, 23 Jul 2025 20:41:07 +0200 Subject: [PATCH 10/18] Increased game cache revision --- Source/Core/UICommon/GameFileCache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp index f72f47c11e..9a09349d84 100644 --- a/Source/Core/UICommon/GameFileCache.cpp +++ b/Source/Core/UICommon/GameFileCache.cpp @@ -26,7 +26,7 @@ namespace UICommon { -static constexpr u32 CACHE_REVISION = 26; // Last changed in PR 10084 +static constexpr u32 CACHE_REVISION = 27; // Last changed in PR 10084 std::vector FindAllGamePaths(const std::vector& directories_to_scan, bool recursive_scan) From f2932352e56ff3550eca5793be65ea6731ebe73c Mon Sep 17 00:00:00 2001 From: crediar Date: Tue, 29 Jul 2025 15:39:16 +0200 Subject: [PATCH 11/18] Added three new mappable buttons for when using Triforce games Changed the window title text for the gamecube controller interface when the Triforce Baseboard is connected Added new buttons: test, service and coin to the Triforce Baseboard UI Changed code to use new buttons --- Source/Core/Core/HW/GBAPadEmu.h | 3 ++- Source/Core/Core/HW/GCPadEmu.cpp | 18 ++++++++++++++++++ Source/Core/Core/HW/GCPadEmu.h | 10 +++++++++- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 18 +++++++++--------- .../Core/DolphinQt/Config/Mapping/GCPadEmu.cpp | 13 +++++++++++-- .../DolphinQt/Config/Mapping/MappingWindow.cpp | 15 +++++++++++++-- Source/Core/InputCommon/GCPadStatus.h | 9 +++++++++ Source/dolphin-emu.sln | 3 +-- 8 files changed, 72 insertions(+), 17 deletions(-) diff --git a/Source/Core/Core/HW/GBAPadEmu.h b/Source/Core/Core/HW/GBAPadEmu.h index c9c38f1e5a..3cb23b9b98 100644 --- a/Source/Core/Core/HW/GBAPadEmu.h +++ b/Source/Core/Core/HW/GBAPadEmu.h @@ -17,7 +17,8 @@ class Buttons; enum class GBAPadGroup { DPad, - Buttons + Buttons, + Triforce }; class GBAPad : public ControllerEmu::EmulatedController diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index 0058bc74e4..f66318cb46 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -34,6 +34,7 @@ static const u16 trigger_bitmasks[] = { static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT, PAD_BUTTON_RIGHT}; +static const u8 triforce_bitmask[] = { PAD_SWITCH_TEST, PAD_SWITCH_SERVICE, PAD_SWITCH_COIN }; GCPad::GCPad(const unsigned int index) : m_index(index) { @@ -68,6 +69,13 @@ GCPad::GCPad(const unsigned int index) : m_index(index) m_dpad->AddInput(Translatability::Translate, named_direction); } + // triforce + groups.emplace_back(m_triforce = new ControllerEmu::Buttons(TRIFORCE_GROUP)); + for (const char* named_button : { TEST_BUTTON, SERVICE_BUTTON, COIN_BUTTON }) + { + m_triforce->AddInput(Translatability::DoNotTranslate, named_button); + } + // Microphone groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP)); m_mic->AddInput(Translatability::Translate, _trans("Button")); @@ -119,6 +127,8 @@ ControllerEmu::ControlGroup* GCPad::GetGroup(PadGroup group) return m_mic; case PadGroup::Options: return m_options; + case PadGroup::Triforce: + return m_triforce; default: return nullptr; } @@ -150,6 +160,9 @@ GCPadStatus GCPad::GetInput() const // dpad m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function); + // triforce + m_triforce->GetState(&pad.switches, triforce_bitmask, m_input_override_function); + // sticks const auto main_stick_state = m_main_stick->GetState(m_input_override_function); pad.stickX = MapFloat(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1); @@ -202,6 +215,11 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface) m_dpad->SetControlExpression(2, "`F`"); // Left m_dpad->SetControlExpression(3, "`H`"); // Right + // Triforce + m_dpad->SetControlExpression(0, "`1`"); // Test + m_dpad->SetControlExpression(1, "`2`"); // Service + m_dpad->SetControlExpression(2, "`3`"); // Coin + // C Stick m_c_stick->SetControlExpression(0, "`I`"); // Up m_c_stick->SetControlExpression(1, "`K`"); // Down diff --git a/Source/Core/Core/HW/GCPadEmu.h b/Source/Core/Core/HW/GCPadEmu.h index 2555ee05b1..1918b34659 100644 --- a/Source/Core/Core/HW/GCPadEmu.h +++ b/Source/Core/Core/HW/GCPadEmu.h @@ -29,7 +29,8 @@ enum class PadGroup Triggers, Rumble, Mic, - Options + Options, + Triforce }; class GCPad : public ControllerEmu::EmulatedController @@ -61,6 +62,7 @@ public: static constexpr const char* RUMBLE_GROUP = _trans("Rumble"); static constexpr const char* MIC_GROUP = _trans("Microphone"); static constexpr const char* OPTIONS_GROUP = _trans("Options"); + static constexpr const char* TRIFORCE_GROUP = _trans("Triforce"); static constexpr const char* A_BUTTON = "A"; static constexpr const char* B_BUTTON = "B"; @@ -69,6 +71,11 @@ public: static constexpr const char* Z_BUTTON = "Z"; static constexpr const char* START_BUTTON = "Start"; + // Special Triforce buttons + static constexpr const char* TEST_BUTTON = "Test"; + static constexpr const char* SERVICE_BUTTON = "Service"; + static constexpr const char* COIN_BUTTON = "Coin"; + // i18n: The left trigger button (labeled L on real controllers) static constexpr const char* L_DIGITAL = _trans("L"); // i18n: The right trigger button (labeled R on real controllers) @@ -87,6 +94,7 @@ private: ControllerEmu::ControlGroup* m_rumble; ControllerEmu::Buttons* m_mic; ControllerEmu::ControlGroup* m_options; + ControllerEmu::Buttons* m_triforce; ControllerEmu::SettingValue m_always_connected_setting; diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index f30f01dcc1..93abb824b6 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1546,7 +1546,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(0); // Test button - if (PadStatus.button & PAD_TRIGGER_Z) + if (PadStatus.switches&PAD_SWITCH_TEST) { // Trying to access the test menu without SegaBoot present will cause a crash if (AMMediaboard::GetTestMenu()) @@ -1581,7 +1581,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // Boost if (PadStatus.button & PAD_BUTTON_Y) @@ -1639,7 +1639,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // Boost if (PadStatus.button & PAD_BUTTON_Y) @@ -1689,7 +1689,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) @@ -1721,7 +1721,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) @@ -1762,7 +1762,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // A if (PadStatus.button & PAD_BUTTON_B) @@ -1813,7 +1813,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Service button - if (PadStatus.button & PAD_BUTTON_X) + if (PadStatus.switches&PAD_SWITCH_SERVICE) player_data[0] |= 0x40; // Switch 1 if (PadStatus.button & PAD_BUTTON_A) @@ -1843,11 +1843,11 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { GCPadStatus PadStatus; PadStatus = Pad::GetStatus(i); - if ((PadStatus.button & PAD_BUTTON_X) && !m_coin_pressed[i]) + if ((PadStatus.switches&PAD_SWITCH_COIN) && !m_coin_pressed[i]) { m_coin[i]++; } - m_coin_pressed[i] = PadStatus.button & PAD_BUTTON_X; + m_coin_pressed[i] = PadStatus.switches & PAD_SWITCH_COIN; message.addData((m_coin[i] >> 8) & 0x3f); message.addData(m_coin[i] & 0xff); } diff --git a/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp b/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp index d3c9f8ccfc..94ebbaeeef 100644 --- a/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp @@ -8,6 +8,10 @@ #include "Core/HW/GCPad.h" #include "Core/HW/GCPadEmu.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" + +#include "Core/Config/MainSettings.h" #include "InputCommon/ControllerEmu/Setting/NumericSetting.h" #include "InputCommon/InputConfig.h" @@ -23,8 +27,13 @@ void GCPadEmu::CreateMainLayout() layout->addWidget(CreateGroupBox(tr("Buttons"), Pad::GetGroup(GetPort(), PadGroup::Buttons)), 0, 0); - layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0, -1, - 1); + layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0 ); + + if (Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) + { + layout->addWidget(CreateGroupBox(tr("Triforce"), Pad::GetGroup(GetPort(), PadGroup::Triforce)), 2, 0); + } + layout->addWidget( CreateGroupBox(tr("Control Stick"), Pad::GetGroup(GetPort(), PadGroup::MainStick)), 0, 1, -1, 1); diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index e134902115..1d1a5ffa83 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -24,6 +24,9 @@ #include "Common/IniFile.h" #include "Common/StringUtil.h" +#include "Core/HW/SI/SI.h" +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" + #include "DolphinQt/Config/Mapping/FreeLookGeneral.h" #include "DolphinQt/Config/Mapping/FreeLookRotation.h" #include "DolphinQt/Config/Mapping/GBAPadEmu.h" @@ -437,8 +440,16 @@ void MappingWindow::SetMappingType(MappingWindow::Type type) case Type::MAPPING_GC_DANCEMAT: case Type::MAPPING_GCPAD: widget = new GCPadEmu(this); - setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1)); - AddWidget(tr("GameCube Controller"), widget); + if (Config::Get(Config::GetInfoForSIDevice(GetPort())) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) + { + setWindowTitle(tr("Triforce Baseboard at Port %1").arg(GetPort() + 1)); + AddWidget(tr("Triforce Baseboard"), widget); + } + else + { + setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1)); + AddWidget(tr("GameCube Controller"), widget); + } break; case Type::MAPPING_GC_MICROPHONE: widget = new GCMicrophone(this); diff --git a/Source/Core/InputCommon/GCPadStatus.h b/Source/Core/InputCommon/GCPadStatus.h index 74849e5594..6f7353ce34 100644 --- a/Source/Core/InputCommon/GCPadStatus.h +++ b/Source/Core/InputCommon/GCPadStatus.h @@ -28,6 +28,13 @@ enum PadButton PAD_BUTTON_START = 0x1000, }; +enum SwitchButton +{ + PAD_SWITCH_TEST = 0x0001, + PAD_SWITCH_SERVICE = 0x0002, + PAD_SWITCH_COIN = 0x0004, +}; + struct GCPadStatus { u16 button = 0; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits @@ -39,6 +46,8 @@ struct GCPadStatus u8 triggerRight = 0; // 0 <= triggerRight <= 255 u8 analogA = 0; // 0 <= analogA <= 255 u8 analogB = 0; // 0 <= analogB <= 255 + //Triforce + u8 switches = 0; bool isConnected = true; static const u8 MAIN_STICK_CENTER_X = 0x80; diff --git a/Source/dolphin-emu.sln b/Source/dolphin-emu.sln index f1183c287f..36f9c87c3a 100644 --- a/Source/dolphin-emu.sln +++ b/Source/dolphin-emu.sln @@ -25,7 +25,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Languages", "..\Languages\L EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LZO", "..\Externals\LZO\LZO.vcxproj", "{AB993F38-C31D-4897-B139-A620C42BC565}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "lz4", "..\Externals\LZ4\LZ4.vcxproj", "{9092C5CC-3E71-41B3-BF68-4A7BDD8A5476}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "LZ4", "..\Externals\lz4\LZ4.vcxproj", "{9092C5CC-3E71-41B3-BF68-4A7BDD8A5476}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "miniupnpc", "..\Externals\miniupnpc\miniupnpc.vcxproj", "{31643FDB-1BB8-4965-9DE7-000FC88D35AE}" EndProject @@ -186,7 +186,6 @@ Global {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Debug|ARM64.ActiveCfg = Debug|ARM64 {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Debug|ARM64.Build.0 = Debug|ARM64 {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Debug|x64.ActiveCfg = Debug|x64 - {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Debug|x64.Build.0 = Debug|x64 {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Release|ARM64.ActiveCfg = Release|ARM64 {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Release|ARM64.Build.0 = Release|ARM64 {31643FDB-1BB8-4965-9DE7-000FC88D35AE}.Release|x64.ActiveCfg = Release|x64 From c4a0d61c9077ea49d07b57e87ef699583122f04b Mon Sep 17 00:00:00 2001 From: crediar Date: Fri, 1 Aug 2025 20:19:58 +0200 Subject: [PATCH 12/18] Optimised code --- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 32 ++++++------------- 1 file changed, 9 insertions(+), 23 deletions(-) diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index 93abb824b6..2a2537e5e5 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1565,6 +1565,10 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { unsigned char player_data[3] = {0, 0, 0}; + // Service button + if (PadStatus.switches & PAD_SWITCH_SERVICE) + player_data[0] |= 0x40; + switch (AMMediaboard::GetGameType()) { // Controller configuration for F-Zero AX (DX) @@ -1580,9 +1584,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) // Start if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; // Boost if (PadStatus.button & PAD_BUTTON_Y) player_data[0] |= 0x02; @@ -1637,10 +1638,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; + player_data[0] |= 0x80; // Boost if (PadStatus.button & PAD_BUTTON_Y) player_data[0] |= 0x02; @@ -1687,10 +1685,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; + player_data[0] |= 0x80; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) player_data[0] |= 0x01; @@ -1719,10 +1714,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; + player_data[0] |= 0x80; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) player_data[0] |= 0x01; @@ -1760,10 +1752,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; + player_data[0] |= 0x80; // A if (PadStatus.button & PAD_BUTTON_B) player_data[0] |= 0x01; @@ -1811,10 +1800,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(0); // Debug On if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; - // Service button - if (PadStatus.switches&PAD_SWITCH_SERVICE) - player_data[0] |= 0x40; + player_data[0] |= 0x80; // Switch 1 if (PadStatus.button & PAD_BUTTON_A) player_data[0] |= 0x04; From 595f6234c35a373763aaeb54980f4f2a22de3434 Mon Sep 17 00:00:00 2001 From: crediar Date: Sat, 2 Aug 2025 12:00:13 +0200 Subject: [PATCH 13/18] Fixed default Triforce button mapping --- Source/Core/Core/HW/GCPadEmu.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index f66318cb46..5b282068b5 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -216,9 +216,9 @@ void GCPad::LoadDefaults(const ControllerInterface& ciface) m_dpad->SetControlExpression(3, "`H`"); // Right // Triforce - m_dpad->SetControlExpression(0, "`1`"); // Test - m_dpad->SetControlExpression(1, "`2`"); // Service - m_dpad->SetControlExpression(2, "`3`"); // Coin + m_triforce->SetControlExpression(0, "`1`"); // Test + m_triforce->SetControlExpression(1, "`2`"); // Service + m_triforce->SetControlExpression(2, "`3`"); // Coin // C Stick m_c_stick->SetControlExpression(0, "`I`"); // Up From 96e06f2081ad2a58b962af8178072c33dbf36832 Mon Sep 17 00:00:00 2001 From: crediar Date: Sat, 2 Aug 2025 12:24:47 +0200 Subject: [PATCH 14/18] Added game settings for Triforce games that require FPRF to be enabled --- Data/Sys/GameSettings/SBEY.ini | 2 ++ Data/Sys/GameSettings/SBGG.ini | 3 +++ Data/Sys/GameSettings/SBHA.ini | 3 +++ Data/Sys/GameSettings/SBHN.ini | 2 ++ Data/Sys/GameSettings/SBHZ.ini | 2 ++ Data/Sys/GameSettings/SBJA.ini | 2 ++ Data/Sys/GameSettings/SBLK.ini | 2 ++ Data/Sys/GameSettings/SBLL.ini | 2 ++ 8 files changed, 18 insertions(+) create mode 100644 Data/Sys/GameSettings/SBEY.ini create mode 100644 Data/Sys/GameSettings/SBGG.ini create mode 100644 Data/Sys/GameSettings/SBHA.ini create mode 100644 Data/Sys/GameSettings/SBHN.ini create mode 100644 Data/Sys/GameSettings/SBHZ.ini create mode 100644 Data/Sys/GameSettings/SBJA.ini create mode 100644 Data/Sys/GameSettings/SBLK.ini create mode 100644 Data/Sys/GameSettings/SBLL.ini diff --git a/Data/Sys/GameSettings/SBEY.ini b/Data/Sys/GameSettings/SBEY.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBEY.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True diff --git a/Data/Sys/GameSettings/SBGG.ini b/Data/Sys/GameSettings/SBGG.ini new file mode 100644 index 0000000000..412b5048c7 --- /dev/null +++ b/Data/Sys/GameSettings/SBGG.ini @@ -0,0 +1,3 @@ +[Core] +FPRF = True +CPUThread = True diff --git a/Data/Sys/GameSettings/SBHA.ini b/Data/Sys/GameSettings/SBHA.ini new file mode 100644 index 0000000000..412b5048c7 --- /dev/null +++ b/Data/Sys/GameSettings/SBHA.ini @@ -0,0 +1,3 @@ +[Core] +FPRF = True +CPUThread = True diff --git a/Data/Sys/GameSettings/SBHN.ini b/Data/Sys/GameSettings/SBHN.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBHN.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True diff --git a/Data/Sys/GameSettings/SBHZ.ini b/Data/Sys/GameSettings/SBHZ.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBHZ.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True diff --git a/Data/Sys/GameSettings/SBJA.ini b/Data/Sys/GameSettings/SBJA.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBJA.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True diff --git a/Data/Sys/GameSettings/SBLK.ini b/Data/Sys/GameSettings/SBLK.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBLK.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True diff --git a/Data/Sys/GameSettings/SBLL.ini b/Data/Sys/GameSettings/SBLL.ini new file mode 100644 index 0000000000..912626553a --- /dev/null +++ b/Data/Sys/GameSettings/SBLL.ini @@ -0,0 +1,2 @@ +[Core] +FPRF = True From 05c2f5d09fd06a55e72ab4e2e5d2aa91787c36e3 Mon Sep 17 00:00:00 2001 From: crediar Date: Sun, 3 Aug 2025 13:00:58 +0200 Subject: [PATCH 15/18] Fixed formattingFixed formatting Special DI Config bit is now only set when loading a Triforce game Made logtype names unique Removed GetPointer function and updated code to use GetSpanForAddress instead Removed incorrect value from GBAPadGroup struct Optimized the code in GetGameID that retrieves the Triforce game ID --- Source/Core/Common/CommonPaths.h | 2 +- Source/Core/Common/FileUtil.h | 2 +- Source/Core/Common/Logging/LogManager.cpp | 8 +- Source/Core/Core/Boot/Boot.cpp | 2 +- Source/Core/Core/Boot/Boot_BS2Emu.cpp | 2 +- Source/Core/Core/Core.cpp | 4 +- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 93 ++--- Source/Core/Core/HW/DVD/DVDInterface.cpp | 6 +- Source/Core/Core/HW/EXI/EXI.cpp | 4 +- Source/Core/Core/HW/EXI/EXI_Device.cpp | 1 - .../Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp | 14 +- Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h | 27 +- Source/Core/Core/HW/GBAPadEmu.h | 3 +- Source/Core/Core/HW/GCPadEmu.cpp | 4 +- Source/Core/Core/HW/Memmap.cpp | 19 - Source/Core/Core/HW/Memmap.h | 1 - Source/Core/Core/HW/SI/SI_Device.cpp | 2 +- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 16 +- Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h | 6 +- Source/Core/DiscIO/VolumeDisc.cpp | 9 +- Source/Core/DiscIO/VolumeGC.cpp | 14 +- .../DolphinQt/Config/Mapping/GCPadEmu.cpp | 8 +- .../Config/Mapping/MappingWindow.cpp | 3 +- Source/Core/DolphinQt/MainWindow.cpp | 352 +++++++++--------- Source/Core/DolphinQt/MenuBar.cpp | 4 +- .../Core/DolphinQt/Settings/GameCubePane.cpp | 14 +- Source/Core/InputCommon/GCPadStatus.h | 4 +- Source/Core/UICommon/GameFile.cpp | 7 +- Source/Core/UICommon/GameFile.h | 2 +- 29 files changed, 303 insertions(+), 330 deletions(-) diff --git a/Source/Core/Common/CommonPaths.h b/Source/Core/Common/CommonPaths.h index 0c2b0ec947..96c9af0f2d 100644 --- a/Source/Core/Common/CommonPaths.h +++ b/Source/Core/Common/CommonPaths.h @@ -41,7 +41,7 @@ #define USA_DIR "USA" #define JAP_DIR "JAP" #define JPN_DIR "JPN" -#define DEV_DIR "DEV" +#define DEV_DIR "DEV" // Subdirs in the User dir returned by GetUserPath(D_USER_IDX) #define GC_USER_DIR "GC" diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index 06562ee280..93e3e702bf 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -28,7 +28,7 @@ enum { D_USER_IDX, D_GCUSER_IDX, - D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory + D_WIIROOT_IDX, // always points to User/Wii or global user-configured directory D_TRIUSER_IDX, D_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism D_CONFIG_IDX, // global settings diff --git a/Source/Core/Common/Logging/LogManager.cpp b/Source/Core/Common/Logging/LogManager.cpp index 2f67a22afd..1814e7d53c 100644 --- a/Source/Core/Common/Logging/LogManager.cpp +++ b/Source/Core/Common/Logging/LogManager.cpp @@ -113,7 +113,7 @@ LogManager::LogManager() m_log[LogType::DSP_MAIL] = {"DSPMails", "DSP Mails"}; m_log[LogType::DSPINTERFACE] = {"DSP", "DSP Interface"}; m_log[LogType::DVDINTERFACE] = {"DVD", "DVD Interface"}; - m_log[LogType::DVDINTERFACE_AMMB] = { "DVD", "AMMB Interface" }; + m_log[LogType::DVDINTERFACE_AMMB] = {"DVD_AMMB", "AMMB Interface"}; m_log[LogType::DYNA_REC] = {"JIT", "JIT Dynamic Recompiler"}; m_log[LogType::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"}; m_log[LogType::FILEMON] = {"FileMon", "File Monitor"}; @@ -145,9 +145,9 @@ LogManager::LogManager() m_log[LogType::PROCESSORINTERFACE] = {"PI", "Processor Interface"}; m_log[LogType::POWERPC] = {"PowerPC", "PowerPC IBM CPU"}; m_log[LogType::SERIALINTERFACE] = {"SI", "Serial Interface"}; - m_log[LogType::SERIALINTERFACE_AMBB] = { "SI", "AMBB Interface" }; - m_log[LogType::SERIALINTERFACE_CARD] = { "SI", "CARD Interface" }; - m_log[LogType::SERIALINTERFACE_JVSIO] = { "SI", "JVS-I/O" }; + m_log[LogType::SERIALINTERFACE_AMBB] = {"SI_AMBB", "AMBB Interface"}; + m_log[LogType::SERIALINTERFACE_CARD] = {"SI_CARD", "CARD Interface"}; + m_log[LogType::SERIALINTERFACE_JVSIO] = {"SI_JVS", "JVS-I/O"}; m_log[LogType::SP1] = {"SP1", "Serial Port 1"}; m_log[LogType::SYMBOLS] = {"SYMBOLS", "Symbols"}; m_log[LogType::VIDEO] = {"Video", "Video Backend"}; diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index 76b612856a..eb16af8511 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -37,8 +37,8 @@ #include "Core/ConfigManager.h" #include "Core/FifoPlayer/FifoPlayer.h" #include "Core/HLE/HLE.h" -#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/AMMediaboard.h" +#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/Memmap.h" #include "Core/HW/VideoInterface.h" diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 12f76d43ab..57112245db 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -21,8 +21,8 @@ #include "Core/Core.h" #include "Core/Debugger/BranchWatch.h" #include "Core/HLE/HLE.h" -#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/AMMediaboard.h" +#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/Memmap.h" #include "Core/IOS/DI/DI.h" diff --git a/Source/Core/Core/Core.cpp b/Source/Core/Core/Core.cpp index 3405a2bdae..dbe774abec 100644 --- a/Source/Core/Core/Core.cpp +++ b/Source/Core/Core/Core.cpp @@ -303,9 +303,9 @@ void Stop(Core::System& system) // - Hammertime! system.GetFifo().ExitGpuLoop(); } - const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); + const ExpansionInterface::EXIDeviceType type = Config::Get(Config::MAIN_SERIAL_PORT_1); - if ((Type == ExpansionInterface::EXIDeviceType::Baseboard)) + if ((type == ExpansionInterface::EXIDeviceType::Baseboard)) { AMMediaboard::Shutdown(); } diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 2f4f4adfe4..4198824968 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -61,7 +61,7 @@ typedef int SOCKET; -#define INVALID_SOCKET (SOCKET)(~0) +#define INVALID_SOCKET (SOCKET)(~0) static int WSAGetLastError(void) { @@ -219,7 +219,8 @@ void Init(void) if (!File::Exists(sega_boot_filename)) { - PanicAlertFmt("Failed to open segaboot.gcm({}), which is required for test menus.", sega_boot_filename.c_str()); + PanicAlertFmt("Failed to open segaboot.gcm({}), which is required for test menus.", + sega_boot_filename.c_str()); return; } @@ -468,7 +469,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Returned value is used to set the protocol version. switch (GetGameType()) - { + { default: return Version1; case KeyOfAvalon: @@ -482,15 +483,15 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset & 0x8FFF0000) == 0x80000000) { switch (offset) - { + { case MediaBoardStatus1: memory.Write_U16(Common::swap16(0x0100), address); - break; + break; case MediaBoardStatus2: - memset(memory.GetPointer(address), 0, length); + memset(memory.GetSpanForAddress(address).data(), 0, length); break; case MediaBoardStatus3: - memset(memory.GetPointer(address), 0xFF, length); + memset(memory.GetSpanForAddress(address).data(), 0xFF, length); // DIMM size (512MB) memory.Write_U32(Common::swap32(0x20000000), address); // GCAM signature @@ -526,7 +527,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if (offset == 0x00000000 && length == 0x80) { s_netcfg->Seek(0, File::SeekOrigin::Begin); - s_netcfg->ReadBytes(memory.GetPointer(address), length); + s_netcfg->ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -534,7 +535,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if (offset == DIMMExtraSettings && length == 0x20) { s_extra->Seek(0, File::SeekOrigin::Begin); - s_extra->ReadBytes(memory.GetPointer(address), length); + s_extra->ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -543,14 +544,14 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - DIMMMemory; s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); - s_dimm->ReadBytes(memory.GetPointer(address), length); + s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } if (offset >= DIMMCommandVersion1 && offset < 0x1F900040) { u32 dimmoffset = offset - DIMMCommandVersion1; - memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_media_buffer + dimmoffset, length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (1) ({:08x},{})", offset, length); @@ -562,7 +563,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkBufferAddress4; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (4) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_buffer + dimmoffset, length); return 0; } @@ -570,7 +571,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkBufferAddress5; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (5) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_buffer + dimmoffset, length); return 0; } @@ -579,7 +580,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) u32 dimmoffset = offset - NetworkCommandAddress; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK COMMAND BUFFER ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_command_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_command_buffer + dimmoffset, + length); return 0; } @@ -588,7 +590,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) u32 dimmoffset = offset - NetworkCommandAddress2; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK COMMAND BUFFER (2) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_command_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_command_buffer + dimmoffset, + length); return 0; } @@ -596,7 +599,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkBufferAddress1; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (1) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_buffer + dimmoffset, length); return 0; } @@ -604,7 +607,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkBufferAddress2; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (2) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_buffer + dimmoffset, length); return 0; } @@ -612,14 +615,14 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkBufferAddress3; INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read NETWORK BUFFER (3) ({:08x},{})", offset, length); - memcpy(memory.GetPointer(address), s_network_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_network_buffer + dimmoffset, length); return 0; } if (offset >= DIMMCommandVersion2 && offset < 0x84000060) { u32 dimmoffset = offset - DIMMCommandVersion2; - memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_media_buffer + dimmoffset, length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (2) ({:08x},{})", offset, length); @@ -636,7 +639,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) s_media_buffer[0x204] = 1; // Recast for easier access - u32* media_buffer_32 = (u32*)(s_media_buffer); + u32* media_buffer_32 = (u32*)(s_media_buffer); switch (AMMBCommand(*(u16*)(s_media_buffer + 2))) { @@ -1024,7 +1027,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) s_media_buffer[3] |= 0x80; // Command complete flag - memset(memory.GetPointer(address), 0, length); + memset(memory.GetSpanForAddress(address).data(), 0, length); ExpansionInterface::GenerateInterrupt(0x10); return 0; @@ -1033,7 +1036,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if (offset >= DIMMCommandVersion2_2 && offset <= 0x89000200) { u32 dimmoffset = offset - DIMMCommandVersion2_2; - memcpy(memory.GetPointer(address), s_media_buffer + dimmoffset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_media_buffer + dimmoffset, length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read MEDIA BOARD COMM AREA (3) ({:08x})", dimmoffset); PrintMBBuffer(address, length); @@ -1045,14 +1048,14 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - DIMMMemory2; s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); - s_dimm->ReadBytes(memory.GetPointer(address), length); + s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } if (offset == NetworkControl && length == 0x20) { s_netctrl->Seek(0, File::SeekOrigin::Begin); - s_netctrl->ReadBytes(memory.GetPointer(address), length); + s_netctrl->ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1070,13 +1073,13 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) DICMDBUF[1] &= ~0x00100000; DICMDBUF[1] -= 0x20; } - memcpy(memory.GetPointer(address), s_firmware + offset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_firmware + offset, length); return 0; } if (s_dimm_disc) { - memcpy(memory.GetPointer(address), s_dimm_disc + offset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_dimm_disc + offset, length); return 0; } @@ -1104,7 +1107,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= 0x00400000) && (offset <= 0x600000)) { u32 fwoffset = offset - 0x00400000; - memcpy(s_firmware + fwoffset, memory.GetPointer(address), length); + memcpy(s_firmware + fwoffset, memory.GetSpanForAddress(address).data(), length); return 0; } } @@ -1112,21 +1115,21 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Network configuration if ((offset == 0x00000000) && (length == 0x80)) { - FileWriteData(s_netcfg, 0, memory.GetPointer(address), length); + FileWriteData(s_netcfg, 0, memory.GetSpanForAddress(address).data(), length); return 0; } // media crc check on/off if ((offset == DIMMExtraSettings) && (length == 0x20)) { - FileWriteData(s_extra, 0, memory.GetPointer(address), length); + FileWriteData(s_extra, 0, memory.GetSpanForAddress(address).data(), length); return 0; } // Backup memory (8MB) if ((offset >= BackupMemory) && (offset <= 0x00800000)) { - FileWriteData(s_backup, 0, memory.GetPointer(address), length); + FileWriteData(s_backup, 0, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1134,7 +1137,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= DIMMMemory) && (offset <= 0x1F800000)) { u32 dimmoffset = offset - DIMMMemory; - FileWriteData(s_dimm, dimmoffset, memory.GetPointer(address), length); + FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1142,7 +1145,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkCommandAddress; - memcpy(s_network_command_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_network_command_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), + length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK COMMAND BUFFER ({:08x},{})", dimmoffset, length); @@ -1154,7 +1158,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - NetworkCommandAddress2; - memcpy(s_network_command_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_network_command_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), + length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK COMMAND BUFFER (2) ({:08x},{})", dimmoffset, length); @@ -1166,7 +1171,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - 0x1FA00000; - memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_network_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (1) ({:08x},{})", dimmoffset, length); @@ -1178,7 +1183,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - 0x1FD00000; - memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_network_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (2) ({:08x},{})", dimmoffset, length); @@ -1190,7 +1195,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) { u32 dimmoffset = offset - 0x89100000; - memcpy(s_network_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_network_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write NETWORK BUFFER (3) ({:08x},{})", dimmoffset, length); @@ -1201,7 +1206,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= DIMMCommandVersion1) && (offset <= 0x1F90003F)) { u32 dimmoffset = offset - 0x1F900000; - memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_media_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Write MEDIA BOARD COMM AREA (1) ({:08x},{})", offset, length); @@ -1278,7 +1283,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) break; } - memcpy(memory.GetPointer(address), s_media_buffer, length); + memcpy(memory.GetSpanForAddress(address).data(), s_media_buffer, length); memset(s_media_buffer + 0x20, 0, 0x20); @@ -1287,7 +1292,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) } else { - memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_media_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); } return 0; } @@ -1299,7 +1304,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) dimmoffset); PrintMBBuffer(address, length); - memcpy(s_media_buffer + dimmoffset, memory.GetPointer(address), length); + memcpy(s_media_buffer + dimmoffset, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1318,13 +1323,13 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= DIMMMemory2) && (offset <= 0xFF800000)) { u32 dimmoffset = offset - 0xFF000000; - FileWriteData(s_dimm, dimmoffset, memory.GetPointer(address), length); + FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); return 0; } if ((offset == NetworkControl) && (length == 0x20)) { - FileWriteData(s_netctrl, 0, memory.GetPointer(address), length); + FileWriteData(s_netctrl, 0, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1711,8 +1716,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) PanicAlertFmtT("Unhandled Media Board Execute:{0:08x}", *(u16*)(s_media_buffer + 0x22)); break; - default: - PanicAlertFmtT("Unhandled Media Board Command:{0:02x}", command ); + default: + PanicAlertFmtT("Unhandled Media Board Command:{0:02x}", command); break; } diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 054ea25a12..6b81eafa08 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -27,8 +27,8 @@ #include "Core/CoreTiming.h" #include "Core/DolphinAnalytics.h" #include "Core/HW/AudioInterface.h" -#include "Core/HW/DVD/DVDMath.h" #include "Core/HW/DVD/AMMediaboard.h" +#include "Core/HW/DVD/DVDMath.h" #include "Core/HW/DVD/DVDThread.h" #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/MMIO.h" @@ -270,8 +270,6 @@ void DVDInterface::Init() m_DIIMMBUF = 0; m_DICFG.Hex = 0; m_DICFG.CONFIG = 1; // Disable bootrom descrambler - m_DICFG.Hex |= 8; /* The Triforce IPL checks this bit - to set the physical memory to either 50MB(unset) or 24MB(set) */ ResetDrive(false); @@ -292,6 +290,8 @@ void DVDInterface::Init() // The Triforce IPL expects the cover to be closed m_DICVR.Hex = 0; + m_DICFG.Hex |= 8; /* The Triforce IPL checks this bit + to set the physical memory to either 50MB(unset) or 24MB(set) */ } } diff --git a/Source/Core/Core/HW/EXI/EXI.cpp b/Source/Core/Core/HW/EXI/EXI.cpp index 515cae925f..144fdd57b9 100644 --- a/Source/Core/Core/HW/EXI/EXI.cpp +++ b/Source/Core/Core/HW/EXI/EXI.cpp @@ -152,12 +152,10 @@ void ExpansionInterfaceManager::Init(const Sram* override_sram) { sp1_device = EXIDeviceType::Baseboard; } - - m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(sp1_device, SlotToEXIDevice(Slot::SP1)); + m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(sp1_device, SlotToEXIDevice(Slot::SP1)); m_channels[SlotToEXIChannel(Slot::SP2)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_2), SlotToEXIDevice(Slot::SP2)); - m_event_type_change_device = core_timing.RegisterEvent("ChangeEXIDevice", ChangeDeviceCallback); m_event_type_update_interrupts = diff --git a/Source/Core/Core/HW/EXI/EXI_Device.cpp b/Source/Core/Core/HW/EXI/EXI_Device.cpp index 328c9fa529..ac92c7b723 100644 --- a/Source/Core/Core/HW/EXI/EXI_Device.cpp +++ b/Source/Core/Core/HW/EXI/EXI_Device.cpp @@ -16,7 +16,6 @@ #include "Core/HW/EXI/EXI_DeviceIPL.h" #include "Core/HW/EXI/EXI_DeviceMemoryCard.h" #include "Core/HW/EXI/EXI_DeviceModem.h" -#include "Core/HW/EXI/EXI_DeviceBaseboard.h" #include "Core/HW/Memmap.h" #include "Core/System.h" diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp index 694ef3524f..e728aaf250 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp @@ -1,7 +1,7 @@ -// Copyright 2017 Dolphin Emulator Project +// Copyright 2013 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later -#include "Core/HW/EXI/EXI_DeviceBaseboard.h" +#include "Core/HW/EXI/EXI_DeviceBaseboard.h" #include #include #include @@ -88,7 +88,7 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit // Some games share the same ID Client/Server if (!m_backup->IsGood()) { - PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str() ); + PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str()); std::srand(static_cast(std::time(nullptr))); @@ -167,7 +167,7 @@ void CEXIBaseboard::DMAWrite(u32 addr, u32 size) m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); - m_backup->WriteBytes(memory.GetPointer(addr), size); + m_backup->WriteBytes(memory.GetSpanForAddress(addr).data(), size); m_backup->Flush(); } @@ -183,7 +183,7 @@ void CEXIBaseboard::DMARead(u32 addr, u32 size) m_backup->Flush(); - m_backup->ReadBytes(memory.GetPointer(addr), size); + m_backup->ReadBytes(memory.GetSpanForAddress(addr).data(), size); } void CEXIBaseboard::TransferByte(u8& _byte) @@ -241,8 +241,8 @@ void CEXIBaseboard::TransferByte(u8& _byte) case DMAOffsetLengthSet: m_backup_dma_offset = (m_command[1] << 8) | m_command[2]; m_backup_dma_length = m_command[3]; - NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: DMAOffsetLengthSet :{:04x} {:02x}", m_backup_dma_offset, - m_backup_dma_length); + NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: DMAOffsetLengthSet :{:04x} {:02x}", + m_backup_dma_offset, m_backup_dma_length); _byte = 0x01; break; case ReadISR: diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h index a0632fb098..8fdc7d8ffd 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h @@ -1,3 +1,5 @@ +// Copyright 2013 Dolphin Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later #pragma once @@ -10,15 +12,14 @@ #include "Common/CommonTypes.h" #include "Common/Flag.h" -#include "Core/HW/EXI/EXI_Device.h" #include "Common/IOFile.h" +#include "Core/HW/EXI/EXI_Device.h" namespace Core { class System; } - namespace ExpansionInterface { void GenerateInterrupt(int flag); @@ -36,30 +37,28 @@ public: void DMAWrite(u32 addr, u32 size) override; void DMARead(u32 addr, u32 size) override; - private: - -enum Command -{ - BackupOffsetSet = 0x01, + enum Command + { + BackupOffsetSet = 0x01, BackupWrite = 0x02, BackupRead = 0x03, DMAOffsetLengthSet = 0x05, ReadISR = 0x82, - WriteISR = 0x83, - ReadIMR = 0x86, + WriteISR = 0x83, + ReadIMR = 0x86, WriteIMR = 0x87, - WriteLANCNT = 0xFF, -}; + WriteLANCNT = 0xFF, + }; - u32 m_position; + u32 m_position; u32 m_backup_dma_offset; u32 m_backup_dma_length; - u8 m_command[4]; - u16 m_backoffset; + u8 m_command[4]; + u16 m_backoffset; File::IOFile* m_backup; protected: diff --git a/Source/Core/Core/HW/GBAPadEmu.h b/Source/Core/Core/HW/GBAPadEmu.h index 3cb23b9b98..c9c38f1e5a 100644 --- a/Source/Core/Core/HW/GBAPadEmu.h +++ b/Source/Core/Core/HW/GBAPadEmu.h @@ -17,8 +17,7 @@ class Buttons; enum class GBAPadGroup { DPad, - Buttons, - Triforce + Buttons }; class GBAPad : public ControllerEmu::EmulatedController diff --git a/Source/Core/Core/HW/GCPadEmu.cpp b/Source/Core/Core/HW/GCPadEmu.cpp index 5b282068b5..d53823026b 100644 --- a/Source/Core/Core/HW/GCPadEmu.cpp +++ b/Source/Core/Core/HW/GCPadEmu.cpp @@ -34,7 +34,7 @@ static const u16 trigger_bitmasks[] = { static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT, PAD_BUTTON_RIGHT}; -static const u8 triforce_bitmask[] = { PAD_SWITCH_TEST, PAD_SWITCH_SERVICE, PAD_SWITCH_COIN }; +static const u8 triforce_bitmask[] = {PAD_SWITCH_TEST, PAD_SWITCH_SERVICE, PAD_SWITCH_COIN}; GCPad::GCPad(const unsigned int index) : m_index(index) { @@ -71,7 +71,7 @@ GCPad::GCPad(const unsigned int index) : m_index(index) // triforce groups.emplace_back(m_triforce = new ControllerEmu::Buttons(TRIFORCE_GROUP)); - for (const char* named_button : { TEST_BUTTON, SERVICE_BUTTON, COIN_BUTTON }) + for (const char* named_button : {TEST_BUTTON, SERVICE_BUTTON, COIN_BUTTON}) { m_triforce->AddInput(Translatability::DoNotTranslate, named_button); } diff --git a/Source/Core/Core/HW/Memmap.cpp b/Source/Core/Core/HW/Memmap.cpp index 61d5e50464..675e942009 100644 --- a/Source/Core/Core/HW/Memmap.cpp +++ b/Source/Core/Core/HW/Memmap.cpp @@ -526,25 +526,6 @@ std::span MemoryManager::GetSpanForAddress(u32 address) const LR(ppc_state)); return {}; } -u8* MemoryManager::GetPointer(u32 address) const -{ - // TODO: Should we be masking off more bits here? Can all devices access - // EXRAM? - address &= 0x3FFFFFFF; - if (address < GetRamSizeReal()) - return m_ram + address; - - if (m_exram) - { - if ((address >> 28) == 0x1 && (address & 0x0fffffff) < GetExRamSizeReal()) - return m_exram + (address & GetExRamMask()); - } - - auto& ppc_state = m_system.GetPPCState(); - PanicAlertFmt("Unknown Pointer {:#010x} PC {:#010x} LR {:#010x}", address, ppc_state.pc, - LR(ppc_state)); - return nullptr; -} u8 MemoryManager::Read_U8(u32 address) const { diff --git a/Source/Core/Core/HW/Memmap.h b/Source/Core/Core/HW/Memmap.h index 48c63e2dd4..11fca7fc10 100644 --- a/Source/Core/Core/HW/Memmap.h +++ b/Source/Core/Core/HW/Memmap.h @@ -115,7 +115,6 @@ public: // If the specified range is within a single valid memory region, returns a pointer to the start // of the corresponding range in host memory. Otherwise, returns nullptr. u8* GetPointerForRange(u32 address, size_t size) const; - u8* GetPointer(u32 address) const; void CopyFromEmu(void* data, u32 address, size_t size) const; void CopyToEmu(u32 address, const void* data, size_t size); void Memset(u32 address, u8 value, size_t size); diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp index 9d3f2cea15..2bfd150bea 100644 --- a/Source/Core/Core/HW/SI/SI_Device.cpp +++ b/Source/Core/Core/HW/SI/SI_Device.cpp @@ -16,11 +16,11 @@ #ifdef HAS_LIBMGBA #include "Core/HW/SI/SI_DeviceGBAEmu.h" #endif +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" #include "Core/HW/SI/SI_DeviceGCAdapter.h" #include "Core/HW/SI/SI_DeviceGCController.h" #include "Core/HW/SI/SI_DeviceGCSteeringWheel.h" #include "Core/HW/SI/SI_DeviceKeyboard.h" -#include "Core/HW/SI/SI_DeviceAMBaseboard.h" #include "Core/HW/SI/SI_DeviceNull.h" #include "Core/HW/SystemTimers.h" #include "Core/System.h" diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index 2a2537e5e5..cf8f833a48 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -1544,9 +1544,9 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) GCPadStatus PadStatus; PadStatus = Pad::GetStatus(0); - + // Test button - if (PadStatus.switches&PAD_SWITCH_TEST) + if (PadStatus.switches & PAD_SWITCH_TEST) { // Trying to access the test menu without SegaBoot present will cause a crash if (AMMediaboard::GetTestMenu()) @@ -1638,7 +1638,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; + player_data[0] |= 0x80; // Boost if (PadStatus.button & PAD_BUTTON_Y) player_data[0] |= 0x02; @@ -1685,7 +1685,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; + player_data[0] |= 0x80; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) player_data[0] |= 0x01; @@ -1714,7 +1714,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; + player_data[0] |= 0x80; // Long Pass if (PadStatus.button & PAD_TRIGGER_L) player_data[0] |= 0x01; @@ -1752,7 +1752,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; + player_data[0] |= 0x80; // A if (PadStatus.button & PAD_BUTTON_B) player_data[0] |= 0x01; @@ -1800,7 +1800,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) PadStatus = Pad::GetStatus(0); // Debug On if (PadStatus.button & PAD_BUTTON_START) - player_data[0] |= 0x80; + player_data[0] |= 0x80; // Switch 1 if (PadStatus.button & PAD_BUTTON_A) player_data[0] |= 0x04; @@ -1829,7 +1829,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { GCPadStatus PadStatus; PadStatus = Pad::GetStatus(i); - if ((PadStatus.switches&PAD_SWITCH_COIN) && !m_coin_pressed[i]) + if ((PadStatus.switches & PAD_SWITCH_COIN) && !m_coin_pressed[i]) { m_coin[i]++; } diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h index c4f21bc321..dc79df72a3 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h @@ -49,8 +49,8 @@ private: SerialNumber = 0x11, Unknown_12 = 0x12, Unknown_14 = 0x14, - FirmVersion= 0x15, - FPGAVersion= 0x16, + FirmVersion = 0x15, + FPGAVersion = 0x16, RegionSettings = 0x1F, Unknown_21 = 0x21, @@ -151,7 +151,7 @@ private: BootVersion = 0x62, SensLock = 0x63, SensCard = 0x65, - FirmwareUpdate =0x66, + FirmwareUpdate = 0x66, ShutterGet = 0x67, CameraCheck = 0x68, ShutterCard = 0x69, diff --git a/Source/Core/DiscIO/VolumeDisc.cpp b/Source/Core/DiscIO/VolumeDisc.cpp index 35a99feace..0d6029ccf0 100644 --- a/Source/Core/DiscIO/VolumeDisc.cpp +++ b/Source/Core/DiscIO/VolumeDisc.cpp @@ -27,17 +27,10 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const std::unique_ptr file_info = file_system->FindFileInfo("boot.id"); if (file_info && !file_info->IsDirectory()) { - u8* bootid_buffer = new u8[file_info->GetTotalSize()]; - if (Read(file_info->GetOffset(), file_info->GetTotalSize(), bootid_buffer, partition)) + if (Read(file_info->GetOffset() + 0x30, sizeof(id), reinterpret_cast(id), partition)) { - memcpy(id, bootid_buffer + 0x30, sizeof(id)); - - delete[] bootid_buffer; - return DecodeString(id); } - // Fall back to normal ID from header - delete[] bootid_buffer; } } diff --git a/Source/Core/DiscIO/VolumeGC.cpp b/Source/Core/DiscIO/VolumeGC.cpp index e370a40fb6..8d43ba02df 100644 --- a/Source/Core/DiscIO/VolumeGC.cpp +++ b/Source/Core/DiscIO/VolumeGC.cpp @@ -29,7 +29,7 @@ namespace DiscIO { - Region g_triforce_region; +Region g_triforce_region; VolumeGC::VolumeGC(std::unique_ptr reader) : m_reader(std::move(reader)), m_is_triforce(false) @@ -61,15 +61,15 @@ VolumeGC::VolumeGC(std::unique_ptr reader) // Load region from the file switch (triforce_header.region) { - default: - case 0x02: // JAPAN - case 0x08: // ASIA + default: + case 0x02: // JAPAN + case 0x08: // ASIA g_triforce_region = Region::NTSC_J; break; - case 0x0E: // USA + case 0x0E: // USA g_triforce_region = Region::NTSC_U; break; - case 0x0C: // EXPORT + case 0x0C: // EXPORT g_triforce_region = Region::PAL; break; } @@ -207,7 +207,7 @@ VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const { return {}; } - + GCBanner banner_file; const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr", reinterpret_cast(&banner_file), sizeof(GCBanner)); diff --git a/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp b/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp index 94ebbaeeef..6b9316fc5d 100644 --- a/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/GCPadEmu.cpp @@ -27,11 +27,13 @@ void GCPadEmu::CreateMainLayout() layout->addWidget(CreateGroupBox(tr("Buttons"), Pad::GetGroup(GetPort(), PadGroup::Buttons)), 0, 0); - layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0 ); + layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0); - if (Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) + if (Config::Get(Config::GetInfoForSIDevice(0)) == + SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) { - layout->addWidget(CreateGroupBox(tr("Triforce"), Pad::GetGroup(GetPort(), PadGroup::Triforce)), 2, 0); + layout->addWidget(CreateGroupBox(tr("Triforce"), Pad::GetGroup(GetPort(), PadGroup::Triforce)), + 2, 0); } layout->addWidget( diff --git a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp index 1d1a5ffa83..b4676c06c3 100644 --- a/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp +++ b/Source/Core/DolphinQt/Config/Mapping/MappingWindow.cpp @@ -440,7 +440,8 @@ void MappingWindow::SetMappingType(MappingWindow::Type type) case Type::MAPPING_GC_DANCEMAT: case Type::MAPPING_GCPAD: widget = new GCPadEmu(this); - if (Config::Get(Config::GetInfoForSIDevice(GetPort())) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) + if (Config::Get(Config::GetInfoForSIDevice(GetPort())) == + SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD) { setWindowTitle(tr("Triforce Baseboard at Port %1").arg(GetPort() + 1)); AddWidget(tr("Triforce Baseboard"), widget); diff --git a/Source/Core/DolphinQt/MainWindow.cpp b/Source/Core/DolphinQt/MainWindow.cpp index 1059f89a1f..820a6f11bd 100644 --- a/Source/Core/DolphinQt/MainWindow.cpp +++ b/Source/Core/DolphinQt/MainWindow.cpp @@ -178,8 +178,8 @@ static WindowSystemType GetWindowSystemType() return WindowSystemType::Haiku; ModalMessageBox::critical( - nullptr, QStringLiteral("Error"), - QString::asprintf("Unknown Qt platform: %s", platform_name.toStdString().c_str())); + nullptr, QStringLiteral("Error"), + QString::asprintf("Unknown Qt platform: %s", platform_name.toStdString().c_str())); return WindowSystemType::Headless; } @@ -218,8 +218,8 @@ static std::vector StringListToStdVector(QStringList list) } MainWindow::MainWindow(Core::System& system, std::unique_ptr boot_parameters, - const std::string& movie_path) - : QMainWindow(nullptr), m_system(system) + const std::string& movie_path) + : QMainWindow(nullptr), m_system(system) { setWindowTitle(QString::fromStdString(Common::GetScmRevStr())); setWindowIcon(Resources::GetAppIcon()); @@ -249,21 +249,21 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo #if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) connect(QGuiApplication::styleHints(), &QStyleHints::colorSchemeChanged, this, - [this](Qt::ColorScheme colorScheme) { - Settings::Instance().ApplyStyle(); - if (m_skylander_window) - m_skylander_window->RefreshList(); - }); + [this](Qt::ColorScheme colorScheme) { + Settings::Instance().ApplyStyle(); + if (m_skylander_window) + m_skylander_window->RefreshList(); + }); #endif connect(m_cheats_manager, &CheatsManager::OpenGeneralSettings, this, - &MainWindow::ShowGeneralWindow); + &MainWindow::ShowGeneralWindow); #ifdef USE_RETRO_ACHIEVEMENTS connect(m_cheats_manager, &CheatsManager::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); connect(m_game_list, &GameList::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS InitCoreCallbacks(); @@ -276,7 +276,7 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo Settings::Instance().SetDebugModeEnabled(false); // This needs to trigger on both RA_HARDCORE_ENABLED and RA_ENABLED m_config_changed_callback_id = Config::AddConfigChangedCallback( - [this] { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); }); + [this] { QueueOnObject(this, [this] { this->OnHardcoreChanged(); }); }); // If hardcore is enabled when the emulator starts, make sure it turns off what it needs to if (Config::Get(Config::RA_HARDCORE_ENABLED)) OnHardcoreChanged(); @@ -300,14 +300,14 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (m_system.GetMovie().PlayInput(movie_path, &savestate_path)) { m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path), - DeleteSavestateAfterBoot::No); + DeleteSavestateAfterBoot::No); emit RecordingStatusChanged(true); } } } m_state_slot = - std::clamp(Settings::Instance().GetStateSlot(), 1, static_cast(State::NUM_STATES)); + std::clamp(Settings::Instance().GetStateSlot(), 1, static_cast(State::NUM_STATES)); m_render_widget_geometry = settings.value(QStringLiteral("renderwidget/geometry")).toByteArray(); @@ -318,7 +318,7 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (!ResourcePack::Init()) { ModalMessageBox::critical(this, tr("Error"), - tr("Error occurred while loading some texture packs")); + tr("Error occurred while loading some texture packs")); } for (auto& pack : ResourcePack::GetPacks()) @@ -326,9 +326,9 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr boo if (!pack.IsValid()) { ModalMessageBox::critical(this, tr("Error"), - tr("Invalid Pack %1 provided: %2") - .arg(QString::fromStdString(pack.GetPath())) - .arg(QString::fromStdString(pack.GetError()))); + tr("Invalid Pack %1 provided: %2") + .arg(QString::fromStdString(pack.GetPath())) + .arg(QString::fromStdString(pack.GetError()))); return; } } @@ -433,7 +433,7 @@ void MainWindow::InitCoreCallbacks() FullScreen(); m_fullscreen_requested = false; } - }); + }); installEventFilter(this); m_render_widget->installEventFilter(this); @@ -441,7 +441,7 @@ void MainWindow::InitCoreCallbacks() auto* filter = new FileOpenEventFilter(QGuiApplication::instance()); connect(filter, &FileOpenEventFilter::fileOpened, this, [this](const QString& file_name) { StartGame(BootParameters::GenerateFromFile(file_name.toStdString())); - }); + }); } static void InstallHotkeyFilter(QWidget* dialog) @@ -450,9 +450,9 @@ static void InstallHotkeyFilter(QWidget* dialog) dialog->installEventFilter(filter); filter->connect(filter, &WindowActivationEventFilter::windowDeactivated, - [] { HotkeyManagerEmu::Enable(true); }); + [] { HotkeyManagerEmu::Enable(true); }); filter->connect(filter, &WindowActivationEventFilter::windowActivated, - [] { HotkeyManagerEmu::Enable(false); }); + [] { HotkeyManagerEmu::Enable(false); }); } void MainWindow::CreateComponents() @@ -486,15 +486,15 @@ void MainWindow::CreateComponents() const auto request_watch = [this](QString name, u32 addr) { m_watch_widget->AddWatch(name, addr); - }; + }; const auto request_breakpoint = [this](u32 addr) { m_breakpoint_widget->AddBP(addr); }; const auto request_memory_breakpoint = [this](u32 addr) { m_breakpoint_widget->AddAddressMBP(addr); - }; + }; const auto request_view_in_memory = [this](u32 addr) { m_memory_widget->SetAddress(addr); }; const auto request_view_in_code = [this](u32 addr) { m_code_widget->SetAddress(addr, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }; + }; connect(m_jit_widget, &JITWidget::SetCodeAddress, m_code_widget, &CodeWidget::OnSetCodeAddress); connect(m_watch_widget, &WatchWidget::RequestMemoryBreakpoint, request_memory_breakpoint); @@ -510,19 +510,19 @@ void MainWindow::CreateComponents() connect(m_thread_widget, &ThreadWidget::RequestViewInCode, request_view_in_code); connect(m_code_widget, &CodeWidget::RequestPPCComparison, m_jit_widget, - &JITWidget::OnRequestPPCComparison); + &JITWidget::OnRequestPPCComparison); connect(m_code_widget, &CodeWidget::ShowMemory, m_memory_widget, &MemoryWidget::SetAddress); connect(m_memory_widget, &MemoryWidget::ShowCode, m_code_widget, [this](u32 address) { m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }); + }); connect(m_memory_widget, &MemoryWidget::RequestWatch, request_watch); connect(m_breakpoint_widget, &BreakpointWidget::ShowCode, [this](u32 address) { if (Core::GetState(m_system) == Core::State::Paused) m_code_widget->SetAddress(address, CodeViewWidget::SetAddressUpdate::WithDetailedUpdate); - }); + }); connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget, - &MemoryWidget::SetAddress); + &MemoryWidget::SetAddress); connect(m_cheats_manager, &CheatsManager::ShowMemory, m_memory_widget, &MemoryWidget::SetAddress); connect(m_cheats_manager, &CheatsManager::RequestWatch, request_watch); } @@ -567,7 +567,7 @@ void MainWindow::ConnectMenuBar() // Tools connect(m_menu_bar, &MenuBar::ShowMemcardManager, this, &MainWindow::ShowMemcardManager); connect(m_menu_bar, &MenuBar::ShowResourcePackManager, this, - &MainWindow::ShowResourcePackManager); + &MainWindow::ShowResourcePackManager); connect(m_menu_bar, &MenuBar::ShowCheatsManager, this, &MainWindow::ShowCheatsManager); connect(m_menu_bar, &MenuBar::BootGameCubeIPL, this, &MainWindow::OnBootGameCubeIPL); connect(m_menu_bar, &MenuBar::ImportNANDBackup, this, &MainWindow::OnImportNANDBackup); @@ -599,12 +599,12 @@ void MainWindow::ConnectMenuBar() connect(m_menu_bar, &MenuBar::ShowSearch, m_search_bar, &SearchBar::Show); connect(m_menu_bar, &MenuBar::ColumnVisibilityToggled, m_game_list, - &GameList::OnColumnVisibilityToggled); + &GameList::OnColumnVisibilityToggled); connect(m_menu_bar, &MenuBar::GameListPlatformVisibilityToggled, m_game_list, - &GameList::OnGameListVisibilityChanged); + &GameList::OnGameListVisibilityChanged); connect(m_menu_bar, &MenuBar::GameListRegionVisibilityToggled, m_game_list, - &GameList::OnGameListVisibilityChanged); + &GameList::OnGameListVisibilityChanged); connect(m_menu_bar, &MenuBar::ShowAboutDialog, this, &MainWindow::ShowAboutDialog); @@ -623,9 +623,9 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::TogglePauseHotkey, this, &MainWindow::TogglePause); connect(m_hotkey_scheduler, &HotkeyScheduler::ActivateChat, this, &MainWindow::OnActivateChat); connect(m_hotkey_scheduler, &HotkeyScheduler::RequestGolfControl, this, - &MainWindow::OnRequestGolfControl); + &MainWindow::OnRequestGolfControl); connect(m_hotkey_scheduler, &HotkeyScheduler::RefreshGameListHotkey, this, - &MainWindow::RefreshGameList); + &MainWindow::RefreshGameList); connect(m_hotkey_scheduler, &HotkeyScheduler::StopHotkey, this, &MainWindow::RequestStop); connect(m_hotkey_scheduler, &HotkeyScheduler::ResetHotkey, this, &MainWindow::Reset); connect(m_hotkey_scheduler, &HotkeyScheduler::ScreenShotHotkey, this, &MainWindow::ScreenShot); @@ -634,40 +634,40 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadSlot, this, &MainWindow::StateLoadSlotAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveSlot, this, &MainWindow::StateSaveSlotAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadLastSaved, this, - &MainWindow::StateLoadLastSavedAt); + &MainWindow::StateLoadLastSavedAt); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadUndo, this, &MainWindow::StateLoadUndo); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveUndo, this, &MainWindow::StateSaveUndo); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveOldest, this, - &MainWindow::StateSaveOldest); + &MainWindow::StateSaveOldest); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveFile, this, &MainWindow::StateSave); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadFile, this, &MainWindow::StateLoad); connect(m_hotkey_scheduler, &HotkeyScheduler::StateLoadSlotHotkey, this, - &MainWindow::StateLoadSlot); + &MainWindow::StateLoadSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::StateSaveSlotHotkey, this, - &MainWindow::StateSaveSlot); + &MainWindow::StateSaveSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::SetStateSlotHotkey, this, - &MainWindow::SetStateSlot); + &MainWindow::SetStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::IncrementSelectedStateSlotHotkey, this, - &MainWindow::IncrementSelectedStateSlot); + &MainWindow::IncrementSelectedStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::DecrementSelectedStateSlotHotkey, this, - &MainWindow::DecrementSelectedStateSlot); + &MainWindow::DecrementSelectedStateSlot); connect(m_hotkey_scheduler, &HotkeyScheduler::StartRecording, this, - &MainWindow::OnStartRecording); + &MainWindow::OnStartRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::PlayRecording, this, &MainWindow::OnPlayRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::ExportRecording, this, - &MainWindow::OnExportRecording); + &MainWindow::OnExportRecording); connect(m_hotkey_scheduler, &HotkeyScheduler::ConnectWiiRemote, this, - &MainWindow::OnConnectWiiRemote); + &MainWindow::OnConnectWiiRemote); connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleReadOnlyMode, [this] { auto& movie = m_system.GetMovie(); bool read_only = !movie.IsReadOnly(); movie.SetReadOnly(read_only); emit ReadOnlyModeChanged(read_only); - }); + }); #ifdef USE_RETRO_ACHIEVEMENTS connect(m_hotkey_scheduler, &HotkeyScheduler::OpenAchievements, this, - &MainWindow::ShowAchievementsWindow, Qt::QueuedConnection); + &MainWindow::ShowAchievementsWindow, Qt::QueuedConnection); #endif // USE_RETRO_ACHIEVEMENTS connect(m_hotkey_scheduler, &HotkeyScheduler::Step, m_code_widget, &CodeWidget::Step); @@ -679,14 +679,14 @@ void MainWindow::ConnectHotkeys() connect(m_hotkey_scheduler, &HotkeyScheduler::SetPC, m_code_widget, &CodeWidget::SetPC); connect(m_hotkey_scheduler, &HotkeyScheduler::ToggleBreakpoint, m_code_widget, - &CodeWidget::ToggleBreakpoint); + &CodeWidget::ToggleBreakpoint); connect(m_hotkey_scheduler, &HotkeyScheduler::AddBreakpoint, m_code_widget, - &CodeWidget::AddBreakpoint); + &CodeWidget::AddBreakpoint); connect(m_hotkey_scheduler, &HotkeyScheduler::SkylandersPortalHotkey, this, - &MainWindow::ShowSkylanderPortal); + &MainWindow::ShowSkylanderPortal); connect(m_hotkey_scheduler, &HotkeyScheduler::InfinityBaseHotkey, this, - &MainWindow::ShowInfinityBase); + &MainWindow::ShowInfinityBase); } void MainWindow::ConnectToolBar() @@ -718,7 +718,7 @@ void MainWindow::ConnectGameList() connect(m_game_list, &GameList::GameSelected, this, [this] { Play(); }); connect(m_game_list, &GameList::NetPlayHost, this, &MainWindow::NetPlayHost); connect(m_game_list, &GameList::OnStartWithRiivolution, this, - &MainWindow::ShowRiivolutionBootWidget); + &MainWindow::ShowRiivolutionBootWidget); connect(m_game_list, &GameList::OpenGeneralSettings, this, &MainWindow::ShowGeneralWindow); connect(m_game_list, &GameList::OpenGraphicsSettings, this, &MainWindow::ShowGraphicsWindow); @@ -732,7 +732,7 @@ void MainWindow::ConnectRenderWidget() connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) { if (m_render_widget->isFullScreen()) SetFullScreenResolution(focus); - }); + }); } void MainWindow::ConnectHost() @@ -792,17 +792,17 @@ QStringList MainWindow::PromptFileNames() { auto& settings = Settings::Instance().GetQSettings(); QStringList paths = DolphinFileDialog::getOpenFileNames( - this, tr("Select a File"), - settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(), - QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz " - "hif_000000.nfs *.wad *.dff *.m3u *.json);;%2 (*)") - .arg(tr("All GC/Wii files")) - .arg(tr("All Files"))); + this, tr("Select a File"), + settings.value(QStringLiteral("mainwindow/lastdir"), QString{}).toString(), + QStringLiteral("%1 (*.elf *.dol *.gcm *.bin *.iso *.tgc *.wbfs *.ciso *.gcz *.wia *.rvz " + "hif_000000.nfs *.wad *.dff *.m3u *.json);;%2 (*)") + .arg(tr("All GC/Wii files")) + .arg(tr("All Files"))); if (!paths.isEmpty()) { settings.setValue(QStringLiteral("mainwindow/lastdir"), - QFileInfo(paths.front()).absoluteDir().absolutePath()); + QFileInfo(paths.front()).absoluteDir().absolutePath()); } return paths; @@ -815,12 +815,12 @@ void MainWindow::ChangeDisc() if (paths.empty()) return; - m_system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{ m_system }, paths); + m_system.GetDVDInterface().ChangeDisc(Core::CPUThreadGuard{m_system}, paths); } void MainWindow::EjectDisc() { - m_system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{ m_system }, DVD::EjectCause::User); + m_system.GetDVDInterface().EjectDisc(Core::CPUThreadGuard{m_system}, DVD::EjectCause::User); } void MainWindow::OpenUserFolder() @@ -855,7 +855,7 @@ void MainWindow::Play(const std::optional& savestate_path) if (selection) { StartGame(selection->GetFilePath(), ScanForSecondDisc::Yes, - std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); + std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); } else { @@ -863,7 +863,7 @@ void MainWindow::Play(const std::optional& savestate_path) if (!default_path.isEmpty() && QFile::exists(default_path)) { StartGame(default_path, ScanForSecondDisc::Yes, - std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); + std::make_unique(savestate_path, DeleteSavestateAfterBoot::No)); } else { @@ -930,11 +930,11 @@ bool MainWindow::RequestStop() } const bool rendered_widget_was_active = - Settings::Instance().IsKeepWindowOnTopEnabled() || - (m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen()); + Settings::Instance().IsKeepWindowOnTopEnabled() || + (m_render_widget->isActiveWindow() && !m_render_widget->isFullScreen()); QWidget* confirm_parent = (!m_rendering_to_main && rendered_widget_was_active) ? - m_render_widget : - static_cast(this); + m_render_widget : + static_cast(this); const bool was_cursor_locked = m_render_widget->IsCursorLocked(); if (!m_render_widget->isFullScreen()) @@ -975,14 +975,14 @@ bool MainWindow::RequestStop() if (m_stop_requested) { message = tr("A shutdown is already in progress. Unsaved data " - "may be lost if you stop the current emulation " - "before it completes. Force stop?"); + "may be lost if you stop the current emulation " + "before it completes. Force stop?"); } #ifdef RC_CLIENT_SUPPORTS_RAINTEGRATION else if (AchievementManager::GetInstance().CheckForModifications()) { message = tr( - "Do you want to stop the current emulation? Unsaved achievement modifications detected."); + "Do you want to stop the current emulation? Unsaved achievement modifications detected."); } #endif // RC_CLIENT_SUPPORTS_RAINTEGRATION else @@ -990,8 +990,8 @@ bool MainWindow::RequestStop() message = tr("Do you want to stop the current emulation?"); } auto confirm = ModalMessageBox::question(confirm_parent, tr("Confirm"), message, - QMessageBox::Yes | QMessageBox::No, - QMessageBox::NoButton, Qt::ApplicationModal); + QMessageBox::Yes | QMessageBox::No, + QMessageBox::NoButton, Qt::ApplicationModal); // If a user confirmed stopping the emulation, we do not capture the cursor again, // even if the render widget will stay alive for a while. @@ -1095,11 +1095,11 @@ void MainWindow::ScreenShot() } void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { auto second_game = m_game_list->FindSecondDisc(game); - std::vector paths = { game.GetFilePath() }; + std::vector paths = {game.GetFilePath()}; if (second_game != nullptr) paths.push_back(second_game->GetFilePath()); @@ -1107,13 +1107,13 @@ void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game, } void MainWindow::StartGame(const QString& path, ScanForSecondDisc scan, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { StartGame(path.toStdString(), scan, std::move(boot_session_data)); } void MainWindow::StartGame(const std::string& path, ScanForSecondDisc scan, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { if (scan == ScanForSecondDisc::Yes) { @@ -1126,14 +1126,14 @@ void MainWindow::StartGame(const std::string& path, ScanForSecondDisc scan, } StartGame(BootParameters::GenerateFromFile( - path, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); + path, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); } void MainWindow::StartGame(const std::vector& paths, - std::unique_ptr boot_session_data) + std::unique_ptr boot_session_data) { StartGame(BootParameters::GenerateFromFile( - paths, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); + paths, boot_session_data ? std::move(*boot_session_data) : BootSessionData())); } void MainWindow::StartGame(std::unique_ptr&& parameters) @@ -1150,18 +1150,21 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) When booting Triforce games, we need to ensure that the hardware is set up correctly. */ const auto volume_type = - std::get(parameters->parameters).volume->GetVolumeType(); - - const bool triforce_hardware_sp1 = Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard; - const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) == SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD; + std::get(parameters->parameters).volume->GetVolumeType(); + + const bool triforce_hardware_sp1 = + Config::Get(Config::MAIN_SERIAL_PORT_1) == ExpansionInterface::EXIDeviceType::Baseboard; + const bool triforce_hardware_port_1 = Config::Get(Config::GetInfoForSIDevice(0)) == + SerialInterface::SIDevices::SIDEVICE_AM_BASEBOARD; if (volume_type == DiscIO::Platform::Triforce) { if (!triforce_hardware_sp1 || !triforce_hardware_port_1) { ModalMessageBox::critical( - this, tr("Error"), tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."), - QMessageBox::Ok); + this, tr("Error"), + tr("To boot a Triforce game, SP1 and Port 1 must be set to Triforce Baseboard."), + QMessageBox::Ok); HideRenderWidget(); return; } @@ -1174,11 +1177,12 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) if (triforce_hardware_sp1 || triforce_hardware_port_1) { ModalMessageBox::warning( - this, tr("Warning"), tr("Non-Triforce games cannot be booted with Triforce hardware attached."), - QMessageBox::Ok); + this, tr("Warning"), + tr("Non-Triforce games cannot be booted with Triforce hardware attached."), + QMessageBox::Ok); } } - } + } // If we're running, only start a new game once we've stopped the last. if (!Core::IsUninitialized(m_system)) @@ -1196,7 +1200,7 @@ void MainWindow::StartGame(std::unique_ptr&& parameters) // Boot up, show an error if it fails to load the game. if (!BootManager::BootCore(m_system, std::move(parameters), - ::GetWindowSystemInfo(m_render_widget->windowHandle()))) + ::GetWindowSystemInfo(m_render_widget->windowHandle()))) { ModalMessageBox::critical(this, tr("Error"), tr("Failed to init core"), QMessageBox::Ok); HideRenderWidget(); @@ -1228,7 +1232,7 @@ void MainWindow::SetFullScreenResolution(bool fullscreen) memset(&screen_settings, 0, sizeof(screen_settings)); screen_settings.dmSize = sizeof(screen_settings); sscanf(Config::Get(Config::MAIN_FULLSCREEN_DISPLAY_RES).c_str(), "%dx%d", - &screen_settings.dmPelsWidth, &screen_settings.dmPelsHeight); + &screen_settings.dmPelsWidth, &screen_settings.dmPelsHeight); screen_settings.dmBitsPerPel = 32; screen_settings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; @@ -1299,14 +1303,14 @@ void MainWindow::HideRenderWidget(bool reinit, bool is_exit) connect(m_render_widget, &RenderWidget::FocusChanged, this, [this](bool focus) { if (m_render_widget->isFullScreen()) SetFullScreenResolution(focus); - }); + }); // The controller interface will still be registered to the old render widget, if the core // has booted. Therefore, we should re-bind it to the main window for now. When the core // is next started, it will be swapped back to the new render widget. g_controller_interface.ChangeWindow(::GetWindowSystemInfo(windowHandle()).render_window, - is_exit ? ControllerInterface::WindowChangeReason::Exit : - ControllerInterface::WindowChangeReason::Other); + is_exit ? ControllerInterface::WindowChangeReason::Exit : + ControllerInterface::WindowChangeReason::Other); } } @@ -1325,7 +1329,7 @@ void MainWindow::ShowFreeLookWindow() #ifdef USE_RETRO_ACHIEVEMENTS connect(m_freelook_window, &FreeLookWindow::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS } @@ -1342,9 +1346,9 @@ void MainWindow::ShowSettingsWindow() if (GetWindowSystemType() == WindowSystemType::X11) { m_xrr_config = std::make_unique( - static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow( - "display", windowHandle())), - winId()); + static_cast(QGuiApplication::platformNativeInterface()->nativeResourceForWindow( + "display", windowHandle())), + winId()); } #endif m_settings_window = new SettingsWindow(this); @@ -1370,7 +1374,7 @@ void MainWindow::ShowGeneralWindow() void MainWindow::ShowAboutDialog() { - AboutDialog about{ this }; + AboutDialog about{this}; about.exec(); } @@ -1414,7 +1418,7 @@ void MainWindow::ShowFIFOPlayer() { m_fifo_window = new FIFOPlayerWindow(m_system.GetFifoPlayer(), m_system.GetFifoRecorder()); connect(m_fifo_window, &FIFOPlayerWindow::LoadFIFORequested, this, - [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); + [this](const QString& path) { StartGame(path, ScanForSecondDisc::No); }); } m_fifo_window->show(); @@ -1461,10 +1465,10 @@ void MainWindow::ShowWiiSpeakWindow() void MainWindow::StateLoad() { QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ? - QDir::currentPath() : - QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); + QDir::currentPath() : + QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); QString path = DolphinFileDialog::getOpenFileName( - this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); + this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); if (!path.isEmpty()) State::LoadAs(m_system, path.toStdString()); @@ -1473,10 +1477,10 @@ void MainWindow::StateLoad() void MainWindow::StateSave() { QString dialog_path = (Config::Get(Config::MAIN_CURRENT_STATE_PATH).empty()) ? - QDir::currentPath() : - QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); + QDir::currentPath() : + QString::fromStdString(Config::Get(Config::MAIN_CURRENT_STATE_PATH)); QString path = DolphinFileDialog::getSaveFileName( - this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); + this, tr("Select a File"), dialog_path, tr("All Save States (*.sav *.s##);; All Files (*)")); Config::SetBase(Config::MAIN_CURRENT_STATE_PATH, QFileInfo(path).dir().path().toStdString()); if (!path.isEmpty()) State::SaveAs(m_system, path.toStdString()); @@ -1528,8 +1532,8 @@ void MainWindow::SetStateSlot(int slot) m_state_slot = slot; Core::DisplayMessage(fmt::format("Selected slot {} - {}", m_state_slot, - State::GetInfoStringOfSlot(m_state_slot, false)), - 2500); + State::GetInfoStringOfSlot(m_state_slot, false)), + 2500); } void MainWindow::IncrementSelectedStateSlot() @@ -1557,7 +1561,7 @@ void MainWindow::PerformOnlineUpdate(const std::string& region) void MainWindow::BootWiiSystemMenu() { - StartGame(std::make_unique(BootParameters::NANDTitle{ Titles::SYSTEM_MENU })); + StartGame(std::make_unique(BootParameters::NANDTitle{Titles::SYSTEM_MENU})); } void MainWindow::NetPlayInit() @@ -1565,10 +1569,10 @@ void MainWindow::NetPlayInit() const auto& game_list_model = m_game_list->GetGameListModel(); m_netplay_setup_dialog = new NetPlaySetupDialog(game_list_model, this); m_netplay_dialog = new NetPlayDialog( - game_list_model, - [this](const std::string& path, std::unique_ptr boot_session_data) { - StartGame(path, ScanForSecondDisc::Yes, std::move(boot_session_data)); - }); + game_list_model, + [this](const std::string& path, std::unique_ptr boot_session_data) { + StartGame(path, ScanForSecondDisc::Yes, std::move(boot_session_data)); + }); #ifdef USE_DISCORD_PRESENCE m_netplay_discord = new DiscordHandler(this); #endif @@ -1584,9 +1588,9 @@ void MainWindow::NetPlayInit() m_netplay_discord->Start(); #endif connect(&Settings::Instance(), &Settings::ConfigChanged, this, - &MainWindow::UpdateScreenSaverInhibition); + &MainWindow::UpdateScreenSaverInhibition); connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, - &MainWindow::UpdateScreenSaverInhibition); + &MainWindow::UpdateScreenSaverInhibition); } bool MainWindow::NetPlayJoin() @@ -1594,14 +1598,14 @@ bool MainWindow::NetPlayJoin() if (!Core::IsUninitialized(m_system)) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("Can't start a NetPlay Session while a game is still running!")); + tr("Can't start a NetPlay Session while a game is still running!")); return false; } if (m_netplay_dialog->isVisible()) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("A NetPlay Session is already in progress!")); + tr("A NetPlay Session is already in progress!")); return false; } @@ -1621,7 +1625,7 @@ bool MainWindow::NetPlayJoin() else { host_ip = is_traversal ? Config::Get(Config::NETPLAY_HOST_CODE) : - Config::Get(Config::NETPLAY_ADDRESS); + Config::Get(Config::NETPLAY_ADDRESS); host_port = Config::Get(Config::NETPLAY_CONNECT_PORT); } @@ -1640,9 +1644,9 @@ bool MainWindow::NetPlayJoin() // Create Client const bool is_hosting_netplay = server != nullptr; Settings::Instance().ResetNetPlayClient(new NetPlay::NetPlayClient( - host_ip, host_port, m_netplay_dialog, nickname, - NetPlay::NetTraversalConfig{ is_hosting_netplay ? false : is_traversal, traversal_host, - traversal_port })); + host_ip, host_port, m_netplay_dialog, nickname, + NetPlay::NetTraversalConfig{is_hosting_netplay ? false : is_traversal, traversal_host, + traversal_port})); if (!Settings::Instance().GetNetPlayClient()->IsConnected()) { @@ -1661,14 +1665,14 @@ bool MainWindow::NetPlayHost(const UICommon::GameFile& game) if (!Core::IsUninitialized(m_system)) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("Can't start a NetPlay Session while a game is still running!")); + tr("Can't start a NetPlay Session while a game is still running!")); return false; } if (m_netplay_dialog->isVisible()) { ModalMessageBox::critical(nullptr, tr("Error"), - tr("A NetPlay Session is already in progress!")); + tr("A NetPlay Session is already in progress!")); return false; } @@ -1687,22 +1691,22 @@ bool MainWindow::NetPlayHost(const UICommon::GameFile& game) // Create Server Settings::Instance().ResetNetPlayServer( - new NetPlay::NetPlayServer(host_port, use_upnp, m_netplay_dialog, - NetPlay::NetTraversalConfig{ is_traversal, traversal_host, - traversal_port, traversal_port_alt })); + new NetPlay::NetPlayServer(host_port, use_upnp, m_netplay_dialog, + NetPlay::NetTraversalConfig{is_traversal, traversal_host, + traversal_port, traversal_port_alt})); if (!Settings::Instance().GetNetPlayServer()->is_connected) { ModalMessageBox::critical( - nullptr, tr("Failed to open server"), - tr("Failed to listen on port %1. Is another instance of the NetPlay server running?") - .arg(host_port)); + nullptr, tr("Failed to open server"), + tr("Failed to listen on port %1. Is another instance of the NetPlay server running?") + .arg(host_port)); NetPlayQuit(); return false; } Settings::Instance().GetNetPlayServer()->ChangeGame(game.GetSyncIdentifier(), - m_game_list->GetNetPlayName(game)); + m_game_list->GetNetPlayName(game)); // Join our local server return NetPlayJoin(); @@ -1720,7 +1724,7 @@ void MainWindow::NetPlayQuit() void MainWindow::UpdateScreenSaverInhibition() { const bool inhibit = Config::Get(Config::MAIN_DISABLE_SCREENSAVER) && - (Core::GetState(m_system) == Core::State::Running); + (Core::GetState(m_system) == Core::State::Running); if (inhibit == m_is_screensaver_inhibited) return; @@ -1794,9 +1798,9 @@ void MainWindow::dropEvent(QDropEvent* event) if (show_confirm) { if (ModalMessageBox::question( - this, tr("Confirm"), - tr("Do you want to add \"%1\" to the list of Game Paths?").arg(folder)) != - QMessageBox::Yes) + this, tr("Confirm"), + tr("Do you want to add \"%1\" to the list of Game Paths?").arg(folder)) != + QMessageBox::Yes) return; } settings.AddPath(folder); @@ -1811,25 +1815,25 @@ QSize MainWindow::sizeHint() const void MainWindow::OnBootGameCubeIPL(DiscIO::Region region) { - StartGame(std::make_unique(BootParameters::IPL{ region })); + StartGame(std::make_unique(BootParameters::IPL{region})); } void MainWindow::OnImportNANDBackup() { auto response = ModalMessageBox::question( - this, tr("Question"), - tr("Merging a new NAND over your currently selected NAND will overwrite any channels " - "and savegames that already exist. This process is not reversible, so it is " - "recommended that you keep backups of both NANDs. Are you sure you want to " - "continue?")); + this, tr("Question"), + tr("Merging a new NAND over your currently selected NAND will overwrite any channels " + "and savegames that already exist. This process is not reversible, so it is " + "recommended that you keep backups of both NANDs. Are you sure you want to " + "continue?")); if (response == QMessageBox::No) return; QString file = - DolphinFileDialog::getOpenFileName(this, tr("Select NAND Backup"), QDir::currentPath(), - tr("BootMii NAND backup file (*.bin);;" - "All Files (*)")); + DolphinFileDialog::getOpenFileName(this, tr("Select NAND Backup"), QDir::currentPath(), + tr("BootMii NAND backup file (*.bin);;" + "All Files (*)")); if (file.isEmpty()) return; @@ -1844,26 +1848,26 @@ void MainWindow::OnImportNANDBackup() std::future result = std::async(std::launch::async, [&] { DiscIO::NANDImporter().ImportNANDBin( - file.toStdString(), - [&dialog, beginning] { - dialog.SetLabelText( - tr("Importing NAND backup\n Time elapsed: %1s") - .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); - }, - [this] { - std::optional keys_file = RunOnObject(this, [this] { - return DolphinFileDialog::getOpenFileName( - this, tr("Select Keys File (OTP/SEEPROM Dump)"), QDir::currentPath(), - tr("BootMii keys file (*.bin);;" - "All Files (*)")) - .toStdString(); + file.toStdString(), + [&dialog, beginning] { + dialog.SetLabelText( + tr("Importing NAND backup\n Time elapsed: %1s") + .arg((QDateTime::currentDateTime().toMSecsSinceEpoch() - beginning) / 1000)); + }, + [this] { + std::optional keys_file = RunOnObject(this, [this] { + return DolphinFileDialog::getOpenFileName( + this, tr("Select Keys File (OTP/SEEPROM Dump)"), QDir::currentPath(), + tr("BootMii keys file (*.bin);;" + "All Files (*)")) + .toStdString(); }); - if (keys_file) - return *keys_file; - return std::string(""); - }); + if (keys_file) + return *keys_file; + return std::string(""); + }); dialog.Reset(); - }); + }); dialog.GetRaw()->exec(); @@ -1877,13 +1881,13 @@ void MainWindow::OnPlayRecording() if (AchievementManager::GetInstance().IsHardcoreModeActive()) { ModalMessageBox::critical( - this, tr("Error"), - tr("Playback of input recordings is disabled in RetroAchievements hardcore mode.")); + this, tr("Error"), + tr("Playback of input recordings is disabled in RetroAchievements hardcore mode.")); return; } QString dtm_file = DolphinFileDialog::getOpenFileName( - this, tr("Select the Recording File to Play"), QString(), tr("Dolphin TAS Movies (*.dtm)")); + this, tr("Select the Recording File to Play"), QString(), tr("Dolphin TAS Movies (*.dtm)")); if (dtm_file.isEmpty()) return; @@ -1909,8 +1913,8 @@ void MainWindow::OnStartRecording() { auto& movie = m_system.GetMovie(); if (Core::GetState(m_system) == Core::State::Starting || - Core::GetState(m_system) == Core::State::Stopping || movie.IsRecordingInput() || - movie.IsPlayingInput()) + Core::GetState(m_system) == Core::State::Stopping || movie.IsRecordingInput() || + movie.IsPlayingInput()) { return; } @@ -1961,7 +1965,7 @@ void MainWindow::OnExportRecording() const Core::CPUThreadGuard guard(m_system); QString dtm_file = DolphinFileDialog::getSaveFileName( - this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); + this, tr("Save Recording File As"), QString(), tr("Dolphin TAS Movies (*.dtm)")); if (!dtm_file.isEmpty()) m_system.GetMovie().SaveRecording(dtm_file.toStdString()); } @@ -1991,7 +1995,7 @@ void MainWindow::ShowTASInput() m_gba_tas_input_windows[i]->activateWindow(); } else if (si_device != SerialInterface::SIDEVICE_NONE && - si_device != SerialInterface::SIDEVICE_GC_GBA) + si_device != SerialInterface::SIDEVICE_GC_GBA) { m_gc_tas_input_windows[i]->show(); m_gc_tas_input_windows[i]->raise(); @@ -2002,7 +2006,7 @@ void MainWindow::ShowTASInput() for (int i = 0; i < num_wii_controllers; i++) { if (Config::Get(Config::GetInfoForWiimoteSource(i)) == WiimoteSource::Emulated && - (!Core::IsRunning(m_system) || m_system.IsWii())) + (!Core::IsRunning(m_system) || m_system.IsWii())) { m_wii_tas_input_windows[i]->show(); m_wii_tas_input_windows[i]->raise(); @@ -2032,7 +2036,7 @@ void MainWindow::ShowAchievementsWindow() m_achievements_window->show(); m_achievements_window->raise(); m_achievements_window->activateWindow(); - m_achievements_window->UpdateData(AchievementManager::UpdatedItems{ .all = true }); + m_achievements_window->UpdateData(AchievementManager::UpdatedItems{.all = true}); } void MainWindow::ShowAchievementSettings() @@ -2076,7 +2080,7 @@ void MainWindow::ShowCheatsManager() void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game) { auto second_game = m_game_list->FindSecondDisc(game); - std::vector paths = { game.GetFilePath() }; + std::vector paths = {game.GetFilePath()}; if (second_game != nullptr) paths.push_back(second_game->GetFilePath()); std::unique_ptr boot_params = BootParameters::GenerateFromFile(paths); @@ -2087,11 +2091,11 @@ void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game) auto& disc = std::get(boot_params->parameters); RiivolutionBootWidget w(disc.volume->GetGameID(), disc.volume->GetRevision(), - disc.volume->GetDiscNumber(), game.GetFilePath(), this); + disc.volume->GetDiscNumber(), game.GetFilePath(), this); #ifdef USE_RETRO_ACHIEVEMENTS connect(&w, &RiivolutionBootWidget::OpenAchievementSettings, this, - &MainWindow::ShowAchievementSettings); + &MainWindow::ShowAchievementSettings); #endif // USE_RETRO_ACHIEVEMENTS w.exec(); diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 16ceefb489..9e6d595102 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -310,8 +310,8 @@ void MenuBar::AddToolsMenu() m_pal_ipl = gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); }); - m_dev_ipl = - gc_ipl->addAction(tr("Triforce"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::DEV); }); + m_dev_ipl = gc_ipl->addAction(tr("Triforce"), this, + [this] { emit BootGameCubeIPL(DiscIO::Region::DEV); }); tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); }); diff --git a/Source/Core/DolphinQt/Settings/GameCubePane.cpp b/Source/Core/DolphinQt/Settings/GameCubePane.cpp index 53f93a166f..164b426d38 100644 --- a/Source/Core/DolphinQt/Settings/GameCubePane.cpp +++ b/Source/Core/DolphinQt/Settings/GameCubePane.cpp @@ -138,16 +138,10 @@ void GameCubePane::CreateWidgets() } // Add SP1 devices - for (const auto device : { - EXIDeviceType::None, - EXIDeviceType::Dummy, - EXIDeviceType::Ethernet, - EXIDeviceType::EthernetXLink, - EXIDeviceType::EthernetTapServer, - EXIDeviceType::EthernetBuiltIn, - EXIDeviceType::ModemTapServer, - EXIDeviceType::Baseboard - }) + for (const auto device : + {EXIDeviceType::None, EXIDeviceType::Dummy, EXIDeviceType::Ethernet, + EXIDeviceType::EthernetXLink, EXIDeviceType::EthernetTapServer, + EXIDeviceType::EthernetBuiltIn, EXIDeviceType::ModemTapServer, EXIDeviceType::Baseboard}) { m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()), static_cast(device)); diff --git a/Source/Core/InputCommon/GCPadStatus.h b/Source/Core/InputCommon/GCPadStatus.h index 6f7353ce34..262ee5adea 100644 --- a/Source/Core/InputCommon/GCPadStatus.h +++ b/Source/Core/InputCommon/GCPadStatus.h @@ -46,8 +46,8 @@ struct GCPadStatus u8 triggerRight = 0; // 0 <= triggerRight <= 255 u8 analogA = 0; // 0 <= analogA <= 255 u8 analogB = 0; // 0 <= analogB <= 255 - //Triforce - u8 switches = 0; + // Triforce + u8 switches = 0; bool isConnected = true; static const u8 MAIN_STICK_CENTER_X = 0x80; diff --git a/Source/Core/UICommon/GameFile.cpp b/Source/Core/UICommon/GameFile.cpp index 8883994926..65ab4d8569 100644 --- a/Source/Core/UICommon/GameFile.cpp +++ b/Source/Core/UICommon/GameFile.cpp @@ -130,7 +130,7 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path)) m_internal_name = volume->GetInternalName(); m_game_id = volume->GetGameID(); - m_gametdb_id = volume->GetGameTDBID(); + m_gametdb_id = volume->GetGameTDBID(); m_title_id = volume->GetTitleID().value_or(0); m_maker_id = volume->GetMakerID(); m_revision = volume->GetRevision().value_or(0); @@ -310,7 +310,7 @@ void GameFile::DoState(PointerWrap& p) p.Do(m_descriptions); p.Do(m_internal_name); p.Do(m_game_id); - p.Do(m_gametdb_id); + p.Do(m_gametdb_id); p.Do(m_title_id); p.Do(m_maker_id); @@ -499,8 +499,7 @@ const std::string& GameFile::GetName(const Core::TitleDatabase& title_database) if (IsModDescriptor()) return GetName(Variant::LongAndPossiblyCustom); - const std::string& database_name = - title_database.GetTitleName(m_gametdb_id, GetConfigLanguage()); + const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage()); return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name; } diff --git a/Source/Core/UICommon/GameFile.h b/Source/Core/UICommon/GameFile.h index b56ca9ba57..e43950143b 100644 --- a/Source/Core/UICommon/GameFile.h +++ b/Source/Core/UICommon/GameFile.h @@ -75,7 +75,7 @@ public: std::vector GetLanguages() const; const std::string& GetInternalName() const { return m_internal_name; } const std::string& GetGameID() const { return m_game_id; } - const std::string& GetGameTDBID() const { return m_gametdb_id; } + const std::string& GetGameTDBID() const { return m_gametdb_id; } u64 GetTitleID() const { return m_title_id; } const std::string& GetMakerID() const { return m_maker_id; } u16 GetRevision() const { return m_revision; } From 92265a715829d0ec60f6bd6bab1581ef2eab6e81 Mon Sep 17 00:00:00 2001 From: crediar <145270593+crediar@users.noreply.github.com> Date: Mon, 4 Aug 2025 15:29:19 +0200 Subject: [PATCH 16/18] Update Source/Core/Core/HW/DVD/AMMediaboard.cpp Co-authored-by: Pokechu22 --- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 4198824968..6aecfb6370 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -427,19 +427,16 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) } } - DICMDBUF[0] <<= 24; - DICMDBUF[1] <<= 2; + u32 command = DICMDBUF[0] << 24; + u32 offset = DICMDBUF[1] << 2; // SegaBoot adds bits for some reason to offset/length // also adds 0x20 to offset - if (DICMDBUF[1] == 0x00100440) + if (offset == 0x00100440) { s_segaboot = true; } - u32 command = DICMDBUF[0]; - u32 offset = DICMDBUF[1]; - INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: {:08x} {:08x} DMA=addr:{:08x},len:{:08x} Keys: {:08x} {:08x} {:08x}", command, offset, address, length, s_GCAM_key_a, s_GCAM_key_b, s_GCAM_key_c); From 87c4b1e3568d6def805dfba06a61dc11cd1666c0 Mon Sep 17 00:00:00 2001 From: crediar Date: Sun, 10 Aug 2025 17:33:38 +0200 Subject: [PATCH 17/18] Added missing game IDs and titles to game setting files Removed unused code related to s_segaboot Removed unused variable s_segaboot Optimized code related to the AMMB DI Inquiry command Moved AMMB DI commands to their own enum Changed code to not use pointers related to IOFile Removed unused code in DVDInterface::ExecuteCommand() Changed code to use UniqueBuffer instead of new Remove unused code in JVSIOCommand::GeneralDriverOutput Fixed a bug that broke Triforce disc key setup when using NetPlay Made SIDEVICE_AM_BASEBOARD count as an GC controller for SIDevice_IsGCController Added functions for NetPlay: MapPadStatus/HandleButtonCombos/GetData/SendCommand/SetOrigin/HandleMoviePadStatus/GetPadStatus Updated NetPlayClient::UpdateDevices for handling the SIDEVICE_AM_BASEBOARD correctly and not always connect a GC controller Added NetPlay_GetInput/NetPlay_InGamePadToLocalPad function for the AMBaseboard General code formatting Fixed typo in header name --- Data/Sys/GameSettings/SBEY.ini | 2 + Data/Sys/GameSettings/SBGG.ini | 2 + Data/Sys/GameSettings/SBHA.ini | 2 + Data/Sys/GameSettings/SBHN.ini | 2 + Data/Sys/GameSettings/SBHZ.ini | 2 + Data/Sys/GameSettings/SBJA.ini | 2 + Data/Sys/GameSettings/SBLK.ini | 2 + Data/Sys/GameSettings/SBLL.ini | 2 + Source/Core/Core/Boot/Boot_BS2Emu.cpp | 5 +- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 136 ++++------ Source/Core/Core/HW/DVD/AMMediaboard.h | 22 +- Source/Core/Core/HW/DVD/DVDInterface.cpp | 15 +- .../Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp | 62 ++--- Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h | 2 +- Source/Core/Core/HW/GCPadEmu.h | 2 +- Source/Core/Core/HW/SI/SI_Device.cpp | 1 + .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 255 +++++++++++++++--- Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h | 67 ++++- .../Core/Core/HW/SI/SI_DeviceGCController.h | 3 + Source/Core/Core/NetPlayClient.cpp | 27 +- 20 files changed, 421 insertions(+), 192 deletions(-) diff --git a/Data/Sys/GameSettings/SBEY.ini b/Data/Sys/GameSettings/SBEY.ini index 912626553a..2e79beb657 100644 --- a/Data/Sys/GameSettings/SBEY.ini +++ b/Data/Sys/GameSettings/SBEY.ini @@ -1,2 +1,4 @@ +# SBEY - Virtua Striker 2002 (Export, Japan, Type 3) + [Core] FPRF = True diff --git a/Data/Sys/GameSettings/SBGG.ini b/Data/Sys/GameSettings/SBGG.ini index 412b5048c7..6296633236 100644 --- a/Data/Sys/GameSettings/SBGG.ini +++ b/Data/Sys/GameSettings/SBGG.ini @@ -1,3 +1,5 @@ +# SBGG - F-ZERO AX + [Core] FPRF = True CPUThread = True diff --git a/Data/Sys/GameSettings/SBHA.ini b/Data/Sys/GameSettings/SBHA.ini index 412b5048c7..b62a599c92 100644 --- a/Data/Sys/GameSettings/SBHA.ini +++ b/Data/Sys/GameSettings/SBHA.ini @@ -1,3 +1,5 @@ +# SBHA - F-ZERO AX (Monster) + [Core] FPRF = True CPUThread = True diff --git a/Data/Sys/GameSettings/SBHN.ini b/Data/Sys/GameSettings/SBHN.ini index 912626553a..31be499dca 100644 --- a/Data/Sys/GameSettings/SBHN.ini +++ b/Data/Sys/GameSettings/SBHN.ini @@ -1,2 +1,4 @@ +# SBHN - VIRTUA STRIKER 4 VER.A + [Core] FPRF = True diff --git a/Data/Sys/GameSettings/SBHZ.ini b/Data/Sys/GameSettings/SBHZ.ini index 912626553a..a15b861658 100644 --- a/Data/Sys/GameSettings/SBHZ.ini +++ b/Data/Sys/GameSettings/SBHZ.ini @@ -1,2 +1,4 @@ +# SBHZ - VIRTUA STRIKER 4 (Asia) + [Core] FPRF = True diff --git a/Data/Sys/GameSettings/SBJA.ini b/Data/Sys/GameSettings/SBJA.ini index 912626553a..028fd4b6c0 100644 --- a/Data/Sys/GameSettings/SBJA.ini +++ b/Data/Sys/GameSettings/SBJA.ini @@ -1,2 +1,4 @@ +# SBJA - VIRTUA STRIKER 4 (Export) + [Core] FPRF = True diff --git a/Data/Sys/GameSettings/SBLK.ini b/Data/Sys/GameSettings/SBLK.ini index 912626553a..8931c60490 100644 --- a/Data/Sys/GameSettings/SBLK.ini +++ b/Data/Sys/GameSettings/SBLK.ini @@ -1,2 +1,4 @@ +# SBLK - VIRTUA STRIKER 4 Ver.2006 (Japan) + [Core] FPRF = True diff --git a/Data/Sys/GameSettings/SBLL.ini b/Data/Sys/GameSettings/SBLL.ini index 912626553a..deb8b08379 100644 --- a/Data/Sys/GameSettings/SBLL.ini +++ b/Data/Sys/GameSettings/SBLL.ini @@ -1,2 +1,4 @@ +# SBLL - VIRTUA STRIKER 4 Ver.2006 (Export) + [Core] FPRF = True diff --git a/Source/Core/Core/Boot/Boot_BS2Emu.cpp b/Source/Core/Core/Boot/Boot_BS2Emu.cpp index 57112245db..01a7a97caf 100644 --- a/Source/Core/Core/Boot/Boot_BS2Emu.cpp +++ b/Source/Core/Core/Boot/Boot_BS2Emu.cpp @@ -229,10 +229,7 @@ bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard branch_watch.SetRecordingActive(guard, resume_branch_watch); - // Check for Triforce board being connected - const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1); - bool enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0; - if (enable_gcam) + if (system.IsTriforce()) { auto& memory = system.GetMemory(); u32 dsize = volume.GetDataSize(); diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 6aecfb6370..8af430f090 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -81,8 +81,7 @@ static int WSAGetLastError(void) namespace AMMediaboard { -static bool s_firmwaremap = false; -static bool s_segaboot = false; +static bool s_firmware_map = false; static bool s_test_menu = false; static SOCKET s_namco_cam = 0; static u32 s_timeouts[3] = {20000, 20000, 20000}; @@ -92,11 +91,11 @@ static u32 s_GCAM_key_a = 0; static u32 s_GCAM_key_b = 0; static u32 s_GCAM_key_c = 0; -static File::IOFile* s_netcfg = nullptr; -static File::IOFile* s_netctrl = nullptr; -static File::IOFile* s_extra = nullptr; -static File::IOFile* s_backup = nullptr; -static File::IOFile* s_dimm = nullptr; +static File::IOFile s_netcfg = nullptr; +static File::IOFile s_netctrl = nullptr; +static File::IOFile s_extra = nullptr; +static File::IOFile s_backup = nullptr; +static File::IOFile s_dimm = nullptr; static u8* s_dimm_disc = nullptr; @@ -157,7 +156,7 @@ static inline void PrintMBBuffer(u32 address, u32 length) void FirmwareMap(bool on) { - s_firmwaremap = on; + s_firmware_map = on; } void InitKeys(u32 key_a, u32 key_b, u32 key_c) @@ -167,14 +166,14 @@ void InitKeys(u32 key_a, u32 key_b, u32 key_c) s_GCAM_key_c = key_c; } -static File::IOFile* OpenOrCreateFile(const std::string& filename) +static File::IOFile OpenOrCreateFile(const std::string& filename) { // Try opening for read/write first if (File::Exists(filename)) - return new File::IOFile(filename, "rb+"); + return File::IOFile(filename, "rb+"); // Create new file - return new File::IOFile(filename, "wb+"); + return File::IOFile(filename, "wb+"); } void Init(void) @@ -185,8 +184,7 @@ void Init(void) memset(s_firmware, -1, sizeof(s_firmware)); memset(s_sockets, SOCKET_ERROR, sizeof(s_sockets)); - s_segaboot = false; - s_firmwaremap = false; + s_firmware_map = false; s_test_menu = false; s_last_error = SSC_SUCCESS; @@ -203,15 +201,15 @@ void Init(void) s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetGameID() + ".bin"); s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetGameID() + ".bin"); - if (!s_netcfg) + if (!s_netcfg.IsOpen()) PanicAlertFmt("Failed to open/create: {}", base_path + "s_netcfg.bin"); - if (!s_netctrl) + if (!s_netctrl.IsOpen()) PanicAlertFmt("Failed to open/create: {}", base_path + "s_netctrl.bin"); - if (!s_extra) + if (!s_extra.IsOpen()) PanicAlertFmt("Failed to open/create: {}", base_path + "s_extra.bin"); - if (!s_dimm) + if (!s_dimm.IsOpen()) PanicAlertFmt("Failed to open/create: {}", base_path + "s_dimm.bin"); - if (!s_backup) + if (!s_backup.IsOpen()) PanicAlertFmt("Failed to open/create: {}", base_path + "s_backup.bin"); // This is the firmware for the Triforce @@ -252,7 +250,7 @@ u8* InitDIMM(u32 size) } } - s_firmwaremap = 0; + s_firmware_map = 0; return s_dimm_disc; } @@ -396,7 +394,7 @@ static void FileWriteData(File::IOFile* file, u32 seek_pos, const u8* data, size file->WriteBytes(data, length); file->Flush(); } -u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) +u32 ExecuteCommand(std::array& DICMDBUF, u32* DIIMMBUF, u32 address, u32 length) { auto& system = Core::System::GetInstance(); auto& memory = system.GetMemory(); @@ -413,8 +411,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) /* Key setup for Triforce IPL: - These RAM offset always hold the keys for the next command and since it sends two dummy - commands before a real read we can just use the key from RAM without missing any real commands. +These RAM offsets always hold the key for the next command. Since two dummy commands are sent before +any real ones, you can just use the key from RAM without missing a real command. */ if (s_GCAM_key_a == 0) { @@ -430,13 +428,6 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) u32 command = DICMDBUF[0] << 24; u32 offset = DICMDBUF[1] << 2; - // SegaBoot adds bits for some reason to offset/length - // also adds 0x20 to offset - if (offset == 0x00100440) - { - s_segaboot = true; - } - INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: {:08x} {:08x} DMA=addr:{:08x},len:{:08x} Keys: {:08x} {:08x} {:08x}", command, offset, address, length, s_GCAM_key_a, s_GCAM_key_b, s_GCAM_key_c); @@ -450,33 +441,34 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Don't map firmware while in SegaBoot if (memory.Read_U32(0x8006BF70) != 0x0A536567) { - s_firmwaremap = 1; + s_firmware_map = 1; } } } - switch (AMMBCommand(command >> 24)) + switch (AMMBDICommand(command >> 24)) { - case AMMBCommand::Inquiry: - if (s_firmwaremap) + case AMMBDICommand::Inquiry: + if (s_firmware_map) { - s_firmwaremap = false; - s_segaboot = false; + s_firmware_map = false; } // Returned value is used to set the protocol version. switch (GetGameType()) { default: - return Version1; + *DIIMMBUF = Version1; + return 0; case KeyOfAvalon: case MarioKartGP: case MarioKartGP2: case FirmwareUpdate: - return Version2; + *DIIMMBUF = Version2; + return 0; } break; - case AMMBCommand::Read: + case AMMBDICommand::Read: if ((offset & 0x8FFF0000) == 0x80000000) { switch (offset) @@ -523,16 +515,16 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Network configuration if (offset == 0x00000000 && length == 0x80) { - s_netcfg->Seek(0, File::SeekOrigin::Begin); - s_netcfg->ReadBytes(memory.GetSpanForAddress(address).data(), length); + s_netcfg.Seek(0, File::SeekOrigin::Begin); + s_netcfg.ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } // media crc check on/off if (offset == DIMMExtraSettings && length == 0x20) { - s_extra->Seek(0, File::SeekOrigin::Begin); - s_extra->ReadBytes(memory.GetSpanForAddress(address).data(), length); + s_extra.Seek(0, File::SeekOrigin::Begin); + s_extra.ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -540,8 +532,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if (offset >= DIMMMemory && offset <= 0x1F800000) { u32 dimmoffset = offset - DIMMMemory; - s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); - s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); + s_dimm.Seek(dimmoffset, File::SeekOrigin::Begin); + s_dimm.ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1044,15 +1036,15 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if (offset >= DIMMMemory2 && offset <= 0xFF800000) { u32 dimmoffset = offset - DIMMMemory2; - s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); - s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); + s_dimm.Seek(dimmoffset, File::SeekOrigin::Begin); + s_dimm.ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } if (offset == NetworkControl && length == 0x20) { - s_netctrl->Seek(0, File::SeekOrigin::Begin); - s_netctrl->ReadBytes(memory.GetSpanForAddress(address).data(), length); + s_netctrl.Seek(0, File::SeekOrigin::Begin); + s_netctrl.ReadBytes(memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1063,13 +1055,8 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) return 0; } - if (s_firmwaremap) + if (s_firmware_map) { - if (s_segaboot) - { - DICMDBUF[1] &= ~0x00100000; - DICMDBUF[1] -= 0x20; - } memcpy(memory.GetSpanForAddress(address).data(), s_firmware + offset, length); return 0; } @@ -1082,23 +1069,23 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) return 1; break; - case AMMBCommand::Write: + case AMMBDICommand::Write: /* These two magic writes allow a new firmware to be programmed */ if ((offset == FirmwareMagicWrite1) && (length == 0x20)) { - s_firmwaremap = true; + s_firmware_map = true; return 0; } if ((offset == FirmwareMagicWrite2) && (length == 0x20)) { - s_firmwaremap = true; + s_firmware_map = true; return 0; } - if (s_firmwaremap) + if (s_firmware_map) { // Firmware memory (2MB) if ((offset >= 0x00400000) && (offset <= 0x600000)) @@ -1112,21 +1099,21 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) // Network configuration if ((offset == 0x00000000) && (length == 0x80)) { - FileWriteData(s_netcfg, 0, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_netcfg, 0, memory.GetSpanForAddress(address).data(), length); return 0; } // media crc check on/off if ((offset == DIMMExtraSettings) && (length == 0x20)) { - FileWriteData(s_extra, 0, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_extra, 0, memory.GetSpanForAddress(address).data(), length); return 0; } // Backup memory (8MB) if ((offset >= BackupMemory) && (offset <= 0x00800000)) { - FileWriteData(s_backup, 0, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_backup, 0, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1134,7 +1121,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= DIMMMemory) && (offset <= 0x1F800000)) { u32 dimmoffset = offset - DIMMMemory; - FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1320,13 +1307,13 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) if ((offset >= DIMMMemory2) && (offset <= 0xFF800000)) { u32 dimmoffset = offset - 0xFF000000; - FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); return 0; } if ((offset == NetworkControl) && (length == 0x20)) { - FileWriteData(s_netctrl, 0, memory.GetSpanForAddress(address).data(), length); + FileWriteData(&s_netctrl, 0, memory.GetSpanForAddress(address).data(), length); return 0; } @@ -1337,7 +1324,7 @@ u32 ExecuteCommand(std::array& DICMDBUF, u32 address, u32 length) PanicAlertFmtT("Unhandled Media Board Write:{0:08x}", offset); } break; - case AMMBCommand::Execute: + case AMMBDICommand::Execute: if ((offset == 0) && (length == 0)) { // Recast for easier access @@ -1818,20 +1805,11 @@ bool GetTestMenu(void) } void Shutdown(void) { - if (s_netcfg) - s_netcfg->Close(); - - if (s_netctrl) - s_netctrl->Close(); - - if (s_extra) - s_extra->Close(); - - if (s_backup) - s_backup->Close(); - - if (s_dimm) - s_dimm->Close(); + s_netcfg.Close(); + s_netctrl.Close(); + s_extra.Close(); + s_backup.Close(); + s_dimm.Close(); if (s_dimm_disc) { diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h index 192a551331..d41894f23a 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.h +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -32,7 +32,7 @@ enum GameType VirtuaStriker4, GekitouProYakyuu, KeyOfAvalon, - FirmwareUpdate + FirmwareUpdate, }; enum MediaType { @@ -54,28 +54,32 @@ enum MediaBoardStatus TestingGameProgram = 3, LoadingGameProgram = 4, LoadedGameProgram = 5, - Error = 6 + Error = 6, }; enum InquiryType { Version1 = 0x21484100, - Version2 = 0x29484100 + Version2 = 0x29484100, }; #define SocketCheck(x) (x <= 0x3F ? x : 0) namespace AMMediaboard { + +enum class AMMBDICommand : u16 +{ + Inquiry = 0x12, + Read = 0xA8, + Write = 0xAA, + Execute = 0xAB, +}; + enum class AMMBCommand : u16 { Unknown_000 = 0x000, GetDIMMSize = 0x001, - Inquiry = 0x12, - Read = 0xa8, - Write = 0xaa, - Execute = 0xab, - GetMediaBoardStatus = 0x100, GetSegaBootVersion = 0x101, GetSystemFlags = 0x102, @@ -209,7 +213,7 @@ void Init(void); void FirmwareMap(bool on); u8* InitDIMM(u32 size); void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC); -u32 ExecuteCommand(std::array& DICMDBUF, u32 Address, u32 Length); +u32 ExecuteCommand(std::array& DICMDBUF, u32* DIIMMBUF, u32 Address, u32 Length); u32 GetGameType(void); u32 GetMediaType(void); bool GetTestMenu(void); diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 6b81eafa08..5ed8e03524 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -766,12 +766,8 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) if (m_system.IsTriforce()) { - u32 ret = AMMediaboard::ExecuteCommand(m_DICMDBUF, m_DIMAR, m_DILENGTH); - if (ret != 1) + if (!AMMediaboard::ExecuteCommand(m_DICMDBUF, &m_DIIMMBUF, m_DIMAR, m_DILENGTH)) { - if (m_DICMDBUF[0] == 0x12000000) - m_DIIMMBUF = ret; - // Transfer is done m_DICR.TSTART = 0; m_DIMAR += m_DILENGTH; @@ -784,15 +780,6 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) // Normal read command pass on to normal handling } - // Swaps endian of Triforce DI commands, and zeroes out random bytes to prevent unknown read - // subcommand errors - auto& dvd_thread = m_system.GetDVDThread(); - if (dvd_thread.HasDisc() && dvd_thread.GetDiscType() == DiscIO::Platform::Triforce) - { - // TODO(C++23): Use std::byteswap and a bitwise AND for increased clarity - m_DICMDBUF[0] <<= 24; - } - // DVDLowRequestError needs access to the error code set by the previous command if (static_cast(m_DICMDBUF[0] >> 24) != DICommand::RequestError) SetDriveError(DriveError::None); diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp index e728aaf250..46aff6f009 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp @@ -16,6 +16,7 @@ #include "Common/IOFile.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" +#include "Common/Buffer.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/Config/MainSettings.h" @@ -76,17 +77,14 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit std::string backup_Filename(File::GetUserPath(D_TRIUSER_IDX) + "tribackup_" + SConfig::GetInstance().GetGameID().c_str() + ".bin"); - if (File::Exists(backup_Filename)) + m_backup = File::IOFile(backup_Filename, "rb+"); + if (!m_backup.IsOpen()) { - m_backup = new File::IOFile(backup_Filename, "rb+"); - } - else - { - m_backup = new File::IOFile(backup_Filename, "wb+"); + m_backup = File::IOFile(backup_Filename, "wb+"); } // Some games share the same ID Client/Server - if (!m_backup->IsGood()) + if (!m_backup.IsGood()) { PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str()); @@ -95,7 +93,7 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit backup_Filename = File::GetUserPath(D_TRIUSER_IDX) + "tribackup_tmp_" + std::to_string(rand()) + SConfig::GetInstance().GetGameID().c_str() + ".bin"; - m_backup = new File::IOFile(backup_Filename, "wb+"); + m_backup = File::IOFile(backup_Filename, "wb+"); } // Virtua Striker 4 and Gekitou Pro Yakyuu need a higher FIRM version @@ -103,32 +101,28 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit if (AMMediaboard::GetGameType() == VirtuaStriker4 || AMMediaboard::GetGameType() == GekitouProYakyuu) { - if (m_backup->GetSize() != 0) + if (m_backup.GetSize() != 0) { - u8* data = new u8[m_backup->GetSize()]; - - m_backup->ReadBytes(data, m_backup->GetSize()); + Common::UniqueBuffer data(m_backup.GetSize()); + m_backup.ReadBytes(data.data(), data.size()); // Set FIRM version - *(u16*)(data + 0x12) = 0x1703; - *(u16*)(data + 0x212) = 0x1703; + reinterpret_cast(data[0x12]) = 0x1703; + reinterpret_cast(data[0x212]) = 0x1703; // Update checksum - *(u16*)(data + 0x0A) = Common::swap16(CheckSum(data + 0xC, 0x1F4)); - *(u16*)(data + 0x20A) = Common::swap16(CheckSum(data + 0x20C, 0x1F4)); + reinterpret_cast(data[0x0A]) = Common::swap16(CheckSum(&data[0xC], 0x1F4)); + reinterpret_cast(data[0x20A]) = Common::swap16(CheckSum(&data[0x20C], 0x1F4)); - m_backup->Seek(0, File::SeekOrigin::Begin); - m_backup->WriteBytes(data, m_backup->GetSize()); - m_backup->Flush(); - - delete[] data; + m_backup.Seek(0, File::SeekOrigin::Begin); + m_backup.WriteBytes(data.data(), data.size()); + m_backup.Flush(); } } } CEXIBaseboard::~CEXIBaseboard() { - m_backup->Close(); - delete m_backup; + m_backup.Close(); } void CEXIBaseboard::SetCS(int cs) @@ -165,11 +159,11 @@ void CEXIBaseboard::DMAWrite(u32 addr, u32 size) NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Write: {:08x} {:x}", addr, size); - m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + m_backup.Seek(m_backoffset, File::SeekOrigin::Begin); - m_backup->WriteBytes(memory.GetSpanForAddress(addr).data(), size); + m_backup.WriteBytes(memory.GetSpanForAddress(addr).data(), size); - m_backup->Flush(); + m_backup.Flush(); } void CEXIBaseboard::DMARead(u32 addr, u32 size) @@ -179,11 +173,11 @@ void CEXIBaseboard::DMARead(u32 addr, u32 size) NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Read: {:08x} {:x}", addr, size); - m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + m_backup.Seek(m_backoffset, File::SeekOrigin::Begin); - m_backup->Flush(); + m_backup.Flush(); - m_backup->ReadBytes(memory.GetSpanForAddress(addr).data(), size); + m_backup.ReadBytes(memory.GetSpanForAddress(addr).data(), size); } void CEXIBaseboard::TransferByte(u8& _byte) @@ -225,13 +219,13 @@ void CEXIBaseboard::TransferByte(u8& _byte) case BackupOffsetSet: m_backoffset = (m_command[1] << 8) | m_command[2]; DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupOffsetSet:{:04x}", m_backoffset); - m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); + m_backup.Seek(m_backoffset, File::SeekOrigin::Begin); _byte = 0x01; break; case BackupWrite: DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupWrite:{:04x}-{:02x}", m_backoffset, m_command[1]); - m_backup->WriteBytes(&m_command[1], 1); - m_backup->Flush(); + m_backup.WriteBytes(&m_command[1], 1); + m_backup.Flush(); _byte = 0x01; break; case BackupRead: @@ -292,8 +286,8 @@ void CEXIBaseboard::TransferByte(u8& _byte) { // 1 byte out case BackupRead: - m_backup->Flush(); - m_backup->ReadBytes(&_byte, 1); + m_backup.Flush(); + m_backup.ReadBytes(&_byte, 1); break; case DMAOffsetLengthSet: _byte = 0x01; diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h index 8fdc7d8ffd..ccc8b45a8f 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.h @@ -59,7 +59,7 @@ private: u32 m_backup_dma_length; u8 m_command[4]; u16 m_backoffset; - File::IOFile* m_backup; + File::IOFile m_backup; protected: void TransferByte(u8& _uByte) override; diff --git a/Source/Core/Core/HW/GCPadEmu.h b/Source/Core/Core/HW/GCPadEmu.h index 1918b34659..6e244d139f 100644 --- a/Source/Core/Core/HW/GCPadEmu.h +++ b/Source/Core/Core/HW/GCPadEmu.h @@ -30,7 +30,7 @@ enum class PadGroup Rumble, Mic, Options, - Triforce + Triforce, }; class GCPad : public ControllerEmu::EmulatedController diff --git a/Source/Core/Core/HW/SI/SI_Device.cpp b/Source/Core/Core/HW/SI/SI_Device.cpp index 2bfd150bea..c826cf345b 100644 --- a/Source/Core/Core/HW/SI/SI_Device.cpp +++ b/Source/Core/Core/HW/SI/SI_Device.cpp @@ -140,6 +140,7 @@ bool SIDevice_IsGCController(SIDevices type) case SIDEVICE_GC_TARUKONGA: case SIDEVICE_DANCEMAT: case SIDEVICE_GC_STEERING: + case SIDEVICE_AM_BASEBOARD: return true; default: return false; diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index cf8f833a48..9675d2d50e 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -13,6 +13,8 @@ #include +#include "Common/Buffer.h" +#include "Common/ChunkFile.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Common/FileUtil.h" @@ -20,6 +22,8 @@ #include "Common/IOFile.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" +#include "Common/MsgHandler.h" +#include "Common/Swap.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/Config/MainSettings.h" @@ -28,15 +32,19 @@ #include "Core/ConfigLoaders/NetPlayConfigLoader.h" #include "Core/ConfigManager.h" #include "Core/Core.h" +#include "Core/CoreTiming.h" #include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/EXI/EXI.h" +#include "Core/HW/GCPad.h" #include "Core/HW/MMIO.h" #include "Core/HW/Memmap.h" +#include "Core/HW/ProcessorInterface.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" #include "Core/HW/SI/SI_DeviceGCController.h" #include "Core/HW/Sram.h" +#include "Core/HW/SystemTimers.h" #include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/Movie.h" #include "Core/NetPlayProto.h" @@ -44,6 +52,7 @@ #include "Core/System.h" #include "Core/WiiRoot.h" #include "DiscIO/Enums.h" +#include "InputCommon/GCPadStatus.h" namespace SerialInterface { @@ -1997,7 +2006,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) break; } - u8* buf = new u8[bytes]; + Common::UniqueBuffer buf(bytes); for (u32 i = 0; i < bytes; ++i) { @@ -2008,32 +2017,12 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x32, GPO: {:02x} {:02x} {} {:02x}{:02x}{:02x} ({:02x})", delay, m_rx_reply, bytes, buf[0], buf[1], buf[2], - Common::swap16(*(u16*)(buf + 1)) >> 2); + Common::swap16(*reinterpret_cast(&buf[1])) >> 2); - // TODO: figure this out - - u8 trepl[] = { - 0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, - 0xD0, 0xE0, 0xF0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, - 0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62, - 0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, 0x04, 0x14, 0x24, 0x34, - 0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, 0x05, - 0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5, - 0xE5, 0xF5, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6, - 0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78, - 0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x09, 0x19, 0x29, 0x39, 0x49, - 0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0A, 0x1A, - 0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA, - 0xFA, 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC, - 0xCC, 0xDC, 0xEC, 0xFC, 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D, - 0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E, - 0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE}; - - static u32 off = 0; - if (off > sizeof(trepl)) - off = 0; - - switch (Common::swap16(*(u16*)(buf + 1)) >> 2) + /* + Handling of the motion seat used in F-Zero AXs DX version + */ + switch (Common::swap16(*reinterpret_cast(&buf[1])) >> 2) { case 0x70: delay++; @@ -2050,13 +2039,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) case 0x60: break; } - ////if( buf[1] == 1 && buf[2] == 0x80 ) - ////{ - //// INFO_LOG_FMT(DVDINTERFACE, "GCAM: PC:{:08x}", PC); - //// PowerPC::breakpoints.Add( PC+8, false ); - ////} - - delete[] buf; } break; } @@ -2080,7 +2062,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) else { message.addData(StatusOkay); - ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO:Unknown:{:02x}", cmd_); + ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unknown:{:02x}", cmd_); } break; } @@ -2170,19 +2152,210 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) return buffer_position; } -// Unused -DataResponse CSIDevice_AMBaseboard::GetData(u32& _Hi, u32& _Low) +u32 CSIDevice_AMBaseboard::MapPadStatus(const GCPadStatus& pad_status) { - _Low = 0; - _Hi = 0x00800000; + // Thankfully changing mode does not change the high bits ;) + u32 hi = 0; + hi = pad_status.stickY; + hi |= pad_status.stickX << 8; + hi |= (pad_status.button | PAD_USE_ORIGIN) << 16; + return hi; +} + +CSIDevice_AMBaseboard::EButtonCombo +CSIDevice_AMBaseboard::HandleButtonCombos(const GCPadStatus& pad_status) +{ + // Keep track of the special button combos (embedded in controller hardware... :( ) + EButtonCombo temp_combo; + if ((pad_status.button & 0xff00) == (PAD_BUTTON_Y | PAD_BUTTON_X | PAD_BUTTON_START)) + temp_combo = COMBO_ORIGIN; + else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B | PAD_BUTTON_X | PAD_BUTTON_START)) + temp_combo = COMBO_RESET; + else + temp_combo = COMBO_NONE; + + if (temp_combo != m_last_button_combo) + { + m_last_button_combo = temp_combo; + if (m_last_button_combo != COMBO_NONE) + m_timer_button_combo_start = m_system.GetCoreTiming().GetTicks(); + } + + if (m_last_button_combo != COMBO_NONE) + { + const u64 current_time = m_system.GetCoreTiming().GetTicks(); + const u32 ticks_per_second = m_system.GetSystemTimers().GetTicksPerSecond(); + if (u32(current_time - m_timer_button_combo_start) > ticks_per_second * 3) + { + if (m_last_button_combo == COMBO_RESET) + { + INFO_LOG_FMT(SERIALINTERFACE, "PAD - COMBO_RESET"); + m_system.GetProcessorInterface().ResetButton_Tap(); + } + else if (m_last_button_combo == COMBO_ORIGIN) + { + INFO_LOG_FMT(SERIALINTERFACE, "PAD - COMBO_ORIGIN"); + SetOrigin(pad_status); + } + + m_last_button_combo = COMBO_NONE; + return temp_combo; + } + } + + return COMBO_NONE; +} + +// GetData + +// Return true on new data (max 7 Bytes and 6 bits ;) +// [00?SYXBA] [1LRZUDRL] [x] [y] [cx] [cy] [l] [r] +// |\_ ERR_LATCH (error latched - check SISR) +// |_ ERR_STATUS (error on last GetData or SendCmd?) +DataResponse CSIDevice_AMBaseboard::GetData(u32& hi, u32& low) +{ + GCPadStatus pad_status = GetPadStatus(); + + if (!pad_status.isConnected) + return DataResponse::ErrorNoResponse; + + if (HandleButtonCombos(pad_status) == COMBO_ORIGIN) + pad_status.button |= PAD_GET_ORIGIN; + + hi = MapPadStatus(pad_status); + + // Low bits are packed differently per mode + if (m_mode == 0 || m_mode == 5 || m_mode == 6 || m_mode == 7) + { + low = (pad_status.analogB >> 4); // Top 4 bits + low |= ((pad_status.analogA >> 4) << 4); // Top 4 bits + low |= ((pad_status.triggerRight >> 4) << 8); // Top 4 bits + low |= ((pad_status.triggerLeft >> 4) << 12); // Top 4 bits + low |= ((pad_status.substickY) << 16); // All 8 bits + low |= ((pad_status.substickX) << 24); // All 8 bits + } + else if (m_mode == 1) + { + low = (pad_status.analogB >> 4); // Top 4 bits + low |= ((pad_status.analogA >> 4) << 4); // Top 4 bits + low |= (pad_status.triggerRight << 8); // All 8 bits + low |= (pad_status.triggerLeft << 16); // All 8 bits + low |= ((pad_status.substickY >> 4) << 24); // Top 4 bits + low |= ((pad_status.substickX >> 4) << 28); // Top 4 bits + } + else if (m_mode == 2) + { + low = pad_status.analogB; // All 8 bits + low |= pad_status.analogA << 8; // All 8 bits + low |= ((pad_status.triggerRight >> 4) << 16); // Top 4 bits + low |= ((pad_status.triggerLeft >> 4) << 20); // Top 4 bits + low |= ((pad_status.substickY >> 4) << 24); // Top 4 bits + low |= ((pad_status.substickX >> 4) << 28); // Top 4 bits + } + else if (m_mode == 3) + { + // Analog A/B are always 0 + low = pad_status.triggerRight; // All 8 bits + low |= (pad_status.triggerLeft << 8); // All 8 bits + low |= (pad_status.substickY << 16); // All 8 bits + low |= (pad_status.substickX << 24); // All 8 bits + } + else if (m_mode == 4) + { + low = pad_status.analogB; // All 8 bits + low |= pad_status.analogA << 8; // All 8 bits + // triggerLeft/Right are always 0 + low |= pad_status.substickY << 16; // All 8 bits + low |= pad_status.substickX << 24; // All 8 bits + } return DataResponse::Success; } -void CSIDevice_AMBaseboard::SendCommand(u32 _Cmd, u8 _Poll) +void CSIDevice_AMBaseboard::SendCommand(u32 command, u8 poll) { - ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command (0x{})", _Cmd); - PanicAlertFmt("SI: (GCAM) Unknown direct command"); + UCommand controller_command(command); + + if (static_cast(controller_command.command) == EDirectCommands::CMD_WRITE) + { + const u32 type = controller_command.parameter1; // 0 = stop, 1 = rumble, 2 = stop hard + + // get the correct pad number that should rumble locally when using netplay + const int pad_num = NetPlay_InGamePadToLocalPad(m_device_number); + + if (pad_num < 4) + { + const SIDevices device = m_system.GetSerialInterface().GetDeviceType(pad_num); + if (type == 1) + CSIDevice_GCController::Rumble(pad_num, 1.0, device); + else + CSIDevice_GCController::Rumble(pad_num, 0.0, device); + } + + if (poll == 0) + { + m_mode = controller_command.parameter2; + INFO_LOG_FMT(SERIALINTERFACE, "PAD {} set to mode {}", m_device_number, m_mode); + } + } + else if (controller_command.command != 0x00) + { + // Costis sent 0x00 in some demos :) + ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command ({:#x})", command); + PanicAlertFmt("SI: Unknown direct command"); + } +} + +void CSIDevice_AMBaseboard::SetOrigin(const GCPadStatus& pad_status) +{ + m_origin.origin_stick_x = pad_status.stickX; + m_origin.origin_stick_y = pad_status.stickY; + m_origin.substick_x = pad_status.substickX; + m_origin.substick_y = pad_status.substickY; + m_origin.trigger_left = pad_status.triggerLeft; + m_origin.trigger_right = pad_status.triggerRight; +} + +void CSIDevice_AMBaseboard::HandleMoviePadStatus(Movie::MovieManager& movie, int device_number, + GCPadStatus* pad_status) +{ + movie.SetPolledDevice(); + if (NetPlay_GetInput(device_number, pad_status)) + { + } + else if (movie.IsPlayingInput()) + { + movie.PlayController(pad_status, device_number); + movie.InputUpdate(); + } + else if (movie.IsRecordingInput()) + { + movie.RecordInput(pad_status, device_number); + movie.InputUpdate(); + } + else + { + movie.CheckPadStatus(pad_status, device_number); + } +} + +GCPadStatus CSIDevice_AMBaseboard::GetPadStatus() +{ + GCPadStatus pad_status = {}; + + // For netplay, the local controllers are polled in GetNetPads(), and + // the remote controllers receive their status there as well + if (!NetPlay::IsNetPlayRunning()) + { + pad_status = Pad::GetStatus(m_device_number); + } + + // Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected. + // Watch for this to calibrate real controllers on connection. + if (pad_status.button & PAD_GET_ORIGIN) + SetOrigin(pad_status); + + return pad_status; } } // namespace SerialInterface diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h index dc79df72a3..e7ca8574e0 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h @@ -4,15 +4,18 @@ #pragma once #include -#include -#include -#include -#include -#include +#include #include "Common/CommonTypes.h" #include "Common/Flag.h" +#include "Core/HW/GCPad.h" #include "Core/HW/SI/SI_Device.h" +#include "InputCommon/GCPadStatus.h" + +namespace Movie +{ +class MovieManager; +} namespace SerialInterface { @@ -229,7 +232,7 @@ private: u32 m_fzdx_sensor_left; u8 m_rx_reply; - // F-Zero AX (CryCraft) + // F-Zero AX (CyCraft) u32 m_fzcc_seatbelt; u32 m_fzcc_sensor; u32 m_fzcc_emergency; @@ -237,6 +240,44 @@ private: void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length); +protected: + struct SOrigin + { + u16 button; + u8 origin_stick_x; + u8 origin_stick_y; + u8 substick_x; + u8 substick_y; + u8 trigger_left; + u8 trigger_right; + u8 unk_4; + u8 unk_5; + }; + + enum EButtonCombo + { + COMBO_NONE = 0, + COMBO_ORIGIN, + COMBO_RESET + }; + + // struct to compare input against + // Set on connection to perfect neutral values + // (standard pad only) Set on button combo to current input state + SOrigin m_origin = {}; + + // PADAnalogMode + // Dunno if we need to do this, game/lib should set it? + u8 m_mode = 0x3; + + // Timer to track special button combos: + // y, X, start for 3 seconds updates origin with current status + // Technically, the above is only on standard pad, wavebird does not support it for example + // b, x, start for 3 seconds triggers reset (PI reset button interrupt) + u64 m_timer_button_combo_start = 0; + // Type of button combo from the last/current poll + EButtonCombo m_last_button_combo = COMBO_NONE; + public: // constructor CSIDevice_AMBaseboard(Core::System& system, SIDevices device, int device_number); @@ -249,6 +290,20 @@ public: // send a command directly void SendCommand(u32 command, u8 poll) override; + + virtual GCPadStatus GetPadStatus(); + virtual u32 MapPadStatus(const GCPadStatus& pad_status); + virtual EButtonCombo HandleButtonCombos(const GCPadStatus& pad_status); + + static void HandleMoviePadStatus(Movie::MovieManager& movie, int device_number, + GCPadStatus* pad_status); + + // Send and Receive pad input from network + static bool NetPlay_GetInput(int pad_num, GCPadStatus* status); + static int NetPlay_InGamePadToLocalPad(int pad_num); + +protected: + void SetOrigin(const GCPadStatus& pad_status); }; } // namespace SerialInterface diff --git a/Source/Core/Core/HW/SI/SI_DeviceGCController.h b/Source/Core/Core/HW/SI/SI_DeviceGCController.h index 19b5bdb14b..f4dc5768c9 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceGCController.h +++ b/Source/Core/Core/HW/SI/SI_DeviceGCController.h @@ -3,8 +3,11 @@ #pragma once +#include #include +#include "Common/CommonTypes.h" +#include "Common/Flag.h" #include "Core/HW/GCPad.h" #include "Core/HW/SI/SI_Device.h" #include "InputCommon/GCPadStatus.h" diff --git a/Source/Core/Core/NetPlayClient.cpp b/Source/Core/Core/NetPlayClient.cpp index 3f4344ad88..bfda17a715 100644 --- a/Source/Core/Core/NetPlayClient.cpp +++ b/Source/Core/Core/NetPlayClient.cpp @@ -54,6 +54,7 @@ #include "Core/HW/GCPad.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" +#include "Core/HW/SI/SI_DeviceAMBaseboard.h" #include "Core/HW/SI/SI_DeviceGCController.h" #include "Core/HW/Sram.h" #include "Core/HW/WiiSave.h" @@ -1891,6 +1892,8 @@ void NetPlayClient::UpdateDevices() auto& si = Core::System::GetInstance().GetSerialInterface(); for (auto player_id : m_pad_map) { + const SerialInterface::SIDevices si_device = Config::Get(Config::GetInfoForSIDevice(local_pad)); + if (m_gba_config[pad].enabled && player_id > 0) { si.ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad); @@ -1898,8 +1901,6 @@ void NetPlayClient::UpdateDevices() else if (player_id == m_local_player->pid) { // Use local controller types for local controllers if they are compatible - const SerialInterface::SIDevices si_device = - Config::Get(Config::GetInfoForSIDevice(local_pad)); if (SerialInterface::SIDevice_IsGCController(si_device)) { si.ChangeDevice(si_device, pad); @@ -1917,7 +1918,8 @@ void NetPlayClient::UpdateDevices() } else if (player_id > 0) { - si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); + if (si_device != SerialInterface::SIDEVICE_AM_BASEBOARD) + si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad); } else { @@ -2780,6 +2782,16 @@ bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int pad_num, GCPa return false; } +bool SerialInterface::CSIDevice_AMBaseboard::NetPlay_GetInput(int pad_num, GCPadStatus* status) +{ + std::lock_guard lk(NetPlay::crit_netplay_client); + + if (NetPlay::netplay_client) + return NetPlay::netplay_client->GetNetPads(pad_num, NetPlay::s_si_poll_batching, status); + + return false; +} + bool NetPlay::NetPlay_GetWiimoteData(const std::span& entries) { std::lock_guard lk(crit_netplay_client); @@ -2852,3 +2864,12 @@ int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int num return numPAD; } +int SerialInterface::CSIDevice_AMBaseboard::NetPlay_InGamePadToLocalPad(int numPAD) +{ + std::lock_guard lk(NetPlay::crit_netplay_client); + + if (NetPlay::netplay_client) + return NetPlay::netplay_client->InGamePadToLocalPad(numPAD); + + return numPAD; +} From 11a3fef28fc389a6271d3642e8eb1923ad2ba392 Mon Sep 17 00:00:00 2001 From: crediar Date: Tue, 12 Aug 2025 00:54:26 +0200 Subject: [PATCH 18/18] Fixed video mode handling for EXPORT titles Corrected DVDInterface::ExecuteCommand() normal read behavior Removed DEV region; replaced with IsTriforce checks Removed unused header files Fixed IOFiles not being set to nullptr Added s_allnet_buffer and s_allnet_settings with basic ALL.Net handling Replaced manual loops with std::ranges::fill Added new game type: Virtua Striker 4 Ver. 2006 Implemented AMMB command 0x104 and AllNetInit command 0x700 Updated Baseboard to handle VirtuaStriker4_2006 type Removed analog input from Virtua Striker 3 titles Updated controller config for VS3 and VS4 to use X/Y instead of L/R Addressed unsequenced warning in CoinSubOutput Removed unused triforce_id/GetTriforceID() and updated code General code cleanup and formatting --- Source/Core/Core/Boot/Boot.cpp | 6 + Source/Core/Core/Config/MainSettings.cpp | 13 +- Source/Core/Core/ConfigManager.cpp | 25 +- Source/Core/Core/ConfigManager.h | 5 +- Source/Core/Core/HW/DVD/AMMediaboard.cpp | 76 ++--- Source/Core/Core/HW/DVD/AMMediaboard.h | 13 + Source/Core/Core/HW/DVD/DVDInterface.cpp | 18 +- .../Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp | 2 +- .../Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp | 261 +++++++++--------- Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h | 17 +- Source/Core/Core/HW/VideoInterface.cpp | 15 +- Source/Core/DiscIO/Enums.h | 7 +- Source/Core/DiscIO/Volume.h | 1 - Source/Core/DolphinQt/MenuBar.cpp | 2 +- 14 files changed, 251 insertions(+), 210 deletions(-) diff --git a/Source/Core/Core/Boot/Boot.cpp b/Source/Core/Core/Boot/Boot.cpp index eb16af8511..5185cf7118 100644 --- a/Source/Core/Core/Boot/Boot.cpp +++ b/Source/Core/Core/Boot/Boot.cpp @@ -502,6 +502,12 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard, { SConfig& config = SConfig::GetInstance(); + // Triforce systems are region free and always must use the NTSC video mode + if (system.IsTriforce()) + { + config.m_region = DiscIO::Region::NTSC_J; + } + // PAL Wii uses NTSC framerate and linecount in 60Hz modes system.GetVideoInterface().Preset(DiscIO::IsNTSC(config.m_region) || (system.IsWii() && Config::Get(Config::SYSCONF_PAL60))); diff --git a/Source/Core/Core/Config/MainSettings.cpp b/Source/Core/Core/Config/MainSettings.cpp index 9dd9600dd0..36571747af 100644 --- a/Source/Core/Core/Config/MainSettings.cpp +++ b/Source/Core/Core/Config/MainSettings.cpp @@ -20,6 +20,7 @@ #include "Common/Version.h" #include "Core/AchievementManager.h" #include "Core/Config/DefaultLocale.h" +#include "Core/Core.h" #include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI_Device.h" #include "Core/HW/GCMemcard/GCMemcard.h" @@ -27,6 +28,7 @@ #include "Core/HW/Memmap.h" #include "Core/HW/SI/SI_Device.h" #include "Core/PowerPC/PowerPC.h" +#include "Core/System.h" #include "Core/USBUtils.h" #include "DiscIO/Enums.h" #include "VideoCommon/VideoBackendBase.h" @@ -617,9 +619,17 @@ DiscIO::Region ToGameCubeRegion(DiscIO::Region region) const char* GetDirectoryForRegion(DiscIO::Region region, RegionDirectoryStyle style) { + auto& system = Core::System::GetInstance(); + if (region == DiscIO::Region::Unknown) region = ToGameCubeRegion(Config::Get(Config::MAIN_FALLBACK_REGION)); + // Triforce IPL + if (system.IsTriforce()) + { + return DEV_DIR; + } + switch (region) { case DiscIO::Region::NTSC_J: @@ -636,9 +646,6 @@ const char* GetDirectoryForRegion(DiscIO::Region region, RegionDirectoryStyle st ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region"); return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR; - case DiscIO::Region::DEV: - return DEV_DIR; - default: ASSERT_MSG(BOOT, false, "Default case should not be reached"); return EUR_DIR; diff --git a/Source/Core/Core/ConfigManager.cpp b/Source/Core/Core/ConfigManager.cpp index 6617e443f6..4dee4c212c 100644 --- a/Source/Core/Core/ConfigManager.cpp +++ b/Source/Core/Core/ConfigManager.cpp @@ -141,7 +141,7 @@ u16 SConfig::GetRevision() const void SConfig::ResetRunningGameMetadata() { std::lock_guard lock(m_metadata_lock); - SetRunningGameMetadata("00000000", "", "", 0, 0, DiscIO::Region::Unknown); + SetRunningGameMetadata("00000000", "", 0, 0, DiscIO::Region::Unknown); } void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, @@ -150,14 +150,14 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume, std::lock_guard lock(m_metadata_lock); if (partition == volume.GetGamePartition()) { - SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), volume.GetTriforceID(), + SetRunningGameMetadata(volume.GetGameID(), volume.GetGameTDBID(), volume.GetTitleID().value_or(0), volume.GetRevision().value_or(0), volume.GetRegion()); } else { SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition), - volume.GetTriforceID(), volume.GetTitleID(partition).value_or(0), + volume.GetTitleID(partition).value_or(0), volume.GetRevision(partition).value_or(0), volume.GetRegion()); } } @@ -175,28 +175,25 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat !Core::System::GetInstance().GetDVDInterface().UpdateRunningGameMetadata(tmd_title_id)) { // If not launching a disc game, just read everything from the TMD. - SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), "", tmd_title_id, - tmd.GetTitleVersion(), tmd.GetRegion()); + SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(), + tmd.GetRegion()); } } void SConfig::SetRunningGameMetadata(const std::string& game_id) { std::lock_guard lock(m_metadata_lock); - SetRunningGameMetadata(game_id, "", "", 0, 0, DiscIO::Region::Unknown); + SetRunningGameMetadata(game_id, "", 0, 0, DiscIO::Region::Unknown); } void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, - std::string triforce_id, u64 title_id, u16 revision, - DiscIO::Region region) + u64 title_id, u16 revision, DiscIO::Region region) { std::lock_guard lock(m_metadata_lock); const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id || - m_triforce_id != triforce_id || m_title_id != title_id || - m_revision != revision; + m_title_id != title_id || m_revision != revision; m_game_id = game_id; m_gametdb_id = gametdb_id; - m_triforce_id = triforce_id; m_title_id = title_id; m_revision = revision; @@ -427,12 +424,6 @@ bool SConfig::SetPathsAndGameMetadata(Core::System& system, const BootParameters if (m_region == DiscIO::Region::Unknown) m_region = Config::Get(Config::MAIN_FALLBACK_REGION); - // Triforce IPL - if (m_triforce_id.length()) - { - m_region = DiscIO::Region::DEV; - } - // Set up paths const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region)); m_strSRAM = File::GetUserPath(F_GCSRAM_IDX); diff --git a/Source/Core/Core/ConfigManager.h b/Source/Core/Core/ConfigManager.h index 5aa1f30917..9196c17317 100644 --- a/Source/Core/Core/ConfigManager.h +++ b/Source/Core/Core/ConfigManager.h @@ -57,7 +57,6 @@ struct SConfig // files std::string m_strBootROM; std::string m_strSRAM; - std::string m_debugger_game_id; // TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped. @@ -121,15 +120,13 @@ private: static void ReloadTextures(Core::System& system); void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id, - std::string triforce_id, u64 title_id, u16 revision, - DiscIO::Region region); + u64 title_id, u16 revision, DiscIO::Region region); static SConfig* m_Instance; mutable std::recursive_mutex m_metadata_lock; std::string m_game_id; std::string m_gametdb_id; - std::string m_triforce_id; std::string m_title_name; std::string m_title_description; u64 m_title_id; diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp index 8af430f090..9851de82c9 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp +++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp @@ -4,26 +4,14 @@ #include "Core/HW/DVD/AMMediaboard.h" #include -#include -#include -#include -#include - #include - -#include "Common/CommonPaths.h" +#include #include "Common/CommonTypes.h" -#include "Common/Config/Config.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" -#include "Common/IniFile.h" #include "Common/Logging/Log.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" -#include "Core/Config/MainSettings.h" -#include "Core/Config/SYSCONFSettings.h" -#include "Core/ConfigLoaders/BaseConfigLoader.h" -#include "Core/ConfigLoaders/NetPlayConfigLoader.h" #include "Core/ConfigManager.h" #include "Core/Core.h" #include "Core/HLE/HLE.h" @@ -35,18 +23,10 @@ #include "Core/HW/Memmap.h" #include "Core/HW/SI/SI.h" #include "Core/HW/SI/SI_Device.h" -#include "Core/HW/Sram.h" -#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/IOS/Network/Socket.h" #include "Core/Movie.h" -#include "Core/NetPlayProto.h" -#include "Core/PowerPC/PPCSymbolDB.h" -#include "Core/PowerPC/PowerPC.h" #include "Core/System.h" -#include "Core/WiiRoot.h" #include "DiscIO/DirectoryBlob.h" -#include "DiscIO/Enums.h" -#include "DiscIO/VolumeDisc.h" #if defined(__linux__) or defined(__APPLE__) or defined(__FreeBSD__) or defined(__NetBSD__) or \ defined(__HAIKU__) @@ -91,11 +71,11 @@ static u32 s_GCAM_key_a = 0; static u32 s_GCAM_key_b = 0; static u32 s_GCAM_key_c = 0; -static File::IOFile s_netcfg = nullptr; -static File::IOFile s_netctrl = nullptr; -static File::IOFile s_extra = nullptr; -static File::IOFile s_backup = nullptr; -static File::IOFile s_dimm = nullptr; +static File::IOFile s_netcfg; +static File::IOFile s_netctrl; +static File::IOFile s_extra; +static File::IOFile s_backup; +static File::IOFile s_dimm; static u8* s_dimm_disc = nullptr; @@ -103,6 +83,8 @@ static u8 s_firmware[2 * 1024 * 1024]; static u8 s_media_buffer[0x300]; static u8 s_network_command_buffer[0x4FFE00]; static u8 s_network_buffer[256 * 1024]; +static u8 s_allnet_buffer[4096]; +static u8 s_allnet_settings[0x8500]; /* Sockets FDs are required to go from 0 to 63. Games use the FD as indexes so we have to workaround it. @@ -178,11 +160,13 @@ static File::IOFile OpenOrCreateFile(const std::string& filename) void Init(void) { - memset(s_media_buffer, 0, sizeof(s_media_buffer)); - memset(s_network_buffer, 0, sizeof(s_network_buffer)); - memset(s_network_command_buffer, 0, sizeof(s_network_command_buffer)); - memset(s_firmware, -1, sizeof(s_firmware)); - memset(s_sockets, SOCKET_ERROR, sizeof(s_sockets)); + std::ranges::fill(s_media_buffer, 0); + std::ranges::fill(s_network_buffer, 0); + std::ranges::fill(s_network_command_buffer, 0); + std::ranges::fill(s_firmware, -1); + std::ranges::fill(s_sockets, SOCKET_ERROR); + std::ranges::fill(s_allnet_buffer, 0); + std::ranges::fill(s_allnet_settings, 0); s_firmware_map = false; s_test_menu = false; @@ -460,6 +444,7 @@ any real ones, you can just use the key from RAM without missing a real command. default: *DIIMMBUF = Version1; return 0; + case VirtuaStriker4_2006: case KeyOfAvalon: case MarioKartGP: case MarioKartGP2: @@ -537,6 +522,14 @@ any real ones, you can just use the key from RAM without missing a real command. return 0; } + if (offset >= AllNetBuffer && offset < 0x89011000) + { + u32 allnet_offset = offset - AllNetBuffer; + INFO_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Read All.Net BUFFER (1) ({:08x},{})", offset, length); + memcpy(memory.GetSpanForAddress(address).data(), s_allnet_buffer + allnet_offset, length); + return 0; + } + if (offset >= DIMMCommandVersion1 && offset < 0x1F900040) { u32 dimmoffset = offset - DIMMCommandVersion1; @@ -647,6 +640,9 @@ any real ones, you can just use the key from RAM without missing a real command. // Empty reply case AMMBCommand::Unknown_103: break; + case AMMBCommand::Unknown_104: + s_media_buffer[4] = 1; + break; case AMMBCommand::Accept: { u32 fd = s_sockets[SocketCheck(media_buffer_32[2])]; @@ -1008,6 +1004,9 @@ any real ones, you can just use the key from RAM without missing a real command. case AMMBCommand::InitLink: NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: InitLink"); break; + case AMMBCommand::AllNetInit: + NOTICE_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: AllNetInit"); + break; default: ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Command:{:03X}", *(u16*)(s_media_buffer + 2)); ERROR_LOG_FMT(DVDINTERFACE_AMMB, "GC-AM: Command Unhandled!"); @@ -1041,6 +1040,13 @@ any real ones, you can just use the key from RAM without missing a real command. return 0; } + if (offset >= AllNetSettings && offset <= 0x1F000000) + { + u32 allnet_offset = offset - AllNetSettings; + memcpy(memory.GetSpanForAddress(address).data(), s_allnet_settings + allnet_offset, length); + return 0; + } + if (offset == NetworkControl && length == 0x20) { s_netctrl.Seek(0, File::SeekOrigin::Begin); @@ -1125,6 +1131,13 @@ any real ones, you can just use the key from RAM without missing a real command. return 0; } + if ((offset >= AllNetBuffer) && (offset <= 0x89011000)) + { + u32 allnet_offset = offset - AllNetBuffer; + memcpy(s_allnet_buffer + allnet_offset, memory.GetSpanForAddress(address).data(), length); + return 0; + } + if ((offset >= NetworkCommandAddress) && (offset < 0x1F801240)) { u32 dimmoffset = offset - NetworkCommandAddress; @@ -1769,6 +1782,7 @@ u32 GetGameType(void) case 0x53424C4A: case 0x53424C4B: case 0x53424C4C: + return VirtuaStriker4_2006; // SBHJ/SBHN/SBHZ - VIRTUA STRIKER 4 VER.A case 0x5342484A: case 0x5342484E: diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h index d41894f23a..16de079c8f 100644 --- a/Source/Core/Core/HW/DVD/AMMediaboard.h +++ b/Source/Core/Core/HW/DVD/AMMediaboard.h @@ -30,6 +30,7 @@ enum GameType MarioKartGP2, VirtuaStriker3, VirtuaStriker4, + VirtuaStriker4_2006, GekitouProYakyuu, KeyOfAvalon, FirmwareUpdate, @@ -125,6 +126,14 @@ enum class AMMBCommand : u16 Unknown_608 = 0x608, Unknown_614 = 0x614, + // All.Net + AllNetInit = 0x700, + Unknown_701 = 0x701, + Unknown_702 = 0x702, + Unknown_703 = 0x703, + Unknown_704 = 0x704, + + // NETDIMM Commands Unknown_001 = 0x001, GetNetworkFirmVersion = 0x101, @@ -147,6 +156,8 @@ enum MediaBoardAddress : u32 DIMMExtraSettings = 0x1FFEFFE0, + AllNetSettings = 0x1EFF8000, + NetworkControl = 0xFFFF0000, DIMMCommandVersion1 = 0x1F900000, @@ -163,6 +174,8 @@ enum MediaBoardAddress : u32 NetworkBufferAddress4 = 0x89180000, NetworkBufferAddress5 = 0x1FB00000, + AllNetBuffer = 0x89010000, + FirmwareAddress = 0x84800000, FirmwareMagicWrite1 = 0x00600000, diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp index 5ed8e03524..bda62ca303 100644 --- a/Source/Core/Core/HW/DVD/DVDInterface.cpp +++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp @@ -708,12 +708,16 @@ bool DVDInterface::CheckReadPreconditions() SetDriveError(DriveError::MotorStopped); return false; } - if (m_drive_state == DriveState::DiscIdNotRead) - { - ERROR_LOG_FMT(DVDINTERFACE, "Disc id not read."); - SetDriveError(DriveError::NoDiscID); - return false; - } + + // SegaBoot doesn't read the Disc ID + if (!m_system.IsTriforce()) + if (m_drive_state == DriveState::DiscIdNotRead) + { + ERROR_LOG_FMT(DVDINTERFACE, "Disc id not read."); + SetDriveError(DriveError::NoDiscID); + return false; + } + return true; } @@ -776,8 +780,8 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type) m_error_code = DriveError::None; return; } - m_DICMDBUF[1] >>= 2; // Normal read command pass on to normal handling + m_DICMDBUF[0] <<= 24; } // DVDLowRequestError needs access to the error code set by the previous command diff --git a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp index 46aff6f009..4da3f5402c 100644 --- a/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp +++ b/Source/Core/Core/HW/EXI/EXI_DeviceBaseboard.cpp @@ -10,13 +10,13 @@ #include +#include "Common/Buffer.h" #include "Common/CommonTypes.h" #include "Common/Config/Config.h" #include "Common/FileUtil.h" #include "Common/IOFile.h" #include "Common/IniFile.h" #include "Common/Logging/Log.h" -#include "Common/Buffer.h" #include "Core/Boot/Boot.h" #include "Core/BootManager.h" #include "Core/Config/MainSettings.h" diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp index 9675d2d50e..10c5261f43 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp @@ -63,15 +63,15 @@ JVSIOMessage::JVSIOMessage() m_last_start = 0; } -void JVSIOMessage::start(int node) +void JVSIOMessage::Start(int node) { m_last_start = m_ptr; u8 hdr[3] = {0xE0, (u8)node, 0}; m_csum = 0; - addData(hdr, 3, 1); + AddData(hdr, 3, 1); } -void JVSIOMessage::addData(const u8* dst, size_t len, int sync = 0) +void JVSIOMessage::AddData(const u8* dst, size_t len, int sync = 0) { while (len--) { @@ -94,27 +94,27 @@ void JVSIOMessage::addData(const u8* dst, size_t len, int sync = 0) } } -void JVSIOMessage::addData(const void* data, size_t len) +void JVSIOMessage::AddData(const void* data, size_t len) { - addData((const u8*)data, len); + AddData((const u8*)data, len); } -void JVSIOMessage::addData(const char* data) +void JVSIOMessage::AddData(const char* data) { - addData(data, strlen(data)); + AddData(data, strlen(data)); } -void JVSIOMessage::addData(u32 n) +void JVSIOMessage::AddData(u32 n) { u8 cs = n; - addData(&cs, 1); + AddData(&cs, 1); } -void JVSIOMessage::end() +void JVSIOMessage::End() { u32 len = m_ptr - m_last_start; m_msg[m_last_start + 2] = len - 2; // assuming len <0xD0 - addData(m_csum + len - 2); + AddData(m_csum + len - 2); } static u8 CheckSumXOR(u8* Data, u32 Length) @@ -386,8 +386,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) *data_in++, *data_in++, *data_in++, *data_in++, *data_in++); u8 string[] = "\x00\x00\x30\x00" // "\x01\xfe\x00\x00" // JAPAN - "\x02\xfd\x00\x00" // USA - // "\x03\xfc\x00\x00" // export + //"\x02\xfd\x00\x00" // USA + "\x03\xfc\x00\x00" // export "\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff"; data_out[data_offset++] = gcam_command; data_out[data_offset++] = 0x14; @@ -511,6 +511,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) // Serial IC-CARD / Serial Deck Reader if (AMMediaboard::GetGameType() == VirtuaStriker4 || + AMMediaboard::GetGameType() == VirtuaStriker4_2006 || AMMediaboard::GetGameType() == KeyOfAvalon) { u32 serial_command = data_in[1]; @@ -1159,7 +1160,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (File::Exists(card_filename)) { - File::IOFile card = File::IOFile(card_filename, "rb+"); + File::IOFile card(card_filename, "rb+"); m_card_memory_size = (u32)card.GetSize(); card.ReadBytes(m_card_memory, m_card_memory_size); @@ -1249,7 +1250,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (File::Exists(card_filename)) { - File::IOFile card = File::IOFile(card_filename, "rb+"); + File::IOFile card(card_filename, "rb+"); if (m_card_memory_size == 0) { m_card_memory_size = (u32)card.GetSize(); @@ -1314,7 +1315,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) SConfig::GetInstance().GetGameID().c_str() + ".bin"); - File::IOFile card = File::IOFile(card_filename, "wb+"); + File::IOFile card(card_filename, "wb+"); card.WriteBytes(m_card_memory, m_card_memory_size); card.Close(); @@ -1403,8 +1404,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) u8* jvs_io = jvs_buf + 4; // First payload byte u8* jvs_end = jvs_buf + frame_len; // One byte before checksum - message.start(0); - message.addData(1); + message.Start(0); + message.AddData(1); // Now iterate over the payload while (jvs_io < jvs_end) @@ -1416,41 +1417,42 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) switch (JVSIOCommand(jvsio_command)) { case JVSIOCommand::IOID: - message.addData(StatusOkay); + message.AddData(StatusOkay); switch (AMMediaboard::GetGameType()) { case FZeroAX: // Specific version that enables DX mode on AX machines, all this does is enable the // motion of a chair - message.addData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;"); + message.AddData("SEGA ENTERPRISES,LTD.;837-13844-01 I/O CNTL BD2 ;"); break; case FZeroAXMonster: case MarioKartGP: case MarioKartGP2: default: - message.addData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"); + message.AddData("namco ltd.;FCA-1;Ver1.01;JPN,Multipurpose + Rotary Encoder"); break; case VirtuaStriker3: case VirtuaStriker4: - message.addData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00"); + case VirtuaStriker4_2006: + message.AddData("SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551;Ver1.00"); break; } NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x10, BoardID"); - message.addData((u32)0); + message.AddData((u32)0); break; case JVSIOCommand::CommandRevision: - message.addData(StatusOkay); - message.addData(0x11); + message.AddData(StatusOkay); + message.AddData(0x11); NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x11, CommandRevision"); break; case JVSIOCommand::JVRevision: - message.addData(StatusOkay); - message.addData(0x20); + message.AddData(StatusOkay); + message.AddData(0x20); NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x12, JVRevision"); break; case JVSIOCommand::CommunicationVersion: - message.addData(StatusOkay); - message.addData(0x10); + message.AddData(StatusOkay); + message.AddData(0x10); NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x13, CommunicationVersion"); break; /* @@ -1473,64 +1475,72 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) 0x15: Backup */ case JVSIOCommand::CheckFunctionality: - message.addData(StatusOkay); + message.AddData(StatusOkay); switch (AMMediaboard::GetGameType()) { case FZeroAX: case FZeroAXMonster: // 2 Player (12bit) (p2=paddles), 1 Coin slot, 6 Analog-in - // message.addData((void *)"\x01\x02\x0C\x00", 4); - // message.addData((void *)"\x02\x01\x00\x00", 4); - // message.addData((void *)"\x03\x06\x00\x00", 4); - // message.addData((void *)"\x00\x00\x00\x00", 4); + // message.AddData((void *)"\x01\x02\x0C\x00", 4); + // message.AddData((void *)"\x02\x01\x00\x00", 4); + // message.AddData((void *)"\x03\x06\x00\x00", 4); + // message.AddData((void *)"\x00\x00\x00\x00", 4); // // DX Version: 2 Player (22bit) (p2=paddles), 2 Coin slot, 8 Analog-in, // 22 Driver-out - message.addData((void*)"\x01\x02\x12\x00", 4); - message.addData((void*)"\x02\x02\x00\x00", 4); - message.addData((void*)"\x03\x08\x0A\x00", 4); - message.addData((void*)"\x12\x16\x00\x00", 4); - message.addData((void*)"\x00\x00\x00\x00", 4); + message.AddData((void*)"\x01\x02\x12\x00", 4); + message.AddData((void*)"\x02\x02\x00\x00", 4); + message.AddData((void*)"\x03\x08\x0A\x00", 4); + message.AddData((void*)"\x12\x16\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); break; case VirtuaStriker3: + // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out + message.AddData((void*)"\x01\x02\x0D\x00", 4); + message.AddData((void*)"\x02\x02\x00\x00", 4); + message.AddData((void*)"\x10\x01\x00\x00", 4); + message.AddData((void*)"\x12\x08\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); + break; case GekitouProYakyuu: // 2 Player (13bit), 2 Coin slot, 4 Analog-in, 1 CARD, 8 Driver-out - message.addData((void*)"\x01\x02\x0D\x00", 4); - message.addData((void*)"\x02\x02\x00\x00", 4); - message.addData((void*)"\x03\x04\x00\x00", 4); - message.addData((void*)"\x10\x01\x00\x00", 4); - message.addData((void*)"\x12\x08\x00\x00", 4); - message.addData((void*)"\x00\x00\x00\x00", 4); + message.AddData((void*)"\x01\x02\x0D\x00", 4); + message.AddData((void*)"\x02\x02\x00\x00", 4); + message.AddData((void*)"\x03\x04\x00\x00", 4); + message.AddData((void*)"\x10\x01\x00\x00", 4); + message.AddData((void*)"\x12\x08\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); break; case VirtuaStriker4: + case VirtuaStriker4_2006: // 2 Player (13bit), 1 Coin slot, 4 Analog-in, 1 CARD - message.addData((void*)"\x01\x02\x0D\x00", 4); - message.addData((void*)"\x02\x01\x00\x00", 4); - message.addData((void*)"\x03\x04\x00\x00", 4); - message.addData((void*)"\x10\x01\x00\x00", 4); - message.addData((void*)"\x00\x00\x00\x00", 4); + message.AddData((void*)"\x01\x02\x0D\x00", 4); + message.AddData((void*)"\x02\x01\x00\x00", 4); + message.AddData((void*)"\x03\x04\x00\x00", 4); + message.AddData((void*)"\x10\x01\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); break; case KeyOfAvalon: // 1 Player (15bit), 1 Coin slot, 3 Analog-in, Touch, 1 CARD, 1 Driver-out // (Unconfirmed) - message.addData((void*)"\x01\x01\x0F\x00", 4); - message.addData((void*)"\x02\x01\x00\x00", 4); - message.addData((void*)"\x03\x03\x00\x00", 4); - message.addData((void*)"\x06\x10\x10\x01", 4); - message.addData((void*)"\x10\x01\x00\x00", 4); - message.addData((void*)"\x12\x01\x00\x00", 4); - message.addData((void*)"\x00\x00\x00\x00", 4); + message.AddData((void*)"\x01\x01\x0F\x00", 4); + message.AddData((void*)"\x02\x01\x00\x00", 4); + message.AddData((void*)"\x03\x03\x00\x00", 4); + message.AddData((void*)"\x06\x10\x10\x01", 4); + message.AddData((void*)"\x10\x01\x00\x00", 4); + message.AddData((void*)"\x12\x01\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); break; case MarioKartGP: case MarioKartGP2: default: // 1 Player (15bit), 1 Coin slot, 3 Analog-in, 1 CARD, 1 Driver-out - message.addData((void*)"\x01\x01\x0F\x00", 4); - message.addData((void*)"\x02\x01\x00\x00", 4); - message.addData((void*)"\x03\x03\x00\x00", 4); - message.addData((void*)"\x10\x01\x00\x00", 4); - message.addData((void*)"\x12\x01\x00\x00", 4); - message.addData((void*)"\x00\x00\x00\x00", 4); + message.AddData((void*)"\x01\x01\x0F\x00", 4); + message.AddData((void*)"\x02\x01\x00\x00", 4); + message.AddData((void*)"\x03\x03\x00\x00", 4); + message.AddData((void*)"\x10\x01\x00\x00", 4); + message.AddData((void*)"\x12\x01\x00\x00", 4); + message.AddData((void*)"\x00\x00\x00\x00", 4); break; } NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x14, CheckFunctionality"); @@ -1539,7 +1549,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) while (*jvs_io++) { }; - message.addData(StatusOkay); + message.AddData(StatusOkay); break; case JVSIOCommand::SwitchesInput: { @@ -1549,7 +1559,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x20, SwitchInputs: {} {}", player_count, player_byte_count); - message.addData(StatusOkay); + message.AddData(StatusOkay); GCPadStatus PadStatus; PadStatus = Pad::GetStatus(0); @@ -1560,7 +1570,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) // Trying to access the test menu without SegaBoot present will cause a crash if (AMMediaboard::GetTestMenu()) { - message.addData(0x80); + message.AddData(0x80); } else { @@ -1568,7 +1578,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) } } else - message.addData((u32)0x00); + message.AddData((u32)0x00); for (int i = 0; i < player_count; ++i) { @@ -1696,10 +1706,10 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Long Pass - if (PadStatus.button & PAD_TRIGGER_L) + if (PadStatus.button & PAD_BUTTON_X) player_data[0] |= 0x01; // Short Pass - if (PadStatus.button & PAD_TRIGGER_R) + if (PadStatus.button & PAD_BUTTON_B) player_data[1] |= 0x80; // Shoot if (PadStatus.button & PAD_BUTTON_A) @@ -1719,16 +1729,17 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) break; // Controller configuration for Virtua Striker 4 games case VirtuaStriker4: + case VirtuaStriker4_2006: { PadStatus = Pad::GetStatus(i); // Start if (PadStatus.button & PAD_BUTTON_START) player_data[0] |= 0x80; // Long Pass - if (PadStatus.button & PAD_TRIGGER_L) + if (PadStatus.button & PAD_BUTTON_X) player_data[0] |= 0x01; // Short Pass - if (PadStatus.button & PAD_TRIGGER_R) + if (PadStatus.button & PAD_BUTTON_Y) player_data[0] |= 0x02; // Shoot if (PadStatus.button & PAD_BUTTON_A) @@ -1826,14 +1837,14 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) } for (int j = 0; j < player_byte_count; ++j) - message.addData(player_data[j]); + message.AddData(player_data[j]); } break; } case JVSIOCommand::CoinInput: { int slots = *jvs_io++; - message.addData(StatusOkay); + message.AddData(StatusOkay); for (int i = 0; i < slots; i++) { GCPadStatus PadStatus; @@ -1843,15 +1854,15 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) m_coin[i]++; } m_coin_pressed[i] = PadStatus.switches & PAD_SWITCH_COIN; - message.addData((m_coin[i] >> 8) & 0x3f); - message.addData(m_coin[i] & 0xff); + message.AddData((m_coin[i] >> 8) & 0x3f); + message.AddData(m_coin[i] & 0xff); } DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x21, CoinInput: {}", slots); break; } case JVSIOCommand::AnalogInput: { - message.addData(StatusOkay); + message.AddData(StatusOkay); int analogs = *jvs_io++; GCPadStatus PadStatus; @@ -1870,77 +1881,77 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { if (m_motorforce_x > 0) { - message.addData(0x80 - (m_motorforce_x >> 8)); + message.AddData(0x80 - (m_motorforce_x >> 8)); } else { - message.addData((m_motorforce_x >> 8)); + message.AddData((m_motorforce_x >> 8)); } - message.addData((u8)0); + message.AddData((u8)0); - message.addData(PadStatus.stickY); - message.addData((u8)0); + message.AddData(PadStatus.stickY); + message.AddData((u8)0); } else { - message.addData(PadStatus.stickX); - message.addData((u8)0); + message.AddData(PadStatus.stickX); + message.AddData((u8)0); - message.addData(PadStatus.stickY); - message.addData((u8)0); + message.AddData(PadStatus.stickY); + message.AddData((u8)0); } // Unused - message.addData((u8)0); - message.addData((u8)0); - message.addData((u8)0); - message.addData((u8)0); + message.AddData((u8)0); + message.AddData((u8)0); + message.AddData((u8)0); + message.AddData((u8)0); // Gas - message.addData(PadStatus.triggerRight); - message.addData((u8)0); + message.AddData(PadStatus.triggerRight); + message.AddData((u8)0); // Brake - message.addData(PadStatus.triggerLeft); - message.addData((u8)0); + message.AddData(PadStatus.triggerLeft); + message.AddData((u8)0); - message.addData((u8)0x80); // Motion Stop - message.addData((u8)0); + message.AddData((u8)0x80); // Motion Stop + message.AddData((u8)0); - message.addData((u8)0); - message.addData((u8)0); + message.AddData((u8)0); + message.AddData((u8)0); break; - case VirtuaStriker3: case VirtuaStriker4: + case VirtuaStriker4_2006: { PadStatus2 = Pad::GetStatus(1); - message.addData(PadStatus.stickX); - message.addData((u8)0); - message.addData(PadStatus.stickY); - message.addData((u8)0); + message.AddData(PadStatus.stickY); + message.AddData((u8)0); + message.AddData(PadStatus.stickX); + message.AddData((u8)0); - message.addData(PadStatus2.stickX); - message.addData((u8)0); - message.addData(PadStatus2.stickY); - message.addData((u8)0); + message.AddData(PadStatus2.stickY); + message.AddData((u8)0); + message.AddData(PadStatus2.stickX); + message.AddData((u8)0); } break; default: case MarioKartGP: case MarioKartGP2: // Steering - message.addData(PadStatus.stickX); - message.addData((u8)0); + message.AddData(PadStatus.stickX); + message.AddData((u8)0); // Gas - message.addData(PadStatus.triggerRight); - message.addData((u8)0); + message.AddData(PadStatus.triggerRight); + message.AddData((u8)0); // Brake - message.addData(PadStatus.triggerLeft); - message.addData((u8)0); + message.AddData(PadStatus.triggerLeft); + message.AddData((u8)0); break; } break; @@ -1955,12 +1966,12 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (PadStatus.button & PAD_TRIGGER_R) { // Tap at center of screen (~320,240) - message.addData((void*)"\x01\x00\x8C\x01\x95", + message.AddData((void*)"\x01\x00\x8C\x01\x95", 5); // X=320 (0x0140), Y=240 (0x00F0) } else { - message.addData((void*)"\x01\xFF\xFF\xFF\xFF", 5); + message.AddData((void*)"\x01\xFF\xFF\xFF\xFF", 5); } DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x25, PositionInput:{}", @@ -1970,8 +1981,10 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) case JVSIOCommand::CoinSubOutput: { u32 slot = *jvs_io++; - m_coin[slot] -= (*jvs_io++ << 8) | *jvs_io++; - message.addData(StatusOkay); + u8 coinh = *jvs_io++; + u8 coinl = *jvs_io++; + m_coin[slot] -= (coinh << 8) | coinl; + message.AddData(StatusOkay); DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x30, CoinSubOutput: {}", slot); break; } @@ -1981,7 +1994,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (bytes) { - message.addData(StatusOkay); + message.AddData(StatusOkay); // The lamps are controlled via this if (AMMediaboard::GetGameType() == MarioKartGP) @@ -2046,7 +2059,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) { int slot = *jvs_io++; m_coin[slot] += (*jvs_io++ << 8) | *jvs_io++; - message.addData(StatusOkay); + message.AddData(StatusOkay); DEBUG_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0x35, CoinAddOutput: {}", slot); break; } @@ -2056,12 +2069,12 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) if (cmd_ == 0x18) { // id check jvs_io += 4; - message.addData(StatusOkay); - message.addData(0xff); + message.AddData(StatusOkay); + message.AddData(0xff); } else { - message.addData(StatusOkay); + message.AddData(StatusOkay); ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unknown:{:02x}", cmd_); } break; @@ -2074,7 +2087,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) m_wheelinit = 0; m_ic_card_state = 0x20; } - message.addData(StatusOkay); + message.AddData(StatusOkay); dip_switch_1 |= 1; break; @@ -2082,7 +2095,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) node = *jvs_io++; NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF1, SetAddress: node={}", node); - message.addData(node == 1); + message.AddData(node == 1); dip_switch_1 &= ~1; break; default: @@ -2092,7 +2105,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length) } } - message.end(); + message.End(); data_out[data_offset++] = gcam_command; diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h index e7ca8574e0..7ca6aaca8e 100644 --- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h +++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h @@ -3,11 +3,6 @@ #pragma once -#include -#include - -#include "Common/CommonTypes.h" -#include "Common/Flag.h" #include "Core/HW/GCPad.h" #include "Core/HW/SI/SI_Device.h" #include "InputCommon/GCPadStatus.h" @@ -28,12 +23,12 @@ public: u8 m_msg[0x80]; JVSIOMessage(); - void start(int node); - void addData(const u8* dst, size_t len, int sync); - void addData(const void* data, size_t len); - void addData(const char* data); - void addData(u32 n); - void end(); + void Start(int node); + void AddData(const u8* dst, size_t len, int sync); + void AddData(const void* data, size_t len); + void AddData(const char* data); + void AddData(u32 n); + void End(); }; // end class JVSIOMessage // triforce (GC-AM) baseboard diff --git a/Source/Core/Core/HW/VideoInterface.cpp b/Source/Core/Core/HW/VideoInterface.cpp index 1cdfe6279d..bb6d9a6a73 100644 --- a/Source/Core/Core/HW/VideoInterface.cpp +++ b/Source/Core/Core/HW/VideoInterface.cpp @@ -164,16 +164,19 @@ void VideoInterfaceManager::Preset(bool _bNTSC) // Say component cable is plugged m_dtv_status.component_plugged = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN); - // Triforce IPL requires this to be set - if (region == DiscIO::Region::DEV || region == DiscIO::Region::NTSC_J) - { - m_dtv_status.ntsc_j = true; - } - else + if (region == DiscIO::Region::NTSC_J) { m_dtv_status.ntsc_j = false; } + /* + Triforce IPL requires the DTV NTSC-J flag to be set. + */ + if (m_system.IsTriforce()) + { + m_dtv_status.ntsc_j = true; + } + m_fb_width.Hex = 0; m_border_hblank.Hex = 0; diff --git a/Source/Core/DiscIO/Enums.h b/Source/Core/DiscIO/Enums.h index 966b5bfc15..d488692438 100644 --- a/Source/Core/DiscIO/Enums.h +++ b/Source/Core/DiscIO/Enums.h @@ -19,7 +19,7 @@ enum class Platform WiiDisc, WiiWAD, ELFOrDOL, - NumberOfPlatforms + NumberOfPlatforms, }; enum class Country @@ -38,7 +38,7 @@ enum class Country Taiwan, World, Unknown, - NumberOfCountries + NumberOfCountries, }; // This numbering matches Nintendo's GameCube/Wii region numbering. @@ -49,7 +49,6 @@ enum class Region PAL = 2, // Mainly Europe and Oceania Unknown = 3, // Nintendo uses this to mean region free, but we also use it for unknown regions NTSC_K = 4, // South Korea (Wii only) - DEV = 5, // Workaround for Triforce IPL }; // Languages 0 - 9 match Nintendo's Wii language numbering. @@ -67,7 +66,7 @@ enum class Language SimplifiedChinese = 7, // Not selectable on any unmodded retail Wii TraditionalChinese = 8, // Not selectable on any unmodded retail Wii Korean = 9, - Unknown + Unknown, }; std::string GetName(Country country, bool translate); diff --git a/Source/Core/DiscIO/Volume.h b/Source/Core/DiscIO/Volume.h index a7273b0f0e..838ae28984 100644 --- a/Source/Core/DiscIO/Volume.h +++ b/Source/Core/DiscIO/Volume.h @@ -96,7 +96,6 @@ public: } virtual std::string GetGameID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetGameTDBID(const Partition& partition = PARTITION_NONE) const = 0; - virtual std::string GetTriforceID() const { return ""; } virtual std::string GetMakerID(const Partition& partition = PARTITION_NONE) const = 0; virtual std::optional GetRevision(const Partition& partition = PARTITION_NONE) const = 0; virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0; diff --git a/Source/Core/DolphinQt/MenuBar.cpp b/Source/Core/DolphinQt/MenuBar.cpp index 9e6d595102..45f5ec5401 100644 --- a/Source/Core/DolphinQt/MenuBar.cpp +++ b/Source/Core/DolphinQt/MenuBar.cpp @@ -311,7 +311,7 @@ void MenuBar::AddToolsMenu() gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); }); m_dev_ipl = gc_ipl->addAction(tr("Triforce"), this, - [this] { emit BootGameCubeIPL(DiscIO::Region::DEV); }); + [this] { emit BootGameCubeIPL(DiscIO::Region::Unknown); }); tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });