Merge pull request #8879 from AdmiralCurtiss/gci-folder-savestates
Keep memory card header and use it to reinitialize GCI folders on mismatch. (Fix savestate with GCI folders)
This commit is contained in:
commit
cf36877608
|
@ -9,17 +9,21 @@
|
||||||
|
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Common/IniFile.h"
|
||||||
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/HW/EXI/EXI_Channel.h"
|
#include "Core/HW/EXI/EXI_Channel.h"
|
||||||
#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
|
#include "Core/HW/EXI/EXI_DeviceMemoryCard.h"
|
||||||
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||||
#include "Core/HW/MMIO.h"
|
#include "Core/HW/MMIO.h"
|
||||||
#include "Core/HW/ProcessorInterface.h"
|
#include "Core/HW/ProcessorInterface.h"
|
||||||
#include "Core/HW/Sram.h"
|
#include "Core/HW/Sram.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/Movie.h"
|
#include "Core/Movie.h"
|
||||||
|
|
||||||
|
#include "DiscIO/Enums.h"
|
||||||
|
|
||||||
Sram g_SRAM;
|
Sram g_SRAM;
|
||||||
bool g_SRAM_netplay_initialized = false;
|
bool g_SRAM_netplay_initialized = false;
|
||||||
|
|
||||||
|
@ -69,8 +73,29 @@ void Init()
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIMemoryCard::Init();
|
CEXIMemoryCard::Init();
|
||||||
for (u32 i = 0; i < MAX_EXI_CHANNELS; i++)
|
|
||||||
g_Channels[i] = std::make_unique<CEXIChannel>(i);
|
{
|
||||||
|
bool use_memcard_251;
|
||||||
|
IniFile gameIni = SConfig::GetInstance().LoadGameIni();
|
||||||
|
gameIni.GetOrCreateSection("Core")->Get("MemoryCard251", &use_memcard_251, false);
|
||||||
|
const u16 size_mbits =
|
||||||
|
use_memcard_251 ? Memcard::MBIT_SIZE_MEMORY_CARD_251 : Memcard::MBIT_SIZE_MEMORY_CARD_2043;
|
||||||
|
const bool shift_jis =
|
||||||
|
SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region) == DiscIO::Region::NTSC_J;
|
||||||
|
const CardFlashId& flash_id = g_SRAM.settings_ex.flash_id[Memcard::SLOT_A];
|
||||||
|
const u32 rtc_bias = g_SRAM.settings.rtc_bias;
|
||||||
|
const u32 sram_language = static_cast<u32>(g_SRAM.settings.language);
|
||||||
|
const u64 format_time =
|
||||||
|
Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
|
||||||
|
|
||||||
|
for (u32 i = 0; i < MAX_EXI_CHANNELS; i++)
|
||||||
|
{
|
||||||
|
Memcard::HeaderData header_data;
|
||||||
|
Memcard::InitializeHeaderData(&header_data, flash_id, size_mbits, shift_jis, rtc_bias,
|
||||||
|
sram_language, format_time + i);
|
||||||
|
g_Channels[i] = std::make_unique<CEXIChannel>(i, header_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < MAX_MEMORYCARD_SLOTS; i++)
|
for (int i = 0; i < MAX_MEMORYCARD_SLOTS; i++)
|
||||||
AddMemoryCards(i);
|
AddMemoryCards(i);
|
||||||
|
@ -126,16 +151,16 @@ static void ChangeDeviceCallback(u64 userdata, s64 cyclesLate)
|
||||||
g_Channels.at(channel)->AddDevice((TEXIDevices)type, num);
|
g_Channels.at(channel)->AddDevice((TEXIDevices)type, num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num)
|
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num,
|
||||||
|
CoreTiming::FromThread from_thread)
|
||||||
{
|
{
|
||||||
// Called from GUI, so we need to use FromThread::NON_CPU.
|
|
||||||
// Let the hardware see no device for 1 second
|
// Let the hardware see no device for 1 second
|
||||||
CoreTiming::ScheduleEvent(0, changeDevice,
|
CoreTiming::ScheduleEvent(0, changeDevice,
|
||||||
((u64)channel << 32) | ((u64)EXIDEVICE_NONE << 16) | device_num,
|
((u64)channel << 32) | ((u64)EXIDEVICE_NONE << 16) | device_num,
|
||||||
CoreTiming::FromThread::NON_CPU);
|
from_thread);
|
||||||
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), changeDevice,
|
CoreTiming::ScheduleEvent(SystemTimers::GetTicksPerSecond(), changeDevice,
|
||||||
((u64)channel << 32) | ((u64)device_type << 16) | device_num,
|
((u64)channel << 32) | ((u64)device_type << 16) | device_num,
|
||||||
CoreTiming::FromThread::NON_CPU);
|
from_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIChannel* GetChannel(u32 index)
|
CEXIChannel* GetChannel(u32 index)
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
|
@ -39,7 +40,8 @@ void RegisterMMIO(MMIO::Mapping* mmio, u32 base);
|
||||||
void UpdateInterrupts();
|
void UpdateInterrupts();
|
||||||
void ScheduleUpdateInterrupts(CoreTiming::FromThread from, int cycles_late);
|
void ScheduleUpdateInterrupts(CoreTiming::FromThread from, int cycles_late);
|
||||||
|
|
||||||
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num);
|
void ChangeDevice(const u8 channel, const TEXIDevices device_type, const u8 device_num,
|
||||||
|
CoreTiming::FromThread from_thread = CoreTiming::FromThread::NON_CPU);
|
||||||
|
|
||||||
CEXIChannel* GetChannel(u32 index);
|
CEXIChannel* GetChannel(u32 index);
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,12 @@
|
||||||
#include "Common/Assert.h"
|
#include "Common/Assert.h"
|
||||||
#include "Common/ChunkFile.h"
|
#include "Common/ChunkFile.h"
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/HW/EXI/EXI.h"
|
#include "Core/HW/EXI/EXI.h"
|
||||||
#include "Core/HW/EXI/EXI_Device.h"
|
#include "Core/HW/EXI/EXI_Device.h"
|
||||||
#include "Core/HW/MMIO.h"
|
#include "Core/HW/MMIO.h"
|
||||||
|
#include "Core/Movie.h"
|
||||||
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
|
@ -22,7 +25,8 @@ enum
|
||||||
EXI_READWRITE
|
EXI_READWRITE
|
||||||
};
|
};
|
||||||
|
|
||||||
CEXIChannel::CEXIChannel(u32 channel_id) : m_channel_id(channel_id)
|
CEXIChannel::CEXIChannel(u32 channel_id, const Memcard::HeaderData& memcard_header_data)
|
||||||
|
: m_channel_id(channel_id), m_memcard_header_data(memcard_header_data)
|
||||||
{
|
{
|
||||||
if (m_channel_id == 0 || m_channel_id == 1)
|
if (m_channel_id == 0 || m_channel_id == 1)
|
||||||
m_status.EXTINT = 1;
|
m_status.EXTINT = 1;
|
||||||
|
@ -30,7 +34,7 @@ CEXIChannel::CEXIChannel(u32 channel_id) : m_channel_id(channel_id)
|
||||||
m_status.CHIP_SELECT = 1;
|
m_status.CHIP_SELECT = 1;
|
||||||
|
|
||||||
for (auto& device : m_devices)
|
for (auto& device : m_devices)
|
||||||
device = EXIDevice_Create(EXIDEVICE_NONE, m_channel_id);
|
device = EXIDevice_Create(EXIDEVICE_NONE, m_channel_id, m_memcard_header_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIChannel::~CEXIChannel()
|
CEXIChannel::~CEXIChannel()
|
||||||
|
@ -166,7 +170,7 @@ void CEXIChannel::RemoveDevices()
|
||||||
|
|
||||||
void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num)
|
void CEXIChannel::AddDevice(const TEXIDevices device_type, const int device_num)
|
||||||
{
|
{
|
||||||
AddDevice(EXIDevice_Create(device_type, m_channel_id), device_num);
|
AddDevice(EXIDevice_Create(device_type, m_channel_id, m_memcard_header_data), device_num);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIChannel::AddDevice(std::unique_ptr<IEXIDevice> device, const int device_num,
|
void CEXIChannel::AddDevice(std::unique_ptr<IEXIDevice> device, const int device_num,
|
||||||
|
@ -174,6 +178,11 @@ void CEXIChannel::AddDevice(std::unique_ptr<IEXIDevice> device, const int device
|
||||||
{
|
{
|
||||||
DEBUG_ASSERT(device_num < NUM_DEVICES);
|
DEBUG_ASSERT(device_num < NUM_DEVICES);
|
||||||
|
|
||||||
|
INFO_LOG(EXPANSIONINTERFACE,
|
||||||
|
"Changing EXI channel %d, device %d to type %d (notify software: %s)",
|
||||||
|
static_cast<int>(m_channel_id), device_num, static_cast<int>(device->m_device_type),
|
||||||
|
notify_presence_changed ? "true" : "false");
|
||||||
|
|
||||||
// Replace it with the new one
|
// Replace it with the new one
|
||||||
m_devices[device_num] = std::move(device);
|
m_devices[device_num] = std::move(device);
|
||||||
|
|
||||||
|
@ -230,6 +239,9 @@ void CEXIChannel::DoState(PointerWrap& p)
|
||||||
p.Do(m_control);
|
p.Do(m_control);
|
||||||
p.Do(m_imm_data);
|
p.Do(m_imm_data);
|
||||||
|
|
||||||
|
Memcard::HeaderData old_header_data = m_memcard_header_data;
|
||||||
|
p.DoPOD(m_memcard_header_data);
|
||||||
|
|
||||||
for (int device_index = 0; device_index < NUM_DEVICES; ++device_index)
|
for (int device_index = 0; device_index < NUM_DEVICES; ++device_index)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IEXIDevice>& device = m_devices[device_index];
|
std::unique_ptr<IEXIDevice>& device = m_devices[device_index];
|
||||||
|
@ -242,10 +254,33 @@ void CEXIChannel::DoState(PointerWrap& p)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::unique_ptr<IEXIDevice> save_device = EXIDevice_Create(type, m_channel_id);
|
std::unique_ptr<IEXIDevice> save_device =
|
||||||
|
EXIDevice_Create(type, m_channel_id, m_memcard_header_data);
|
||||||
save_device->DoState(p);
|
save_device->DoState(p);
|
||||||
AddDevice(std::move(save_device), device_index, false);
|
AddDevice(std::move(save_device), device_index, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (type == EXIDEVICE_MEMORYCARDFOLDER && old_header_data != m_memcard_header_data &&
|
||||||
|
!Movie::IsMovieActive())
|
||||||
|
{
|
||||||
|
// We have loaded a savestate that has a GCI folder memcard that is different to the virtual
|
||||||
|
// card that is currently active. In order to prevent the game from recognizing this card as a
|
||||||
|
// 'different' memory card and preventing saving on it, we need to reinitialize the GCI folder
|
||||||
|
// card here with the loaded header data.
|
||||||
|
// We're intentionally calling ExpansionInterface::ChangeDevice() here instead of changing it
|
||||||
|
// directly so we don't switch immediately but after a delay, as if changed in the GUI. This
|
||||||
|
// should prevent games from assuming any stale data about the memory card, such as location
|
||||||
|
// of the individual save blocks, which may be different on the reinitialized card.
|
||||||
|
// Additionally, we immediately force the memory card to None so that any 'in-flight' writes
|
||||||
|
// (if someone managed to savestate while saving...) don't happen to hit the card.
|
||||||
|
// TODO: It might actually be enough to just switch to the card with the
|
||||||
|
// notify_presence_changed flag set to true? Not sure how software behaves if the previous and
|
||||||
|
// the new device type are identical in this case. I assume there is a reason we have this
|
||||||
|
// grace period when switching in the GUI.
|
||||||
|
AddDevice(EXIDEVICE_NONE, device_index);
|
||||||
|
ExpansionInterface::ChangeDevice(m_channel_id, EXIDEVICE_MEMORYCARDFOLDER, device_index,
|
||||||
|
CoreTiming::FromThread::CPU);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,11 @@
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "Common/CommonTypes.h"
|
#include "Common/CommonTypes.h"
|
||||||
|
|
||||||
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
namespace MMIO
|
namespace MMIO
|
||||||
|
@ -23,7 +26,7 @@ enum TEXIDevices : int;
|
||||||
class CEXIChannel
|
class CEXIChannel
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
explicit CEXIChannel(u32 channel_id);
|
explicit CEXIChannel(u32 channel_id, const Memcard::HeaderData& memcard_header_data);
|
||||||
~CEXIChannel();
|
~CEXIChannel();
|
||||||
|
|
||||||
// get device
|
// get device
|
||||||
|
@ -106,6 +109,12 @@ private:
|
||||||
UEXI_CONTROL m_control;
|
UEXI_CONTROL m_control;
|
||||||
u32 m_imm_data = 0;
|
u32 m_imm_data = 0;
|
||||||
|
|
||||||
|
// This data is needed in order to reinitialize a GCI folder memory card when switching between
|
||||||
|
// GCI folder and other devices in the memory card slot or after loading a savestate. Even though
|
||||||
|
// this data is only vaguely related to the EXI_Channel, this seems to be the best place to store
|
||||||
|
// it, as this class creates the CEXIMemoryCard instances.
|
||||||
|
Memcard::HeaderData m_memcard_header_data;
|
||||||
|
|
||||||
// Devices
|
// Devices
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
|
|
@ -102,7 +102,8 @@ void IEXIDevice::TransferByte(u8& byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
// F A C T O R Y
|
// F A C T O R Y
|
||||||
std::unique_ptr<IEXIDevice> EXIDevice_Create(const TEXIDevices device_type, const int channel_num)
|
std::unique_ptr<IEXIDevice> EXIDevice_Create(const TEXIDevices device_type, const int channel_num,
|
||||||
|
const Memcard::HeaderData& memcard_header_data)
|
||||||
{
|
{
|
||||||
std::unique_ptr<IEXIDevice> result;
|
std::unique_ptr<IEXIDevice> result;
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ std::unique_ptr<IEXIDevice> EXIDevice_Create(const TEXIDevices device_type, cons
|
||||||
case EXIDEVICE_MEMORYCARDFOLDER:
|
case EXIDEVICE_MEMORYCARDFOLDER:
|
||||||
{
|
{
|
||||||
bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER);
|
bool gci_folder = (device_type == EXIDEVICE_MEMORYCARDFOLDER);
|
||||||
result = std::make_unique<CEXIMemoryCard>(channel_num, gci_folder);
|
result = std::make_unique<CEXIMemoryCard>(channel_num, gci_folder, memcard_header_data);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case EXIDEVICE_MASKROM:
|
case EXIDEVICE_MASKROM:
|
||||||
|
|
|
@ -9,6 +9,11 @@
|
||||||
|
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace Memcard
|
||||||
|
{
|
||||||
|
struct HeaderData;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
enum TEXIDevices : int
|
enum TEXIDevices : int
|
||||||
|
@ -65,5 +70,6 @@ private:
|
||||||
virtual void TransferByte(u8& byte);
|
virtual void TransferByte(u8& byte);
|
||||||
};
|
};
|
||||||
|
|
||||||
std::unique_ptr<IEXIDevice> EXIDevice_Create(TEXIDevices device_type, int channel_num);
|
std::unique_ptr<IEXIDevice> EXIDevice_Create(TEXIDevices device_type, int channel_num,
|
||||||
|
const Memcard::HeaderData& memcard_header_data);
|
||||||
} // namespace ExpansionInterface
|
} // namespace ExpansionInterface
|
||||||
|
|
|
@ -106,7 +106,9 @@ void CEXIMemoryCard::Shutdown()
|
||||||
s_et_transfer_complete.fill(nullptr);
|
s_et_transfer_complete.fill(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(index)
|
CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder,
|
||||||
|
const Memcard::HeaderData& header_data)
|
||||||
|
: card_index(index)
|
||||||
{
|
{
|
||||||
ASSERT_MSG(EXPANSIONINTERFACE, static_cast<std::size_t>(index) < s_et_cmd_done.size(),
|
ASSERT_MSG(EXPANSIONINTERFACE, static_cast<std::size_t>(index) < s_et_cmd_done.size(),
|
||||||
"Trying to create invalid memory card index %d.", index);
|
"Trying to create invalid memory card index %d.", index);
|
||||||
|
@ -132,25 +134,13 @@ CEXIMemoryCard::CEXIMemoryCard(const int index, bool gciFolder) : card_index(ind
|
||||||
// card_id = 0xc243;
|
// card_id = 0xc243;
|
||||||
card_id = 0xc221; // It's a Nintendo brand memcard
|
card_id = 0xc221; // It's a Nintendo brand memcard
|
||||||
|
|
||||||
// The following games have issues with memory cards bigger than 16Mb
|
|
||||||
// Darkened Skye GDQE6S GDQP6S
|
|
||||||
// WTA Tour Tennis GWTEA4 GWTJA4 GWTPA4
|
|
||||||
// Disney Sports : Skate Boarding GDXEA4 GDXPA4 GDXJA4
|
|
||||||
// Disney Sports : Soccer GDKEA4
|
|
||||||
// Wallace and Gromit in Pet Zoo GWLE6L GWLX6L
|
|
||||||
// Use a 16Mb (251 block) memory card for these games
|
|
||||||
bool useMC251;
|
|
||||||
IniFile gameIni = SConfig::GetInstance().LoadGameIni();
|
|
||||||
gameIni.GetOrCreateSection("Core")->Get("MemoryCard251", &useMC251, false);
|
|
||||||
u16 sizeMb = useMC251 ? Memcard::MBIT_SIZE_MEMORY_CARD_251 : Memcard::MBIT_SIZE_MEMORY_CARD_2043;
|
|
||||||
|
|
||||||
if (gciFolder)
|
if (gciFolder)
|
||||||
{
|
{
|
||||||
SetupGciFolder(sizeMb);
|
SetupGciFolder(header_data);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
SetupRawMemcard(sizeMb);
|
SetupRawMemcard(header_data.m_size_mb);
|
||||||
}
|
}
|
||||||
|
|
||||||
memory_card_size = memorycard->GetCardId() * SIZE_TO_Mb;
|
memory_card_size = memorycard->GetCardId() * SIZE_TO_Mb;
|
||||||
|
@ -185,7 +175,7 @@ CEXIMemoryCard::GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_fo
|
||||||
return {std::move(path), !use_movie_folder};
|
return {std::move(path), !use_movie_folder};
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
|
void CEXIMemoryCard::SetupGciFolder(const Memcard::HeaderData& header_data)
|
||||||
{
|
{
|
||||||
const std::string& game_id = SConfig::GetInstance().GetGameID();
|
const std::string& game_id = SConfig::GetInstance().GetGameID();
|
||||||
u32 CurrentGameId = 0;
|
u32 CurrentGameId = 0;
|
||||||
|
@ -195,8 +185,7 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
|
||||||
CurrentGameId = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
|
CurrentGameId = Common::swap32(reinterpret_cast<const u8*>(game_id.c_str()));
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool shift_jis =
|
const bool shift_jis = header_data.m_encoding != 0;
|
||||||
SConfig::ToGameCubeRegion(SConfig::GetInstance().m_region) == DiscIO::Region::NTSC_J;
|
|
||||||
|
|
||||||
const auto [strDirectoryName, migrate] = GetGCIFolderPath(card_index, AllowMovieFolder::Yes);
|
const auto [strDirectoryName, migrate] = GetGCIFolderPath(card_index, AllowMovieFolder::Yes);
|
||||||
|
|
||||||
|
@ -228,8 +217,8 @@ void CEXIMemoryCard::SetupGciFolder(u16 sizeMb)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
memorycard = std::make_unique<GCMemcardDirectory>(strDirectoryName + DIR_SEP, card_index, sizeMb,
|
memorycard = std::make_unique<GCMemcardDirectory>(strDirectoryName + DIR_SEP, card_index,
|
||||||
shift_jis, CurrentGameId);
|
header_data, CurrentGameId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
|
void CEXIMemoryCard::SetupRawMemcard(u16 sizeMb)
|
||||||
|
|
|
@ -14,6 +14,11 @@
|
||||||
class MemoryCardBase;
|
class MemoryCardBase;
|
||||||
class PointerWrap;
|
class PointerWrap;
|
||||||
|
|
||||||
|
namespace Memcard
|
||||||
|
{
|
||||||
|
struct HeaderData;
|
||||||
|
}
|
||||||
|
|
||||||
namespace ExpansionInterface
|
namespace ExpansionInterface
|
||||||
{
|
{
|
||||||
enum class AllowMovieFolder
|
enum class AllowMovieFolder
|
||||||
|
@ -25,7 +30,7 @@ enum class AllowMovieFolder
|
||||||
class CEXIMemoryCard : public IEXIDevice
|
class CEXIMemoryCard : public IEXIDevice
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
CEXIMemoryCard(const int index, bool gciFolder);
|
CEXIMemoryCard(const int index, bool gciFolder, const Memcard::HeaderData& header_data);
|
||||||
virtual ~CEXIMemoryCard();
|
virtual ~CEXIMemoryCard();
|
||||||
void SetCS(int cs) override;
|
void SetCS(int cs) override;
|
||||||
bool IsInterruptSet() override;
|
bool IsInterruptSet() override;
|
||||||
|
@ -46,7 +51,7 @@ public:
|
||||||
GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder);
|
GetGCIFolderPath(int card_index, AllowMovieFolder allow_movie_folder);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupGciFolder(u16 sizeMb);
|
void SetupGciFolder(const Memcard::HeaderData& header_data);
|
||||||
void SetupRawMemcard(u16 sizeMb);
|
void SetupRawMemcard(u16 sizeMb);
|
||||||
static void EventCompleteFindInstance(u64 userdata,
|
static void EventCompleteFindInstance(u64 userdata,
|
||||||
std::function<void(CEXIMemoryCard*)> callback);
|
std::function<void(CEXIMemoryCard*)> callback);
|
||||||
|
|
|
@ -64,14 +64,16 @@ GCMemcard::GCMemcard()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<GCMemcard> GCMemcard::Create(std::string filename, u16 size_mbits, bool shift_jis)
|
std::optional<GCMemcard> GCMemcard::Create(std::string filename, const CardFlashId& flash_id,
|
||||||
|
u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
|
u32 sram_language, u64 format_time)
|
||||||
{
|
{
|
||||||
GCMemcard card;
|
GCMemcard card;
|
||||||
card.m_filename = std::move(filename);
|
card.m_filename = std::move(filename);
|
||||||
|
|
||||||
// TODO: Format() not only formats the card but also writes it to disk at m_filename.
|
// TODO: Format() not only formats the card but also writes it to disk at m_filename.
|
||||||
// Those tasks should probably be separated.
|
// Those tasks should probably be separated.
|
||||||
if (!card.Format(shift_jis, size_mbits))
|
if (!card.Format(flash_id, size_mbits, shift_jis, rtc_bias, sram_language, format_time))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
|
||||||
return std::move(card);
|
return std::move(card);
|
||||||
|
@ -302,7 +304,7 @@ void GCMemcard::UpdateBat(const BlockAlloc& bat)
|
||||||
|
|
||||||
bool GCMemcard::IsShiftJIS() const
|
bool GCMemcard::IsShiftJIS() const
|
||||||
{
|
{
|
||||||
return m_header_block.m_encoding != 0;
|
return m_header_block.m_data.m_encoding != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCMemcard::Save()
|
bool GCMemcard::Save()
|
||||||
|
@ -1367,29 +1369,32 @@ std::optional<std::vector<GCMemcardAnimationFrameRGBA8>> GCMemcard::ReadAnimRGBA
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCMemcard::Format(u8* card_data, bool shift_jis, u16 SizeMb)
|
bool GCMemcard::Format(u8* card_data, const CardFlashId& flash_id, u16 size_mbits, bool shift_jis,
|
||||||
|
u32 rtc_bias, u32 sram_language, u64 format_time)
|
||||||
{
|
{
|
||||||
if (!card_data)
|
if (!card_data)
|
||||||
return false;
|
return false;
|
||||||
memset(card_data, 0xFF, BLOCK_SIZE * 3);
|
memset(card_data, 0xFF, BLOCK_SIZE * 3);
|
||||||
memset(card_data + BLOCK_SIZE * 3, 0, BLOCK_SIZE * 2);
|
memset(card_data + BLOCK_SIZE * 3, 0, BLOCK_SIZE * 2);
|
||||||
|
|
||||||
*((Header*)card_data) = Header(SLOT_A, SizeMb, shift_jis);
|
*((Header*)card_data) =
|
||||||
|
Header(flash_id, size_mbits, shift_jis, rtc_bias, sram_language, format_time);
|
||||||
|
|
||||||
*((Directory*)(card_data + BLOCK_SIZE)) = Directory();
|
*((Directory*)(card_data + BLOCK_SIZE)) = Directory();
|
||||||
*((Directory*)(card_data + BLOCK_SIZE * 2)) = Directory();
|
*((Directory*)(card_data + BLOCK_SIZE * 2)) = Directory();
|
||||||
*((BlockAlloc*)(card_data + BLOCK_SIZE * 3)) = BlockAlloc(SizeMb);
|
*((BlockAlloc*)(card_data + BLOCK_SIZE * 3)) = BlockAlloc(size_mbits);
|
||||||
*((BlockAlloc*)(card_data + BLOCK_SIZE * 4)) = BlockAlloc(SizeMb);
|
*((BlockAlloc*)(card_data + BLOCK_SIZE * 4)) = BlockAlloc(size_mbits);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GCMemcard::Format(bool shift_jis, u16 SizeMb)
|
bool GCMemcard::Format(const CardFlashId& flash_id, u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
|
u32 sram_language, u64 format_time)
|
||||||
{
|
{
|
||||||
m_header_block = Header(SLOT_A, SizeMb, shift_jis);
|
m_header_block = Header(flash_id, size_mbits, shift_jis, rtc_bias, sram_language, format_time);
|
||||||
m_directory_blocks[0] = m_directory_blocks[1] = Directory();
|
m_directory_blocks[0] = m_directory_blocks[1] = Directory();
|
||||||
m_bat_blocks[0] = m_bat_blocks[1] = BlockAlloc(SizeMb);
|
m_bat_blocks[0] = m_bat_blocks[1] = BlockAlloc(size_mbits);
|
||||||
|
|
||||||
m_size_mb = SizeMb;
|
m_size_mb = size_mbits;
|
||||||
m_size_blocks = (u32)m_size_mb * MBIT_TO_BLOCKS;
|
m_size_blocks = (u32)m_size_mb * MBIT_TO_BLOCKS;
|
||||||
m_data_blocks.clear();
|
m_data_blocks.clear();
|
||||||
m_data_blocks.resize(m_size_blocks - MC_FST_BLOCKS);
|
m_data_blocks.resize(m_size_blocks - MC_FST_BLOCKS);
|
||||||
|
@ -1538,30 +1543,65 @@ void GCMBlock::Erase()
|
||||||
memset(m_block.data(), 0xFF, m_block.size());
|
memset(m_block.data(), 0xFF, m_block.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
Header::Header(int slot, u16 size_mbits, bool shift_jis)
|
Header::Header()
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<Header>);
|
||||||
|
std::memset(this, 0xFF, BLOCK_SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeHeaderData(HeaderData* data, const CardFlashId& flash_id, u16 size_mbits,
|
||||||
|
bool shift_jis, u32 rtc_bias, u32 sram_language, u64 format_time)
|
||||||
{
|
{
|
||||||
// Nintendo format algorithm.
|
// Nintendo format algorithm.
|
||||||
// Constants are fixed by the GC SDK
|
// Constants are fixed by the GC SDK
|
||||||
// Changing the constants will break memory card support
|
// Changing the constants will break memory card support
|
||||||
memset(this, 0xFF, BLOCK_SIZE);
|
data->m_size_mb = size_mbits;
|
||||||
m_size_mb = size_mbits;
|
data->m_encoding = shift_jis ? 1 : 0;
|
||||||
m_encoding = shift_jis ? 1 : 0;
|
data->m_format_time = format_time;
|
||||||
u64 rand = Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
|
u64 rand = format_time;
|
||||||
m_format_time = rand;
|
|
||||||
for (int i = 0; i < 12; i++)
|
for (int i = 0; i < 12; i++)
|
||||||
{
|
{
|
||||||
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
||||||
m_serial[i] = (u8)(g_SRAM.settings_ex.flash_id[slot][i] + (u32)rand);
|
data->m_serial[i] = (u8)(flash_id[i] + (u32)rand);
|
||||||
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
rand = (((rand * (u64)0x0000000041c64e6dULL) + (u64)0x0000000000003039ULL) >> 16);
|
||||||
rand &= (u64)0x0000000000007fffULL;
|
rand &= (u64)0x0000000000007fffULL;
|
||||||
}
|
}
|
||||||
m_sram_bias = g_SRAM.settings.rtc_bias;
|
data->m_sram_bias = rtc_bias;
|
||||||
m_sram_language = static_cast<u32>(g_SRAM.settings.language);
|
data->m_sram_language = sram_language;
|
||||||
// TODO: determine the purpose of m_unknown_2
|
// TODO: determine the purpose of m_unknown_2
|
||||||
// 1 works for slot A, 0 works for both slot A and slot B
|
// 1 works for slot A, 0 works for both slot A and slot B
|
||||||
memset(m_unknown_2.data(), 0,
|
std::memset(
|
||||||
m_unknown_2.size()); // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
|
data->m_unknown_2.data(), 0,
|
||||||
m_device_id = 0;
|
data->m_unknown_2.size()); // = _viReg[55]; static vu16* const _viReg = (u16*)0xCC002000;
|
||||||
|
data->m_device_id = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const HeaderData& lhs, const HeaderData& rhs)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<HeaderData>);
|
||||||
|
return std::memcmp(&lhs, &rhs, sizeof(HeaderData)) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator!=(const HeaderData& lhs, const HeaderData& rhs)
|
||||||
|
{
|
||||||
|
return !(lhs == rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
Header::Header(const CardFlashId& flash_id, u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
|
u32 sram_language, u64 format_time)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<Header>);
|
||||||
|
std::memset(this, 0xFF, BLOCK_SIZE);
|
||||||
|
InitializeHeaderData(&m_data, flash_id, size_mbits, shift_jis, rtc_bias, sram_language,
|
||||||
|
format_time);
|
||||||
|
FixChecksums();
|
||||||
|
}
|
||||||
|
|
||||||
|
Header::Header(const HeaderData& data)
|
||||||
|
{
|
||||||
|
static_assert(std::is_trivially_copyable_v<Header>);
|
||||||
|
std::memset(this, 0xFF, BLOCK_SIZE);
|
||||||
|
m_data = data;
|
||||||
FixChecksums();
|
FixChecksums();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1609,7 +1649,7 @@ std::pair<u16, u16> Header::CalculateChecksums() const
|
||||||
std::array<u8, sizeof(Header)> raw;
|
std::array<u8, sizeof(Header)> raw;
|
||||||
memcpy(raw.data(), this, raw.size());
|
memcpy(raw.data(), this, raw.size());
|
||||||
|
|
||||||
constexpr size_t checksum_area_start = offsetof(Header, m_serial);
|
constexpr size_t checksum_area_start = offsetof(Header, m_data);
|
||||||
constexpr size_t checksum_area_end = offsetof(Header, m_checksum);
|
constexpr size_t checksum_area_end = offsetof(Header, m_checksum);
|
||||||
constexpr size_t checksum_area_size = checksum_area_end - checksum_area_start;
|
constexpr size_t checksum_area_size = checksum_area_end - checksum_area_start;
|
||||||
return CalculateMemcardChecksums(&raw[checksum_area_start], checksum_area_size);
|
return CalculateMemcardChecksums(&raw[checksum_area_start], checksum_area_size);
|
||||||
|
@ -1620,7 +1660,7 @@ GCMemcardErrorCode Header::CheckForErrors(u16 card_size_mbits) const
|
||||||
GCMemcardErrorCode error_code;
|
GCMemcardErrorCode error_code;
|
||||||
|
|
||||||
// total card size should match card size in header
|
// total card size should match card size in header
|
||||||
if (m_size_mb != card_size_mbits)
|
if (m_data.m_size_mb != card_size_mbits)
|
||||||
error_code.Set(GCMemcardValidityIssues::MISMATCHED_CARD_SIZE);
|
error_code.Set(GCMemcardValidityIssues::MISMATCHED_CARD_SIZE);
|
||||||
|
|
||||||
// unused areas, should always be filled with 0xFF
|
// unused areas, should always be filled with 0xFF
|
||||||
|
|
|
@ -166,9 +166,13 @@ struct GCMBlock
|
||||||
void Erase();
|
void Erase();
|
||||||
std::array<u8, BLOCK_SIZE> m_block;
|
std::array<u8, BLOCK_SIZE> m_block;
|
||||||
};
|
};
|
||||||
|
static_assert(sizeof(GCMBlock) == BLOCK_SIZE);
|
||||||
|
static_assert(std::is_trivially_copyable_v<GCMBlock>);
|
||||||
|
|
||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
struct Header
|
// split off from Header to have a small struct with all the data needed to regenerate the header
|
||||||
|
// for GCI folders
|
||||||
|
struct HeaderData
|
||||||
{
|
{
|
||||||
// NOTE: libogc refers to 'Serial' as the first 0x20 bytes of the header,
|
// NOTE: libogc refers to 'Serial' as the first 0x20 bytes of the header,
|
||||||
// so the data from m_serial until m_unknown_2 (inclusive)
|
// so the data from m_serial until m_unknown_2 (inclusive)
|
||||||
|
@ -196,6 +200,18 @@ struct Header
|
||||||
|
|
||||||
// 2 bytes at 0x0024: Encoding (Windows-1252 or Shift JIS)
|
// 2 bytes at 0x0024: Encoding (Windows-1252 or Shift JIS)
|
||||||
Common::BigEndianValue<u16> m_encoding;
|
Common::BigEndianValue<u16> m_encoding;
|
||||||
|
};
|
||||||
|
static_assert(std::is_trivially_copyable_v<HeaderData>);
|
||||||
|
|
||||||
|
void InitializeHeaderData(HeaderData* data, const CardFlashId& flash_id, u16 size_mbits,
|
||||||
|
bool shift_jis, u32 rtc_bias, u32 sram_language, u64 format_time);
|
||||||
|
|
||||||
|
bool operator==(const HeaderData& lhs, const HeaderData& rhs);
|
||||||
|
bool operator!=(const HeaderData& lhs, const HeaderData& rhs);
|
||||||
|
|
||||||
|
struct Header
|
||||||
|
{
|
||||||
|
HeaderData m_data;
|
||||||
|
|
||||||
// 468 bytes at 0x0026: Unused (0xff)
|
// 468 bytes at 0x0026: Unused (0xff)
|
||||||
std::array<u8, 468> m_unused_1;
|
std::array<u8, 468> m_unused_1;
|
||||||
|
@ -213,8 +229,15 @@ struct Header
|
||||||
// 0x1e00 bytes at 0x0200: Unused (0xff)
|
// 0x1e00 bytes at 0x0200: Unused (0xff)
|
||||||
std::array<u8, 7680> m_unused_2;
|
std::array<u8, 7680> m_unused_2;
|
||||||
|
|
||||||
explicit Header(int slot = 0, u16 size_mbits = MBIT_SIZE_MEMORY_CARD_2043,
|
// initialize an unformatted header block
|
||||||
bool shift_jis = false);
|
explicit Header();
|
||||||
|
|
||||||
|
// initialize a formatted header block
|
||||||
|
explicit Header(const CardFlashId& flash_id, u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
|
u32 sram_language, u64 format_time);
|
||||||
|
|
||||||
|
// initialize a header block from existing HeaderData
|
||||||
|
explicit Header(const HeaderData& data);
|
||||||
|
|
||||||
// Calculates the card serial numbers used for encrypting some save files.
|
// Calculates the card serial numbers used for encrypting some save files.
|
||||||
std::pair<u32, u32> CalculateSerial() const;
|
std::pair<u32, u32> CalculateSerial() const;
|
||||||
|
@ -225,6 +248,7 @@ struct Header
|
||||||
GCMemcardErrorCode CheckForErrors(u16 card_size_mbits) const;
|
GCMemcardErrorCode CheckForErrors(u16 card_size_mbits) const;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Header) == BLOCK_SIZE);
|
static_assert(sizeof(Header) == BLOCK_SIZE);
|
||||||
|
static_assert(std::is_trivially_copyable_v<Header>);
|
||||||
|
|
||||||
struct DEntry
|
struct DEntry
|
||||||
{
|
{
|
||||||
|
@ -302,6 +326,7 @@ struct DEntry
|
||||||
Common::BigEndianValue<u32> m_comments_address;
|
Common::BigEndianValue<u32> m_comments_address;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(DEntry) == DENTRY_SIZE);
|
static_assert(sizeof(DEntry) == DENTRY_SIZE);
|
||||||
|
static_assert(std::is_trivially_copyable_v<DEntry>);
|
||||||
|
|
||||||
struct BlockAlloc;
|
struct BlockAlloc;
|
||||||
|
|
||||||
|
@ -337,6 +362,7 @@ struct Directory
|
||||||
GCMemcardErrorCode CheckForErrorsWithBat(const BlockAlloc& bat) const;
|
GCMemcardErrorCode CheckForErrorsWithBat(const BlockAlloc& bat) const;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(Directory) == BLOCK_SIZE);
|
static_assert(sizeof(Directory) == BLOCK_SIZE);
|
||||||
|
static_assert(std::is_trivially_copyable_v<Directory>);
|
||||||
|
|
||||||
struct BlockAlloc
|
struct BlockAlloc
|
||||||
{
|
{
|
||||||
|
@ -371,6 +397,7 @@ struct BlockAlloc
|
||||||
GCMemcardErrorCode CheckForErrors(u16 size_mbits) const;
|
GCMemcardErrorCode CheckForErrors(u16 size_mbits) const;
|
||||||
};
|
};
|
||||||
static_assert(sizeof(BlockAlloc) == BLOCK_SIZE);
|
static_assert(sizeof(BlockAlloc) == BLOCK_SIZE);
|
||||||
|
static_assert(std::is_trivially_copyable_v<BlockAlloc>);
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
class GCMemcard
|
class GCMemcard
|
||||||
|
@ -401,7 +428,9 @@ private:
|
||||||
void UpdateBat(const BlockAlloc& bat);
|
void UpdateBat(const BlockAlloc& bat);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
static std::optional<GCMemcard> Create(std::string filename, u16 size_mbits, bool shift_jis);
|
static std::optional<GCMemcard> Create(std::string filename, const CardFlashId& flash_id,
|
||||||
|
u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
|
u32 sram_language, u64 format_time);
|
||||||
|
|
||||||
static std::pair<GCMemcardErrorCode, std::optional<GCMemcard>> Open(std::string filename);
|
static std::pair<GCMemcardErrorCode, std::optional<GCMemcard>> Open(std::string filename);
|
||||||
|
|
||||||
|
@ -413,9 +442,10 @@ public:
|
||||||
bool IsValid() const { return m_valid; }
|
bool IsValid() const { return m_valid; }
|
||||||
bool IsShiftJIS() const;
|
bool IsShiftJIS() const;
|
||||||
bool Save();
|
bool Save();
|
||||||
bool Format(bool shift_jis = false, u16 SizeMb = MBIT_SIZE_MEMORY_CARD_2043);
|
bool Format(const CardFlashId& flash_id, u16 size_mbits, bool shift_jis, u32 rtc_bias,
|
||||||
static bool Format(u8* card_data, bool shift_jis = false,
|
u32 sram_language, u64 format_time);
|
||||||
u16 SizeMb = MBIT_SIZE_MEMORY_CARD_2043);
|
static bool Format(u8* card_data, const CardFlashId& flash_id, u16 size_mbits, bool shift_jis,
|
||||||
|
u32 rtc_bias, u32 sram_language, u64 format_time);
|
||||||
static s32 FZEROGX_MakeSaveGameValid(const Header& cardheader, const DEntry& direntry,
|
static s32 FZEROGX_MakeSaveGameValid(const Header& cardheader, const DEntry& direntry,
|
||||||
std::vector<GCMBlock>& FileBuffer);
|
std::vector<GCMBlock>& FileBuffer);
|
||||||
static s32 PSO_MakeSaveGameValid(const Header& cardheader, const DEntry& direntry,
|
static s32 PSO_MakeSaveGameValid(const Header& cardheader, const DEntry& direntry,
|
||||||
|
|
|
@ -26,9 +26,13 @@
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
|
#include "Common/Timer.h"
|
||||||
|
|
||||||
#include "Core/Config/MainSettings.h"
|
#include "Core/Config/MainSettings.h"
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
|
#include "Core/HW/Sram.h"
|
||||||
#include "Core/NetPlayProto.h"
|
#include "Core/NetPlayProto.h"
|
||||||
|
|
||||||
static const char* MC_HDR = "MC_SYSTEM_AREA";
|
static const char* MC_HDR = "MC_SYSTEM_AREA";
|
||||||
|
@ -145,11 +149,11 @@ std::vector<std::string> GCMemcardDirectory::GetFileNamesForGameID(const std::st
|
||||||
return filenames;
|
return filenames;
|
||||||
}
|
}
|
||||||
|
|
||||||
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits,
|
GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot,
|
||||||
bool shift_jis, int game_id)
|
const Memcard::HeaderData& header_data, u32 game_id)
|
||||||
: MemoryCardBase(slot, size_mbits), m_game_id(game_id), m_last_block(-1),
|
: MemoryCardBase(slot, header_data.m_size_mb), m_game_id(game_id), m_last_block(-1),
|
||||||
m_hdr(slot, size_mbits, shift_jis), m_bat1(size_mbits), m_saves(0),
|
m_hdr(header_data), m_bat1(header_data.m_size_mb), m_saves(0), m_save_directory(directory),
|
||||||
m_save_directory(directory), m_exiting(false)
|
m_exiting(false)
|
||||||
{
|
{
|
||||||
// Use existing header data if available
|
// Use existing header data if available
|
||||||
{
|
{
|
||||||
|
@ -195,7 +199,8 @@ GCMemcardDirectory::GCMemcardDirectory(const std::string& directory, int slot, u
|
||||||
}
|
}
|
||||||
|
|
||||||
// leave about 10% of free space on the card if possible
|
// leave about 10% of free space on the card if possible
|
||||||
const int total_blocks = m_hdr.m_size_mb * Memcard::MBIT_TO_BLOCKS - Memcard::MC_FST_BLOCKS;
|
const int total_blocks =
|
||||||
|
m_hdr.m_data.m_size_mb * Memcard::MBIT_TO_BLOCKS - Memcard::MC_FST_BLOCKS;
|
||||||
const int reserved_blocks = total_blocks / 10;
|
const int reserved_blocks = total_blocks / 10;
|
||||||
|
|
||||||
// load files for other games
|
// load files for other games
|
||||||
|
|
|
@ -21,8 +21,8 @@ void MigrateFromMemcardFile(const std::string& directory_name, int card_index);
|
||||||
class GCMemcardDirectory : public MemoryCardBase
|
class GCMemcardDirectory : public MemoryCardBase
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
GCMemcardDirectory(const std::string& directory, int slot, u16 size_mbits, bool shift_jis,
|
GCMemcardDirectory(const std::string& directory, int slot, const Memcard::HeaderData& header_data,
|
||||||
int game_id);
|
u32 game_id);
|
||||||
~GCMemcardDirectory();
|
~GCMemcardDirectory();
|
||||||
|
|
||||||
GCMemcardDirectory(const GCMemcardDirectory&) = delete;
|
GCMemcardDirectory(const GCMemcardDirectory&) = delete;
|
||||||
|
|
|
@ -22,9 +22,13 @@
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
#include "Common/StringUtil.h"
|
#include "Common/StringUtil.h"
|
||||||
#include "Common/Thread.h"
|
#include "Common/Thread.h"
|
||||||
|
#include "Common/Timer.h"
|
||||||
|
|
||||||
#include "Core/ConfigManager.h"
|
#include "Core/ConfigManager.h"
|
||||||
#include "Core/Core.h"
|
#include "Core/Core.h"
|
||||||
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
#include "Core/HW/GCMemcard/GCMemcard.h"
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||||
|
#include "Core/HW/Sram.h"
|
||||||
|
|
||||||
#define SIZE_TO_Mb (1024 * 8 * 16)
|
#define SIZE_TO_Mb (1024 * 8 * 16)
|
||||||
#define MC_HDR_SIZE 0xA000
|
#define MC_HDR_SIZE 0xA000
|
||||||
|
@ -51,9 +55,18 @@ MemoryCard::MemoryCard(const std::string& filename, int card_index, u16 size_mbi
|
||||||
m_memory_card_size = size_mbits * SIZE_TO_Mb;
|
m_memory_card_size = size_mbits * SIZE_TO_Mb;
|
||||||
|
|
||||||
m_memcard_data = std::make_unique<u8[]>(m_memory_card_size);
|
m_memcard_data = std::make_unique<u8[]>(m_memory_card_size);
|
||||||
// Fills in MC_HDR_SIZE bytes
|
|
||||||
Memcard::GCMemcard::Format(&m_memcard_data[0], m_filename.find(".JAP.raw") != std::string::npos,
|
// Fills in the first 5 blocks (MC_HDR_SIZE bytes)
|
||||||
size_mbits);
|
const CardFlashId& flash_id = g_SRAM.settings_ex.flash_id[Memcard::SLOT_A];
|
||||||
|
const bool shift_jis = m_filename.find(".JAP.raw") != std::string::npos;
|
||||||
|
const u32 rtc_bias = g_SRAM.settings.rtc_bias;
|
||||||
|
const u32 sram_language = static_cast<u32>(g_SRAM.settings.language);
|
||||||
|
const u64 format_time =
|
||||||
|
Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
|
||||||
|
Memcard::GCMemcard::Format(&m_memcard_data[0], flash_id, size_mbits, shift_jis, rtc_bias,
|
||||||
|
sram_language, format_time);
|
||||||
|
|
||||||
|
// Fills in the remaining blocks
|
||||||
memset(&m_memcard_data[MC_HDR_SIZE], 0xFF, m_memory_card_size - MC_HDR_SIZE);
|
memset(&m_memcard_data[MC_HDR_SIZE], 0xFF, m_memory_card_size - MC_HDR_SIZE);
|
||||||
|
|
||||||
INFO_LOG(EXPANSIONINTERFACE, "No memory card found. A new one was created instead.");
|
INFO_LOG(EXPANSIONINTERFACE, "No memory card found. A new one was created instead.");
|
||||||
|
|
|
@ -74,7 +74,7 @@ static Common::Event g_compressAndDumpStateSyncEvent;
|
||||||
static std::thread g_save_thread;
|
static std::thread g_save_thread;
|
||||||
|
|
||||||
// Don't forget to increase this after doing changes on the savestate system
|
// Don't forget to increase this after doing changes on the savestate system
|
||||||
constexpr u32 STATE_VERSION = 115; // Last changed in PR 8722
|
constexpr u32 STATE_VERSION = 116; // Last changed in PR 8879
|
||||||
|
|
||||||
// Maps savestate versions to Dolphin versions.
|
// Maps savestate versions to Dolphin versions.
|
||||||
// Versions after 42 don't need to be added to this list,
|
// Versions after 42 don't need to be added to this list,
|
||||||
|
|
|
@ -14,8 +14,11 @@
|
||||||
|
|
||||||
#include "Common/FileUtil.h"
|
#include "Common/FileUtil.h"
|
||||||
#include "Common/MsgHandler.h"
|
#include "Common/MsgHandler.h"
|
||||||
|
#include "Common/Timer.h"
|
||||||
|
|
||||||
|
#include "Core/HW/EXI/EXI_DeviceIPL.h"
|
||||||
#include "Core/HW/GCMemcard/GCMemcard.h"
|
#include "Core/HW/GCMemcard/GCMemcard.h"
|
||||||
|
#include "Core/HW/Sram.h"
|
||||||
|
|
||||||
GCMemcardCreateNewDialog::GCMemcardCreateNewDialog(QWidget* parent) : QDialog(parent)
|
GCMemcardCreateNewDialog::GCMemcardCreateNewDialog(QWidget* parent) : QDialog(parent)
|
||||||
{
|
{
|
||||||
|
@ -74,8 +77,16 @@ bool GCMemcardCreateNewDialog::CreateCard()
|
||||||
if (path.isEmpty())
|
if (path.isEmpty())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// TODO: The dependency on g_SRAM here is sketchy. We should instead use sensible default values.
|
||||||
|
const CardFlashId& flash_id = g_SRAM.settings_ex.flash_id[Memcard::SLOT_A];
|
||||||
|
const u32 rtc_bias = g_SRAM.settings.rtc_bias;
|
||||||
|
const u32 sram_language = static_cast<u32>(g_SRAM.settings.language);
|
||||||
|
const u64 format_time =
|
||||||
|
Common::Timer::GetLocalTimeSinceJan1970() - ExpansionInterface::CEXIIPL::GC_EPOCH;
|
||||||
|
|
||||||
const std::string p = path.toStdString();
|
const std::string p = path.toStdString();
|
||||||
auto memcard = Memcard::GCMemcard::Create(p, size, is_shift_jis);
|
auto memcard = Memcard::GCMemcard::Create(p, flash_id, size, is_shift_jis, rtc_bias,
|
||||||
|
sram_language, format_time);
|
||||||
if (memcard && memcard->Save())
|
if (memcard && memcard->Save())
|
||||||
{
|
{
|
||||||
m_card_path = p;
|
m_card_path = p;
|
||||||
|
|
Loading…
Reference in New Issue