Triforce: Implement save states.

This commit is contained in:
Jordan Woyak 2026-02-08 03:25:04 -06:00
parent 63dc3b1972
commit aaa7094442
5 changed files with 175 additions and 30 deletions

View File

@ -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);
}
}
}

View File

@ -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);

View File

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

View File

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

View File

@ -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);
};