summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Woyak <jordan.woyak@gmail.com>2026-02-08 03:25:04 -0600
committerJordan Woyak <jordan.woyak@gmail.com>2026-02-15 20:14:15 -0600
commitaaa709444245ada15b5679e60a02fdfde2e4510c (patch)
treec21d6afd28d34d75359cad1636c1ec82eb93e9dd
parent63dc3b19722215e89af8d6e25ae96c005b63a78e (diff)
Triforce: Implement save states.
-rw-r--r--Source/Core/Core/HW/DVD/AMMediaboard.cpp92
-rw-r--r--Source/Core/Core/HW/DVD/AMMediaboard.h2
-rw-r--r--Source/Core/Core/HW/DVD/DVDInterface.cpp6
-rw-r--r--Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp98
-rw-r--r--Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h7
5 files changed, 175 insertions, 30 deletions
diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.cpp b/Source/Core/Core/HW/DVD/AMMediaboard.cpp
index 8533def250..0c2260628c 100644
--- a/Source/Core/Core/HW/DVD/AMMediaboard.cpp
+++ b/Source/Core/Core/HW/DVD/AMMediaboard.cpp
@@ -152,6 +152,10 @@ static u8 s_network_buffer[512 * 1024];
static u8 s_allnet_buffer[4096];
static u8 s_allnet_settings[0x8500];
+// Fake loading the game to have a chance to enter test mode
+static u32 s_board_status = LoadingGameProgram;
+static u32 s_load_progress = 80;
+
static constexpr std::size_t MAX_IPV4_STRING_LENGTH = 15;
constexpr char s_allnet_reply[] = {
@@ -441,6 +445,9 @@ void Init()
std::ranges::fill(s_allnet_buffer, 0);
std::ranges::fill(s_allnet_settings, 0);
+ s_board_status = LoadingGameProgram;
+ s_load_progress = 80;
+
s_firmware_map = false;
s_test_menu = false;
@@ -1746,19 +1753,15 @@ u32 ExecuteCommand(std::array<u32, 3>& dicmd_buf, u32* diimm_buf, u32 address, u
break;
case AMMBCommand::GetMediaBoardStatus:
{
- // Fake loading the game to have a chance to enter test mode
- static u32 status = LoadingGameProgram;
- static u32 progress = 80;
-
- s_media_buffer_32[1] = status;
- s_media_buffer_32[2] = progress;
- if (progress < 100)
+ s_media_buffer_32[1] = s_board_status;
+ s_media_buffer_32[2] = s_load_progress;
+ if (s_load_progress < 100)
{
- progress++;
+ s_load_progress++;
}
else
{
- status = LoadedGameProgram;
+ s_board_status = LoadedGameProgram;
}
}
break;
@@ -1974,6 +1977,18 @@ bool GetTestMenu()
return s_test_menu;
}
+static void CloseAllSockets()
+{
+ for (u32 i = FIRST_VALID_FD; i < std::size(s_sockets); ++i)
+ {
+ if (s_sockets[i] != SOCKET_ERROR)
+ {
+ closesocket(s_sockets[i]);
+ s_sockets[i] = SOCKET_ERROR;
+ }
+ }
+}
+
void Shutdown()
{
s_netcfg.Close();
@@ -1983,12 +1998,63 @@ void Shutdown()
s_dimm.Close();
s_dimm_disc.clear();
- // Close all sockets
- for (u32 i = FIRST_VALID_FD; i < std::size(s_sockets); ++i)
+ CloseAllSockets();
+}
+
+void DoState(PointerWrap& p)
+{
+ p.Do(s_firmware_map);
+ p.Do(s_test_menu);
+ p.Do(s_timeouts);
+ p.Do(s_last_error);
+ p.Do(s_gcam_key_a);
+ p.Do(s_gcam_key_b);
+ p.Do(s_gcam_key_c);
+ p.Do(s_firmware);
+ p.Do(s_media_buffer_32);
+ p.Do(s_network_command_buffer);
+ p.Do(s_network_buffer);
+ p.Do(s_allnet_buffer);
+ p.Do(s_allnet_settings);
+
+ p.Do(s_board_status);
+ p.Do(s_load_progress);
+
+ // TODO: Handle the files better.
+ // Data corruption is probably currently possible.
+
+ // s_netcfg
+ // s_netctrl
+ // s_extra
+ // s_backup
+ // s_dimm
+
+ // TODO: Handle sockets better.
+ // For now, we just recreate a TCP socket for any socket that existed.
+ // We should probably re-bind sockets and handle UDP sockets.
+
+ GuestFdSet created_sockets{};
+ if (p.IsWriteMode() || p.IsVerifyMode())
{
- if (s_sockets[i] != SOCKET_ERROR)
+ for (u32 i = FIRST_VALID_FD; i < std::size(s_sockets); ++i)
{
- closesocket(s_sockets[i]);
+ if (s_sockets[i] != SOCKET_ERROR)
+ created_sockets.SetFd(GuestSocket(i));
+ }
+ }
+
+ p.Do(created_sockets);
+
+ if (p.IsReadMode())
+ {
+ CloseAllSockets();
+
+ for (u32 i = FIRST_VALID_FD; i < std::size(s_sockets); ++i)
+ {
+ if (!created_sockets.IsFdSet(GuestSocket(i)))
+ continue;
+
+ s_sockets[i] = socket(AF_INET, SOCK_STREAM, 0);
}
}
}
diff --git a/Source/Core/Core/HW/DVD/AMMediaboard.h b/Source/Core/Core/HW/DVD/AMMediaboard.h
index cc0bcf9b89..f5d19662ea 100644
--- a/Source/Core/Core/HW/DVD/AMMediaboard.h
+++ b/Source/Core/Core/HW/DVD/AMMediaboard.h
@@ -8,6 +8,7 @@
#include <string_view>
#include <utility>
+#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h"
enum GameType
@@ -236,6 +237,7 @@ u32 GetGameType();
u32 GetMediaType();
bool GetTestMenu();
void Shutdown();
+void DoState(PointerWrap& p);
std::optional<std::pair<std::string_view, std::string_view>> ParseIPOverride(std::string_view str);
diff --git a/Source/Core/Core/HW/DVD/DVDInterface.cpp b/Source/Core/Core/HW/DVD/DVDInterface.cpp
index 0801b8d26a..33b0efc548 100644
--- a/Source/Core/Core/HW/DVD/DVDInterface.cpp
+++ b/Source/Core/Core/HW/DVD/DVDInterface.cpp
@@ -124,6 +124,12 @@ void DVDInterface::DoState(PointerWrap& p)
m_system.GetDVDThread().DoState(p);
m_adpcm_decoder.DoState(p);
+
+ if (m_system.IsTriforce())
+ {
+ AMMediaboard::DoState(p);
+ p.DoMarker("AMMediaboard");
+ }
}
size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t target_block_count,
diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp
index 060ff44773..6841d81702 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp
+++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.cpp
@@ -228,9 +228,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
std::array<u8, 0x80> data_out{};
u32 data_offset = 0;
- static u32 dip_switch_1 = 0xFE;
- static u32 dip_switch_0 = 0xFF;
-
data_out[data_offset++] = 1;
data_out[data_offset++] = 1;
@@ -304,18 +301,18 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
if (AMMediaboard::GetGameType() == FZeroAX ||
AMMediaboard::GetGameType() == FZeroAXMonster)
{
- dip_switch_0 &= ~0x20;
+ m_dip_switch_0 &= ~0x20;
}
// Disable camera in MKGP1/2
if (AMMediaboard::GetGameType() == MarioKartGP ||
AMMediaboard::GetGameType() == MarioKartGP2)
{
- dip_switch_0 &= ~0x10;
+ m_dip_switch_0 &= ~0x10;
}
- data_out[data_offset++] = dip_switch_0;
- data_out[data_offset++] = dip_switch_1;
+ data_out[data_offset++] = m_dip_switch_0;
+ data_out[data_offset++] = m_dip_switch_1;
break;
}
case GCAMCommand::SerialNumber:
@@ -1656,8 +1653,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
JVSIOMessage message;
- static int delay = 0;
-
const u8* const frame = &data_in[0];
const u8 nr_bytes = frame[3]; // Byte after E0 xx
u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum
@@ -1694,8 +1689,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
" - jvs_io(begin={}, current={}, end={}, n={})\n"
" - delay={}, node={}\n"
" - frame(begin={}, len={})",
- fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node,
- fmt::ptr(frame), frame_len);
+ fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, m_delay,
+ node, fmt::ptr(frame), frame_len);
jvs_io = jvs_end;
return false;
};
@@ -2355,7 +2350,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
INFO_LOG_FMT(SERIALINTERFACE_JVSIO,
"JVS-IO: Command 0x32, GPO: delay=0x{:02x}, rx_reply=0x{:02x},"
" bytes={}, buffer:\n{}",
- delay, m_rx_reply, bytes, HexDump(jvs_io, bytes));
+ m_delay, m_rx_reply, bytes, HexDump(jvs_io, bytes));
if (bytes < 3)
{
@@ -2370,8 +2365,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
switch (seat_state)
{
case 0x70:
- delay++;
- if ((delay % 10) == 0)
+ m_delay++;
+ if ((m_delay % 10) == 0)
{
m_rx_reply = 0xFB;
}
@@ -2429,13 +2424,13 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
if (*jvs_io++ == 0xD9)
{
NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF0, Reset");
- delay = 0;
+ m_delay = 0;
m_wheel_init = 0;
m_ic_card_state = 0x20;
}
message.AddData(StatusOkay);
- dip_switch_1 |= 1;
+ m_dip_switch_1 |= 1;
break;
case JVSIOCommand::SetAddress:
if (!validate_jvs_io(1, "SetAddress"))
@@ -2444,7 +2439,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF1, SetAddress: node={}",
node);
message.AddData(node == 1);
- dip_switch_1 &= ~1u;
+ m_dip_switch_1 &= ~1u;
break;
default:
ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unhandled: node={}, command={:02x}",
@@ -2739,4 +2734,73 @@ GCPadStatus CSIDevice_AMBaseboard::GetPadStatus()
return pad_status;
}
+void CSIDevice_AMBaseboard::DoState(PointerWrap& p)
+{
+ p.Do(m_origin);
+ p.Do(m_mode);
+ p.Do(m_timer_button_combo_start);
+ p.Do(m_last_button_combo);
+
+ p.Do(m_last);
+ p.Do(m_lastptr);
+
+ p.Do(m_coin);
+ p.Do(m_coin_pressed);
+
+ p.Do(m_ic_card_data);
+
+ // Setup IC-card
+ p.Do(m_ic_card_state);
+ p.Do(m_ic_card_status);
+ p.Do(m_ic_card_session);
+
+ p.Do(m_ic_write_buffer);
+ p.Do(m_ic_write_offset);
+ p.Do(m_ic_write_size);
+
+ p.Do(m_card_memory);
+ p.Do(m_card_read_packet);
+ p.Do(m_card_buffer);
+
+ // Setup CARD
+ p.Do(m_card_memory_size);
+ p.Do(m_card_is_inserted);
+
+ p.Do(m_card_command);
+ p.Do(m_card_clean);
+ p.Do(m_card_write_length);
+ p.Do(m_card_wrote);
+ p.Do(m_card_read_length);
+ p.Do(m_card_read);
+ p.Do(m_card_bit);
+ p.Do(m_card_shutter);
+ p.Do(m_card_state_call_count);
+ p.Do(m_card_offset);
+
+ // Serial
+ p.Do(m_wheel_init);
+
+ p.Do(m_motor_init);
+ p.Do(m_motor_reply);
+ p.Do(m_motor_force_y);
+
+ // F-Zero AX (DX)
+ p.Do(m_fzdx_seatbelt);
+ p.Do(m_fzdx_motion_stop);
+ p.Do(m_fzdx_sensor_right);
+ p.Do(m_fzdx_sensor_left);
+ p.Do(m_rx_reply);
+
+ // F-Zero AX (CyCraft)
+ p.Do(m_fzcc_seatbelt);
+ p.Do(m_fzcc_sensor);
+ p.Do(m_fzcc_emergency);
+ p.Do(m_fzcc_service);
+
+ p.Do(m_dip_switch_1);
+ p.Do(m_dip_switch_0);
+
+ p.Do(m_delay);
+}
+
} // namespace SerialInterface
diff --git a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h
index 95b19a86c6..974325c7c2 100644
--- a/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h
+++ b/Source/Core/Core/HW/SI/SI_DeviceAMBaseboard.h
@@ -71,6 +71,8 @@ public:
static bool NetPlay_GetInput(int pad_num, GCPadStatus* status);
static int NetPlay_InGamePadToLocalPad(int pad_num);
+ void DoState(PointerWrap&) override;
+
protected:
struct SOrigin
{
@@ -311,6 +313,11 @@ private:
bool m_fzcc_emergency = false;
bool m_fzcc_service = false;
+ u32 m_dip_switch_1 = 0xFE;
+ u32 m_dip_switch_0 = 0xFF;
+
+ int m_delay = 0;
+
void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length);
};