Added Triforce support
This commit is contained in:
@@ -0,0 +1,4 @@
|
|||||||
|
# GEYJ6E - Virtua Striker 2002 (Japan)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GEYP6E - Virtua Striker 2002 (Export, Type3)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# GGGE6E - F-ZERO AX
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# GHAE6E - F-ZERO AX (Monster)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
|
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GHZW6E - VIRTUA STRIKER 4 (Asia)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GJAP6E - VIRTUA STRIKER 4 (Export)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GLKJ6E - VIRTUA STRIKER 4 Ver.2006 (Japan)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
# GLLP6E - VIRTUA STRIKER 4 Ver.2006 (Export)
|
||||||
|
|
||||||
|
[Core]
|
||||||
|
FPRF = True
|
||||||
@@ -41,11 +41,13 @@
|
|||||||
#define USA_DIR "USA"
|
#define USA_DIR "USA"
|
||||||
#define JAP_DIR "JAP"
|
#define JAP_DIR "JAP"
|
||||||
#define JPN_DIR "JPN"
|
#define JPN_DIR "JPN"
|
||||||
|
#define DEV_DIR "DEV"
|
||||||
|
|
||||||
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
|
// Subdirs in the User dir returned by GetUserPath(D_USER_IDX)
|
||||||
#define GC_USER_DIR "GC"
|
#define GC_USER_DIR "GC"
|
||||||
#define GBA_USER_DIR "GBA"
|
#define GBA_USER_DIR "GBA"
|
||||||
#define WII_USER_DIR "Wii"
|
#define WII_USER_DIR "Wii"
|
||||||
|
#define TRI_USER_DIR "Triforce"
|
||||||
#define CONFIG_DIR "Config"
|
#define CONFIG_DIR "Config"
|
||||||
#define GAMESETTINGS_DIR "GameSettings"
|
#define GAMESETTINGS_DIR "GameSettings"
|
||||||
#define MAPS_DIR "Maps"
|
#define MAPS_DIR "Maps"
|
||||||
@@ -155,6 +157,7 @@
|
|||||||
// Subdirs in Sys
|
// Subdirs in Sys
|
||||||
#define GC_SYS_DIR "GC"
|
#define GC_SYS_DIR "GC"
|
||||||
#define WII_SYS_DIR "Wii"
|
#define WII_SYS_DIR "Wii"
|
||||||
|
#define TRI_SYS_DIR "Triforce"
|
||||||
|
|
||||||
// Subdirs in Config
|
// Subdirs in Config
|
||||||
#define GRAPHICSMOD_CONFIG_DIR "GraphicMods"
|
#define GRAPHICSMOD_CONFIG_DIR "GraphicMods"
|
||||||
|
|||||||
@@ -838,6 +838,7 @@ static void RebuildUserDirectories(unsigned int dir_index)
|
|||||||
case D_USER_IDX:
|
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_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_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_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_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;
|
s_user_paths[D_MAPS_IDX] = s_user_paths[D_USER_IDX] + MAPS_DIR DIR_SEP;
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ enum
|
|||||||
D_USER_IDX,
|
D_USER_IDX,
|
||||||
D_GCUSER_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_SESSION_WIIROOT_IDX, // may point to minimal temporary directory for determinism
|
||||||
D_CONFIG_IDX, // global settings
|
D_CONFIG_IDX, // global settings
|
||||||
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default
|
D_GAMESETTINGS_IDX, // user-specified settings which override both the global and the default
|
||||||
|
|||||||
@@ -27,6 +27,8 @@ enum class LogType : int
|
|||||||
DSP_MAIL,
|
DSP_MAIL,
|
||||||
DSPINTERFACE,
|
DSPINTERFACE,
|
||||||
DVDINTERFACE,
|
DVDINTERFACE,
|
||||||
|
AMMEDIABOARD,
|
||||||
|
AMMEDIABOARD_NET,
|
||||||
DYNA_REC,
|
DYNA_REC,
|
||||||
EXPANSIONINTERFACE,
|
EXPANSIONINTERFACE,
|
||||||
FILEMON,
|
FILEMON,
|
||||||
@@ -58,6 +60,9 @@ enum class LogType : int
|
|||||||
PROCESSORINTERFACE,
|
PROCESSORINTERFACE,
|
||||||
POWERPC,
|
POWERPC,
|
||||||
SERIALINTERFACE,
|
SERIALINTERFACE,
|
||||||
|
SERIALINTERFACE_AMBB,
|
||||||
|
SERIALINTERFACE_CARD,
|
||||||
|
SERIALINTERFACE_JVSIO,
|
||||||
SP1,
|
SP1,
|
||||||
SYMBOLS,
|
SYMBOLS,
|
||||||
VIDEO,
|
VIDEO,
|
||||||
|
|||||||
@@ -111,6 +111,8 @@ LogManager::LogManager()
|
|||||||
m_log[LogType::DSP_MAIL] = {"DSPMails", "DSP Mails"};
|
m_log[LogType::DSP_MAIL] = {"DSPMails", "DSP Mails"};
|
||||||
m_log[LogType::DSPINTERFACE] = {"DSP", "DSP Interface"};
|
m_log[LogType::DSPINTERFACE] = {"DSP", "DSP Interface"};
|
||||||
m_log[LogType::DVDINTERFACE] = {"DVD", "DVD Interface"};
|
m_log[LogType::DVDINTERFACE] = {"DVD", "DVD Interface"};
|
||||||
|
m_log[LogType::AMMEDIABOARD] = {"AMMB", "AMMB Interface"};
|
||||||
|
m_log[LogType::AMMEDIABOARD_NET] = {"AMMB_NET", "AMMB - Network"};
|
||||||
m_log[LogType::DYNA_REC] = {"JIT", "JIT Dynamic Recompiler"};
|
m_log[LogType::DYNA_REC] = {"JIT", "JIT Dynamic Recompiler"};
|
||||||
m_log[LogType::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"};
|
m_log[LogType::EXPANSIONINTERFACE] = {"EXI", "Expansion Interface"};
|
||||||
m_log[LogType::FILEMON] = {"FileMon", "File Monitor"};
|
m_log[LogType::FILEMON] = {"FileMon", "File Monitor"};
|
||||||
@@ -142,6 +144,9 @@ LogManager::LogManager()
|
|||||||
m_log[LogType::PROCESSORINTERFACE] = {"PI", "Processor Interface"};
|
m_log[LogType::PROCESSORINTERFACE] = {"PI", "Processor Interface"};
|
||||||
m_log[LogType::POWERPC] = {"PowerPC", "PowerPC IBM CPU"};
|
m_log[LogType::POWERPC] = {"PowerPC", "PowerPC IBM CPU"};
|
||||||
m_log[LogType::SERIALINTERFACE] = {"SI", "Serial Interface"};
|
m_log[LogType::SERIALINTERFACE] = {"SI", "Serial Interface"};
|
||||||
|
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::SP1] = {"SP1", "Serial Port 1"};
|
||||||
m_log[LogType::SYMBOLS] = {"SYMBOLS", "Symbols"};
|
m_log[LogType::SYMBOLS] = {"SYMBOLS", "Symbols"};
|
||||||
m_log[LogType::VIDEO] = {"Video", "Video Backend"};
|
m_log[LogType::VIDEO] = {"Video", "Video Backend"};
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/FifoPlayer/FifoPlayer.h"
|
#include "Core/FifoPlayer/FifoPlayer.h"
|
||||||
#include "Core/HLE/HLE.h"
|
#include "Core/HLE/HLE.h"
|
||||||
|
#include "Core/HW/DVD/AMMediaboard.h"
|
||||||
#include "Core/HW/DVD/DVDInterface.h"
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
@@ -373,7 +374,8 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
|
|||||||
constexpr u32 PAL_v1_0 = 0x4F319F43;
|
constexpr u32 PAL_v1_0 = 0x4F319F43;
|
||||||
// DOL-101(EUR) (PAL Revision 1.2)
|
// DOL-101(EUR) (PAL Revision 1.2)
|
||||||
constexpr u32 PAL_v1_2 = 0xAD1B7F16;
|
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.
|
// 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;
|
constexpr size_t max_ipl_size = 2 * 1024 * 1024;
|
||||||
@@ -407,6 +409,7 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
|
|||||||
break;
|
break;
|
||||||
case Triforce:
|
case Triforce:
|
||||||
known_ipl = true;
|
known_ipl = true;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
PanicAlertFmtT("The IPL file is not a known good dump. (CRC32: {0:x})", ipl_hash);
|
PanicAlertFmtT("The IPL file is not a known good dump. (CRC32: {0:x})", ipl_hash);
|
||||||
break;
|
break;
|
||||||
@@ -457,6 +460,14 @@ bool CBoot::Load_BS2(Core::System& system, const std::string& boot_rom_filename)
|
|||||||
ppc_state.spr[SPR_DBAT3L] = 0xfff00001;
|
ppc_state.spr[SPR_DBAT3L] = 0xfff00001;
|
||||||
SetupBAT(system, /*is_wii*/ false);
|
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;
|
ppc_state.pc = 0x81200150;
|
||||||
|
|
||||||
system.GetPowerPC().MSRUpdated();
|
system.GetPowerPC().MSRUpdated();
|
||||||
@@ -490,6 +501,12 @@ bool CBoot::BootUp(Core::System& system, const Core::CPUThreadGuard& guard,
|
|||||||
{
|
{
|
||||||
SConfig& config = SConfig::GetInstance();
|
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
|
// PAL Wii uses NTSC framerate and linecount in 60Hz modes
|
||||||
system.GetVideoInterface().Preset(DiscIO::IsNTSC(config.m_region) ||
|
system.GetVideoInterface().Preset(DiscIO::IsNTSC(config.m_region) ||
|
||||||
(system.IsWii() && Config::Get(Config::SYSCONF_PAL60)));
|
(system.IsWii() && Config::Get(Config::SYSCONF_PAL60)));
|
||||||
|
|||||||
@@ -21,6 +21,7 @@
|
|||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
#include "Core/Debugger/BranchWatch.h"
|
#include "Core/Debugger/BranchWatch.h"
|
||||||
#include "Core/HLE/HLE.h"
|
#include "Core/HLE/HLE.h"
|
||||||
|
#include "Core/HW/DVD/AMMediaboard.h"
|
||||||
#include "Core/HW/DVD/DVDInterface.h"
|
#include "Core/HW/DVD/DVDInterface.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
@@ -228,12 +229,20 @@ bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard
|
|||||||
ppc_state.pc = ppc_state.gpr[3];
|
ppc_state.pc = ppc_state.gpr[3];
|
||||||
|
|
||||||
branch_watch.SetRecordingActive(guard, resume_branch_watch);
|
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)
|
if (system.IsTriforce())
|
||||||
{
|
{
|
||||||
auto& memory = system.GetMemory();
|
auto& memory = system.GetMemory();
|
||||||
|
const u32 disc_size = volume.GetDataSize();
|
||||||
|
|
||||||
memory.Memset(0, 0, 12);
|
// Load game into RAM, like on the actual Triforce
|
||||||
|
u8* dimm = AMMediaboard::InitDIMM(disc_size);
|
||||||
|
|
||||||
|
volume.Read(0, disc_size, dimm, DiscIO::PARTITION_NONE);
|
||||||
|
|
||||||
|
// Triforce disc register obfuscation
|
||||||
|
AMMediaboard::InitKeys(memory.Read_U32(0), memory.Read_U32(4), memory.Read_U32(8));
|
||||||
|
AMMediaboard::FirmwareMap(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -181,6 +181,8 @@ add_library(core
|
|||||||
HW/DSPLLE/DSPSymbols.h
|
HW/DSPLLE/DSPSymbols.h
|
||||||
HW/DVD/DVDInterface.cpp
|
HW/DVD/DVDInterface.cpp
|
||||||
HW/DVD/DVDInterface.h
|
HW/DVD/DVDInterface.h
|
||||||
|
HW/DVD/AMMediaboard.cpp
|
||||||
|
HW/DVD/AMMediaboard.h
|
||||||
HW/DVD/DVDMath.cpp
|
HW/DVD/DVDMath.cpp
|
||||||
HW/DVD/DVDMath.h
|
HW/DVD/DVDMath.h
|
||||||
HW/DVD/DVDThread.cpp
|
HW/DVD/DVDThread.cpp
|
||||||
@@ -274,6 +276,8 @@ add_library(core
|
|||||||
HW/SI/SI_DeviceGCSteeringWheel.h
|
HW/SI/SI_DeviceGCSteeringWheel.h
|
||||||
HW/SI/SI_DeviceKeyboard.cpp
|
HW/SI/SI_DeviceKeyboard.cpp
|
||||||
HW/SI/SI_DeviceKeyboard.h
|
HW/SI/SI_DeviceKeyboard.h
|
||||||
|
HW/SI/SI_DeviceAMBaseboard.cpp
|
||||||
|
HW/SI/SI_DeviceAMBaseboard.h
|
||||||
HW/SI/SI_DeviceNull.cpp
|
HW/SI/SI_DeviceNull.cpp
|
||||||
HW/SI/SI_DeviceNull.h
|
HW/SI/SI_DeviceNull.h
|
||||||
HW/SI/SI.cpp
|
HW/SI/SI.cpp
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "Common/Version.h"
|
#include "Common/Version.h"
|
||||||
#include "Core/AchievementManager.h"
|
#include "Core/AchievementManager.h"
|
||||||
#include "Core/Config/DefaultLocale.h"
|
#include "Core/Config/DefaultLocale.h"
|
||||||
|
#include "Core/Core.h"
|
||||||
#include "Core/HW/EXI/EXI.h"
|
#include "Core/HW/EXI/EXI.h"
|
||||||
#include "Core/HW/EXI/EXI_Device.h"
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
#include "Core/HW/GCMemcard/GCMemcard.h"
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
#include "Core/HW/Memmap.h"
|
#include "Core/HW/Memmap.h"
|
||||||
#include "Core/HW/SI/SI_Device.h"
|
#include "Core/HW/SI/SI_Device.h"
|
||||||
#include "Core/PowerPC/PowerPC.h"
|
#include "Core/PowerPC/PowerPC.h"
|
||||||
|
#include "Core/System.h"
|
||||||
#include "Core/USBUtils.h"
|
#include "Core/USBUtils.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "VideoCommon/VideoBackendBase.h"
|
#include "VideoCommon/VideoBackendBase.h"
|
||||||
@@ -672,6 +674,9 @@ const char* GetDirectoryForRegion(DiscIO::Region region, RegionDirectoryStyle st
|
|||||||
ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
|
ASSERT_MSG(BOOT, false, "NTSC-K is not a valid GameCube region");
|
||||||
return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR;
|
return style == RegionDirectoryStyle::Legacy ? JAP_DIR : JPN_DIR;
|
||||||
|
|
||||||
|
case DiscIO::Region::DEV:
|
||||||
|
return DEV_DIR;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
ASSERT_MSG(BOOT, false, "Default case should not be reached");
|
ASSERT_MSG(BOOT, false, "Default case should not be reached");
|
||||||
return EUR_DIR;
|
return EUR_DIR;
|
||||||
@@ -725,6 +730,7 @@ std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::
|
|||||||
constexpr std::string_view us_region = "." USA_DIR;
|
constexpr std::string_view us_region = "." USA_DIR;
|
||||||
constexpr std::string_view jp_region = "." JAP_DIR;
|
constexpr std::string_view jp_region = "." JAP_DIR;
|
||||||
constexpr std::string_view eu_region = "." EUR_DIR;
|
constexpr std::string_view eu_region = "." EUR_DIR;
|
||||||
|
constexpr std::string_view dv_region = "." DEV_DIR;
|
||||||
std::optional<DiscIO::Region> path_region = std::nullopt;
|
std::optional<DiscIO::Region> path_region = std::nullopt;
|
||||||
if (name.ends_with(us_region))
|
if (name.ends_with(us_region))
|
||||||
{
|
{
|
||||||
@@ -741,6 +747,11 @@ std::string GetMemcardPath(std::string configured_filename, ExpansionInterface::
|
|||||||
name = name.substr(0, name.size() - eu_region.size());
|
name = name.substr(0, name.size() - eu_region.size());
|
||||||
path_region = DiscIO::Region::PAL;
|
path_region = DiscIO::Region::PAL;
|
||||||
}
|
}
|
||||||
|
else if (name.ends_with(dv_region))
|
||||||
|
{
|
||||||
|
name = name.substr(0, name.size() - dv_region.size());
|
||||||
|
path_region = DiscIO::Region::DEV;
|
||||||
|
}
|
||||||
|
|
||||||
const DiscIO::Region used_region =
|
const DiscIO::Region used_region =
|
||||||
region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION));
|
region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION));
|
||||||
@@ -784,6 +795,7 @@ std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::
|
|||||||
constexpr std::string_view us_region = "/" USA_DIR;
|
constexpr std::string_view us_region = "/" USA_DIR;
|
||||||
constexpr std::string_view jp_region = "/" JPN_DIR;
|
constexpr std::string_view jp_region = "/" JPN_DIR;
|
||||||
constexpr std::string_view eu_region = "/" EUR_DIR;
|
constexpr std::string_view eu_region = "/" EUR_DIR;
|
||||||
|
constexpr std::string_view dv_region = "/" DEV_DIR;
|
||||||
std::string_view base_path = configured_folder;
|
std::string_view base_path = configured_folder;
|
||||||
std::optional<DiscIO::Region> path_region = std::nullopt;
|
std::optional<DiscIO::Region> path_region = std::nullopt;
|
||||||
if (base_path.ends_with(us_region))
|
if (base_path.ends_with(us_region))
|
||||||
@@ -801,6 +813,11 @@ std::string GetGCIFolderPath(std::string configured_folder, ExpansionInterface::
|
|||||||
base_path = base_path.substr(0, base_path.size() - eu_region.size());
|
base_path = base_path.substr(0, base_path.size() - eu_region.size());
|
||||||
path_region = DiscIO::Region::PAL;
|
path_region = DiscIO::Region::PAL;
|
||||||
}
|
}
|
||||||
|
else if (base_path.ends_with(dv_region))
|
||||||
|
{
|
||||||
|
base_path = base_path.substr(0, base_path.size() - dv_region.size());
|
||||||
|
path_region = DiscIO::Region::DEV;
|
||||||
|
}
|
||||||
|
|
||||||
const DiscIO::Region used_region =
|
const DiscIO::Region used_region =
|
||||||
region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION));
|
region ? *region : (path_region ? *path_region : Config::Get(Config::MAIN_FALLBACK_REGION));
|
||||||
|
|||||||
@@ -142,12 +142,6 @@ const std::string SConfig::GetTitleDescription() const
|
|||||||
return m_title_description;
|
return m_title_description;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string SConfig::GetTriforceID() const
|
|
||||||
{
|
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
|
||||||
return m_triforce_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
u64 SConfig::GetTitleID() const
|
u64 SConfig::GetTitleID() const
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
||||||
@@ -163,7 +157,7 @@ u16 SConfig::GetRevision() const
|
|||||||
void SConfig::ResetRunningGameMetadata()
|
void SConfig::ResetRunningGameMetadata()
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
std::lock_guard<std::recursive_mutex> 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,
|
void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
|
||||||
@@ -172,14 +166,14 @@ void SConfig::SetRunningGameMetadata(const DiscIO::Volume& volume,
|
|||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
||||||
if (partition == volume.GetGamePartition())
|
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.GetTitleID().value_or(0), volume.GetRevision().value_or(0),
|
||||||
volume.GetRegion());
|
volume.GetRegion());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetRunningGameMetadata(volume.GetGameID(partition), volume.GetGameTDBID(partition),
|
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());
|
volume.GetRevision(partition).value_or(0), volume.GetRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -197,28 +191,25 @@ void SConfig::SetRunningGameMetadata(const IOS::ES::TMDReader& tmd, DiscIO::Plat
|
|||||||
!Core::System::GetInstance().GetDVDInterface().UpdateRunningGameMetadata(tmd_title_id))
|
!Core::System::GetInstance().GetDVDInterface().UpdateRunningGameMetadata(tmd_title_id))
|
||||||
{
|
{
|
||||||
// If not launching a disc game, just read everything from the TMD.
|
// If not launching a disc game, just read everything from the TMD.
|
||||||
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), "", tmd_title_id,
|
SetRunningGameMetadata(tmd.GetGameID(), tmd.GetGameTDBID(), tmd_title_id, tmd.GetTitleVersion(),
|
||||||
tmd.GetTitleVersion(), tmd.GetRegion());
|
tmd.GetRegion());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SConfig::SetRunningGameMetadata(const std::string& game_id)
|
void SConfig::SetRunningGameMetadata(const std::string& game_id)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
std::lock_guard<std::recursive_mutex> 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,
|
void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
|
||||||
std::string triforce_id, u64 title_id, u16 revision,
|
u64 title_id, u16 revision, DiscIO::Region region)
|
||||||
DiscIO::Region region)
|
|
||||||
{
|
{
|
||||||
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
std::lock_guard<std::recursive_mutex> lock(m_metadata_lock);
|
||||||
const bool was_changed = m_game_id != game_id || m_gametdb_id != gametdb_id ||
|
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_title_id != title_id || m_revision != revision;
|
||||||
m_revision != revision;
|
|
||||||
m_game_id = game_id;
|
m_game_id = game_id;
|
||||||
m_gametdb_id = gametdb_id;
|
m_gametdb_id = gametdb_id;
|
||||||
m_triforce_id = triforce_id;
|
|
||||||
m_title_id = title_id;
|
m_title_id = title_id;
|
||||||
m_revision = revision;
|
m_revision = revision;
|
||||||
|
|
||||||
@@ -251,7 +242,7 @@ void SConfig::SetRunningGameMetadata(const std::string& game_id, const std::stri
|
|||||||
const Core::TitleDatabase title_database;
|
const Core::TitleDatabase title_database;
|
||||||
auto& system = Core::System::GetInstance();
|
auto& system = Core::System::GetInstance();
|
||||||
const DiscIO::Language language = GetLanguageAdjustedForRegion(system.IsWii(), region);
|
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);
|
m_title_description = title_database.Describe(m_gametdb_id, language);
|
||||||
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
|
NOTICE_LOG_FMT(CORE, "Active title: {}", m_title_description);
|
||||||
Host_TitleChanged();
|
Host_TitleChanged();
|
||||||
@@ -458,6 +449,9 @@ bool SConfig::SetPathsAndGameMetadata(Core::System& system, const BootParameters
|
|||||||
if (m_region == DiscIO::Region::Unknown)
|
if (m_region == DiscIO::Region::Unknown)
|
||||||
m_region = Config::Get(Config::MAIN_FALLBACK_REGION);
|
m_region = Config::Get(Config::MAIN_FALLBACK_REGION);
|
||||||
|
|
||||||
|
if (system.IsTriforce())
|
||||||
|
m_region = DiscIO::Region::DEV;
|
||||||
|
|
||||||
// Set up paths
|
// Set up paths
|
||||||
const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region));
|
const std::string region_dir = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(m_region));
|
||||||
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
m_strSRAM = File::GetUserPath(F_GCSRAM_IDX);
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ struct SConfig
|
|||||||
// files
|
// files
|
||||||
std::string m_strBootROM;
|
std::string m_strBootROM;
|
||||||
std::string m_strSRAM;
|
std::string m_strSRAM;
|
||||||
|
|
||||||
std::string m_debugger_game_id;
|
std::string m_debugger_game_id;
|
||||||
|
|
||||||
// TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped.
|
// TODO: remove this as soon as the ticket view hack in IOS/ES/Views is dropped.
|
||||||
@@ -64,7 +63,6 @@ struct SConfig
|
|||||||
const std::string GetGameTDBID() const;
|
const std::string GetGameTDBID() const;
|
||||||
const std::string GetTitleName() const;
|
const std::string GetTitleName() const;
|
||||||
const std::string GetTitleDescription() const;
|
const std::string GetTitleDescription() const;
|
||||||
std::string GetTriforceID() const;
|
|
||||||
u64 GetTitleID() const;
|
u64 GetTitleID() const;
|
||||||
u16 GetRevision() const;
|
u16 GetRevision() const;
|
||||||
void ResetRunningGameMetadata();
|
void ResetRunningGameMetadata();
|
||||||
@@ -121,15 +119,13 @@ private:
|
|||||||
static void ReloadTextures(Core::System& system);
|
static void ReloadTextures(Core::System& system);
|
||||||
|
|
||||||
void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
|
void SetRunningGameMetadata(const std::string& game_id, const std::string& gametdb_id,
|
||||||
std::string triforce_id, u64 title_id, u16 revision,
|
u64 title_id, u16 revision, DiscIO::Region region);
|
||||||
DiscIO::Region region);
|
|
||||||
|
|
||||||
static SConfig* m_Instance;
|
static SConfig* m_Instance;
|
||||||
mutable std::recursive_mutex m_metadata_lock;
|
mutable std::recursive_mutex m_metadata_lock;
|
||||||
|
|
||||||
std::string m_game_id;
|
std::string m_game_id;
|
||||||
std::string m_gametdb_id;
|
std::string m_gametdb_id;
|
||||||
std::string m_triforce_id;
|
|
||||||
std::string m_title_name;
|
std::string m_title_name;
|
||||||
std::string m_title_description;
|
std::string m_title_description;
|
||||||
u64 m_title_id;
|
u64 m_title_id;
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
#include "Core/HLE/HLE.h"
|
#include "Core/HLE/HLE.h"
|
||||||
#include "Core/HW/CPU.h"
|
#include "Core/HW/CPU.h"
|
||||||
#include "Core/HW/DSP.h"
|
#include "Core/HW/DSP.h"
|
||||||
|
#include "Core/HW/DVD/AMMediaboard.h"
|
||||||
#include "Core/HW/EXI/EXI.h"
|
#include "Core/HW/EXI/EXI.h"
|
||||||
#include "Core/HW/GBAPad.h"
|
#include "Core/HW/GBAPad.h"
|
||||||
#include "Core/HW/GCKeyboard.h"
|
#include "Core/HW/GCKeyboard.h"
|
||||||
@@ -288,6 +289,13 @@ void Stop(Core::System& system) // - Hammertime!
|
|||||||
// Stop the CPU
|
// Stop the CPU
|
||||||
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
|
INFO_LOG_FMT(CONSOLE, "{}", StopMessage(true, "Stop CPU"));
|
||||||
system.GetCPU().Stop();
|
system.GetCPU().Stop();
|
||||||
|
|
||||||
|
const ExpansionInterface::EXIDeviceType type = Config::Get(Config::MAIN_SERIAL_PORT_1);
|
||||||
|
|
||||||
|
if ((type == ExpansionInterface::EXIDeviceType::Baseboard))
|
||||||
|
{
|
||||||
|
AMMediaboard::Shutdown();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DeclareAsCPUThread()
|
void DeclareAsCPUThread()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace Core
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace File
|
||||||
|
{
|
||||||
|
class IOFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GameType
|
||||||
|
{
|
||||||
|
FZeroAX = 1,
|
||||||
|
FZeroAXMonster,
|
||||||
|
MarioKartGP,
|
||||||
|
MarioKartGP2,
|
||||||
|
VirtuaStriker3,
|
||||||
|
VirtuaStriker4,
|
||||||
|
VirtuaStriker4_2006,
|
||||||
|
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 AMMBDICommand : u16
|
||||||
|
{
|
||||||
|
Inquiry = 0x12,
|
||||||
|
Read = 0xA8,
|
||||||
|
Write = 0xAA,
|
||||||
|
Execute = 0xAB,
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class AMMBCommand : u16
|
||||||
|
{
|
||||||
|
Unknown_000 = 0x000,
|
||||||
|
GetDIMMSize = 0x001,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
// All.Net used by Virtua Striker 4 2006 (Japan)
|
||||||
|
AllNetInit = 0x700,
|
||||||
|
Unknown_701 = 0x701,
|
||||||
|
Unknown_702 = 0x702,
|
||||||
|
Unknown_703 = 0x703,
|
||||||
|
Unknown_704 = 0x704,
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
|
||||||
|
AllNetSettings = 0x1EFF8000,
|
||||||
|
|
||||||
|
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,
|
||||||
|
|
||||||
|
AllNetBuffer = 0x89010000,
|
||||||
|
|
||||||
|
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 FirmwareMap(bool on);
|
||||||
|
u8* InitDIMM(u32 size);
|
||||||
|
void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC);
|
||||||
|
u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32* DIIMMBUF, u32 Address, u32 Length);
|
||||||
|
u32 GetGameType();
|
||||||
|
u32 GetMediaType();
|
||||||
|
bool GetTestMenu();
|
||||||
|
void Shutdown();
|
||||||
|
}; // namespace AMMediaboard
|
||||||
@@ -26,6 +26,7 @@
|
|||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/DolphinAnalytics.h"
|
#include "Core/DolphinAnalytics.h"
|
||||||
#include "Core/HW/AudioInterface.h"
|
#include "Core/HW/AudioInterface.h"
|
||||||
|
#include "Core/HW/DVD/AMMediaboard.h"
|
||||||
#include "Core/HW/DVD/DVDMath.h"
|
#include "Core/HW/DVD/DVDMath.h"
|
||||||
#include "Core/HW/DVD/DVDThread.h"
|
#include "Core/HW/DVD/DVDThread.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
@@ -281,6 +282,16 @@ void DVDInterface::Init()
|
|||||||
|
|
||||||
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
u64 userdata = PackFinishExecutingCommandUserdata(ReplyType::DTK, DIInterruptType::TCINT);
|
||||||
core_timing.ScheduleEvent(0, m_finish_executing_command, userdata);
|
core_timing.ScheduleEvent(0, m_finish_executing_command, userdata);
|
||||||
|
|
||||||
|
if (m_system.IsTriforce())
|
||||||
|
{
|
||||||
|
AMMediaboard::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) */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
|
// Resets state on the MN102 chip in the drive itself, but not the DI registers exposed on the
|
||||||
@@ -697,12 +708,17 @@ bool DVDInterface::CheckReadPreconditions()
|
|||||||
SetDriveError(DriveError::MotorStopped);
|
SetDriveError(DriveError::MotorStopped);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SegaBoot doesn't read the Disc ID
|
||||||
|
if (!m_system.IsTriforce())
|
||||||
|
{
|
||||||
if (m_drive_state == DriveState::DiscIdNotRead)
|
if (m_drive_state == DriveState::DiscIdNotRead)
|
||||||
{
|
{
|
||||||
ERROR_LOG_FMT(DVDINTERFACE, "Disc id not read.");
|
ERROR_LOG_FMT(DVDINTERFACE, "Disc id not read.");
|
||||||
SetDriveError(DriveError::NoDiscID);
|
SetDriveError(DriveError::NoDiscID);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,12 +769,19 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
|
|||||||
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
DIInterruptType interrupt_type = DIInterruptType::TCINT;
|
||||||
bool command_handled_by_thread = false;
|
bool command_handled_by_thread = false;
|
||||||
|
|
||||||
// Swaps endian of Triforce DI commands, and zeroes out random bytes to prevent unknown read
|
if (m_system.IsTriforce())
|
||||||
// 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
|
if (!AMMediaboard::ExecuteCommand(m_DICMDBUF, &m_DIIMMBUF, m_DIMAR, m_DILENGTH))
|
||||||
|
{
|
||||||
|
// Transfer is done
|
||||||
|
m_DICR.TSTART = 0;
|
||||||
|
m_DIMAR += m_DILENGTH;
|
||||||
|
m_DILENGTH = 0;
|
||||||
|
GenerateDIInterrupt(DIInterruptType::TCINT);
|
||||||
|
m_error_code = DriveError::None;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Normal read command pass on to normal handling
|
||||||
m_DICMDBUF[0] <<= 24;
|
m_DICMDBUF[0] <<= 24;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -66,18 +66,6 @@ void ExpansionInterfaceManager::AddMemoryCard(Slot slot)
|
|||||||
m_channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(slot));
|
m_channels[SlotToEXIChannel(slot)]->AddDevice(memorycard_device, SlotToEXIDevice(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));
|
|
||||||
}
|
|
||||||
|
|
||||||
u8 SlotToEXIChannel(Slot slot)
|
u8 SlotToEXIChannel(Slot slot)
|
||||||
{
|
{
|
||||||
switch (slot)
|
switch (slot)
|
||||||
@@ -157,9 +145,15 @@ void ExpansionInterfaceManager::Init(const Sram* override_sram)
|
|||||||
AddMemoryCard(slot);
|
AddMemoryCard(slot);
|
||||||
|
|
||||||
m_channels[0]->AddDevice(EXIDeviceType::MaskROM, 1);
|
m_channels[0]->AddDevice(EXIDeviceType::MaskROM, 1);
|
||||||
AddSP1Device();
|
|
||||||
m_channels[SlotToEXIChannel(Slot::SP1)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_1),
|
// Automatically connect the Triforce Baseboard in Triforce mode
|
||||||
SlotToEXIDevice(Slot::SP1));
|
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),
|
m_channels[SlotToEXIChannel(Slot::SP2)]->AddDevice(Config::Get(Config::MAIN_SERIAL_PORT_2),
|
||||||
SlotToEXIDevice(Slot::SP2));
|
SlotToEXIDevice(Slot::SP2));
|
||||||
|
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void AddMemoryCard(Slot slot);
|
void AddMemoryCard(Slot slot);
|
||||||
void AddSP1Device();
|
|
||||||
|
|
||||||
static void ChangeDeviceCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
static void ChangeDeviceCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
||||||
static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
static void UpdateInterruptsCallback(Core::System& system, u64 userdata, s64 cycles_late);
|
||||||
|
|||||||
@@ -1,20 +1,137 @@
|
|||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#include "Core/HW/EXI/EXI_DeviceBaseboard.h"
|
#include "Core/HW/EXI/EXI_DeviceBaseboard.h"
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
|
#include <optional>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include <fmt/format.h>
|
||||||
|
|
||||||
|
#include "Common/Buffer.h"
|
||||||
#include "Common/CommonTypes.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/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"
|
||||||
|
|
||||||
|
static bool s_interrupt_set = false;
|
||||||
|
static u32 s_irq_timer = 0;
|
||||||
|
static u32 s_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
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system)
|
|
||||||
|
void GenerateInterrupt(int flag)
|
||||||
{
|
{
|
||||||
|
auto& system = Core::System::GetInstance();
|
||||||
|
|
||||||
|
s_interrupt_set = true;
|
||||||
|
s_irq_timer = 0;
|
||||||
|
s_irq_status = flag;
|
||||||
|
|
||||||
|
system.GetExpansionInterface().UpdateInterrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_position(0)
|
||||||
|
{
|
||||||
|
std::string backup_filename(fmt::format("{}tribackup_{}.bin", File::GetUserPath(D_TRIUSER_IDX),
|
||||||
|
SConfig::GetInstance().GetGameID()));
|
||||||
|
|
||||||
|
m_backup = File::IOFile(backup_filename, "rb+");
|
||||||
|
if (!m_backup.IsOpen())
|
||||||
|
{
|
||||||
|
m_backup = 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<u32>(std::time(nullptr)));
|
||||||
|
|
||||||
|
backup_filename = fmt::format("{}tribackup_tmp_{}{}.bin", File::GetUserPath(D_TRIUSER_IDX),
|
||||||
|
rand(), SConfig::GetInstance().GetGameID());
|
||||||
|
|
||||||
|
m_backup = 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())
|
||||||
|
{
|
||||||
|
Common::UniqueBuffer<u8> data(m_backup.GetSize());
|
||||||
|
m_backup.ReadBytes(data.data(), data.size());
|
||||||
|
|
||||||
|
// Set FIRM version
|
||||||
|
reinterpret_cast<u16&>(data[0x12]) = 0x1703;
|
||||||
|
reinterpret_cast<u16&>(data[0x212]) = 0x1703;
|
||||||
|
|
||||||
|
// Update checksum
|
||||||
|
reinterpret_cast<u16&>(data[0x0A]) = Common::swap16(CheckSum(&data[0xC], 0x1F4));
|
||||||
|
reinterpret_cast<u16&>(data[0x20A]) = Common::swap16(CheckSum(&data[0x20C], 0x1F4));
|
||||||
|
|
||||||
|
m_backup.Seek(0, File::SeekOrigin::Begin);
|
||||||
|
m_backup.WriteBytes(data.data(), data.size());
|
||||||
|
m_backup.Flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CEXIBaseboard::~CEXIBaseboard()
|
||||||
|
{
|
||||||
|
m_backup.Close();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIBaseboard::SetCS(int cs)
|
void CEXIBaseboard::SetCS(int cs)
|
||||||
{
|
{
|
||||||
|
DEBUG_LOG_FMT(SP1, "AM-BB: ChipSelect={}", cs);
|
||||||
if (cs)
|
if (cs)
|
||||||
m_position = 0;
|
m_position = 0;
|
||||||
}
|
}
|
||||||
@@ -24,33 +141,197 @@ bool CEXIBaseboard::IsPresent() const
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIBaseboard::TransferByte(u8& byte)
|
bool CEXIBaseboard::IsInterruptSet()
|
||||||
{
|
{
|
||||||
if (m_position == 0)
|
if (s_interrupt_set)
|
||||||
{
|
{
|
||||||
m_command = byte;
|
DEBUG_LOG_FMT(SP1, "AM-BB: IRQ");
|
||||||
|
if (++s_irq_timer > 12)
|
||||||
|
s_interrupt_set = false;
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
switch (m_command)
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIBaseboard::DMAWrite(u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
case 0x00:
|
const auto& system = Core::System::GetInstance();
|
||||||
|
const auto& memory = system.GetMemory();
|
||||||
|
|
||||||
|
NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Write: {:08x} {:x}", addr, size);
|
||||||
|
|
||||||
|
m_backup.Seek(m_backup_offset, File::SeekOrigin::Begin);
|
||||||
|
|
||||||
|
m_backup.WriteBytes(memory.GetSpanForAddress(addr).data(), size);
|
||||||
|
|
||||||
|
m_backup.Flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CEXIBaseboard::DMARead(u32 addr, u32 size)
|
||||||
{
|
{
|
||||||
static constexpr std::array<u8, 4> ID = {0x06, 0x04, 0x10, 0x00};
|
const auto& system = Core::System::GetInstance();
|
||||||
byte = ID[(m_position - 2) & 3];
|
const auto& memory = system.GetMemory();
|
||||||
|
|
||||||
|
NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Read: {:08x} {:x}", addr, size);
|
||||||
|
|
||||||
|
m_backup.Seek(m_backup_offset, File::SeekOrigin::Begin);
|
||||||
|
|
||||||
|
m_backup.Flush();
|
||||||
|
|
||||||
|
m_backup.ReadBytes(memory.GetSpanForAddress(addr).data(), 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)
|
||||||
|
{
|
||||||
|
if (checksum & bit)
|
||||||
|
checksum ^= check;
|
||||||
|
check >>= 1;
|
||||||
|
bit >>= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
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_backup_offset = (m_command[1] << 8) | m_command[2];
|
||||||
|
DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupOffsetSet:{:04x}", m_backup_offset);
|
||||||
|
m_backup.Seek(m_backup_offset, File::SeekOrigin::Begin);
|
||||||
|
byte = 0x01;
|
||||||
|
break;
|
||||||
|
case BackupWrite:
|
||||||
|
DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupWrite:{:04x}-{:02x}", m_backup_offset,
|
||||||
|
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_backup_offset);
|
||||||
|
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, s_irq_status);
|
||||||
|
byte = 0x04;
|
||||||
|
break;
|
||||||
|
case WriteISR:
|
||||||
|
NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: WriteISR :{:02x} {:02x}", m_command[1], m_command[2]);
|
||||||
|
s_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))
|
||||||
|
{
|
||||||
|
s_interrupt_set = true;
|
||||||
|
s_irq_timer = 0;
|
||||||
|
s_irq_status = 0x02;
|
||||||
|
}
|
||||||
|
if ((m_command[1] == 2) && (m_command[2] == 1))
|
||||||
|
{
|
||||||
|
s_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;
|
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 = s_irq_status;
|
||||||
|
s_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:
|
default:
|
||||||
ERROR_LOG_FMT(EXPANSIONINTERFACE, "EXI BASEBOARD: Unhandled command {:#x} {:#x}", m_command,
|
ERROR_LOG_FMT(SP1, "Unknown AM-BB command: {:02x}", m_command[0]);
|
||||||
m_position);
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
byte = 0xFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG_FMT(SP1, "AM-BB < {:02x}", byte);
|
||||||
m_position++;
|
m_position++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIBaseboard::DoState(PointerWrap& p)
|
void CEXIBaseboard::DoState(PointerWrap& p)
|
||||||
{
|
{
|
||||||
p.Do(m_position);
|
p.Do(m_position);
|
||||||
|
p.Do(s_interrupt_set);
|
||||||
p.Do(m_command);
|
p.Do(m_command);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
|||||||
@@ -1,27 +1,59 @@
|
|||||||
// Copyright 2013 Dolphin Emulator Project
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/IOFile.h"
|
||||||
#include "Core/HW/EXI/EXI_Device.h"
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
|
|
||||||
class PointerWrap;
|
namespace Core
|
||||||
|
{
|
||||||
|
class System;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
|
void GenerateInterrupt(int flag);
|
||||||
|
|
||||||
class CEXIBaseboard : public IEXIDevice
|
class CEXIBaseboard : public IEXIDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CEXIBaseboard(Core::System& system);
|
explicit CEXIBaseboard(Core::System& system);
|
||||||
|
virtual ~CEXIBaseboard();
|
||||||
|
|
||||||
void SetCS(int cs) override;
|
void SetCS(int cs) override;
|
||||||
|
bool IsInterruptSet() override;
|
||||||
bool IsPresent() const override;
|
bool IsPresent() const override;
|
||||||
void DoState(PointerWrap& p) override;
|
void DoState(PointerWrap& p) override;
|
||||||
|
void DMAWrite(u32 addr, u32 size) override;
|
||||||
|
void DMARead(u32 addr, u32 size) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// STATE_TO_SAVE
|
enum Command
|
||||||
u32 m_position = 0;
|
{
|
||||||
u32 m_command = 0;
|
BackupOffsetSet = 0x01,
|
||||||
|
BackupWrite = 0x02,
|
||||||
|
BackupRead = 0x03,
|
||||||
|
|
||||||
void TransferByte(u8& byte) override;
|
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_backup_offset;
|
||||||
|
File::IOFile m_backup;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void TransferByte(u8& _uByte) override;
|
||||||
};
|
};
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ static const u16 trigger_bitmasks[] = {
|
|||||||
|
|
||||||
static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT,
|
static const u16 dpad_bitmasks[] = {PAD_BUTTON_UP, PAD_BUTTON_DOWN, PAD_BUTTON_LEFT,
|
||||||
PAD_BUTTON_RIGHT};
|
PAD_BUTTON_RIGHT};
|
||||||
|
static const u8 triforce_bitmask[] = {SWITCH_TEST, SWITCH_SERVICE, SWITCH_COIN};
|
||||||
|
|
||||||
GCPad::GCPad(const unsigned int index) : m_index(index)
|
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);
|
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
|
// Microphone
|
||||||
groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP));
|
groups.emplace_back(m_mic = new ControllerEmu::Buttons(MIC_GROUP));
|
||||||
m_mic->AddInput(Translatability::Translate, _trans("Button"));
|
m_mic->AddInput(Translatability::Translate, _trans("Button"));
|
||||||
@@ -119,6 +127,8 @@ ControllerEmu::ControlGroup* GCPad::GetGroup(PadGroup group)
|
|||||||
return m_mic;
|
return m_mic;
|
||||||
case PadGroup::Options:
|
case PadGroup::Options:
|
||||||
return m_options;
|
return m_options;
|
||||||
|
case PadGroup::Triforce:
|
||||||
|
return m_triforce;
|
||||||
default:
|
default:
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
@@ -150,6 +160,9 @@ GCPadStatus GCPad::GetInput() const
|
|||||||
// dpad
|
// dpad
|
||||||
m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function);
|
m_dpad->GetState(&pad.button, dpad_bitmasks, m_input_override_function);
|
||||||
|
|
||||||
|
// triforce
|
||||||
|
m_triforce->GetState(&pad.switches, triforce_bitmask, m_input_override_function);
|
||||||
|
|
||||||
// sticks
|
// sticks
|
||||||
const auto main_stick_state = m_main_stick->GetState(m_input_override_function);
|
const auto main_stick_state = m_main_stick->GetState(m_input_override_function);
|
||||||
pad.stickX = MapFloat<u8>(main_stick_state.x, GCPadStatus::MAIN_STICK_CENTER_X, 1);
|
pad.stickX = MapFloat<u8>(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(2, "`F`"); // Left
|
||||||
m_dpad->SetControlExpression(3, "`H`"); // Right
|
m_dpad->SetControlExpression(3, "`H`"); // Right
|
||||||
|
|
||||||
|
// Triforce
|
||||||
|
m_triforce->SetControlExpression(0, "`1`"); // Test
|
||||||
|
m_triforce->SetControlExpression(1, "`2`"); // Service
|
||||||
|
m_triforce->SetControlExpression(2, "`3`"); // Coin
|
||||||
|
|
||||||
// C Stick
|
// C Stick
|
||||||
m_c_stick->SetControlExpression(0, "`I`"); // Up
|
m_c_stick->SetControlExpression(0, "`I`"); // Up
|
||||||
m_c_stick->SetControlExpression(1, "`K`"); // Down
|
m_c_stick->SetControlExpression(1, "`K`"); // Down
|
||||||
|
|||||||
@@ -29,7 +29,8 @@ enum class PadGroup
|
|||||||
Triggers,
|
Triggers,
|
||||||
Rumble,
|
Rumble,
|
||||||
Mic,
|
Mic,
|
||||||
Options
|
Options,
|
||||||
|
Triforce,
|
||||||
};
|
};
|
||||||
|
|
||||||
class GCPad : public ControllerEmu::EmulatedController
|
class GCPad : public ControllerEmu::EmulatedController
|
||||||
@@ -61,6 +62,7 @@ public:
|
|||||||
static constexpr const char* RUMBLE_GROUP = _trans("Rumble");
|
static constexpr const char* RUMBLE_GROUP = _trans("Rumble");
|
||||||
static constexpr const char* MIC_GROUP = _trans("Microphone");
|
static constexpr const char* MIC_GROUP = _trans("Microphone");
|
||||||
static constexpr const char* OPTIONS_GROUP = _trans("Options");
|
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* A_BUTTON = "A";
|
||||||
static constexpr const char* B_BUTTON = "B";
|
static constexpr const char* B_BUTTON = "B";
|
||||||
@@ -69,6 +71,11 @@ public:
|
|||||||
static constexpr const char* Z_BUTTON = "Z";
|
static constexpr const char* Z_BUTTON = "Z";
|
||||||
static constexpr const char* START_BUTTON = "Start";
|
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)
|
// i18n: The left trigger button (labeled L on real controllers)
|
||||||
static constexpr const char* L_DIGITAL = _trans("L");
|
static constexpr const char* L_DIGITAL = _trans("L");
|
||||||
// i18n: The right trigger button (labeled R on real controllers)
|
// i18n: The right trigger button (labeled R on real controllers)
|
||||||
@@ -87,6 +94,7 @@ private:
|
|||||||
ControllerEmu::ControlGroup* m_rumble;
|
ControllerEmu::ControlGroup* m_rumble;
|
||||||
ControllerEmu::Buttons* m_mic;
|
ControllerEmu::Buttons* m_mic;
|
||||||
ControllerEmu::ControlGroup* m_options;
|
ControllerEmu::ControlGroup* m_options;
|
||||||
|
ControllerEmu::Buttons* m_triforce;
|
||||||
|
|
||||||
ControllerEmu::SettingValue<bool> m_always_connected_setting;
|
ControllerEmu::SettingValue<bool> m_always_connected_setting;
|
||||||
|
|
||||||
|
|||||||
@@ -123,7 +123,6 @@ public:
|
|||||||
// If the specified range is within a single valid memory region, returns a pointer to the start
|
// 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.
|
// of the corresponding range in host memory. Otherwise, returns nullptr.
|
||||||
u8* GetPointerForRange(u32 address, size_t size) const;
|
u8* GetPointerForRange(u32 address, size_t size) const;
|
||||||
|
|
||||||
void CopyFromEmu(void* data, u32 address, size_t size) const;
|
void CopyFromEmu(void* data, u32 address, size_t size) const;
|
||||||
void CopyToEmu(u32 address, const void* data, size_t size);
|
void CopyToEmu(u32 address, const void* data, size_t size);
|
||||||
void Memset(u32 address, u8 value, size_t size);
|
void Memset(u32 address, u8 value, size_t size);
|
||||||
|
|||||||
@@ -56,6 +56,7 @@ public:
|
|||||||
void RemoveEvent(int device_number);
|
void RemoveEvent(int device_number);
|
||||||
|
|
||||||
void UpdateDevices();
|
void UpdateDevices();
|
||||||
|
u32 GetInLength(void) const { return m_com_csr.INLNGTH; }
|
||||||
|
|
||||||
void RemoveDevice(int device_number);
|
void RemoveDevice(int device_number);
|
||||||
void AddDevice(SIDevices device, int device_number);
|
void AddDevice(SIDevices device, int device_number);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@
|
|||||||
#ifdef HAS_LIBMGBA
|
#ifdef HAS_LIBMGBA
|
||||||
#include "Core/HW/SI/SI_DeviceGBAEmu.h"
|
#include "Core/HW/SI/SI_DeviceGBAEmu.h"
|
||||||
#endif
|
#endif
|
||||||
|
#include "Core/HW/SI/SI_DeviceAMBaseboard.h"
|
||||||
#include "Core/HW/SI/SI_DeviceGCAdapter.h"
|
#include "Core/HW/SI/SI_DeviceGCAdapter.h"
|
||||||
#include "Core/HW/SI/SI_DeviceGCController.h"
|
#include "Core/HW/SI/SI_DeviceGCController.h"
|
||||||
#include "Core/HW/SI/SI_DeviceGCSteeringWheel.h"
|
#include "Core/HW/SI/SI_DeviceGCSteeringWheel.h"
|
||||||
@@ -149,6 +150,7 @@ bool SIDevice_IsGCController(SIDevices type)
|
|||||||
case SIDEVICE_GC_TARUKONGA:
|
case SIDEVICE_GC_TARUKONGA:
|
||||||
case SIDEVICE_DANCEMAT:
|
case SIDEVICE_DANCEMAT:
|
||||||
case SIDEVICE_GC_STEERING:
|
case SIDEVICE_GC_STEERING:
|
||||||
|
case SIDEVICE_AM_BASEBOARD:
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
@@ -191,6 +193,8 @@ std::unique_ptr<ISIDevice> SIDevice_Create(Core::System& system, const SIDevices
|
|||||||
return std::make_unique<CSIDevice_Keyboard>(system, device, port_number);
|
return std::make_unique<CSIDevice_Keyboard>(system, device, port_number);
|
||||||
|
|
||||||
case SIDEVICE_AM_BASEBOARD:
|
case SIDEVICE_AM_BASEBOARD:
|
||||||
|
return std::make_unique<CSIDevice_AMBaseboard>(system, device, port_number);
|
||||||
|
|
||||||
case SIDEVICE_NONE:
|
case SIDEVICE_NONE:
|
||||||
default:
|
default:
|
||||||
return std::make_unique<CSIDevice_Null>(system, device, port_number);
|
return std::make_unique<CSIDevice_Null>(system, device, port_number);
|
||||||
|
|||||||
@@ -97,8 +97,6 @@ enum SIDevices : int
|
|||||||
SIDEVICE_GC_STEERING,
|
SIDEVICE_GC_STEERING,
|
||||||
SIDEVICE_DANCEMAT,
|
SIDEVICE_DANCEMAT,
|
||||||
SIDEVICE_GC_TARUKONGA,
|
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_AM_BASEBOARD,
|
||||||
SIDEVICE_WIIU_ADAPTER,
|
SIDEVICE_WIIU_ADAPTER,
|
||||||
SIDEVICE_GC_GBA_EMULATED,
|
SIDEVICE_GC_GBA_EMULATED,
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,309 @@
|
|||||||
|
// Copyright 2025 Dolphin Emulator Project
|
||||||
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "Core/HW/GCPad.h"
|
||||||
|
#include "Core/HW/SI/SI_Device.h"
|
||||||
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|
||||||
|
namespace Movie
|
||||||
|
{
|
||||||
|
class MovieManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace SerialInterface
|
||||||
|
{
|
||||||
|
|
||||||
|
// "JAMMA Video Standard" I/O
|
||||||
|
class JVSIOMessage
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
u32 m_ptr;
|
||||||
|
u32 m_last_start;
|
||||||
|
u32 m_csum;
|
||||||
|
u8 m_msg[0x80];
|
||||||
|
|
||||||
|
JVSIOMessage();
|
||||||
|
void Start(int node);
|
||||||
|
void AddData(const u8* dst, std::size_t len, int sync);
|
||||||
|
void AddData(const void* data, std::size_t len);
|
||||||
|
void AddData(const char* data);
|
||||||
|
void AddData(u32 n);
|
||||||
|
void End();
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 ICCARDStatus
|
||||||
|
{
|
||||||
|
Okay = 0,
|
||||||
|
NoCard = 0x8000,
|
||||||
|
Unknown = 0x800E,
|
||||||
|
BadCard = 0xFFFF,
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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;
|
||||||
|
bool 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;
|
||||||
|
bool m_card_shutter;
|
||||||
|
u32 m_card_state_call_count;
|
||||||
|
u8 m_card_offset;
|
||||||
|
|
||||||
|
u32 m_wheel_init;
|
||||||
|
|
||||||
|
u32 m_motor_init;
|
||||||
|
u8 m_motor_reply[64];
|
||||||
|
s16 m_motor_force_y;
|
||||||
|
|
||||||
|
// F-Zero AX (DX)
|
||||||
|
bool m_fzdx_seatbelt;
|
||||||
|
bool m_fzdx_motion_stop;
|
||||||
|
bool m_fzdx_sensor_right;
|
||||||
|
bool m_fzdx_sensor_left;
|
||||||
|
u8 m_rx_reply;
|
||||||
|
|
||||||
|
// F-Zero AX (CyCraft)
|
||||||
|
bool m_fzcc_seatbelt;
|
||||||
|
bool m_fzcc_sensor;
|
||||||
|
bool m_fzcc_emergency;
|
||||||
|
bool m_fzcc_service;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
|
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
|
||||||
@@ -3,8 +3,11 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <SFML/Network.hpp>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
|
||||||
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/Flag.h"
|
||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/HW/SI/SI_Device.h"
|
#include "Core/HW/SI/SI_Device.h"
|
||||||
#include "InputCommon/GCPadStatus.h"
|
#include "InputCommon/GCPadStatus.h"
|
||||||
|
|||||||
@@ -163,7 +163,9 @@ void VideoInterfaceManager::Preset(bool _bNTSC)
|
|||||||
|
|
||||||
// Say component cable is plugged
|
// Say component cable is plugged
|
||||||
m_dtv_status.component_plugged = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN);
|
m_dtv_status.component_plugged = Config::Get(Config::SYSCONF_PROGRESSIVE_SCAN);
|
||||||
m_dtv_status.ntsc_j = region == DiscIO::Region::NTSC_J;
|
|
||||||
|
// Triforce IPL requires the DTV NTSC-J flag to be set.
|
||||||
|
m_dtv_status.ntsc_j = m_system.IsTriforce() || region == DiscIO::Region::NTSC_J;
|
||||||
|
|
||||||
m_fb_width.Hex = 0;
|
m_fb_width.Hex = 0;
|
||||||
m_border_hblank.Hex = 0;
|
m_border_hblank.Hex = 0;
|
||||||
|
|||||||
@@ -52,6 +52,7 @@
|
|||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/HW/SI/SI.h"
|
#include "Core/HW/SI/SI.h"
|
||||||
#include "Core/HW/SI/SI_Device.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/SI/SI_DeviceGCController.h"
|
||||||
#include "Core/HW/Sram.h"
|
#include "Core/HW/Sram.h"
|
||||||
#include "Core/HW/WiiSave.h"
|
#include "Core/HW/WiiSave.h"
|
||||||
@@ -1883,6 +1884,8 @@ void NetPlayClient::UpdateDevices()
|
|||||||
auto& si = Core::System::GetInstance().GetSerialInterface();
|
auto& si = Core::System::GetInstance().GetSerialInterface();
|
||||||
for (auto player_id : m_pad_map)
|
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)
|
if (m_gba_config[pad].enabled && player_id > 0)
|
||||||
{
|
{
|
||||||
si.ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad);
|
si.ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad);
|
||||||
@@ -1890,8 +1893,6 @@ void NetPlayClient::UpdateDevices()
|
|||||||
else if (player_id == m_local_player->pid)
|
else if (player_id == m_local_player->pid)
|
||||||
{
|
{
|
||||||
// Use local controller types for local controllers if they are compatible
|
// 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))
|
if (SerialInterface::SIDevice_IsGCController(si_device))
|
||||||
{
|
{
|
||||||
si.ChangeDevice(si_device, pad);
|
si.ChangeDevice(si_device, pad);
|
||||||
@@ -1909,6 +1910,7 @@ void NetPlayClient::UpdateDevices()
|
|||||||
}
|
}
|
||||||
else if (player_id > 0)
|
else if (player_id > 0)
|
||||||
{
|
{
|
||||||
|
if (si_device != SerialInterface::SIDEVICE_AM_BASEBOARD)
|
||||||
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
|
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -2772,6 +2774,16 @@ bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int pad_num, GCPa
|
|||||||
return false;
|
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<NetPlayClient::WiimoteDataBatchEntry>& entries)
|
bool NetPlay::NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry>& entries)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(crit_netplay_client);
|
std::lock_guard lk(crit_netplay_client);
|
||||||
@@ -2835,12 +2847,21 @@ u64 ExpansionInterface::CEXIIPL::NetPlay_GetEmulatedTime()
|
|||||||
|
|
||||||
// called from ---CPU--- thread
|
// called from ---CPU--- thread
|
||||||
// return the local pad num that should rumble given a ingame pad num
|
// return the local pad num that should rumble given a ingame pad num
|
||||||
int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int numPAD)
|
int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int pad_num)
|
||||||
{
|
{
|
||||||
std::lock_guard lk(NetPlay::crit_netplay_client);
|
std::lock_guard lk(NetPlay::crit_netplay_client);
|
||||||
|
|
||||||
if (NetPlay::netplay_client)
|
if (NetPlay::netplay_client)
|
||||||
return NetPlay::netplay_client->InGamePadToLocalPad(numPAD);
|
return NetPlay::netplay_client->InGamePadToLocalPad(pad_num);
|
||||||
|
|
||||||
return numPAD;
|
return pad_num;
|
||||||
|
}
|
||||||
|
int SerialInterface::CSIDevice_AMBaseboard::NetPlay_InGamePadToLocalPad(int pad_num)
|
||||||
|
{
|
||||||
|
std::lock_guard lk(NetPlay::crit_netplay_client);
|
||||||
|
|
||||||
|
if (NetPlay::netplay_client)
|
||||||
|
return NetPlay::netplay_client->InGamePadToLocalPad(pad_num);
|
||||||
|
|
||||||
|
return pad_num;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ struct CompressAndDumpStateArgs
|
|||||||
static Common::WorkQueueThreadSP<CompressAndDumpStateArgs> s_compress_and_dump_thread;
|
static Common::WorkQueueThreadSP<CompressAndDumpStateArgs> s_compress_and_dump_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
constexpr u32 STATE_VERSION = 176; // Last changed in PR 13768
|
constexpr u32 STATE_VERSION = 177; // Last changed in PR 13844
|
||||||
|
|
||||||
// Increase this if the StateExtendedHeader definition changes
|
// Increase this if the StateExtendedHeader definition changes
|
||||||
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
constexpr u32 EXTENDED_HEADER_VERSION = 1; // Last changed in PR 12217
|
||||||
|
|||||||
@@ -91,32 +91,8 @@ TitleDatabase::TitleDatabase()
|
|||||||
TitleDatabase::~TitleDatabase() = default;
|
TitleDatabase::~TitleDatabase() = default;
|
||||||
|
|
||||||
const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id,
|
const std::string& TitleDatabase::GetTitleName(const std::string& gametdb_id,
|
||||||
const std::string& triforce_id,
|
|
||||||
DiscIO::Language language) const
|
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);
|
auto it = m_user_title_map.find(gametdb_id);
|
||||||
if (it != m_user_title_map.end())
|
if (it != m_user_title_map.end())
|
||||||
return it->second;
|
return it->second;
|
||||||
@@ -149,12 +125,12 @@ const std::string& TitleDatabase::GetChannelName(u64 title_id, DiscIO::Language
|
|||||||
const std::string id{
|
const std::string id{
|
||||||
{static_cast<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
|
{static_cast<char>((title_id >> 24) & 0xff), static_cast<char>((title_id >> 16) & 0xff),
|
||||||
static_cast<char>((title_id >> 8) & 0xff), static_cast<char>(title_id & 0xff)}};
|
static_cast<char>((title_id >> 8) & 0xff), static_cast<char>(title_id & 0xff)}};
|
||||||
return GetTitleName(id, "", language);
|
return GetTitleName(id, language);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string TitleDatabase::Describe(const std::string& gametdb_id, DiscIO::Language language) const
|
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())
|
if (title_name.empty())
|
||||||
return gametdb_id;
|
return gametdb_id;
|
||||||
return fmt::format("{} ({})", title_name, gametdb_id);
|
return fmt::format("{} ({})", title_name, gametdb_id);
|
||||||
|
|||||||
@@ -25,8 +25,7 @@ public:
|
|||||||
|
|
||||||
// Get a user friendly title name for a GameTDB ID.
|
// Get a user friendly title name for a GameTDB ID.
|
||||||
// This falls back to returning an empty string if none could be found.
|
// 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,
|
const std::string& GetTitleName(const std::string& gametdb_id, DiscIO::Language language) const;
|
||||||
DiscIO::Language language) const;
|
|
||||||
|
|
||||||
// Same as above, but takes a title ID instead of a GameTDB ID, and only works for channels.
|
// 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;
|
const std::string& GetChannelName(u64 title_id, DiscIO::Language language) const;
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ enum class Platform
|
|||||||
WiiDisc,
|
WiiDisc,
|
||||||
WiiWAD,
|
WiiWAD,
|
||||||
ELFOrDOL,
|
ELFOrDOL,
|
||||||
NumberOfPlatforms
|
NumberOfPlatforms,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Country
|
enum class Country
|
||||||
@@ -38,7 +38,7 @@ enum class Country
|
|||||||
Taiwan,
|
Taiwan,
|
||||||
World,
|
World,
|
||||||
Unknown,
|
Unknown,
|
||||||
NumberOfCountries
|
NumberOfCountries,
|
||||||
};
|
};
|
||||||
|
|
||||||
// This numbering matches Nintendo's GameCube/Wii region numbering.
|
// This numbering matches Nintendo's GameCube/Wii region numbering.
|
||||||
@@ -48,7 +48,8 @@ enum class Region
|
|||||||
NTSC_U = 1, // Mainly North America
|
NTSC_U = 1, // Mainly North America
|
||||||
PAL = 2, // Mainly Europe and Oceania
|
PAL = 2, // Mainly Europe and Oceania
|
||||||
Unknown = 3, // Nintendo uses this to mean region free, but we also use it for unknown regions
|
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, // Triforce has no regions
|
||||||
};
|
};
|
||||||
|
|
||||||
// Languages 0 - 9 match Nintendo's Wii language numbering.
|
// Languages 0 - 9 match Nintendo's Wii language numbering.
|
||||||
@@ -66,7 +67,7 @@ enum class Language
|
|||||||
SimplifiedChinese = 7, // Not selectable on any unmodded retail Wii
|
SimplifiedChinese = 7, // Not selectable on any unmodded retail Wii
|
||||||
TraditionalChinese = 8, // Not selectable on any unmodded retail Wii
|
TraditionalChinese = 8, // Not selectable on any unmodded retail Wii
|
||||||
Korean = 9,
|
Korean = 9,
|
||||||
Unknown
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string GetName(Country country, bool translate);
|
std::string GetName(Country country, bool translate);
|
||||||
|
|||||||
@@ -96,7 +96,6 @@ public:
|
|||||||
}
|
}
|
||||||
virtual std::string GetGameID(const Partition& partition = PARTITION_NONE) const = 0;
|
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 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::string GetMakerID(const Partition& partition = PARTITION_NONE) const = 0;
|
||||||
virtual std::optional<u16> GetRevision(const Partition& partition = PARTITION_NONE) const = 0;
|
virtual std::optional<u16> GetRevision(const Partition& partition = PARTITION_NONE) const = 0;
|
||||||
virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0;
|
virtual std::string GetInternalName(const Partition& partition = PARTITION_NONE) const = 0;
|
||||||
|
|||||||
@@ -12,6 +12,7 @@
|
|||||||
#include "DiscIO/DiscUtils.h"
|
#include "DiscIO/DiscUtils.h"
|
||||||
#include "DiscIO/Enums.h"
|
#include "DiscIO/Enums.h"
|
||||||
#include "DiscIO/Filesystem.h"
|
#include "DiscIO/Filesystem.h"
|
||||||
|
#include "DiscIO/VolumeGC.h"
|
||||||
|
|
||||||
namespace DiscIO
|
namespace DiscIO
|
||||||
{
|
{
|
||||||
@@ -19,6 +20,55 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const
|
|||||||
{
|
{
|
||||||
char id[6];
|
char id[6];
|
||||||
|
|
||||||
|
if (GetVolumeType() == Platform::Triforce)
|
||||||
|
{
|
||||||
|
// Triforce games have their Game ID stored in the boot.id file
|
||||||
|
const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();
|
||||||
|
|
||||||
|
// Construct game ID from the BTID
|
||||||
|
id[0] = 'G';
|
||||||
|
|
||||||
|
memcpy(id + 1, boot_id->gameId + 2, 2);
|
||||||
|
|
||||||
|
switch (boot_id->regionFlags)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0x02: // JAPAN
|
||||||
|
id[3] = 'J';
|
||||||
|
break;
|
||||||
|
case 0x08: // ASIA
|
||||||
|
id[3] = 'W';
|
||||||
|
break;
|
||||||
|
case 0x0E: // USA
|
||||||
|
id[3] = 'E';
|
||||||
|
break;
|
||||||
|
case 0x0C: // EXPORT
|
||||||
|
id[3] = 'P';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// There only seem to be three different makers here,
|
||||||
|
// so we can just check the first char to difference between them.
|
||||||
|
//
|
||||||
|
// NAMCO CORPORATION, SEGA CORPORATION and Hitmaker co,ltd.
|
||||||
|
|
||||||
|
switch (boot_id->manufacturer[0])
|
||||||
|
{
|
||||||
|
case 'N': // NAMCO CORPORATION
|
||||||
|
id[4] = '8';
|
||||||
|
id[5] = '2';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case 'H': // Hitmaker co,ltd.
|
||||||
|
case 'S': // SEGA CORPORATION
|
||||||
|
id[4] = '6';
|
||||||
|
id[5] = 'E';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DecodeString(id);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Read(0, sizeof(id), reinterpret_cast<u8*>(id), partition))
|
if (!Read(0, sizeof(id), reinterpret_cast<u8*>(id), partition))
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
||||||
@@ -26,9 +76,36 @@ std::string VolumeDisc::GetGameID(const Partition& partition) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
Country VolumeDisc::GetCountry(const Partition& partition) const
|
Country VolumeDisc::GetCountry(const Partition& partition) const
|
||||||
|
{
|
||||||
|
u8 country_byte = 0;
|
||||||
|
|
||||||
|
if (GetVolumeType() == Platform::Triforce)
|
||||||
|
{
|
||||||
|
const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();
|
||||||
|
|
||||||
|
switch (boot_id->regionFlags)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0x02: // JAPAN
|
||||||
|
country_byte = 'J';
|
||||||
|
break;
|
||||||
|
case 0x08: // ASIA
|
||||||
|
country_byte = 'W';
|
||||||
|
break;
|
||||||
|
case 0x0E: // USA
|
||||||
|
country_byte = 'E';
|
||||||
|
break;
|
||||||
|
case 0x0C: // EXPORT
|
||||||
|
country_byte = 'P';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
// The 0 that we use as a default value is mapped to Country::Unknown and Region::Unknown
|
// The 0 that we use as a default value is mapped to Country::Unknown and Region::Unknown
|
||||||
const u8 country_byte = ReadSwapped<u8>(3, partition).value_or(0);
|
country_byte = ReadSwapped<u8>(3, partition).value_or(0);
|
||||||
|
}
|
||||||
|
|
||||||
const Region region = GetRegion();
|
const Region region = GetRegion();
|
||||||
const std::optional<u16> revision = GetRevision();
|
const std::optional<u16> revision = GetRevision();
|
||||||
|
|
||||||
@@ -51,6 +128,24 @@ std::string VolumeDisc::GetMakerID(const Partition& partition) const
|
|||||||
{
|
{
|
||||||
char maker_id[2];
|
char maker_id[2];
|
||||||
|
|
||||||
|
// Triforce games have their maker stored in the boot.id file as an actual string
|
||||||
|
if (GetVolumeType() == Platform::Triforce)
|
||||||
|
{
|
||||||
|
const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();
|
||||||
|
|
||||||
|
switch (boot_id->manufacturer[0])
|
||||||
|
{
|
||||||
|
case 'S': // SEGA CORPORATION
|
||||||
|
case 'H': // Hitmaker co,ltd
|
||||||
|
return DecodeString("6E");
|
||||||
|
case 'N': // NAMCO CORPORATION
|
||||||
|
return DecodeString("82");
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Fall back to normal maker from header
|
||||||
|
}
|
||||||
|
|
||||||
if (!Read(0x4, sizeof(maker_id), reinterpret_cast<u8*>(&maker_id), partition))
|
if (!Read(0x4, sizeof(maker_id), reinterpret_cast<u8*>(&maker_id), partition))
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
||||||
@@ -66,6 +161,14 @@ std::optional<u16> VolumeDisc::GetRevision(const Partition& partition) const
|
|||||||
std::string VolumeDisc::GetInternalName(const Partition& partition) const
|
std::string VolumeDisc::GetInternalName(const Partition& partition) const
|
||||||
{
|
{
|
||||||
char name[0x60];
|
char name[0x60];
|
||||||
|
|
||||||
|
// Triforce games have their Title stored in the boot.id file
|
||||||
|
if (GetVolumeType() == Platform::Triforce)
|
||||||
|
{
|
||||||
|
const BootID* boot_id = static_cast<const VolumeGC*>(this)->GetTriforceBootID();
|
||||||
|
return DecodeString(boot_id->gameName);
|
||||||
|
}
|
||||||
|
|
||||||
if (!Read(0x20, sizeof(name), reinterpret_cast<u8*>(&name), partition))
|
if (!Read(0x20, sizeof(name), reinterpret_cast<u8*>(&name), partition))
|
||||||
return std::string();
|
return std::string();
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,30 @@ namespace DiscIO
|
|||||||
class VolumeDisc : public Volume
|
class VolumeDisc : public Volume
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct BootID
|
||||||
|
{
|
||||||
|
u32 magic; // 0x00 (Always BTID)
|
||||||
|
uint32_t unknown0[3];
|
||||||
|
uint32_t unknown1[4];
|
||||||
|
char mediaboardType[4]; // 0x20 (GCAM for Triforce)
|
||||||
|
uint32_t unknown2;
|
||||||
|
uint16_t year; // 0x28
|
||||||
|
uint8_t month; // 0x2A
|
||||||
|
uint8_t day; // 0x2B
|
||||||
|
uint8_t videoMode; // 0x2C unknown bitmask, resolutions + horizontal/vertical
|
||||||
|
uint8_t unknown3;
|
||||||
|
uint8_t type3Compatible; // 0x2E (Type-3 compatible titles have this set to 1)
|
||||||
|
uint8_t unknown4;
|
||||||
|
char gameId[8]; // 0x30
|
||||||
|
uint32_t regionFlags; // 0x38
|
||||||
|
uint32_t unknown6[9];
|
||||||
|
char manufacturer[0x20]; // 0x60
|
||||||
|
char gameName[0x20]; // 0x80
|
||||||
|
char gameExecutable[0x20]; // 0xA0
|
||||||
|
char testExecutable[0x20]; // 0xC0
|
||||||
|
char creditTypes[8][0x20]; // 0xE0
|
||||||
|
};
|
||||||
|
|
||||||
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
|
std::string GetGameID(const Partition& partition = PARTITION_NONE) const override;
|
||||||
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
|
Country GetCountry(const Partition& partition = PARTITION_NONE) const override;
|
||||||
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
|
std::string GetMakerID(const Partition& partition = PARTITION_NONE) const override;
|
||||||
|
|||||||
@@ -45,13 +45,11 @@ VolumeGC::VolumeGC(std::unique_ptr<BlobReader> reader)
|
|||||||
std::unique_ptr<FileInfo> file_info = tmp_fs->FindFileInfo("boot.id");
|
std::unique_ptr<FileInfo> file_info = tmp_fs->FindFileInfo("boot.id");
|
||||||
if (!file_info)
|
if (!file_info)
|
||||||
return;
|
return;
|
||||||
BootID triforce_header;
|
|
||||||
const u64 file_size = ReadFile(*this, PARTITION_NONE, file_info.get(),
|
const u64 file_size = ReadFile(*this, PARTITION_NONE, file_info.get(),
|
||||||
reinterpret_cast<u8*>(&triforce_header), sizeof(BootID));
|
reinterpret_cast<u8*>(&m_triforce_header), sizeof(BootID));
|
||||||
if (file_size >= 4 && triforce_header.magic == BTID_MAGIC)
|
if (file_size >= 4 && m_triforce_header.magic == BTID_MAGIC)
|
||||||
{
|
{
|
||||||
m_is_triforce = true;
|
m_is_triforce = true;
|
||||||
m_triforce_id = triforce_header.id;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -90,16 +88,23 @@ std::string VolumeGC::GetGameTDBID(const Partition& partition) const
|
|||||||
return GetGameID(partition);
|
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
|
Region VolumeGC::GetRegion() const
|
||||||
{
|
{
|
||||||
|
if (m_is_triforce)
|
||||||
|
{
|
||||||
|
switch (m_triforce_header.regionFlags)
|
||||||
|
{
|
||||||
|
default:
|
||||||
|
case 0x02: // JAPAN
|
||||||
|
case 0x08: // ASIA
|
||||||
|
return Region::NTSC_J;
|
||||||
|
case 0x0E: // USA
|
||||||
|
return Region::NTSC_U;
|
||||||
|
case 0x0C: // EXPORT
|
||||||
|
return Region::PAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458));
|
return RegionCodeToRegion(m_reader->ReadSwapped<u32>(0x458));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -184,6 +189,12 @@ std::array<u8, 20> VolumeGC::GetSyncHash() const
|
|||||||
|
|
||||||
VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const
|
VolumeGC::ConvertedGCBanner VolumeGC::LoadBannerFile() const
|
||||||
{
|
{
|
||||||
|
// There is at least one Triforce game that has an opening.bnr file but from a different game.
|
||||||
|
if (m_is_triforce)
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
GCBanner banner_file;
|
GCBanner banner_file;
|
||||||
const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr",
|
const u64 file_size = ReadFile(*this, PARTITION_NONE, "opening.bnr",
|
||||||
reinterpret_cast<u8*>(&banner_file), sizeof(GCBanner));
|
reinterpret_cast<u8*>(&banner_file), sizeof(GCBanner));
|
||||||
|
|||||||
@@ -34,7 +34,6 @@ public:
|
|||||||
const Partition& partition = PARTITION_NONE) const override;
|
const Partition& partition = PARTITION_NONE) const override;
|
||||||
const FileSystem* GetFileSystem(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 GetGameTDBID(const Partition& partition = PARTITION_NONE) const override;
|
||||||
std::string GetTriforceID() const override;
|
|
||||||
std::map<Language, std::string> GetShortNames() const override;
|
std::map<Language, std::string> GetShortNames() const override;
|
||||||
std::map<Language, std::string> GetLongNames() const override;
|
std::map<Language, std::string> GetLongNames() const override;
|
||||||
std::map<Language, std::string> GetShortMakers() const override;
|
std::map<Language, std::string> GetShortMakers() const override;
|
||||||
@@ -50,9 +49,10 @@ public:
|
|||||||
DataSizeType GetDataSizeType() const override;
|
DataSizeType GetDataSizeType() const override;
|
||||||
u64 GetRawSize() const override;
|
u64 GetRawSize() const override;
|
||||||
const BlobReader& GetBlobReader() const override;
|
const BlobReader& GetBlobReader() const override;
|
||||||
|
|
||||||
std::array<u8, 20> GetSyncHash() const override;
|
std::array<u8, 20> GetSyncHash() const override;
|
||||||
|
|
||||||
|
const BootID* GetTriforceBootID() const { return &m_triforce_header; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static constexpr u32 GC_BANNER_WIDTH = 96;
|
static constexpr u32 GC_BANNER_WIDTH = 96;
|
||||||
static constexpr u32 GC_BANNER_HEIGHT = 32;
|
static constexpr u32 GC_BANNER_HEIGHT = 32;
|
||||||
@@ -77,13 +77,6 @@ private:
|
|||||||
// (only one for BNR1 type)
|
// (only one for BNR1 type)
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BootID
|
|
||||||
{
|
|
||||||
u32 magic; // "BTID"
|
|
||||||
u32 padding[11];
|
|
||||||
std::array<char, 4> id;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ConvertedGCBanner
|
struct ConvertedGCBanner
|
||||||
{
|
{
|
||||||
ConvertedGCBanner();
|
ConvertedGCBanner();
|
||||||
@@ -112,8 +105,9 @@ private:
|
|||||||
|
|
||||||
std::unique_ptr<BlobReader> m_reader;
|
std::unique_ptr<BlobReader> m_reader;
|
||||||
|
|
||||||
|
BootID m_triforce_header;
|
||||||
|
|
||||||
bool m_is_triforce;
|
bool m_is_triforce;
|
||||||
std::array<char, 4> m_triforce_id;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace DiscIO
|
} // namespace DiscIO
|
||||||
|
|||||||
@@ -279,6 +279,7 @@
|
|||||||
<ClInclude Include="Core\HW\DSPLLE\DSPDebugInterface.h" />
|
<ClInclude Include="Core\HW\DSPLLE\DSPDebugInterface.h" />
|
||||||
<ClInclude Include="Core\HW\DSPLLE\DSPLLE.h" />
|
<ClInclude Include="Core\HW\DSPLLE\DSPLLE.h" />
|
||||||
<ClInclude Include="Core\HW\DSPLLE\DSPSymbols.h" />
|
<ClInclude Include="Core\HW\DSPLLE\DSPSymbols.h" />
|
||||||
|
<ClInclude Include="Core\HW\DVD\AMMediaboard.h" />
|
||||||
<ClInclude Include="Core\HW\DVD\DVDInterface.h" />
|
<ClInclude Include="Core\HW\DVD\DVDInterface.h" />
|
||||||
<ClInclude Include="Core\HW\DVD\DVDMath.h" />
|
<ClInclude Include="Core\HW\DVD\DVDMath.h" />
|
||||||
<ClInclude Include="Core\HW\DVD\DVDThread.h" />
|
<ClInclude Include="Core\HW\DVD\DVDThread.h" />
|
||||||
@@ -322,6 +323,7 @@
|
|||||||
<ClInclude Include="Core\HW\MMIOHandlers.h" />
|
<ClInclude Include="Core\HW\MMIOHandlers.h" />
|
||||||
<ClInclude Include="Core\HW\ProcessorInterface.h" />
|
<ClInclude Include="Core\HW\ProcessorInterface.h" />
|
||||||
<ClInclude Include="Core\HW\SI\SI_Device.h" />
|
<ClInclude Include="Core\HW\SI\SI_Device.h" />
|
||||||
|
<ClInclude Include="Core\HW\SI\SI_DeviceAMBaseboard.h" />
|
||||||
<ClInclude Include="Core\HW\SI\SI_DeviceDanceMat.h" />
|
<ClInclude Include="Core\HW\SI\SI_DeviceDanceMat.h" />
|
||||||
<ClInclude Include="Core\HW\SI\SI_DeviceGBA.h" />
|
<ClInclude Include="Core\HW\SI\SI_DeviceGBA.h" />
|
||||||
<ClInclude Include="Core\HW\SI\SI_DeviceGBAEmu.h" />
|
<ClInclude Include="Core\HW\SI\SI_DeviceGBAEmu.h" />
|
||||||
@@ -971,6 +973,7 @@
|
|||||||
<ClCompile Include="Core\HW\DSPLLE\DSPHost.cpp" />
|
<ClCompile Include="Core\HW\DSPLLE\DSPHost.cpp" />
|
||||||
<ClCompile Include="Core\HW\DSPLLE\DSPLLE.cpp" />
|
<ClCompile Include="Core\HW\DSPLLE\DSPLLE.cpp" />
|
||||||
<ClCompile Include="Core\HW\DSPLLE\DSPSymbols.cpp" />
|
<ClCompile Include="Core\HW\DSPLLE\DSPSymbols.cpp" />
|
||||||
|
<ClCompile Include="Core\HW\DVD\AMMediaboard.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\DVDInterface.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDInterface.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\DVDMath.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDMath.cpp" />
|
||||||
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
<ClCompile Include="Core\HW\DVD\DVDThread.cpp" />
|
||||||
@@ -1017,6 +1020,7 @@
|
|||||||
<ClCompile Include="Core\HW\MMIO.cpp" />
|
<ClCompile Include="Core\HW\MMIO.cpp" />
|
||||||
<ClCompile Include="Core\HW\ProcessorInterface.cpp" />
|
<ClCompile Include="Core\HW\ProcessorInterface.cpp" />
|
||||||
<ClCompile Include="Core\HW\SI\SI_Device.cpp" />
|
<ClCompile Include="Core\HW\SI\SI_Device.cpp" />
|
||||||
|
<ClCompile Include="Core\HW\SI\SI_DeviceAMBaseboard.cpp" />
|
||||||
<ClCompile Include="Core\HW\SI\SI_DeviceDanceMat.cpp" />
|
<ClCompile Include="Core\HW\SI\SI_DeviceDanceMat.cpp" />
|
||||||
<ClCompile Include="Core\HW\SI\SI_DeviceGBA.cpp" />
|
<ClCompile Include="Core\HW\SI\SI_DeviceGBA.cpp" />
|
||||||
<ClCompile Include="Core\HW\SI\SI_DeviceGBAEmu.cpp" />
|
<ClCompile Include="Core\HW\SI\SI_DeviceGBAEmu.cpp" />
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ static constexpr std::array s_gc_types = {
|
|||||||
#endif
|
#endif
|
||||||
SIDeviceName{SerialInterface::SIDEVICE_GC_GBA, _trans("GBA (TCP)")},
|
SIDeviceName{SerialInterface::SIDEVICE_GC_GBA, _trans("GBA (TCP)")},
|
||||||
SIDeviceName{SerialInterface::SIDEVICE_GC_KEYBOARD, _trans("Keyboard Controller")},
|
SIDeviceName{SerialInterface::SIDEVICE_GC_KEYBOARD, _trans("Keyboard Controller")},
|
||||||
|
SIDeviceName{SerialInterface::SIDEVICE_AM_BASEBOARD, _trans("Triforce Baseboard")},
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::optional<int> ToGCMenuIndex(const SerialInterface::SIDevices sidevice)
|
static std::optional<int> ToGCMenuIndex(const SerialInterface::SIDevices sidevice)
|
||||||
@@ -132,6 +133,7 @@ void GamecubeControllersWidget::OnGCPadConfigure(size_t index)
|
|||||||
case SerialInterface::SIDEVICE_NONE:
|
case SerialInterface::SIDEVICE_NONE:
|
||||||
case SerialInterface::SIDEVICE_GC_GBA:
|
case SerialInterface::SIDEVICE_GC_GBA:
|
||||||
return;
|
return;
|
||||||
|
case SerialInterface::SIDEVICE_AM_BASEBOARD:
|
||||||
case SerialInterface::SIDEVICE_GC_CONTROLLER:
|
case SerialInterface::SIDEVICE_GC_CONTROLLER:
|
||||||
type = MappingWindow::Type::MAPPING_GCPAD;
|
type = MappingWindow::Type::MAPPING_GCPAD;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -122,12 +122,6 @@ QGroupBox* InfoWidget::CreateGameDetails()
|
|||||||
m_game.GetMakerID() + ")");
|
m_game.GetMakerID() + ")");
|
||||||
|
|
||||||
layout->addRow(tr("Name:"), internal_name);
|
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("Game ID:"), game_id);
|
||||||
layout->addRow(tr("Country:"), country);
|
layout->addRow(tr("Country:"), country);
|
||||||
layout->addRow(tr("Maker:"), maker);
|
layout->addRow(tr("Maker:"), maker);
|
||||||
|
|||||||
@@ -8,6 +8,10 @@
|
|||||||
|
|
||||||
#include "Core/HW/GCPad.h"
|
#include "Core/HW/GCPad.h"
|
||||||
#include "Core/HW/GCPadEmu.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/InputConfig.h"
|
#include "InputCommon/InputConfig.h"
|
||||||
|
|
||||||
@@ -22,8 +26,15 @@ void GCPadEmu::CreateMainLayout()
|
|||||||
|
|
||||||
layout->addWidget(CreateGroupBox(tr("Buttons"), Pad::GetGroup(GetPort(), PadGroup::Buttons)), 0,
|
layout->addWidget(CreateGroupBox(tr("Buttons"), Pad::GetGroup(GetPort(), PadGroup::Buttons)), 0,
|
||||||
0);
|
0);
|
||||||
layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0, -1,
|
layout->addWidget(CreateGroupBox(tr("D-Pad"), Pad::GetGroup(GetPort(), PadGroup::DPad)), 1, 0);
|
||||||
1);
|
|
||||||
|
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(
|
layout->addWidget(
|
||||||
CreateGroupBox(tr("Control Stick"), Pad::GetGroup(GetPort(), PadGroup::MainStick)), 0, 1, -1,
|
CreateGroupBox(tr("Control Stick"), Pad::GetGroup(GetPort(), PadGroup::MainStick)), 0, 1, -1,
|
||||||
1);
|
1);
|
||||||
|
|||||||
@@ -26,6 +26,9 @@
|
|||||||
#include "Common/IniFile.h"
|
#include "Common/IniFile.h"
|
||||||
#include "Common/StringUtil.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/FreeLookGeneral.h"
|
||||||
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
|
#include "DolphinQt/Config/Mapping/FreeLookRotation.h"
|
||||||
#include "DolphinQt/Config/Mapping/GBAPadEmu.h"
|
#include "DolphinQt/Config/Mapping/GBAPadEmu.h"
|
||||||
@@ -458,8 +461,17 @@ void MappingWindow::SetMappingType(MappingWindow::Type type)
|
|||||||
case Type::MAPPING_GC_DANCEMAT:
|
case Type::MAPPING_GC_DANCEMAT:
|
||||||
case Type::MAPPING_GCPAD:
|
case Type::MAPPING_GCPAD:
|
||||||
widget = new GCPadEmu(this);
|
widget = new GCPadEmu(this);
|
||||||
|
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));
|
setWindowTitle(tr("GameCube Controller at Port %1").arg(GetPort() + 1));
|
||||||
AddWidget(tr("GameCube Controller"), widget);
|
AddWidget(tr("GameCube Controller"), widget);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case Type::MAPPING_GC_MICROPHONE:
|
case Type::MAPPING_GC_MICROPHONE:
|
||||||
widget = new GCMicrophone(this);
|
widget = new GCMicrophone(this);
|
||||||
|
|||||||
@@ -250,7 +250,12 @@ MainWindow::MainWindow(Core::System& system, std::unique_ptr<BootParameters> boo
|
|||||||
});
|
});
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
connect(m_cheats_manager, &CheatsManager::OpenGeneralSettings, this,
|
||||||
|
&MainWindow::ShowGeneralWindow);
|
||||||
|
|
||||||
#ifdef USE_RETRO_ACHIEVEMENTS
|
#ifdef USE_RETRO_ACHIEVEMENTS
|
||||||
|
connect(m_cheats_manager, &CheatsManager::OpenAchievementSettings, this,
|
||||||
|
&MainWindow::ShowAchievementSettings);
|
||||||
connect(m_game_list, &GameList::OpenAchievementSettings, this,
|
connect(m_game_list, &GameList::OpenAchievementSettings, this,
|
||||||
&MainWindow::ShowAchievementSettings);
|
&MainWindow::ShowAchievementSettings);
|
||||||
#endif // USE_RETRO_ACHIEVEMENTS
|
#endif // USE_RETRO_ACHIEVEMENTS
|
||||||
@@ -511,6 +516,8 @@ void MainWindow::CreateComponents()
|
|||||||
});
|
});
|
||||||
connect(m_breakpoint_widget, &BreakpointWidget::ShowMemory, m_memory_widget,
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::ConnectMenuBar()
|
void MainWindow::ConnectMenuBar()
|
||||||
@@ -1151,6 +1158,43 @@ void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
|
|||||||
if (!NKitWarningDialog::ShowUnlessDisabled())
|
if (!NKitWarningDialog::ShowUnlessDisabled())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
When booting Triforce games, we need to ensure that the hardware is set up correctly.
|
||||||
|
*/
|
||||||
|
const auto volume_type =
|
||||||
|
std::get<BootParameters::Disc>(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.
|
// If we're running, only start a new game once we've stopped the last.
|
||||||
|
|||||||
@@ -319,6 +319,9 @@ void MenuBar::AddToolsMenu()
|
|||||||
m_pal_ipl =
|
m_pal_ipl =
|
||||||
gc_ipl->addAction(tr("PAL"), this, [this] { emit BootGameCubeIPL(DiscIO::Region::PAL); });
|
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::Unknown); });
|
||||||
|
|
||||||
tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });
|
tools_menu->addAction(tr("Memory Card Manager"), this, [this] { emit ShowMemcardManager(); });
|
||||||
|
|
||||||
tools_menu->addSeparator();
|
tools_menu->addSeparator();
|
||||||
@@ -1091,6 +1094,7 @@ void MenuBar::UpdateToolsMenu(const Core::State state)
|
|||||||
m_ntscj_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(JAP_DIR)));
|
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_ntscu_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(USA_DIR)));
|
||||||
m_pal_ipl->setEnabled(is_uninitialized && File::Exists(Config::GetBootROMPath(EUR_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_wad_install_action->setEnabled(is_uninitialized);
|
||||||
m_import_backup->setEnabled(is_uninitialized);
|
m_import_backup->setEnabled(is_uninitialized);
|
||||||
m_check_nand->setEnabled(is_uninitialized);
|
m_check_nand->setEnabled(is_uninitialized);
|
||||||
|
|||||||
@@ -227,6 +227,7 @@ private:
|
|||||||
QAction* m_ntscj_ipl;
|
QAction* m_ntscj_ipl;
|
||||||
QAction* m_ntscu_ipl;
|
QAction* m_ntscu_ipl;
|
||||||
QAction* m_pal_ipl;
|
QAction* m_pal_ipl;
|
||||||
|
QAction* m_dev_ipl;
|
||||||
QMenu* m_manage_nand_menu;
|
QMenu* m_manage_nand_menu;
|
||||||
QAction* m_import_backup;
|
QAction* m_import_backup;
|
||||||
QAction* m_check_nand;
|
QAction* m_check_nand;
|
||||||
|
|||||||
@@ -151,6 +151,7 @@ void GameCubePane::CreateWidgets()
|
|||||||
EXIDeviceType::EthernetIPC,
|
EXIDeviceType::EthernetIPC,
|
||||||
#endif
|
#endif
|
||||||
EXIDeviceType::ModemTapServer,
|
EXIDeviceType::ModemTapServer,
|
||||||
|
EXIDeviceType::Baseboard,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
|
m_slot_combos[ExpansionInterface::Slot::SP1]->addItem(tr(fmt::format("{:n}", device).c_str()),
|
||||||
@@ -418,6 +419,10 @@ void GameCubePane::OnConfigPressed(ExpansionInterface::Slot slot)
|
|||||||
dialog.exec();
|
dialog.exec();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
case ExpansionInterface::EXIDeviceType::Baseboard:
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
PanicAlertFmt("Unknown settings pressed for {}", device);
|
PanicAlertFmt("Unknown settings pressed for {}", device);
|
||||||
return;
|
return;
|
||||||
@@ -452,19 +457,23 @@ bool GameCubePane::SetMemcard(ExpansionInterface::Slot slot, const QString& file
|
|||||||
const std::string jp_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_J);
|
const std::string jp_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_J);
|
||||||
const std::string us_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_U);
|
const std::string us_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::NTSC_U);
|
||||||
const std::string eu_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::PAL);
|
const std::string eu_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::PAL);
|
||||||
const bool raw_path_valid = raw_path == jp_path || raw_path == us_path || raw_path == eu_path;
|
const std::string dv_path = Config::GetMemcardPath(raw_path, slot, DiscIO::Region::DEV);
|
||||||
|
const bool raw_path_valid =
|
||||||
|
raw_path == jp_path || raw_path == us_path || raw_path == eu_path || raw_path == dv_path;
|
||||||
|
|
||||||
if (!raw_path_valid)
|
if (!raw_path_valid)
|
||||||
{
|
{
|
||||||
// TODO: We could try to autodetect the card region here and offer automatic renaming.
|
// TODO: We could try to autodetect the card region here and offer automatic renaming.
|
||||||
ModalMessageBox::critical(this, tr("Error"),
|
ModalMessageBox::critical(
|
||||||
|
this, tr("Error"),
|
||||||
tr("The filename %1 does not conform to Dolphin's region code format "
|
tr("The filename %1 does not conform to Dolphin's region code format "
|
||||||
"for memory cards. Please rename this file to either %2, %3, or "
|
"for memory cards. Please rename this file to either %2, %3, %4, or "
|
||||||
"%4, matching the region of the save files that are on it.")
|
"%5, matching the region of the save files that are on it.")
|
||||||
.arg(QString::fromStdString(PathToFileName(raw_path)))
|
.arg(QString::fromStdString(PathToFileName(raw_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(us_path)))
|
.arg(QString::fromStdString(PathToFileName(us_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(eu_path)))
|
.arg(QString::fromStdString(PathToFileName(eu_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(jp_path))));
|
.arg(QString::fromStdString(PathToFileName(jp_path)))
|
||||||
|
.arg(QString::fromStdString(PathToFileName(dv_path))));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -556,8 +565,9 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
|
|||||||
const std::string default_jp_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_J);
|
const std::string default_jp_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_J);
|
||||||
const std::string default_us_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_U);
|
const std::string default_us_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::NTSC_U);
|
||||||
const std::string default_eu_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::PAL);
|
const std::string default_eu_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::PAL);
|
||||||
const bool is_default_path =
|
const std::string default_dv_path = Config::GetGCIFolderPath("", slot, DiscIO::Region::DEV);
|
||||||
raw_path == default_jp_path || raw_path == default_us_path || raw_path == default_eu_path;
|
const bool is_default_path = raw_path == default_jp_path || raw_path == default_us_path ||
|
||||||
|
raw_path == default_eu_path || raw_path == default_dv_path;
|
||||||
|
|
||||||
bool path_changed;
|
bool path_changed;
|
||||||
if (is_default_path)
|
if (is_default_path)
|
||||||
@@ -574,7 +584,9 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
|
|||||||
const std::string jp_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_J);
|
const std::string jp_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_J);
|
||||||
const std::string us_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_U);
|
const std::string us_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::NTSC_U);
|
||||||
const std::string eu_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::PAL);
|
const std::string eu_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::PAL);
|
||||||
const bool raw_path_valid = raw_path == jp_path || raw_path == us_path || raw_path == eu_path;
|
const std::string dv_path = Config::GetGCIFolderPath(raw_path, slot, DiscIO::Region::DEV);
|
||||||
|
const bool raw_path_valid =
|
||||||
|
raw_path == jp_path || raw_path == us_path || raw_path == eu_path || raw_path == dv_path;
|
||||||
|
|
||||||
if (!raw_path_valid)
|
if (!raw_path_valid)
|
||||||
{
|
{
|
||||||
@@ -582,12 +594,13 @@ bool GameCubePane::SetGCIFolder(ExpansionInterface::Slot slot, const QString& pa
|
|||||||
ModalMessageBox::critical(
|
ModalMessageBox::critical(
|
||||||
this, tr("Error"),
|
this, tr("Error"),
|
||||||
tr("The folder %1 does not conform to Dolphin's region code format "
|
tr("The folder %1 does not conform to Dolphin's region code format "
|
||||||
"for GCI folders. Please rename this folder to either %2, %3, or "
|
"for GCI folders. Please rename this folder to either %2, %3, %4, or "
|
||||||
"%4, matching the region of the save files that are in it.")
|
"%5, matching the region of the save files that are in it.")
|
||||||
.arg(QString::fromStdString(PathToFileName(raw_path)))
|
.arg(QString::fromStdString(PathToFileName(raw_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(us_path)))
|
.arg(QString::fromStdString(PathToFileName(us_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(eu_path)))
|
.arg(QString::fromStdString(PathToFileName(eu_path)))
|
||||||
.arg(QString::fromStdString(PathToFileName(jp_path))));
|
.arg(QString::fromStdString(PathToFileName(jp_path)))
|
||||||
|
.arg(QString::fromStdString(PathToFileName(dv_path))));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ enum PadButton
|
|||||||
PAD_BUTTON_START = 0x1000,
|
PAD_BUTTON_START = 0x1000,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum SwitchButton
|
||||||
|
{
|
||||||
|
SWITCH_TEST = 0x0001,
|
||||||
|
SWITCH_SERVICE = 0x0002,
|
||||||
|
SWITCH_COIN = 0x0004,
|
||||||
|
};
|
||||||
|
|
||||||
struct GCPadStatus
|
struct GCPadStatus
|
||||||
{
|
{
|
||||||
u16 button = 0; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
|
u16 button = 0; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
|
||||||
@@ -39,6 +46,8 @@ struct GCPadStatus
|
|||||||
u8 triggerRight = 0; // 0 <= triggerRight <= 255
|
u8 triggerRight = 0; // 0 <= triggerRight <= 255
|
||||||
u8 analogA = 0; // 0 <= analogA <= 255
|
u8 analogA = 0; // 0 <= analogA <= 255
|
||||||
u8 analogB = 0; // 0 <= analogB <= 255
|
u8 analogB = 0; // 0 <= analogB <= 255
|
||||||
|
// Triforce
|
||||||
|
u8 switches = 0;
|
||||||
bool isConnected = true;
|
bool isConnected = true;
|
||||||
|
|
||||||
static const u8 MAIN_STICK_CENTER_X = 0x80;
|
static const u8 MAIN_STICK_CENTER_X = 0x80;
|
||||||
|
|||||||
@@ -128,7 +128,6 @@ GameFile::GameFile(std::string path) : m_file_path(std::move(path))
|
|||||||
m_internal_name = volume->GetInternalName();
|
m_internal_name = volume->GetInternalName();
|
||||||
m_game_id = volume->GetGameID();
|
m_game_id = volume->GetGameID();
|
||||||
m_gametdb_id = volume->GetGameTDBID();
|
m_gametdb_id = volume->GetGameTDBID();
|
||||||
m_triforce_id = volume->GetTriforceID();
|
|
||||||
m_title_id = volume->GetTitleID().value_or(0);
|
m_title_id = volume->GetTitleID().value_or(0);
|
||||||
m_maker_id = volume->GetMakerID();
|
m_maker_id = volume->GetMakerID();
|
||||||
m_revision = volume->GetRevision().value_or(0);
|
m_revision = volume->GetRevision().value_or(0);
|
||||||
@@ -309,7 +308,6 @@ void GameFile::DoState(PointerWrap& p)
|
|||||||
p.Do(m_internal_name);
|
p.Do(m_internal_name);
|
||||||
p.Do(m_game_id);
|
p.Do(m_game_id);
|
||||||
p.Do(m_gametdb_id);
|
p.Do(m_gametdb_id);
|
||||||
p.Do(m_triforce_id);
|
|
||||||
p.Do(m_title_id);
|
p.Do(m_title_id);
|
||||||
p.Do(m_maker_id);
|
p.Do(m_maker_id);
|
||||||
|
|
||||||
@@ -498,8 +496,7 @@ const std::string& GameFile::GetName(const Core::TitleDatabase& title_database)
|
|||||||
if (IsModDescriptor())
|
if (IsModDescriptor())
|
||||||
return GetName(Variant::LongAndPossiblyCustom);
|
return GetName(Variant::LongAndPossiblyCustom);
|
||||||
|
|
||||||
const std::string& database_name =
|
const std::string& database_name = title_database.GetTitleName(m_gametdb_id, GetConfigLanguage());
|
||||||
title_database.GetTitleName(m_gametdb_id, m_triforce_id, GetConfigLanguage());
|
|
||||||
return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name;
|
return database_name.empty() ? GetName(Variant::LongAndPossiblyCustom) : database_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ public:
|
|||||||
const std::string& GetInternalName() const { return m_internal_name; }
|
const std::string& GetInternalName() const { return m_internal_name; }
|
||||||
const std::string& GetGameID() const { return m_game_id; }
|
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; }
|
||||||
std::string GetTriforceID() const { return m_triforce_id; }
|
|
||||||
u64 GetTitleID() const { return m_title_id; }
|
u64 GetTitleID() const { return m_title_id; }
|
||||||
const std::string& GetMakerID() const { return m_maker_id; }
|
const std::string& GetMakerID() const { return m_maker_id; }
|
||||||
u16 GetRevision() const { return m_revision; }
|
u16 GetRevision() const { return m_revision; }
|
||||||
@@ -160,7 +159,6 @@ private:
|
|||||||
std::string m_internal_name;
|
std::string m_internal_name;
|
||||||
std::string m_game_id;
|
std::string m_game_id;
|
||||||
std::string m_gametdb_id;
|
std::string m_gametdb_id;
|
||||||
std::string m_triforce_id;
|
|
||||||
u64 m_title_id{};
|
u64 m_title_id{};
|
||||||
std::string m_maker_id;
|
std::string m_maker_id;
|
||||||
|
|
||||||
|
|||||||
@@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
namespace UICommon
|
namespace UICommon
|
||||||
{
|
{
|
||||||
static constexpr u32 CACHE_REVISION = 26; // Last changed in PR 10084
|
static constexpr u32 CACHE_REVISION = 27; // Last changed in PR 13844
|
||||||
|
|
||||||
std::vector<std::string> FindAllGamePaths(std::span<const std::string_view> directories_to_scan,
|
std::vector<std::string> FindAllGamePaths(std::span<const std::string_view> directories_to_scan,
|
||||||
bool recursive_scan)
|
bool recursive_scan)
|
||||||
|
|||||||
@@ -280,6 +280,7 @@ void CreateDirectories()
|
|||||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + USA_DIR DIR_SEP);
|
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) + EUR_DIR DIR_SEP);
|
||||||
File::CreateFullPath(File::GetUserPath(D_GCUSER_IDX) + JAP_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_HIRESTEXTURES_IDX));
|
||||||
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
File::CreateFullPath(File::GetUserPath(D_GRAPHICSMOD_IDX));
|
||||||
File::CreateFullPath(File::GetUserPath(D_MAPS_IDX));
|
File::CreateFullPath(File::GetUserPath(D_MAPS_IDX));
|
||||||
|
|||||||
Reference in New Issue
Block a user