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]
FPRF = True

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,2 +1,4 @@
# SBLL - VIRTUA STRIKER 4 Ver.2006 (Export)
[Core]
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);
// Check for Triforce board being connected
const ExpansionInterface::EXIDeviceType Type = Config::Get(Config::MAIN_SERIAL_PORT_1);
bool enable_gcam = (Type == ExpansionInterface::EXIDeviceType::Baseboard) ? 1 : 0;
if (enable_gcam)
if (system.IsTriforce())
{
auto& memory = system.GetMemory();
u32 dsize = volume.GetDataSize();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -140,6 +140,7 @@ bool SIDevice_IsGCController(SIDevices type)
case SIDEVICE_GC_TARUKONGA:
case SIDEVICE_DANCEMAT:
case SIDEVICE_GC_STEERING:
case SIDEVICE_AM_BASEBOARD:
return true;
default:
return false;

View file

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

View file

@ -4,15 +4,18 @@
#pragma once
#include <SFML/Network.hpp>
#include <deque>
#include <memory>
#include <mutex>
#include <queue>
#include <thread>
#include <array>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/SI/SI_Device.h"
#include "InputCommon/GCPadStatus.h"
namespace Movie
{
class MovieManager;
}
namespace SerialInterface
{
@ -229,7 +232,7 @@ private:
u32 m_fzdx_sensor_left;
u8 m_rx_reply;
// F-Zero AX (CryCraft)
// F-Zero AX (CyCraft)
u32 m_fzcc_seatbelt;
u32 m_fzcc_sensor;
u32 m_fzcc_emergency;
@ -237,6 +240,44 @@ private:
void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length);
protected:
struct SOrigin
{
u16 button;
u8 origin_stick_x;
u8 origin_stick_y;
u8 substick_x;
u8 substick_y;
u8 trigger_left;
u8 trigger_right;
u8 unk_4;
u8 unk_5;
};
enum EButtonCombo
{
COMBO_NONE = 0,
COMBO_ORIGIN,
COMBO_RESET
};
// struct to compare input against
// Set on connection to perfect neutral values
// (standard pad only) Set on button combo to current input state
SOrigin m_origin = {};
// PADAnalogMode
// Dunno if we need to do this, game/lib should set it?
u8 m_mode = 0x3;
// Timer to track special button combos:
// y, X, start for 3 seconds updates origin with current status
// Technically, the above is only on standard pad, wavebird does not support it for example
// b, x, start for 3 seconds triggers reset (PI reset button interrupt)
u64 m_timer_button_combo_start = 0;
// Type of button combo from the last/current poll
EButtonCombo m_last_button_combo = COMBO_NONE;
public:
// constructor
CSIDevice_AMBaseboard(Core::System& system, SIDevices device, int device_number);
@ -249,6 +290,20 @@ public:
// send a command directly
void SendCommand(u32 command, u8 poll) override;
virtual GCPadStatus GetPadStatus();
virtual u32 MapPadStatus(const GCPadStatus& pad_status);
virtual EButtonCombo HandleButtonCombos(const GCPadStatus& pad_status);
static void HandleMoviePadStatus(Movie::MovieManager& movie, int device_number,
GCPadStatus* pad_status);
// Send and Receive pad input from network
static bool NetPlay_GetInput(int pad_num, GCPadStatus* status);
static int NetPlay_InGamePadToLocalPad(int pad_num);
protected:
void SetOrigin(const GCPadStatus& pad_status);
};
} // namespace SerialInterface

View file

@ -3,8 +3,11 @@
#pragma once
#include <SFML/Network.hpp>
#include <array>
#include "Common/CommonTypes.h"
#include "Common/Flag.h"
#include "Core/HW/GCPad.h"
#include "Core/HW/SI/SI_Device.h"
#include "InputCommon/GCPadStatus.h"

View file

@ -54,6 +54,7 @@
#include "Core/HW/GCPad.h"
#include "Core/HW/SI/SI.h"
#include "Core/HW/SI/SI_Device.h"
#include "Core/HW/SI/SI_DeviceAMBaseboard.h"
#include "Core/HW/SI/SI_DeviceGCController.h"
#include "Core/HW/Sram.h"
#include "Core/HW/WiiSave.h"
@ -1891,6 +1892,8 @@ void NetPlayClient::UpdateDevices()
auto& si = Core::System::GetInstance().GetSerialInterface();
for (auto player_id : m_pad_map)
{
const SerialInterface::SIDevices si_device = Config::Get(Config::GetInfoForSIDevice(local_pad));
if (m_gba_config[pad].enabled && player_id > 0)
{
si.ChangeDevice(SerialInterface::SIDEVICE_GC_GBA_EMULATED, pad);
@ -1898,8 +1901,6 @@ void NetPlayClient::UpdateDevices()
else if (player_id == m_local_player->pid)
{
// Use local controller types for local controllers if they are compatible
const SerialInterface::SIDevices si_device =
Config::Get(Config::GetInfoForSIDevice(local_pad));
if (SerialInterface::SIDevice_IsGCController(si_device))
{
si.ChangeDevice(si_device, pad);
@ -1917,7 +1918,8 @@ void NetPlayClient::UpdateDevices()
}
else if (player_id > 0)
{
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
if (si_device != SerialInterface::SIDEVICE_AM_BASEBOARD)
si.ChangeDevice(SerialInterface::SIDEVICE_GC_CONTROLLER, pad);
}
else
{
@ -2780,6 +2782,16 @@ bool SerialInterface::CSIDevice_GCController::NetPlay_GetInput(int pad_num, GCPa
return false;
}
bool SerialInterface::CSIDevice_AMBaseboard::NetPlay_GetInput(int pad_num, GCPadStatus* status)
{
std::lock_guard lk(NetPlay::crit_netplay_client);
if (NetPlay::netplay_client)
return NetPlay::netplay_client->GetNetPads(pad_num, NetPlay::s_si_poll_batching, status);
return false;
}
bool NetPlay::NetPlay_GetWiimoteData(const std::span<NetPlayClient::WiimoteDataBatchEntry>& entries)
{
std::lock_guard lk(crit_netplay_client);
@ -2852,3 +2864,12 @@ int SerialInterface::CSIDevice_GCController::NetPlay_InGamePadToLocalPad(int num
return numPAD;
}
int SerialInterface::CSIDevice_AMBaseboard::NetPlay_InGamePadToLocalPad(int numPAD)
{
std::lock_guard lk(NetPlay::crit_netplay_client);
if (NetPlay::netplay_client)
return NetPlay::netplay_client->InGamePadToLocalPad(numPAD);
return numPAD;
}