Added missing game IDs and titles to game setting files

Removed unused code related to s_segaboot
Removed unused variable s_segaboot
Optimized code related to the AMMB DI Inquiry command
Moved AMMB DI commands to their own enum
Changed code to not use pointers related to IOFile
Removed unused code in DVDInterface::ExecuteCommand()
Changed code to use UniqueBuffer instead of new
Remove unused code in JVSIOCommand::GeneralDriverOutput
Fixed a bug that broke Triforce disc key setup when using NetPlay
Made SIDEVICE_AM_BASEBOARD count as an GC controller for SIDevice_IsGCController
Added functions for NetPlay: MapPadStatus/HandleButtonCombos/GetData/SendCommand/SetOrigin/HandleMoviePadStatus/GetPadStatus
Updated NetPlayClient::UpdateDevices for handling the SIDEVICE_AM_BASEBOARD correctly and not always connect a GC controller
Added NetPlay_GetInput/NetPlay_InGamePadToLocalPad function for the AMBaseboard
General code formatting
Fixed typo in header name
This commit is contained in:
crediar 2025-08-10 17:33:38 +02:00
commit 87c4b1e356
20 changed files with 421 additions and 192 deletions

View file

@ -1,2 +1,4 @@
# SBEY - Virtua Striker 2002 (Export, Japan, Type 3)
[Core] [Core]
FPRF = True FPRF = True

View file

@ -1,3 +1,5 @@
# SBGG - F-ZERO AX
[Core] [Core]
FPRF = True FPRF = True
CPUThread = True CPUThread = True

View file

@ -1,3 +1,5 @@
# SBHA - F-ZERO AX (Monster)
[Core] [Core]
FPRF = True FPRF = True
CPUThread = True CPUThread = True

View file

@ -1,2 +1,4 @@
# SBHN - VIRTUA STRIKER 4 VER.A
[Core] [Core]
FPRF = True FPRF = True

View file

@ -1,2 +1,4 @@
# SBHZ - VIRTUA STRIKER 4 (Asia)
[Core] [Core]
FPRF = True FPRF = True

View file

@ -1,2 +1,4 @@
# SBJA - VIRTUA STRIKER 4 (Export)
[Core] [Core]
FPRF = True FPRF = True

View file

@ -1,2 +1,4 @@
# SBLK - VIRTUA STRIKER 4 Ver.2006 (Japan)
[Core] [Core]
FPRF = True FPRF = True

View file

@ -1,2 +1,4 @@
# SBLL - VIRTUA STRIKER 4 Ver.2006 (Export)
[Core] [Core]
FPRF = True FPRF = True

View file

@ -229,10 +229,7 @@ bool CBoot::RunApploader(Core::System& system, const Core::CPUThreadGuard& guard
branch_watch.SetRecordingActive(guard, resume_branch_watch); branch_watch.SetRecordingActive(guard, resume_branch_watch);
// Check for Triforce board being connected if (system.IsTriforce())
const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1);
bool enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0;
if (enable_gcam)
{ {
auto& memory = system.GetMemory(); auto& memory = system.GetMemory();
u32 dsize = volume.GetDataSize(); u32 dsize = volume.GetDataSize();

View file

@ -81,8 +81,7 @@ static int WSAGetLastError(void)
namespace AMMediaboard namespace AMMediaboard
{ {
static bool s_firmwaremap = false; static bool s_firmware_map = false;
static bool s_segaboot = false;
static bool s_test_menu = false; static bool s_test_menu = false;
static SOCKET s_namco_cam = 0; static SOCKET s_namco_cam = 0;
static u32 s_timeouts[3] = {20000, 20000, 20000}; static u32 s_timeouts[3] = {20000, 20000, 20000};
@ -92,11 +91,11 @@ static u32 s_GCAM_key_a = 0;
static u32 s_GCAM_key_b = 0; static u32 s_GCAM_key_b = 0;
static u32 s_GCAM_key_c = 0; static u32 s_GCAM_key_c = 0;
static File::IOFile* s_netcfg = nullptr; static File::IOFile s_netcfg = nullptr;
static File::IOFile* s_netctrl = nullptr; static File::IOFile s_netctrl = nullptr;
static File::IOFile* s_extra = nullptr; static File::IOFile s_extra = nullptr;
static File::IOFile* s_backup = nullptr; static File::IOFile s_backup = nullptr;
static File::IOFile* s_dimm = nullptr; static File::IOFile s_dimm = nullptr;
static u8* s_dimm_disc = nullptr; static u8* s_dimm_disc = nullptr;
@ -157,7 +156,7 @@ static inline void PrintMBBuffer(u32 address, u32 length)
void FirmwareMap(bool on) void FirmwareMap(bool on)
{ {
s_firmwaremap = on; s_firmware_map = on;
} }
void InitKeys(u32 key_a, u32 key_b, u32 key_c) void InitKeys(u32 key_a, u32 key_b, u32 key_c)
@ -167,14 +166,14 @@ void InitKeys(u32 key_a, u32 key_b, u32 key_c)
s_GCAM_key_c = key_c; s_GCAM_key_c = key_c;
} }
static File::IOFile* OpenOrCreateFile(const std::string& filename) static File::IOFile OpenOrCreateFile(const std::string& filename)
{ {
// Try opening for read/write first // Try opening for read/write first
if (File::Exists(filename)) if (File::Exists(filename))
return new File::IOFile(filename, "rb+"); return File::IOFile(filename, "rb+");
// Create new file // Create new file
return new File::IOFile(filename, "wb+"); return File::IOFile(filename, "wb+");
} }
void Init(void) void Init(void)
@ -185,8 +184,7 @@ void Init(void)
memset(s_firmware, -1, sizeof(s_firmware)); memset(s_firmware, -1, sizeof(s_firmware));
memset(s_sockets, SOCKET_ERROR, sizeof(s_sockets)); memset(s_sockets, SOCKET_ERROR, sizeof(s_sockets));
s_segaboot = false; s_firmware_map = false;
s_firmwaremap = false;
s_test_menu = false; s_test_menu = false;
s_last_error = SSC_SUCCESS; s_last_error = SSC_SUCCESS;
@ -203,15 +201,15 @@ void Init(void)
s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetGameID() + ".bin"); s_dimm = OpenOrCreateFile(base_path + "tridimm_" + SConfig::GetInstance().GetGameID() + ".bin");
s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetGameID() + ".bin"); s_backup = OpenOrCreateFile(base_path + "backup_" + SConfig::GetInstance().GetGameID() + ".bin");
if (!s_netcfg) if (!s_netcfg.IsOpen())
PanicAlertFmt("Failed to open/create: {}", base_path + "s_netcfg.bin"); PanicAlertFmt("Failed to open/create: {}", base_path + "s_netcfg.bin");
if (!s_netctrl) if (!s_netctrl.IsOpen())
PanicAlertFmt("Failed to open/create: {}", base_path + "s_netctrl.bin"); PanicAlertFmt("Failed to open/create: {}", base_path + "s_netctrl.bin");
if (!s_extra) if (!s_extra.IsOpen())
PanicAlertFmt("Failed to open/create: {}", base_path + "s_extra.bin"); PanicAlertFmt("Failed to open/create: {}", base_path + "s_extra.bin");
if (!s_dimm) if (!s_dimm.IsOpen())
PanicAlertFmt("Failed to open/create: {}", base_path + "s_dimm.bin"); PanicAlertFmt("Failed to open/create: {}", base_path + "s_dimm.bin");
if (!s_backup) if (!s_backup.IsOpen())
PanicAlertFmt("Failed to open/create: {}", base_path + "s_backup.bin"); PanicAlertFmt("Failed to open/create: {}", base_path + "s_backup.bin");
// This is the firmware for the Triforce // This is the firmware for the Triforce
@ -252,7 +250,7 @@ u8* InitDIMM(u32 size)
} }
} }
s_firmwaremap = 0; s_firmware_map = 0;
return s_dimm_disc; return s_dimm_disc;
} }
@ -396,7 +394,7 @@ static void FileWriteData(File::IOFile* file, u32 seek_pos, const u8* data, size
file->WriteBytes(data, length); file->WriteBytes(data, length);
file->Flush(); file->Flush();
} }
u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length) u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32* DIIMMBUF, u32 address, u32 length)
{ {
auto& system = Core::System::GetInstance(); auto& system = Core::System::GetInstance();
auto& memory = system.GetMemory(); auto& memory = system.GetMemory();
@ -413,8 +411,8 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
/* /*
Key setup for Triforce IPL: Key setup for Triforce IPL:
These RAM offset always hold the keys for the next command and since it sends two dummy These RAM offsets always hold the key for the next command. Since two dummy commands are sent before
commands before a real read we can just use the key from RAM without missing any real commands. any real ones, you can just use the key from RAM without missing a real command.
*/ */
if (s_GCAM_key_a == 0) if (s_GCAM_key_a == 0)
{ {
@ -430,13 +428,6 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
u32 command = DICMDBUF[0] << 24; u32 command = DICMDBUF[0] << 24;
u32 offset = DICMDBUF[1] << 2; u32 offset = DICMDBUF[1] << 2;
// SegaBoot adds bits for some reason to offset/length
// also adds 0x20 to offset
if (offset == 0x00100440)
{
s_segaboot = true;
}
INFO_LOG_FMT(DVDINTERFACE_AMMB, INFO_LOG_FMT(DVDINTERFACE_AMMB,
"GC-AM: {:08x} {:08x} DMA=addr:{:08x},len:{:08x} Keys: {:08x} {:08x} {:08x}", "GC-AM: {:08x} {:08x} DMA=addr:{:08x},len:{:08x} Keys: {:08x} {:08x} {:08x}",
command, offset, address, length, s_GCAM_key_a, s_GCAM_key_b, s_GCAM_key_c); command, offset, address, length, s_GCAM_key_a, s_GCAM_key_b, s_GCAM_key_c);
@ -450,33 +441,34 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
// Don't map firmware while in SegaBoot // Don't map firmware while in SegaBoot
if (memory.Read_U32(0x8006BF70) != 0x0A536567) if (memory.Read_U32(0x8006BF70) != 0x0A536567)
{ {
s_firmwaremap = 1; s_firmware_map = 1;
} }
} }
} }
switch (AMMBCommand(command >> 24)) switch (AMMBDICommand(command >> 24))
{ {
case AMMBCommand::Inquiry: case AMMBDICommand::Inquiry:
if (s_firmwaremap) if (s_firmware_map)
{ {
s_firmwaremap = false; s_firmware_map = false;
s_segaboot = false;
} }
// Returned value is used to set the protocol version. // Returned value is used to set the protocol version.
switch (GetGameType()) switch (GetGameType())
{ {
default: default:
return Version1; *DIIMMBUF = Version1;
return 0;
case KeyOfAvalon: case KeyOfAvalon:
case MarioKartGP: case MarioKartGP:
case MarioKartGP2: case MarioKartGP2:
case FirmwareUpdate: case FirmwareUpdate:
return Version2; *DIIMMBUF = Version2;
return 0;
} }
break; break;
case AMMBCommand::Read: case AMMBDICommand::Read:
if ((offset & 0x8FFF0000) == 0x80000000) if ((offset & 0x8FFF0000) == 0x80000000)
{ {
switch (offset) switch (offset)
@ -523,16 +515,16 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
// Network configuration // Network configuration
if (offset == 0x00000000 && length == 0x80) if (offset == 0x00000000 && length == 0x80)
{ {
s_netcfg->Seek(0, File::SeekOrigin::Begin); s_netcfg.Seek(0, File::SeekOrigin::Begin);
s_netcfg->ReadBytes(memory.GetSpanForAddress(address).data(), length); s_netcfg.ReadBytes(memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
// media crc check on/off // media crc check on/off
if (offset == DIMMExtraSettings && length == 0x20) if (offset == DIMMExtraSettings && length == 0x20)
{ {
s_extra->Seek(0, File::SeekOrigin::Begin); s_extra.Seek(0, File::SeekOrigin::Begin);
s_extra->ReadBytes(memory.GetSpanForAddress(address).data(), length); s_extra.ReadBytes(memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -540,8 +532,8 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
if (offset >= DIMMMemory && offset <= 0x1F800000) if (offset >= DIMMMemory && offset <= 0x1F800000)
{ {
u32 dimmoffset = offset - DIMMMemory; u32 dimmoffset = offset - DIMMMemory;
s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); s_dimm.Seek(dimmoffset, File::SeekOrigin::Begin);
s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); s_dimm.ReadBytes(memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -1044,15 +1036,15 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
if (offset >= DIMMMemory2 && offset <= 0xFF800000) if (offset >= DIMMMemory2 && offset <= 0xFF800000)
{ {
u32 dimmoffset = offset - DIMMMemory2; u32 dimmoffset = offset - DIMMMemory2;
s_dimm->Seek(dimmoffset, File::SeekOrigin::Begin); s_dimm.Seek(dimmoffset, File::SeekOrigin::Begin);
s_dimm->ReadBytes(memory.GetSpanForAddress(address).data(), length); s_dimm.ReadBytes(memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
if (offset == NetworkControl && length == 0x20) if (offset == NetworkControl && length == 0x20)
{ {
s_netctrl->Seek(0, File::SeekOrigin::Begin); s_netctrl.Seek(0, File::SeekOrigin::Begin);
s_netctrl->ReadBytes(memory.GetSpanForAddress(address).data(), length); s_netctrl.ReadBytes(memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -1063,13 +1055,8 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
return 0; return 0;
} }
if (s_firmwaremap) if (s_firmware_map)
{ {
if (s_segaboot)
{
DICMDBUF[1] &= ~0x00100000;
DICMDBUF[1] -= 0x20;
}
memcpy(memory.GetSpanForAddress(address).data(), s_firmware + offset, length); memcpy(memory.GetSpanForAddress(address).data(), s_firmware + offset, length);
return 0; return 0;
} }
@ -1082,23 +1069,23 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
return 1; return 1;
break; break;
case AMMBCommand::Write: case AMMBDICommand::Write:
/* /*
These two magic writes allow a new firmware to be programmed These two magic writes allow a new firmware to be programmed
*/ */
if ((offset == FirmwareMagicWrite1) && (length == 0x20)) if ((offset == FirmwareMagicWrite1) && (length == 0x20))
{ {
s_firmwaremap = true; s_firmware_map = true;
return 0; return 0;
} }
if ((offset == FirmwareMagicWrite2) && (length == 0x20)) if ((offset == FirmwareMagicWrite2) && (length == 0x20))
{ {
s_firmwaremap = true; s_firmware_map = true;
return 0; return 0;
} }
if (s_firmwaremap) if (s_firmware_map)
{ {
// Firmware memory (2MB) // Firmware memory (2MB)
if ((offset >= 0x00400000) && (offset <= 0x600000)) if ((offset >= 0x00400000) && (offset <= 0x600000))
@ -1112,21 +1099,21 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
// Network configuration // Network configuration
if ((offset == 0x00000000) && (length == 0x80)) if ((offset == 0x00000000) && (length == 0x80))
{ {
FileWriteData(s_netcfg, 0, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_netcfg, 0, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
// media crc check on/off // media crc check on/off
if ((offset == DIMMExtraSettings) && (length == 0x20)) if ((offset == DIMMExtraSettings) && (length == 0x20))
{ {
FileWriteData(s_extra, 0, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_extra, 0, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
// Backup memory (8MB) // Backup memory (8MB)
if ((offset >= BackupMemory) && (offset <= 0x00800000)) if ((offset >= BackupMemory) && (offset <= 0x00800000))
{ {
FileWriteData(s_backup, 0, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_backup, 0, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -1134,7 +1121,7 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
if ((offset >= DIMMMemory) && (offset <= 0x1F800000)) if ((offset >= DIMMMemory) && (offset <= 0x1F800000))
{ {
u32 dimmoffset = offset - DIMMMemory; u32 dimmoffset = offset - DIMMMemory;
FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -1320,13 +1307,13 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
if ((offset >= DIMMMemory2) && (offset <= 0xFF800000)) if ((offset >= DIMMMemory2) && (offset <= 0xFF800000))
{ {
u32 dimmoffset = offset - 0xFF000000; u32 dimmoffset = offset - 0xFF000000;
FileWriteData(s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_dimm, dimmoffset, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
if ((offset == NetworkControl) && (length == 0x20)) if ((offset == NetworkControl) && (length == 0x20))
{ {
FileWriteData(s_netctrl, 0, memory.GetSpanForAddress(address).data(), length); FileWriteData(&s_netctrl, 0, memory.GetSpanForAddress(address).data(), length);
return 0; return 0;
} }
@ -1337,7 +1324,7 @@ u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 address, u32 length)
PanicAlertFmtT("Unhandled Media Board Write:{0:08x}", offset); PanicAlertFmtT("Unhandled Media Board Write:{0:08x}", offset);
} }
break; break;
case AMMBCommand::Execute: case AMMBDICommand::Execute:
if ((offset == 0) && (length == 0)) if ((offset == 0) && (length == 0))
{ {
// Recast for easier access // Recast for easier access
@ -1818,20 +1805,11 @@ bool GetTestMenu(void)
} }
void Shutdown(void) void Shutdown(void)
{ {
if (s_netcfg) s_netcfg.Close();
s_netcfg->Close(); s_netctrl.Close();
s_extra.Close();
if (s_netctrl) s_backup.Close();
s_netctrl->Close(); s_dimm.Close();
if (s_extra)
s_extra->Close();
if (s_backup)
s_backup->Close();
if (s_dimm)
s_dimm->Close();
if (s_dimm_disc) if (s_dimm_disc)
{ {

View file

@ -32,7 +32,7 @@ enum GameType
VirtuaStriker4, VirtuaStriker4,
GekitouProYakyuu, GekitouProYakyuu,
KeyOfAvalon, KeyOfAvalon,
FirmwareUpdate FirmwareUpdate,
}; };
enum MediaType enum MediaType
{ {
@ -54,28 +54,32 @@ enum MediaBoardStatus
TestingGameProgram = 3, TestingGameProgram = 3,
LoadingGameProgram = 4, LoadingGameProgram = 4,
LoadedGameProgram = 5, LoadedGameProgram = 5,
Error = 6 Error = 6,
}; };
enum InquiryType enum InquiryType
{ {
Version1 = 0x21484100, Version1 = 0x21484100,
Version2 = 0x29484100 Version2 = 0x29484100,
}; };
#define SocketCheck(x) (x <= 0x3F ? x : 0) #define SocketCheck(x) (x <= 0x3F ? x : 0)
namespace AMMediaboard namespace AMMediaboard
{ {
enum class AMMBDICommand : u16
{
Inquiry = 0x12,
Read = 0xA8,
Write = 0xAA,
Execute = 0xAB,
};
enum class AMMBCommand : u16 enum class AMMBCommand : u16
{ {
Unknown_000 = 0x000, Unknown_000 = 0x000,
GetDIMMSize = 0x001, GetDIMMSize = 0x001,
Inquiry = 0x12,
Read = 0xa8,
Write = 0xaa,
Execute = 0xab,
GetMediaBoardStatus = 0x100, GetMediaBoardStatus = 0x100,
GetSegaBootVersion = 0x101, GetSegaBootVersion = 0x101,
GetSystemFlags = 0x102, GetSystemFlags = 0x102,
@ -209,7 +213,7 @@ void Init(void);
void FirmwareMap(bool on); void FirmwareMap(bool on);
u8* InitDIMM(u32 size); u8* InitDIMM(u32 size);
void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC); void InitKeys(u32 KeyA, u32 KeyB, u32 KeyC);
u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32 Address, u32 Length); u32 ExecuteCommand(std::array<u32, 3>& DICMDBUF, u32* DIIMMBUF, u32 Address, u32 Length);
u32 GetGameType(void); u32 GetGameType(void);
u32 GetMediaType(void); u32 GetMediaType(void);
bool GetTestMenu(void); bool GetTestMenu(void);

View file

@ -766,12 +766,8 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
if (m_system.IsTriforce()) if (m_system.IsTriforce())
{ {
u32 ret = AMMediaboard::ExecuteCommand(m_DICMDBUF, m_DIMAR, m_DILENGTH); if (!AMMediaboard::ExecuteCommand(m_DICMDBUF, &m_DIIMMBUF, m_DIMAR, m_DILENGTH))
if (ret != 1)
{ {
if (m_DICMDBUF[0] == 0x12000000)
m_DIIMMBUF = ret;
// Transfer is done // Transfer is done
m_DICR.TSTART = 0; m_DICR.TSTART = 0;
m_DIMAR += m_DILENGTH; m_DIMAR += m_DILENGTH;
@ -784,15 +780,6 @@ void DVDInterface::ExecuteCommand(ReplyType reply_type)
// Normal read command pass on to normal handling // Normal read command pass on to normal handling
} }
// Swaps endian of Triforce DI commands, and zeroes out random bytes to prevent unknown read
// subcommand errors
auto& dvd_thread = m_system.GetDVDThread();
if (dvd_thread.HasDisc() && dvd_thread.GetDiscType() == DiscIO::Platform::Triforce)
{
// TODO(C++23): Use std::byteswap and a bitwise AND for increased clarity
m_DICMDBUF[0] <<= 24;
}
// DVDLowRequestError needs access to the error code set by the previous command // DVDLowRequestError needs access to the error code set by the previous command
if (static_cast<DICommand>(m_DICMDBUF[0] >> 24) != DICommand::RequestError) if (static_cast<DICommand>(m_DICMDBUF[0] >> 24) != DICommand::RequestError)
SetDriveError(DriveError::None); SetDriveError(DriveError::None);

View file

@ -16,6 +16,7 @@
#include "Common/IOFile.h" #include "Common/IOFile.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/Buffer.h"
#include "Core/Boot/Boot.h" #include "Core/Boot/Boot.h"
#include "Core/BootManager.h" #include "Core/BootManager.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
@ -76,17 +77,14 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit
std::string backup_Filename(File::GetUserPath(D_TRIUSER_IDX) + "tribackup_" + std::string backup_Filename(File::GetUserPath(D_TRIUSER_IDX) + "tribackup_" +
SConfig::GetInstance().GetGameID().c_str() + ".bin"); SConfig::GetInstance().GetGameID().c_str() + ".bin");
if (File::Exists(backup_Filename)) m_backup = File::IOFile(backup_Filename, "rb+");
if (!m_backup.IsOpen())
{ {
m_backup = new File::IOFile(backup_Filename, "rb+"); m_backup = File::IOFile(backup_Filename, "wb+");
}
else
{
m_backup = new File::IOFile(backup_Filename, "wb+");
} }
// Some games share the same ID Client/Server // Some games share the same ID Client/Server
if (!m_backup->IsGood()) if (!m_backup.IsGood())
{ {
PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str()); PanicAlertFmt("Failed to open {}\nFile might be in use.", backup_Filename.c_str());
@ -95,7 +93,7 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit
backup_Filename = File::GetUserPath(D_TRIUSER_IDX) + "tribackup_tmp_" + std::to_string(rand()) + backup_Filename = File::GetUserPath(D_TRIUSER_IDX) + "tribackup_tmp_" + std::to_string(rand()) +
SConfig::GetInstance().GetGameID().c_str() + ".bin"; SConfig::GetInstance().GetGameID().c_str() + ".bin";
m_backup = new File::IOFile(backup_Filename, "wb+"); m_backup = File::IOFile(backup_Filename, "wb+");
} }
// Virtua Striker 4 and Gekitou Pro Yakyuu need a higher FIRM version // Virtua Striker 4 and Gekitou Pro Yakyuu need a higher FIRM version
@ -103,32 +101,28 @@ CEXIBaseboard::CEXIBaseboard(Core::System& system) : IEXIDevice(system), m_posit
if (AMMediaboard::GetGameType() == VirtuaStriker4 || if (AMMediaboard::GetGameType() == VirtuaStriker4 ||
AMMediaboard::GetGameType() == GekitouProYakyuu) AMMediaboard::GetGameType() == GekitouProYakyuu)
{ {
if (m_backup->GetSize() != 0) if (m_backup.GetSize() != 0)
{ {
u8* data = new u8[m_backup->GetSize()]; Common::UniqueBuffer<u8> data(m_backup.GetSize());
m_backup.ReadBytes(data.data(), data.size());
m_backup->ReadBytes(data, m_backup->GetSize());
// Set FIRM version // Set FIRM version
*(u16*)(data + 0x12) = 0x1703; reinterpret_cast<u16&>(data[0x12]) = 0x1703;
*(u16*)(data + 0x212) = 0x1703; reinterpret_cast<u16&>(data[0x212]) = 0x1703;
// Update checksum // Update checksum
*(u16*)(data + 0x0A) = Common::swap16(CheckSum(data + 0xC, 0x1F4)); reinterpret_cast<u16&>(data[0x0A]) = Common::swap16(CheckSum(&data[0xC], 0x1F4));
*(u16*)(data + 0x20A) = Common::swap16(CheckSum(data + 0x20C, 0x1F4)); reinterpret_cast<u16&>(data[0x20A]) = Common::swap16(CheckSum(&data[0x20C], 0x1F4));
m_backup->Seek(0, File::SeekOrigin::Begin); m_backup.Seek(0, File::SeekOrigin::Begin);
m_backup->WriteBytes(data, m_backup->GetSize()); m_backup.WriteBytes(data.data(), data.size());
m_backup->Flush(); m_backup.Flush();
delete[] data;
} }
} }
} }
CEXIBaseboard::~CEXIBaseboard() CEXIBaseboard::~CEXIBaseboard()
{ {
m_backup->Close(); m_backup.Close();
delete m_backup;
} }
void CEXIBaseboard::SetCS(int cs) void CEXIBaseboard::SetCS(int cs)
@ -165,11 +159,11 @@ void CEXIBaseboard::DMAWrite(u32 addr, u32 size)
NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Write: {:08x} {:x}", addr, size); NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Write: {:08x} {:x}", addr, size);
m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); m_backup.Seek(m_backoffset, File::SeekOrigin::Begin);
m_backup->WriteBytes(memory.GetSpanForAddress(addr).data(), size); m_backup.WriteBytes(memory.GetSpanForAddress(addr).data(), size);
m_backup->Flush(); m_backup.Flush();
} }
void CEXIBaseboard::DMARead(u32 addr, u32 size) void CEXIBaseboard::DMARead(u32 addr, u32 size)
@ -179,11 +173,11 @@ void CEXIBaseboard::DMARead(u32 addr, u32 size)
NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Read: {:08x} {:x}", addr, size); NOTICE_LOG_FMT(SP1, "AM-BB: COMMAND: Backup DMA Read: {:08x} {:x}", addr, size);
m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); m_backup.Seek(m_backoffset, File::SeekOrigin::Begin);
m_backup->Flush(); m_backup.Flush();
m_backup->ReadBytes(memory.GetSpanForAddress(addr).data(), size); m_backup.ReadBytes(memory.GetSpanForAddress(addr).data(), size);
} }
void CEXIBaseboard::TransferByte(u8& _byte) void CEXIBaseboard::TransferByte(u8& _byte)
@ -225,13 +219,13 @@ void CEXIBaseboard::TransferByte(u8& _byte)
case BackupOffsetSet: case BackupOffsetSet:
m_backoffset = (m_command[1] << 8) | m_command[2]; m_backoffset = (m_command[1] << 8) | m_command[2];
DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupOffsetSet:{:04x}", m_backoffset); DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupOffsetSet:{:04x}", m_backoffset);
m_backup->Seek(m_backoffset, File::SeekOrigin::Begin); m_backup.Seek(m_backoffset, File::SeekOrigin::Begin);
_byte = 0x01; _byte = 0x01;
break; break;
case BackupWrite: case BackupWrite:
DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupWrite:{:04x}-{:02x}", m_backoffset, m_command[1]); DEBUG_LOG_FMT(SP1, "AM-BB: COMMAND: BackupWrite:{:04x}-{:02x}", m_backoffset, m_command[1]);
m_backup->WriteBytes(&m_command[1], 1); m_backup.WriteBytes(&m_command[1], 1);
m_backup->Flush(); m_backup.Flush();
_byte = 0x01; _byte = 0x01;
break; break;
case BackupRead: case BackupRead:
@ -292,8 +286,8 @@ void CEXIBaseboard::TransferByte(u8& _byte)
{ {
// 1 byte out // 1 byte out
case BackupRead: case BackupRead:
m_backup->Flush(); m_backup.Flush();
m_backup->ReadBytes(&_byte, 1); m_backup.ReadBytes(&_byte, 1);
break; break;
case DMAOffsetLengthSet: case DMAOffsetLengthSet:
_byte = 0x01; _byte = 0x01;

View file

@ -59,7 +59,7 @@ private:
u32 m_backup_dma_length; u32 m_backup_dma_length;
u8 m_command[4]; u8 m_command[4];
u16 m_backoffset; u16 m_backoffset;
File::IOFile* m_backup; File::IOFile m_backup;
protected: protected:
void TransferByte(u8& _uByte) override; void TransferByte(u8& _uByte) override;

View file

@ -30,7 +30,7 @@ enum class PadGroup
Rumble, Rumble,
Mic, Mic,
Options, Options,
Triforce Triforce,
}; };
class GCPad : public ControllerEmu::EmulatedController class GCPad : public ControllerEmu::EmulatedController

View file

@ -140,6 +140,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;

View file

@ -13,6 +13,8 @@
#include <fmt/format.h> #include <fmt/format.h>
#include "Common/Buffer.h"
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Config/Config.h" #include "Common/Config/Config.h"
#include "Common/FileUtil.h" #include "Common/FileUtil.h"
@ -20,6 +22,8 @@
#include "Common/IOFile.h" #include "Common/IOFile.h"
#include "Common/IniFile.h" #include "Common/IniFile.h"
#include "Common/Logging/Log.h" #include "Common/Logging/Log.h"
#include "Common/MsgHandler.h"
#include "Common/Swap.h"
#include "Core/Boot/Boot.h" #include "Core/Boot/Boot.h"
#include "Core/BootManager.h" #include "Core/BootManager.h"
#include "Core/Config/MainSettings.h" #include "Core/Config/MainSettings.h"
@ -28,15 +32,19 @@
#include "Core/ConfigLoaders/NetPlayConfigLoader.h" #include "Core/ConfigLoaders/NetPlayConfigLoader.h"
#include "Core/ConfigManager.h" #include "Core/ConfigManager.h"
#include "Core/Core.h" #include "Core/Core.h"
#include "Core/CoreTiming.h"
#include "Core/HW/DVD/AMMediaboard.h" #include "Core/HW/DVD/AMMediaboard.h"
#include "Core/HW/DVD/DVDInterface.h" #include "Core/HW/DVD/DVDInterface.h"
#include "Core/HW/EXI/EXI.h" #include "Core/HW/EXI/EXI.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/MMIO.h" #include "Core/HW/MMIO.h"
#include "Core/HW/Memmap.h" #include "Core/HW/Memmap.h"
#include "Core/HW/ProcessorInterface.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_DeviceGCController.h" #include "Core/HW/SI/SI_DeviceGCController.h"
#include "Core/HW/Sram.h" #include "Core/HW/Sram.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/WiimoteReal/WiimoteReal.h" #include "Core/HW/WiimoteReal/WiimoteReal.h"
#include "Core/Movie.h" #include "Core/Movie.h"
#include "Core/NetPlayProto.h" #include "Core/NetPlayProto.h"
@ -44,6 +52,7 @@
#include "Core/System.h" #include "Core/System.h"
#include "Core/WiiRoot.h" #include "Core/WiiRoot.h"
#include "DiscIO/Enums.h" #include "DiscIO/Enums.h"
#include "InputCommon/GCPadStatus.h"
namespace SerialInterface namespace SerialInterface
{ {
@ -1997,7 +2006,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
break; break;
} }
u8* buf = new u8[bytes]; Common::UniqueBuffer<u8> buf(bytes);
for (u32 i = 0; i < bytes; ++i) for (u32 i = 0; i < bytes; ++i)
{ {
@ -2008,32 +2017,12 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
SERIALINTERFACE_JVSIO, SERIALINTERFACE_JVSIO,
"JVS-IO: Command 0x32, GPO: {:02x} {:02x} {} {:02x}{:02x}{:02x} ({:02x})", "JVS-IO: Command 0x32, GPO: {:02x} {:02x} {} {:02x}{:02x}{:02x} ({:02x})",
delay, m_rx_reply, bytes, buf[0], buf[1], buf[2], delay, m_rx_reply, bytes, buf[0], buf[1], buf[2],
Common::swap16(*(u16*)(buf + 1)) >> 2); Common::swap16(*reinterpret_cast<u16*>(&buf[1])) >> 2);
// TODO: figure this out /*
Handling of the motion seat used in F-Zero AXs DX version
u8 trepl[] = { */
0x00, 0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 0xB0, 0xC0, switch (Common::swap16(*reinterpret_cast<u16*>(&buf[1])) >> 2)
0xD0, 0xE0, 0xF0, 0x01, 0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91,
0xA1, 0xB1, 0xC1, 0xD1, 0xE1, 0xF1, 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, 0x62,
0x72, 0x82, 0x92, 0xA2, 0xB2, 0xC2, 0xD2, 0xE2, 0xF2, 0x04, 0x14, 0x24, 0x34,
0x44, 0x54, 0x64, 0x74, 0x84, 0x94, 0xA4, 0xB4, 0xC4, 0xD4, 0xE4, 0xF4, 0x05,
0x15, 0x25, 0x35, 0x45, 0x55, 0x65, 0x75, 0x85, 0x95, 0xA5, 0xB5, 0xC5, 0xD5,
0xE5, 0xF5, 0x06, 0x16, 0x26, 0x36, 0x46, 0x56, 0x66, 0x76, 0x86, 0x96, 0xA6,
0xB6, 0xC6, 0xD6, 0xE6, 0xF6, 0x08, 0x18, 0x28, 0x38, 0x48, 0x58, 0x68, 0x78,
0x88, 0x98, 0xA8, 0xB8, 0xC8, 0xD8, 0xE8, 0xF8, 0x09, 0x19, 0x29, 0x39, 0x49,
0x59, 0x69, 0x79, 0x89, 0x99, 0xA9, 0xB9, 0xC9, 0xD9, 0xE9, 0xF9, 0x0A, 0x1A,
0x2A, 0x3A, 0x4A, 0x5A, 0x6A, 0x7A, 0x8A, 0x9A, 0xAA, 0xBA, 0xCA, 0xDA, 0xEA,
0xFA, 0x0C, 0x1C, 0x2C, 0x3C, 0x4C, 0x5C, 0x6C, 0x7C, 0x8C, 0x9C, 0xAC, 0xBC,
0xCC, 0xDC, 0xEC, 0xFC, 0x0D, 0x1D, 0x2D, 0x3D, 0x4D, 0x5D, 0x6D, 0x7D, 0x8D,
0x9D, 0xAD, 0xBD, 0xCD, 0xDD, 0xED, 0xFD, 0x0E, 0x1E, 0x2E, 0x3E, 0x4E, 0x5E,
0x6E, 0x7E, 0x8E, 0x9E, 0xAE, 0xBE, 0xCE, 0xDE, 0xEE, 0xFE};
static u32 off = 0;
if (off > sizeof(trepl))
off = 0;
switch (Common::swap16(*(u16*)(buf + 1)) >> 2)
{ {
case 0x70: case 0x70:
delay++; delay++;
@ -2050,13 +2039,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
case 0x60: case 0x60:
break; break;
} }
////if( buf[1] == 1 && buf[2] == 0x80 )
////{
//// INFO_LOG_FMT(DVDINTERFACE, "GCAM: PC:{:08x}", PC);
//// PowerPC::breakpoints.Add( PC+8, false );
////}
delete[] buf;
} }
break; break;
} }
@ -2170,19 +2152,210 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
return buffer_position; return buffer_position;
} }
// Unused u32 CSIDevice_AMBaseboard::MapPadStatus(const GCPadStatus& pad_status)
DataResponse CSIDevice_AMBaseboard::GetData(u32& _Hi, u32& _Low)
{ {
_Low = 0; // Thankfully changing mode does not change the high bits ;)
_Hi = 0x00800000; u32 hi = 0;
hi = pad_status.stickY;
hi |= pad_status.stickX << 8;
hi |= (pad_status.button | PAD_USE_ORIGIN) << 16;
return hi;
}
CSIDevice_AMBaseboard::EButtonCombo
CSIDevice_AMBaseboard::HandleButtonCombos(const GCPadStatus& pad_status)
{
// Keep track of the special button combos (embedded in controller hardware... :( )
EButtonCombo temp_combo;
if ((pad_status.button & 0xff00) == (PAD_BUTTON_Y | PAD_BUTTON_X | PAD_BUTTON_START))
temp_combo = COMBO_ORIGIN;
else if ((pad_status.button & 0xff00) == (PAD_BUTTON_B | PAD_BUTTON_X | PAD_BUTTON_START))
temp_combo = COMBO_RESET;
else
temp_combo = COMBO_NONE;
if (temp_combo != m_last_button_combo)
{
m_last_button_combo = temp_combo;
if (m_last_button_combo != COMBO_NONE)
m_timer_button_combo_start = m_system.GetCoreTiming().GetTicks();
}
if (m_last_button_combo != COMBO_NONE)
{
const u64 current_time = m_system.GetCoreTiming().GetTicks();
const u32 ticks_per_second = m_system.GetSystemTimers().GetTicksPerSecond();
if (u32(current_time - m_timer_button_combo_start) > ticks_per_second * 3)
{
if (m_last_button_combo == COMBO_RESET)
{
INFO_LOG_FMT(SERIALINTERFACE, "PAD - COMBO_RESET");
m_system.GetProcessorInterface().ResetButton_Tap();
}
else if (m_last_button_combo == COMBO_ORIGIN)
{
INFO_LOG_FMT(SERIALINTERFACE, "PAD - COMBO_ORIGIN");
SetOrigin(pad_status);
}
m_last_button_combo = COMBO_NONE;
return temp_combo;
}
}
return COMBO_NONE;
}
// GetData
// Return true on new data (max 7 Bytes and 6 bits ;)
// [00?SYXBA] [1LRZUDRL] [x] [y] [cx] [cy] [l] [r]
// |\_ ERR_LATCH (error latched - check SISR)
// |_ ERR_STATUS (error on last GetData or SendCmd?)
DataResponse CSIDevice_AMBaseboard::GetData(u32& hi, u32& low)
{
GCPadStatus pad_status = GetPadStatus();
if (!pad_status.isConnected)
return DataResponse::ErrorNoResponse;
if (HandleButtonCombos(pad_status) == COMBO_ORIGIN)
pad_status.button |= PAD_GET_ORIGIN;
hi = MapPadStatus(pad_status);
// Low bits are packed differently per mode
if (m_mode == 0 || m_mode == 5 || m_mode == 6 || m_mode == 7)
{
low = (pad_status.analogB >> 4); // Top 4 bits
low |= ((pad_status.analogA >> 4) << 4); // Top 4 bits
low |= ((pad_status.triggerRight >> 4) << 8); // Top 4 bits
low |= ((pad_status.triggerLeft >> 4) << 12); // Top 4 bits
low |= ((pad_status.substickY) << 16); // All 8 bits
low |= ((pad_status.substickX) << 24); // All 8 bits
}
else if (m_mode == 1)
{
low = (pad_status.analogB >> 4); // Top 4 bits
low |= ((pad_status.analogA >> 4) << 4); // Top 4 bits
low |= (pad_status.triggerRight << 8); // All 8 bits
low |= (pad_status.triggerLeft << 16); // All 8 bits
low |= ((pad_status.substickY >> 4) << 24); // Top 4 bits
low |= ((pad_status.substickX >> 4) << 28); // Top 4 bits
}
else if (m_mode == 2)
{
low = pad_status.analogB; // All 8 bits
low |= pad_status.analogA << 8; // All 8 bits
low |= ((pad_status.triggerRight >> 4) << 16); // Top 4 bits
low |= ((pad_status.triggerLeft >> 4) << 20); // Top 4 bits
low |= ((pad_status.substickY >> 4) << 24); // Top 4 bits
low |= ((pad_status.substickX >> 4) << 28); // Top 4 bits
}
else if (m_mode == 3)
{
// Analog A/B are always 0
low = pad_status.triggerRight; // All 8 bits
low |= (pad_status.triggerLeft << 8); // All 8 bits
low |= (pad_status.substickY << 16); // All 8 bits
low |= (pad_status.substickX << 24); // All 8 bits
}
else if (m_mode == 4)
{
low = pad_status.analogB; // All 8 bits
low |= pad_status.analogA << 8; // All 8 bits
// triggerLeft/Right are always 0
low |= pad_status.substickY << 16; // All 8 bits
low |= pad_status.substickX << 24; // All 8 bits
}
return DataResponse::Success; return DataResponse::Success;
} }
void CSIDevice_AMBaseboard::SendCommand(u32 _Cmd, u8 _Poll) void CSIDevice_AMBaseboard::SendCommand(u32 command, u8 poll)
{ {
ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command (0x{})", _Cmd); UCommand controller_command(command);
PanicAlertFmt("SI: (GCAM) Unknown direct command");
if (static_cast<EDirectCommands>(controller_command.command) == EDirectCommands::CMD_WRITE)
{
const u32 type = controller_command.parameter1; // 0 = stop, 1 = rumble, 2 = stop hard
// get the correct pad number that should rumble locally when using netplay
const int pad_num = NetPlay_InGamePadToLocalPad(m_device_number);
if (pad_num < 4)
{
const SIDevices device = m_system.GetSerialInterface().GetDeviceType(pad_num);
if (type == 1)
CSIDevice_GCController::Rumble(pad_num, 1.0, device);
else
CSIDevice_GCController::Rumble(pad_num, 0.0, device);
}
if (poll == 0)
{
m_mode = controller_command.parameter2;
INFO_LOG_FMT(SERIALINTERFACE, "PAD {} set to mode {}", m_device_number, m_mode);
}
}
else if (controller_command.command != 0x00)
{
// Costis sent 0x00 in some demos :)
ERROR_LOG_FMT(SERIALINTERFACE, "Unknown direct command ({:#x})", command);
PanicAlertFmt("SI: Unknown direct command");
}
}
void CSIDevice_AMBaseboard::SetOrigin(const GCPadStatus& pad_status)
{
m_origin.origin_stick_x = pad_status.stickX;
m_origin.origin_stick_y = pad_status.stickY;
m_origin.substick_x = pad_status.substickX;
m_origin.substick_y = pad_status.substickY;
m_origin.trigger_left = pad_status.triggerLeft;
m_origin.trigger_right = pad_status.triggerRight;
}
void CSIDevice_AMBaseboard::HandleMoviePadStatus(Movie::MovieManager& movie, int device_number,
GCPadStatus* pad_status)
{
movie.SetPolledDevice();
if (NetPlay_GetInput(device_number, pad_status))
{
}
else if (movie.IsPlayingInput())
{
movie.PlayController(pad_status, device_number);
movie.InputUpdate();
}
else if (movie.IsRecordingInput())
{
movie.RecordInput(pad_status, device_number);
movie.InputUpdate();
}
else
{
movie.CheckPadStatus(pad_status, device_number);
}
}
GCPadStatus CSIDevice_AMBaseboard::GetPadStatus()
{
GCPadStatus pad_status = {};
// For netplay, the local controllers are polled in GetNetPads(), and
// the remote controllers receive their status there as well
if (!NetPlay::IsNetPlayRunning())
{
pad_status = Pad::GetStatus(m_device_number);
}
// Our GCAdapter code sets PAD_GET_ORIGIN when a new device has been connected.
// Watch for this to calibrate real controllers on connection.
if (pad_status.button & PAD_GET_ORIGIN)
SetOrigin(pad_status);
return pad_status;
} }
} // namespace SerialInterface } // namespace SerialInterface

View file

@ -4,15 +4,18 @@
#pragma once #pragma once
#include <SFML/Network.hpp> #include <SFML/Network.hpp>
#include <deque> #include <array>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
#include "Common/Flag.h" #include "Common/Flag.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/SI/SI_Device.h" #include "Core/HW/SI/SI_Device.h"
#include "InputCommon/GCPadStatus.h"
namespace Movie
{
class MovieManager;
}
namespace SerialInterface namespace SerialInterface
{ {
@ -229,7 +232,7 @@ private:
u32 m_fzdx_sensor_left; u32 m_fzdx_sensor_left;
u8 m_rx_reply; u8 m_rx_reply;
// F-Zero AX (CryCraft) // F-Zero AX (CyCraft)
u32 m_fzcc_seatbelt; u32 m_fzcc_seatbelt;
u32 m_fzcc_sensor; u32 m_fzcc_sensor;
u32 m_fzcc_emergency; u32 m_fzcc_emergency;
@ -237,6 +240,44 @@ private:
void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length); 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: public:
// constructor // constructor
CSIDevice_AMBaseboard(Core::System& system, SIDevices device, int device_number); CSIDevice_AMBaseboard(Core::System& system, SIDevices device, int device_number);
@ -249,6 +290,20 @@ public:
// send a command directly // send a command directly
void SendCommand(u32 command, u8 poll) override; 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 } // namespace SerialInterface

View file

@ -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"

View file

@ -54,6 +54,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"
@ -1891,6 +1892,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);
@ -1898,8 +1901,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);
@ -1917,6 +1918,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
@ -2780,6 +2782,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);
@ -2852,3 +2864,12 @@ int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int num
return numPAD; return numPAD;
} }
int SerialInterface::CSIDevice_AMBaseboard::NetPlay_InGamePadToLocalPad(int numPAD)
{
std::lock_guard lk(NetPlay::crit_netplay_client);
if (NetPlay::netplay_client)
return NetPlay::netplay_client->InGamePadToLocalPad(numPAD);
return numPAD;
}