[SAVEVERSION+] Multitap: Make multitaps manage their own states

This commit is contained in:
RedPanda4552 2023-11-06 00:17:29 -05:00 committed by Connor McLaughlin
parent eb1d93d4e5
commit 2443e06745
7 changed files with 45 additions and 42 deletions

View File

@ -16,6 +16,7 @@
#include "PrecompiledHeader.h"
#include "Common.h"
#include "StateWrapper.h"
#include "SIO/Multitap/MultitapProtocol.h"
@ -25,8 +26,7 @@
#define MT_LOG_ENABLE 0
#define MT_LOG if (MT_LOG_ENABLE) DevCon
MultitapProtocol g_MultitapPort0;
MultitapProtocol g_MultitapPort1;
std::array<MultitapProtocol, SIO::PORTS> g_MultitapArr;
void MultitapProtocol::SupportCheck()
{
@ -114,6 +114,16 @@ void MultitapProtocol::FullReset()
this->currentMemcardSlot = 0;
}
bool MultitapProtocol::DoState(StateWrapper& sw)
{
if (!sw.DoMarker("Multitap"))
return false;
sw.Do(&currentPadSlot);
sw.Do(&currentMemcardSlot);
return true;
}
u8 MultitapProtocol::GetPadSlot()
{
return this->currentPadSlot;

View File

@ -15,6 +15,12 @@
#pragma once
#include "SIO/SioTypes.h"
#include <array>
class StateWrapper;
enum class MultitapMode
{
NOT_SET = 0xff,
@ -39,6 +45,7 @@ public:
void SoftReset();
void FullReset();
bool DoState(StateWrapper& sw);
u8 GetPadSlot();
u8 GetMemcardSlot();
@ -46,6 +53,5 @@ public:
void SendToMultitap();
};
extern MultitapProtocol g_MultitapPort0;
extern MultitapProtocol g_MultitapPort1;
extern std::array<MultitapProtocol, SIO::PORTS> g_MultitapArr;

View File

@ -49,8 +49,7 @@ namespace Pad
static const char* GetControllerTypeName(Pad::ControllerType type);
static std::unique_ptr<PadBase> CreatePad(u8 unifiedSlot, Pad::ControllerType controllerType);
static PadBase* ChangePadType(u8 unifiedSlot, Pad::ControllerType controllerType);
static PadBase* CreatePad(u8 unifiedSlot, Pad::ControllerType controllerType, size_t ejectTicks = 0);
static void LoadMacroButtonConfig(
const SettingsInterface& si, u32 pad, const ControllerInfo* ci, const std::string& section);
@ -91,7 +90,6 @@ void Pad::LoadConfig(const SettingsInterface& si)
{
s_macro_buttons = {};
// This is where we would load controller types, if onepad supported them.
for (u32 i = 0; i < Pad::NUM_CONTROLLER_PORTS; i++)
{
const std::string section = GetConfigSection(i);
@ -99,11 +97,12 @@ void Pad::LoadConfig(const SettingsInterface& si)
pxAssert(ci);
// If a pad is not yet constructed, at minimum place a NotConnected pad in the slot.
// Do not abort the for loop - If there pad settings, we want those to be applied to the slot.
// Do not abort the for loop - If there are pad settings, we want those to be applied to the slot.
PadBase* pad = Pad::GetPad(i);
if (!pad || pad->GetType() != ci->type)
{
pad = Pad::ChangePadType(i, ci->type);
pad = Pad::CreatePad(i, ci->type);
pxAssert(pad);
}
@ -471,22 +470,22 @@ std::string Pad::GetConfigSection(u32 pad_index)
return fmt::format("Pad{}", pad_index + 1);
}
std::unique_ptr<PadBase> Pad::CreatePad(u8 unifiedSlot, ControllerType controllerType)
// Create a new pad instance, update the smart pointer for this pad slot, and return a dumb pointer to the new pad.
PadBase* Pad::CreatePad(u8 unifiedSlot, ControllerType controllerType, size_t ejectTicks)
{
switch (controllerType)
{
case ControllerType::DualShock2:
return std::make_unique<PadDualshock2>(unifiedSlot);
s_controllers[unifiedSlot] = std::make_unique<PadDualshock2>(unifiedSlot, ejectTicks);
break;
case ControllerType::Guitar:
return std::make_unique<PadGuitar>(unifiedSlot);
s_controllers[unifiedSlot] = std::make_unique<PadGuitar>(unifiedSlot, ejectTicks);
break;
default:
return std::make_unique<PadNotConnected>(unifiedSlot);
s_controllers[unifiedSlot] = std::make_unique<PadNotConnected>(unifiedSlot, ejectTicks);
break;
}
}
PadBase* Pad::ChangePadType(u8 unifiedSlot, ControllerType controllerType)
{
s_controllers[unifiedSlot] = CreatePad(unifiedSlot, controllerType);
return s_controllers[unifiedSlot].get();
}
@ -532,7 +531,7 @@ bool Pad::Freeze(StateWrapper& sw)
if (sw.HasError())
return false;
std::unique_ptr<PadBase> tempPad;
PadBase* tempPad;
PadBase* pad = GetPad(unifiedSlot);
if (!pad || pad->GetType() != type)
{
@ -551,7 +550,7 @@ bool Pad::Freeze(StateWrapper& sw)
// But we still need to pull the data from the state..
tempPad = CreatePad(unifiedSlot, type);
pad = tempPad.get();
pad = tempPad;
}
if (!pad->Freeze(sw))

View File

@ -144,9 +144,7 @@ void Sio2::SetRecv1(u32 value)
void Sio2::Pad()
{
MultitapProtocol& mtap = (port ? g_MultitapPort1 : g_MultitapPort0);
// Send PAD our current port, and get back whatever it says the first response byte should be.
MultitapProtocol& mtap = g_MultitapArr.at(port);
PadBase* pad = Pad::GetPad(port, mtap.GetPadSlot());
// Update the third nibble with which ports have been accessed
@ -177,10 +175,9 @@ void Sio2::Pad()
}
g_Sio2FifoOut.push_back(0xff);
// Then for every byte in g_Sio2FifoIn, pass to PAD and see what it kicks back to us.
pad->SoftReset();
// Then for every byte in g_Sio2FifoIn, pass to PAD and see what it kicks back to us.
while (!g_Sio2FifoIn.empty())
{
const u8 commandByte = g_Sio2FifoIn.front();
@ -208,8 +205,8 @@ void Sio2::Multitap()
// This bit is always set, whether the pad is present or missing
this->recv1 |= Recv1::NO_DEVICES_MISSING;
// If the currently accessed pad is missing, also tick those bits.
// MTAPMAN is special - at least, the variant found in Gauntlet: Dark Legacy.
// If the currently accessed multitap is missing, also tick those bits.
// MTAPMAN is special though.
//
// For PADMAN and pads, the bits represented by PORT_1_MISSING and PORT_2_MISSING
// are always faithful - suppose your game only opened port 2 for some reason,
@ -223,18 +220,7 @@ void Sio2::Multitap()
this->recv1 |= Recv1::PORT_1_MISSING;
}
switch (this->port)
{
case 0:
g_MultitapPort0.SendToMultitap();
break;
case 1:
g_MultitapPort1.SendToMultitap();
break;
default:
Console.Warning("%s() Port value from SEND3 out of bounds! (%d)", __FUNCTION__, this->port);
break;
}
g_MultitapArr.at(this->port).SendToMultitap();
}
void Sio2::Infrared()
@ -252,7 +238,7 @@ void Sio2::Infrared()
void Sio2::Memcard()
{
MultitapProtocol& mtap = (port ? g_MultitapPort1 : g_MultitapPort0);
MultitapProtocol& mtap = g_MultitapArr.at(this->port);
mcd = &mcds[port][mtap.GetMemcardSlot()];
@ -511,7 +497,6 @@ bool Sio2::DoState(StateWrapper& sw)
sw.Do(&unknown2);
sw.Do(&iStat);
sw.Do(&port);
sw.Do(&slot);
sw.Do(&send3Read);
sw.Do(&send3Position);
sw.Do(&commandLength);

View File

@ -40,7 +40,6 @@ public:
u32 iStat; // 0x1f808280
u8 port = 0;
u8 slot = 0;
// The current working index of SEND3. The SEND3 register is a 16 position
// array of command descriptors. Each descriptor describes the port the command

View File

@ -33,6 +33,7 @@
#include "R3000A.h"
#include "SIO/Sio0.h"
#include "SIO/Sio2.h"
#include "SIO/Multitap/MultitapProtocol.h"
#include "SPU2/spu2.h"
#include "SaveState.h"
#include "StateWrapper.h"
@ -259,6 +260,9 @@ bool SaveStateBase::FreezeInternals()
okay = okay && g_Sio0.DoState(sw);
okay = okay && g_Sio2.DoState(sw);
okay = okay && g_MultitapArr.at(0).DoState(sw);
okay = okay && g_MultitapArr.at(1).DoState(sw);
if (!okay || !sw.IsGood())
return false;

View File

@ -37,7 +37,7 @@ enum class FreezeAction
// [SAVEVERSION+]
// This informs the auto updater that the users savestates will be invalidated.
static const u32 g_SaveVersion = (0x9A48 << 16) | 0x0000;
static const u32 g_SaveVersion = (0x9A49 << 16) | 0x0000;
// the freezing data between submodules and core