Triforce: Implement save states.

This commit is contained in:
Jordan Woyak committed 2026-02-15 20:14:15 -06:00
1 parent 63dc3b1972
commit aaa7094442
5 files changed
+175 -30

No files matched your search

+79 -13
View File
@@ -152,6 +152,10 @@ static u8 s_network_buffer[512 * 1024];
static u8 s_allnet_buffer[4096]; static u8 s_allnet_buffer[4096];
static u8 s_allnet_settings[0x8500]; 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; static constexpr std::size_t MAX_IPV4_STRING_LENGTH = 15;
constexpr char s_allnet_reply[] = { constexpr char s_allnet_reply[] = {
@@ -441,6 +445,9 @@ void Init()
std::ranges::fill(s_allnet_buffer, 0); std::ranges::fill(s_allnet_buffer, 0);
std::ranges::fill(s_allnet_settings, 0); std::ranges::fill(s_allnet_settings, 0);
s_board_status = LoadingGameProgram;
s_load_progress = 80;
s_firmware_map = false; s_firmware_map = false;
s_test_menu = false; s_test_menu = false;
@@ -1746,19 +1753,15 @@ u32 ExecuteCommand(std::array<u32, 3>& dicmd_buf, u32* diimm_buf, u32 address, u
break; break;
case AMMBCommand::GetMediaBoardStatus: case AMMBCommand::GetMediaBoardStatus:
{ {
// Fake loading the game to have a chance to enter test mode s_media_buffer_32[1] = s_board_status;
static u32 status = LoadingGameProgram; s_media_buffer_32[2] = s_load_progress;
static u32 progress = 80; if (s_load_progress < 100)
s_media_buffer_32[1] = status;
s_media_buffer_32[2] = progress;
if (progress < 100)
{ {
progress++; s_load_progress++;
} }
else else
{ {
status = LoadedGameProgram; s_board_status = LoadedGameProgram;
} }
} }
break; break;
@@ -1974,6 +1977,18 @@ bool GetTestMenu()
return s_test_menu; 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() void Shutdown()
{ {
s_netcfg.Close(); s_netcfg.Close();
@@ -1983,12 +1998,63 @@ void Shutdown()
s_dimm.Close(); s_dimm.Close();
s_dimm_disc.clear(); s_dimm_disc.clear();
// Close all sockets CloseAllSockets();
for (u32 i = FIRST_VALID_FD; i < std::size(s_sockets); ++i) }
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);
} }
} }
} }
+2
View File
@@ -8,6 +8,7 @@
#include <string_view> #include <string_view>
#include <utility> #include <utility>
#include "Common/ChunkFile.h"
#include "Common/CommonTypes.h" #include "Common/CommonTypes.h"
enum GameType enum GameType
@@ -236,6 +237,7 @@ u32 GetGameType();
u32 GetMediaType(); u32 GetMediaType();
bool GetTestMenu(); bool GetTestMenu();
void Shutdown(); void Shutdown();
void DoState(PointerWrap& p);
std::optional<std::pair<std::string_view, std::string_view>> ParseIPOverride(std::string_view str); std::optional<std::pair<std::string_view, std::string_view>> ParseIPOverride(std::string_view str);
+6
View File
@@ -124,6 +124,12 @@ void DVDInterface::DoState(PointerWrap& p)
m_system.GetDVDThread().DoState(p); m_system.GetDVDThread().DoState(p);
m_adpcm_decoder.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, size_t DVDInterface::ProcessDTKSamples(s16* target_samples, size_t target_block_count,
+81 -17
View File
@@ -228,9 +228,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
std::array<u8, 0x80> data_out{}; std::array<u8, 0x80> data_out{};
u32 data_offset = 0; 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;
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 || if (AMMediaboard::GetGameType() == FZeroAX ||
AMMediaboard::GetGameType() == FZeroAXMonster) AMMediaboard::GetGameType() == FZeroAXMonster)
{ {
dip_switch_0 &= ~0x20; m_dip_switch_0 &= ~0x20;
} }
// Disable camera in MKGP1/2 // Disable camera in MKGP1/2
if (AMMediaboard::GetGameType() == MarioKartGP || if (AMMediaboard::GetGameType() == MarioKartGP ||
AMMediaboard::GetGameType() == MarioKartGP2) AMMediaboard::GetGameType() == MarioKartGP2)
{ {
dip_switch_0 &= ~0x10; m_dip_switch_0 &= ~0x10;
} }
data_out[data_offset++] = dip_switch_0; data_out[data_offset++] = m_dip_switch_0;
data_out[data_offset++] = dip_switch_1; data_out[data_offset++] = m_dip_switch_1;
break; break;
} }
case GCAMCommand::SerialNumber: case GCAMCommand::SerialNumber:
@@ -1656,8 +1653,6 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
JVSIOMessage message; JVSIOMessage message;
static int delay = 0;
const u8* const frame = &data_in[0]; const u8* const frame = &data_in[0];
const u8 nr_bytes = frame[3]; // Byte after E0 xx const u8 nr_bytes = frame[3]; // Byte after E0 xx
u32 frame_len = nr_bytes + 3; // Header(2) + length byte + payload + checksum 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" " - jvs_io(begin={}, current={}, end={}, n={})\n"
" - delay={}, node={}\n" " - delay={}, node={}\n"
" - frame(begin={}, len={})", " - frame(begin={}, len={})",
fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, delay, node, fmt::ptr(jvs_begin), fmt::ptr(jvs_io), fmt::ptr(jvs_end), n, m_delay,
fmt::ptr(frame), frame_len); node, fmt::ptr(frame), frame_len);
jvs_io = jvs_end; jvs_io = jvs_end;
return false; return false;
}; };
@@ -2355,7 +2350,7 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
INFO_LOG_FMT(SERIALINTERFACE_JVSIO, INFO_LOG_FMT(SERIALINTERFACE_JVSIO,
"JVS-IO: Command 0x32, GPO: delay=0x{:02x}, rx_reply=0x{:02x}," "JVS-IO: Command 0x32, GPO: delay=0x{:02x}, rx_reply=0x{:02x},"
" bytes={}, buffer:\n{}", " 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) if (bytes < 3)
{ {
@@ -2370,8 +2365,8 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
switch (seat_state) switch (seat_state)
{ {
case 0x70: case 0x70:
delay++; m_delay++;
if ((delay % 10) == 0) if ((m_delay % 10) == 0)
{ {
m_rx_reply = 0xFB; m_rx_reply = 0xFB;
} }
@@ -2429,13 +2424,13 @@ int CSIDevice_AMBaseboard::RunBuffer(u8* buffer, int request_length)
if (*jvs_io++ == 0xD9) if (*jvs_io++ == 0xD9)
{ {
NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF0, Reset"); NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF0, Reset");
delay = 0; m_delay = 0;
m_wheel_init = 0; m_wheel_init = 0;
m_ic_card_state = 0x20; m_ic_card_state = 0x20;
} }
message.AddData(StatusOkay); message.AddData(StatusOkay);
dip_switch_1 |= 1; m_dip_switch_1 |= 1;
break; break;
case JVSIOCommand::SetAddress: case JVSIOCommand::SetAddress:
if (!validate_jvs_io(1, "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={}", NOTICE_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Command 0xF1, SetAddress: node={}",
node); node);
message.AddData(node == 1); message.AddData(node == 1);
dip_switch_1 &= ~1u; m_dip_switch_1 &= ~1u;
break; break;
default: default:
ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unhandled: node={}, command={:02x}", ERROR_LOG_FMT(SERIALINTERFACE_JVSIO, "JVS-IO: Unhandled: node={}, command={:02x}",
@@ -2739,4 +2734,73 @@ GCPadStatus CSIDevice_AMBaseboard::GetPadStatus()
return pad_status; 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 } // namespace SerialInterface
@@ -71,6 +71,8 @@ public:
static bool NetPlay_GetInput(int pad_num, GCPadStatus* status); static bool NetPlay_GetInput(int pad_num, GCPadStatus* status);
static int NetPlay_InGamePadToLocalPad(int pad_num); static int NetPlay_InGamePadToLocalPad(int pad_num);
void DoState(PointerWrap&) override;
protected: protected:
struct SOrigin struct SOrigin
{ {
@@ -311,6 +313,11 @@ private:
bool m_fzcc_emergency = false; bool m_fzcc_emergency = false;
bool m_fzcc_service = 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); void ICCardSendReply(ICCommand* iccommand, u8* buffer, u32* length);
}; };