Work on CMemoryCardDriver

This commit is contained in:
Jack Andersen 2016-12-20 11:51:50 -10:00
parent 29222827c6
commit 9639ac75cc
14 changed files with 672 additions and 107 deletions

View File

@ -9,6 +9,7 @@ namespace urde
/** Options tracked persistently between game sessions */ /** Options tracked persistently between game sessions */
class CPersistentOptions class CPersistentOptions
{ {
friend class CGameState;
bool x0_[98] = {}; bool x0_[98] = {};
bool x68_[64] = {}; bool x68_[64] = {};
std::vector<std::pair<ResId, TEditorId>> xac_cinematicStates; /* (MLVL, Cinematic) */ std::vector<std::pair<ResId, TEditorId>> xac_cinematicStates; /* (MLVL, Cinematic) */
@ -40,11 +41,16 @@ public:
CPersistentOptions(CBitStreamReader& stream); CPersistentOptions(CBitStreamReader& stream);
void SetCinematicState(ResId mlvlId, TEditorId cineId, bool state); void SetCinematicState(ResId mlvlId, TEditorId cineId, bool state);
bool PlayerHasHardMode() const { return xd0_25_hasHardMode; } bool GetPlayerHasHardMode() const { return xd0_25_hasHardMode; }
bool PlayerBeatHardMode() const { return xd0_26_hardModeBeat; } void SetPlayerHasHardMode(bool v) { xd0_25_hasHardMode = v; }
bool PlayerHasFusion() const { return xd0_28_hasFusion; } bool GetPlayerBeatHardMode() const { return xd0_26_hardModeBeat; }
bool AllItemsCollected() const { return xd0_29_allItemsCollected; } void SetPlayerBeatHardMode(bool v) { xd0_26_hardModeBeat = v; }
bool GetPlayerHasFusion() const { return xd0_28_hasFusion; }
void SetPlayerHasFusion(bool v) { xd0_28_hasFusion = v; }
bool GetAllItemsCollected() const { return xd0_29_allItemsCollected; }
void SetAllItemsCollected(bool v) { xd0_29_allItemsCollected = v; }
u32 GetLogScanCount() const { return xcc_logScanCount; } u32 GetLogScanCount() const { return xcc_logScanCount; }
void SetLogScanCount(u32 v) { xcc_logScanCount = v; }
}; };
/** Options tracked per game session */ /** Options tracked per game session */

View File

@ -88,6 +88,52 @@ void CWorldState::PutTo(CBitStreamWriter& writer, const CSaveWorld& savw) const
x14_layerState->PutTo(writer); x14_layerState->PutTo(writer);
} }
CGameState::GameFileStateInfo CGameState::LoadGameFileState(const u8* data)
{
CBitStreamReader stream(data, 4096);
GameFileStateInfo ret;
for (u32 i = 0; i < 128; i++)
stream.ReadEncoded(8);
ret.x14_timestamp = stream.ReadEncoded(32);
ret.x20_hardMode = stream.ReadEncoded(1);
stream.ReadEncoded(1);
ret.x8_mlvlId = stream.ReadEncoded(32);
union BitsToDouble
{
struct
{
u32 low;
u32 high;
};
double doub;
} conv;
conv.low = stream.ReadEncoded(32);
conv.high = stream.ReadEncoded(32);
ret.x0_playTime = conv.doub;
CPlayerState playerState(stream);
ret.x10_energyTanks = playerState.GetItemCapacity(CPlayerState::EItemType::EnergyTanks);
u32 itemPercent;
if (ret.x8_mlvlId == 0x158EFE17)
itemPercent = 0;
else
itemPercent = playerState.CalculateItemCollectionRate() * 100 / playerState.GetPickupTotal();
ret.x18_itemPercent = itemPercent;
float somePercent;
if (playerState.GetTotalLogScans() == 0)
somePercent = 0.f;
else
somePercent = 100.f * playerState.GetLogScans() / float(playerState.GetTotalLogScans());
ret.x1c_scanPercent = somePercent;
return ret;
}
CGameState::CGameState() CGameState::CGameState()
{ {
x98_playerState.reset(new CPlayerState()); x98_playerState.reset(new CPlayerState());
@ -137,6 +183,19 @@ CGameState::CGameState(CBitStreamReader& stream)
} }
} }
void CGameState::MergePersistentOptions(const CPersistentOptions& opts)
{
if (opts.xd0_24_)
xa8_systemOptions.xd0_24_ = true;
if (opts.xd0_27_)
xa8_systemOptions.xd0_27_ = true;
if (&opts != &xa8_systemOptions)
memcpy(xa8_systemOptions.x0_, opts.x0_, 98);
xa8_systemOptions.SetLogScanCount(opts.GetLogScanCount());
xa8_systemOptions.SetAllItemsCollected(opts.GetAllItemsCollected());
xa8_systemOptions.SetPlayerHasHardMode(opts.GetPlayerHasHardMode());
xa8_systemOptions.SetPlayerBeatHardMode(opts.GetPlayerBeatHardMode());
}
void CGameState::PutTo(CBitStreamWriter& writer) const void CGameState::PutTo(CBitStreamWriter& writer) const
{ {

View File

@ -108,7 +108,21 @@ public:
CWorldState& CurrentWorldState() { return StateForWorld(x84_mlvlId); } CWorldState& CurrentWorldState() { return StateForWorld(x84_mlvlId); }
ResId CurrentWorldAssetId() const { return x84_mlvlId; } ResId CurrentWorldAssetId() const { return x84_mlvlId; }
void SetHardMode(bool v) { x228_24_hardMode = v; } void SetHardMode(bool v) { x228_24_hardMode = v; }
void MergePersistentOptions(const CPersistentOptions& opts);
void PutTo(CBitStreamWriter& writer) const; void PutTo(CBitStreamWriter& writer) const;
struct GameFileStateInfo
{
double x0_playTime;
u32 x8_mlvlId;
float xc_health;
u32 x10_energyTanks;
u32 x14_timestamp;
u32 x18_itemPercent;
float x1c_scanPercent;
bool x20_hardMode;
};
static GameFileStateInfo LoadGameFileState(const u8* data);
}; };
} }

View File

@ -117,7 +117,7 @@ bool CMemoryCardSys::InitializePump()
x20_scanStates.reserve(x20_scanStates.size() + savw.GetScans().size()); x20_scanStates.reserve(x20_scanStates.size() + savw.GetScans().size());
for (const CSaveWorld::SScanState& scan : savw.GetScans()) for (const CSaveWorld::SScanState& scan : savw.GetScans())
x20_scanStates[scan.x0_id] = scan.x4_category; x20_scanStates.emplace_back(scan.x0_id, scan.x4_category);
wldMemOut.x3c_saveWorld = std::move(world.x34_saveWorld); wldMemOut.x3c_saveWorld = std::move(world.x34_saveWorld);
wldMemOut.x2c_worldName = g_SimplePool->GetObj(SObjectTag{FOURCC('STRG'), wldMemOut.x0_strgId}); wldMemOut.x2c_worldName = g_SimplePool->GetObj(SObjectTag{FOURCC('STRG'), wldMemOut.x0_strgId});
@ -157,4 +157,24 @@ CMemoryCardSys::ECardResult CMemoryCardSys::GetSerialNo(EMemoryCardPort port, u6
return ECardResult::CARD_RESULT_READY; return ECardResult::CARD_RESULT_READY;
} }
CMemoryCardSys::ECardResult CMemoryCardSys::GetResultCode(EMemoryCardPort port)
{
return ECardResult::CARD_RESULT_READY;
}
CMemoryCardSys::ECardResult CMemoryCardSys::GetStatus(EMemoryCardPort port, int fileNo, CARDStat& statOut)
{
return ECardResult::CARD_RESULT_READY;
}
CMemoryCardSys::ECardResult CMemoryCardSys::DeleteFile(EMemoryCardPort port, const char* name)
{
return ECardResult::CARD_RESULT_READY;
}
CMemoryCardSys::ECardResult CMemoryCardSys::FastDeleteFile(EMemoryCardPort port, int fileNo)
{
return ECardResult::CARD_RESULT_READY;
}
} }

View File

@ -7,6 +7,11 @@
#include "CSaveWorld.hpp" #include "CSaveWorld.hpp"
#include <vector> #include <vector>
// longest file name string excluding terminating zero
#define CARD_FILENAME_MAX 32
#define CARD_ICON_MAX 8
namespace urde namespace urde
{ {
class CDummyWorld; class CDummyWorld;
@ -53,10 +58,11 @@ class CMemoryCardSys
TLockedToken<CGameHintInfo> x0_hints; TLockedToken<CGameHintInfo> x0_hints;
std::vector<std::pair<ResId, CSaveWorldMemory>> xc_memoryWorlds; /* MLVL as key */ std::vector<std::pair<ResId, CSaveWorldMemory>> xc_memoryWorlds; /* MLVL as key */
std::experimental::optional<std::vector<CSaveWorldIntermediate>> x1c_worldInter; /* used to be auto_ptr of vector */ std::experimental::optional<std::vector<CSaveWorldIntermediate>> x1c_worldInter; /* used to be auto_ptr of vector */
std::unordered_map<ResId, CSaveWorld::EScanCategory> x20_scanStates; std::vector<std::pair<ResId, CSaveWorld::EScanCategory>> x20_scanStates;
public: public:
const std::vector<CGameHintInfo::CGameHint>& GetHints() const { return x0_hints->GetHints(); } const std::vector<CGameHintInfo::CGameHint>& GetHints() const { return x0_hints->GetHints(); }
const std::vector<std::pair<ResId, CSaveWorldMemory>>& GetMemoryWorlds() const { return xc_memoryWorlds; } const std::vector<std::pair<ResId, CSaveWorldMemory>>& GetMemoryWorlds() const { return xc_memoryWorlds; }
const std::vector<std::pair<ResId, CSaveWorld::EScanCategory>>& GetScanStates() const { return x20_scanStates; }
CMemoryCardSys(); CMemoryCardSys();
bool InitializePump(); bool InitializePump();
@ -68,6 +74,7 @@ public:
enum class ECardResult enum class ECardResult
{ {
CARD_RESULT_CRC_MISMATCH = -1003,
CARD_RESULT_FATAL_ERROR = -128, CARD_RESULT_FATAL_ERROR = -128,
CARD_RESULT_ENCODING = -13, CARD_RESULT_ENCODING = -13,
CARD_RESULT_BROKEN = -6, CARD_RESULT_BROKEN = -6,
@ -85,11 +92,59 @@ public:
u32 x8_sectorSize; // in bytes u32 x8_sectorSize; // in bytes
}; };
struct CARDStat
{
// read-only (Set by CARDGetStatus)
char x0_fileName[CARD_FILENAME_MAX];
u32 x20_length;
u32 x24_time; // seconds since 01/01/2000 midnight
u8 x28_gameName[4];
u8 x2c_company[2];
// read/write (Set by CARDGetStatus/CARDSetStatus)
u8 x2e_bannerFormat;
u8 x2f___padding;
u32 x30_iconAddr; // offset to the banner, bannerTlut, icon, iconTlut data set.
u16 x34_iconFormat;
u16 x36_iconSpeed;
u32 x38_commentAddr; // offset to the pair of 32 byte character strings.
// read-only (Set by CARDGetStatus)
u32 x3c_offsetBanner;
u32 x40_offsetBannerTlut;
u32 x44_offsetIcon[CARD_ICON_MAX];
u32 x64_offsetIconTlut;
u32 x68_offsetData;
u32 GetTime() const { return x24_time; }
u32 GetBannerFormat() const { return x2e_bannerFormat & 0x3; }
void SetBannerFormat(u32 fmt) { x2e_bannerFormat = (x2e_bannerFormat & ~0x3) | fmt; }
u32 GetIconFormat(int idx) const { return (x34_iconFormat >> (idx * 2)) & 0x3; }
void SetIconFormat(u32 fmt, int idx)
{
x34_iconFormat &= ~(0x3 << (idx * 2));
x34_iconFormat |= fmt << (idx * 2);
}
void SetIconSpeed(u32 sp, int idx)
{
x36_iconSpeed &= ~(0x3 << (idx * 2));
x36_iconSpeed |= sp << (idx * 2);
}
u32 GetIconAddr() const { return x30_iconAddr; }
void SetIconAddr(u32 addr) { x30_iconAddr = addr; }
u32 GetCommentAddr() const { return x38_commentAddr; }
void SetCommentAddr(u32 addr) { x38_commentAddr = addr; }
};
static CardProbeResults CardProbe(EMemoryCardPort port); static CardProbeResults CardProbe(EMemoryCardPort port);
static ECardResult MountCard(EMemoryCardPort port); static ECardResult MountCard(EMemoryCardPort port);
static ECardResult CheckCard(EMemoryCardPort port); static ECardResult CheckCard(EMemoryCardPort port);
static ECardResult GetNumFreeBytes(EMemoryCardPort port, s32& freeBytes, s32& freeFiles); static ECardResult GetNumFreeBytes(EMemoryCardPort port, s32& freeBytes, s32& freeFiles);
static ECardResult GetSerialNo(EMemoryCardPort port, u64& serialOut); static ECardResult GetSerialNo(EMemoryCardPort port, u64& serialOut);
static ECardResult GetResultCode(EMemoryCardPort port);
static ECardResult GetStatus(EMemoryCardPort port, int fileNo, CARDStat& statOut);
static ECardResult DeleteFile(EMemoryCardPort port, const char* name);
static ECardResult FastDeleteFile(EMemoryCardPort port, int fileNo);
}; };
} }

View File

@ -86,8 +86,8 @@ CPlayerState::CPlayerState(CBitStreamReader& stream)
x170_scanTimes[i].second = 0.f; x170_scanTimes[i].second = 0.f;
} }
x180_ = stream.ReadEncoded(CBitStreamReader::GetBitCount(0x100)); x180_logScans = stream.ReadEncoded(CBitStreamReader::GetBitCount(0x100));
x184_ = stream.ReadEncoded(CBitStreamReader::GetBitCount(0x100)); x184_totalLogScans = stream.ReadEncoded(CBitStreamReader::GetBitCount(0x100));
} }
void CPlayerState::PutTo(CBitStreamWriter &stream) void CPlayerState::PutTo(CBitStreamWriter &stream)
@ -112,8 +112,8 @@ void CPlayerState::PutTo(CBitStreamWriter &stream)
stream.WriteEncoded(false, 1); stream.WriteEncoded(false, 1);
} }
stream.WriteEncoded(x180_, CBitStreamWriter::GetBitCount(0x100)); stream.WriteEncoded(x180_logScans, CBitStreamWriter::GetBitCount(0x100));
stream.WriteEncoded(x184_, CBitStreamWriter::GetBitCount(0x100)); stream.WriteEncoded(x184_totalLogScans, CBitStreamWriter::GetBitCount(0x100));
} }
static const float unk[] static const float unk[]

View File

@ -124,8 +124,8 @@ private:
EPlayerSuit x20_currentSuit = EPlayerSuit::Power; EPlayerSuit x20_currentSuit = EPlayerSuit::Power;
rstl::reserved_vector<CPowerUp, 41> x24_powerups; rstl::reserved_vector<CPowerUp, 41> x24_powerups;
rstl::reserved_vector<std::pair<u32, float>, 846> x170_scanTimes; rstl::reserved_vector<std::pair<u32, float>, 846> x170_scanTimes;
u32 x180_ = 0; u32 x180_logScans = 0;
u32 x184_ = 0; u32 x184_totalLogScans = 0;
CStaticInterference x188_staticIntf; CStaticInterference x188_staticIntf;
public: public:
@ -166,6 +166,9 @@ public:
float CalculateHealth(u32 health); float CalculateHealth(u32 health);
void ReInitalizePowerUp(EItemType type, u32 capacity); void ReInitalizePowerUp(EItemType type, u32 capacity);
void InitializePowerUp(EItemType type, u32 capacity); void InitializePowerUp(EItemType type, u32 capacity);
u32 GetLogScans() const { return x180_logScans; }
u32 GetTotalLogScans() const { return x184_totalLogScans; }
const rstl::reserved_vector<std::pair<u32, float>, 846>& GetScanTimes() const { return x170_scanTimes; }
CPlayerState() : x188_staticIntf(5) { x0_24_ = true; } CPlayerState() : x188_staticIntf(5) { x0_24_ = true; }
CPlayerState(CBitStreamReader& stream); CPlayerState(CBitStreamReader& stream);
void PutTo(CBitStreamWriter& stream); void PutTo(CBitStreamWriter& stream);

View File

@ -22,6 +22,7 @@
#include "World/CScriptSpawnPoint.hpp" #include "World/CScriptSpawnPoint.hpp"
#include "AutoMapper/CMapWorldInfo.hpp" #include "AutoMapper/CMapWorldInfo.hpp"
#include "Particle/CGenDescription.hpp" #include "Particle/CGenDescription.hpp"
#include "CMemoryCardSys.hpp"
#include <cmath> #include <cmath>
@ -808,4 +809,23 @@ TUniqueId CStateManager::AllocateUniqueId()
return 0; return 0;
} }
std::pair<u32, u32> CStateManager::CalculateScanCompletionRate() const
{
u32 num = 0;
u32 denom = 0;
int idx = 0;
for (const std::pair<u32, float>& scan : x8b8_playerState->GetScanTimes())
{
CSaveWorld::EScanCategory category = g_MemoryCardSys->GetScanStates()[idx++].second;
if (category != CSaveWorld::EScanCategory::None &&
category != CSaveWorld::EScanCategory::Research)
{
++denom;
if (scan.second == 1.f)
++num;
}
}
return {num, denom};
}
} }

View File

@ -303,6 +303,8 @@ public:
CListeningAiList& GetListeningAiObjectList() const { return *x834_listenAiObjs; } CListeningAiList& GetListeningAiObjectList() const { return *x834_listenAiObjs; }
CAiWaypointList& GetAiWaypointObjectList() const { return *x83c_aiWaypointObjs; } CAiWaypointList& GetAiWaypointObjectList() const { return *x83c_aiWaypointObjs; }
CPlatformAndDoorList& GetPlatformAndDoorObjectList() const { return *x844_platformAndDoorObjs; } CPlatformAndDoorList& GetPlatformAndDoorObjectList() const { return *x844_platformAndDoorObjs; }
std::pair<u32, u32> CalculateScanCompletionRate() const;
}; };
} }

View File

@ -277,7 +277,7 @@ void CFrontEndUI::SNewFileSelectFrame::ActivateNewGamePopup()
PlayAdvanceSfx(); PlayAdvanceSfx();
if (g_GameState->SystemOptions().PlayerHasHardMode()) if (g_GameState->SystemOptions().GetPlayerHasHardMode())
{ {
x48_textpane_popupadvance.SetPairText(g_MainStringTable->GetString(102)); x48_textpane_popupadvance.SetPairText(g_MainStringTable->GetString(102));
x50_textpane_popupcancel.SetPairText(g_MainStringTable->GetString(94)); x50_textpane_popupcancel.SetPairText(g_MainStringTable->GetString(94));
@ -414,7 +414,7 @@ void CFrontEndUI::SNewFileSelectFrame::DoPopupAdvance(CGuiTableGroup* caller)
} }
else else
{ {
if (g_GameState->SystemOptions().PlayerHasHardMode()) if (g_GameState->SystemOptions().GetPlayerHasHardMode())
{ {
if (x40_tablegroup_popup->GetUserSelection() == 1) if (x40_tablegroup_popup->GetUserSelection() == 1)
{ {
@ -502,7 +502,7 @@ void CFrontEndUI::SGBASupportFrame::FinishedLoading()
x2c_tablegroup_fusionsuit->SetIsActive(false); x2c_tablegroup_fusionsuit->SetIsActive(false);
x2c_tablegroup_fusionsuit->SetIsVisible(false); x2c_tablegroup_fusionsuit->SetIsVisible(false);
x2c_tablegroup_fusionsuit->SetD1(false); x2c_tablegroup_fusionsuit->SetD1(false);
x2c_tablegroup_fusionsuit->SetUserSelection(g_GameState->SystemOptions().PlayerHasFusion()); x2c_tablegroup_fusionsuit->SetUserSelection(g_GameState->SystemOptions().GetPlayerHasFusion());
SetTableColors(x28_tablegroup_options); SetTableColors(x28_tablegroup_options);
SetTableColors(x2c_tablegroup_fusionsuit); SetTableColors(x2c_tablegroup_fusionsuit);

View File

@ -1,24 +1,130 @@
#include "CMemoryCardDriver.hpp" #include "CMemoryCardDriver.hpp"
#include "MP1.hpp" #include "MP1.hpp"
#include "CCRC32.hpp"
namespace urde namespace urde
{ {
namespace MP1 namespace MP1
{ {
CMemoryCardDriver::SMemoryCardSlotInfo::SMemoryCardSlotInfo(CMemoryCardSys::EMemoryCardPort parentIdx, using ECardResult = CMemoryCardSys::ECardResult;
const std::string& name) using EMemoryCardPort = CMemoryCardSys::EMemoryCardPort;
: x0_cardPort(parentIdx), x14_name(name)
{}
CMemoryCardDriver::CMemoryCardDriver(CMemoryCardSys::EMemoryCardPort cardPort, ResId saveBanner, ECardResult CMemoryCardDriver::SFileInfo::Close()
ResId saveIcon0, ResId saveIcon1, bool flag)
: x0_cardPort(cardPort), x4_saveBanner(saveBanner),
x8_saveIcon0(saveIcon0), xc_saveIcon1(saveIcon1), x19d_flag(flag)
{ {
x100_mcSlotInfos.reserve(2); auto backup = GetFileCardPort();
x100_mcSlotInfos.emplace_back(0, SMemoryCardSlotInfo(x0_cardPort, "MetroidPrime A")); ECardResult result = ECardResult::CARD_RESULT_READY;
x100_mcSlotInfos.emplace_back(0, SMemoryCardSlotInfo(x0_cardPort, "MetroidPrime B")); //result = CARDClose(backup);
x0_fileInfo.x0_cardPort = backup;
return result;
}
ECardResult CMemoryCardDriver::SFileInfo::TryFileRead()
{
ECardResult res = CMemoryCardSys::GetResultCode(GetFileCardPort());
if (res == ECardResult::CARD_RESULT_READY)
res = FileRead();
return res;
}
ECardResult CMemoryCardDriver::SFileInfo::FileRead()
{
x34_saveData.clear();
u32 existingCrc = hecl::SBig(*reinterpret_cast<u32*>(x24_saveFileData.data()));
u32 newCrc = CCRC32::Calculate(x24_saveFileData.data() + 4, x24_saveFileData.size() - 4);
if (existingCrc == newCrc)
{
u32 saveDataOff;
ECardResult result = GetSaveDataOffset(saveDataOff);
if (result != ECardResult::CARD_RESULT_READY)
{
x24_saveFileData.clear();
return result;
}
u32 saveSize = x24_saveFileData.size() - saveDataOff;
x34_saveData.resize(saveSize);
memmove(x34_saveData.data(), x24_saveFileData.data() + saveDataOff, saveSize);
x24_saveFileData.clear();
return ECardResult::CARD_RESULT_READY;
}
else
{
x24_saveFileData.clear();
return ECardResult::CARD_RESULT_CRC_MISMATCH;
}
}
ECardResult CMemoryCardDriver::SFileInfo::GetSaveDataOffset(u32& offOut)
{
CMemoryCardSys::CARDStat stat = {};
ECardResult result = CMemoryCardSys::GetStatus(GetFileCardPort(), GetFileNo(), stat);
if (result != ECardResult::CARD_RESULT_READY)
{
offOut = -1;
return result;
}
offOut = 4;
offOut += 64;
switch (stat.GetBannerFormat())
{
case 1:
offOut += 3584;
break;
case 2:
offOut += 6144;
break;
default: break;
}
int idx = 0;
bool paletteIcon = false;
while (u32 fmt = stat.GetIconFormat(idx))
{
if (fmt == 1)
{
paletteIcon = true;
offOut += 1024;
}
else
offOut += 2048;
++idx;
}
if (paletteIcon)
offOut += 512;
return ECardResult::CARD_RESULT_READY;
}
CMemoryCardDriver::SGameFileSlot::SGameFileSlot()
{
InitializeFromGameState();
}
void CMemoryCardDriver::SGameFileSlot::InitializeFromGameState()
{
CBitStreamWriter w(x0_saveBuffer, 940);
g_GameState->PutTo(w);
x944_fileInfo = CGameState::LoadGameFileState(x0_saveBuffer);
}
CMemoryCardDriver::SFileInfo::SFileInfo(EMemoryCardPort port,
const std::string& name)
: x14_name(name)
{
x0_fileInfo.x0_cardPort = port;
}
CMemoryCardDriver::CMemoryCardDriver(EMemoryCardPort cardPort, ResId saveBanner,
ResId saveIcon0, ResId saveIcon1, bool mergePersistent)
: x0_cardPort(cardPort), x4_saveBanner(saveBanner),
x8_saveIcon0(saveIcon0), xc_saveIcon1(saveIcon1), x19d_doMergePersistent(mergePersistent)
{
x100_mcFileInfos.reserve(2);
x100_mcFileInfos.emplace_back(0, SFileInfo(x0_cardPort, "MetroidPrime A"));
x100_mcFileInfos.emplace_back(0, SFileInfo(x0_cardPort, "MetroidPrime B"));
} }
void CMemoryCardDriver::FinishedLoading() void CMemoryCardDriver::FinishedLoading()
@ -33,7 +139,7 @@ void CMemoryCardDriver::FinishedLoading2()
auto result = CMemoryCardSys::CardProbe(x0_cardPort); auto result = CMemoryCardSys::CardProbe(x0_cardPort);
switch (result.x0_error) switch (result.x0_error)
{ {
case CMemoryCardSys::ECardResult::CARD_RESULT_READY: case ECardResult::CARD_RESULT_READY:
if (result.x8_sectorSize != 0x2000) if (result.x8_sectorSize != 0x2000)
{ {
x10_state = EState::Twelve; x10_state = EState::Twelve;
@ -43,9 +149,9 @@ void CMemoryCardDriver::FinishedLoading2()
x10_state = EState::Five; x10_state = EState::Five;
MountCard(); MountCard();
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_BUSY: case ECardResult::CARD_RESULT_BUSY:
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_WRONGDEVICE: case ECardResult::CARD_RESULT_WRONGDEVICE:
x10_state = EState::Twelve; x10_state = EState::Twelve;
x14_error = EError::Four; x14_error = EError::Four;
break; break;
@ -65,20 +171,97 @@ void CMemoryCardDriver::MountCard()
{ {
x10_state = EState::TwentySix; x10_state = EState::TwentySix;
x14_error = EError::Zero; x14_error = EError::Zero;
CMemoryCardSys::ECardResult result = CMemoryCardSys::MountCard(x0_cardPort); ECardResult result = CMemoryCardSys::MountCard(x0_cardPort);
if (result != CMemoryCardSys::ECardResult::CARD_RESULT_READY) if (result != ECardResult::CARD_RESULT_READY)
MountCardFailed(result); Case26(result);
} }
void CMemoryCardDriver::MountCardFailed(CMemoryCardSys::ECardResult result) void CMemoryCardDriver::CheckCard()
{
ECardResult result = CMemoryCardSys::CheckCard(x0_cardPort);
if (result != ECardResult::CARD_RESULT_READY)
Case27(result);
}
CGameState::GameFileStateInfo* CMemoryCardDriver::GetGameFileStateInfo(int idx)
{
SGameFileSlot* slot = xe4_fileSlots[idx].get();
if (!slot)
return nullptr;
return &slot->x944_fileInfo;
}
CMemoryCardDriver::SSaveHeader CMemoryCardDriver::LoadSaveHeader(CMemoryInStream& in)
{
SSaveHeader ret;
ret.x0_ = in.readUint32Big();
for (int i=0 ; i<3 ; ++i)
ret.x4_[i] = in.readBool();
return ret;
}
std::unique_ptr<CMemoryCardDriver::SGameFileSlot> CMemoryCardDriver::LoadSaveFile(CMemoryInStream& in)
{
auto ret = std::make_unique<CMemoryCardDriver::SGameFileSlot>();
in.readBytesToBuf(ret->x0_saveBuffer, 940);
ret->x944_fileInfo = CGameState::LoadGameFileState(ret->x0_saveBuffer);
return ret;
}
void CMemoryCardDriver::ReadFinished()
{
CMemoryCardSys::CARDStat stat = {};
SFileInfo& fileInfo = x100_mcFileInfos[x194_fileIdx].second;
if (CMemoryCardSys::GetStatus(x0_cardPort, fileInfo.GetFileNo(), stat) != ECardResult::CARD_RESULT_READY)
{
NoCardFound();
return;
}
x20_fileTime = stat.GetTime();
CMemoryInStream r(fileInfo.x34_saveData.data(), 3004);
SSaveHeader header = LoadSaveHeader(r);
r.readBytesToBuf(x30_systemData, 174);
for (int i=0 ; i<3 ; ++i)
xe4_fileSlots[i] = LoadSaveFile(r);
if (x19d_doMergePersistent)
MergePersistentOptions();
}
void CMemoryCardDriver::MergePersistentOptions()
{
CBitStreamReader r(x30_systemData, 174);
CPersistentOptions opts(r);
g_GameState->MergePersistentOptions(opts);
}
void CMemoryCardDriver::DeleteFile()
{
x14_error = EError::Zero;
x10_state = EState::Thirty;
SFileInfo& fileInfo = x100_mcFileInfos[bool(x194_fileIdx)].second;
ECardResult result = CMemoryCardSys::FastDeleteFile(x0_cardPort, fileInfo.GetFileNo());
if (result != ECardResult::CARD_RESULT_READY)
Case30(result);
}
void CMemoryCardDriver::CheckCardCapacity()
{
if (x18_cardFreeBytes < 0x2000 || !x1c_cardFreeFiles)
x14_error = EError::Six;
}
void CMemoryCardDriver::Case26(ECardResult result)
{ {
switch (result) switch (result)
{ {
case CMemoryCardSys::ECardResult::CARD_RESULT_READY: case ECardResult::CARD_RESULT_READY:
x10_state = EState::Six; x10_state = EState::Six;
CheckCard(); CheckCard();
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_BROKEN: case ECardResult::CARD_RESULT_BROKEN:
x10_state = EState::Six; x10_state = EState::Six;
x14_error = EError::One; x14_error = EError::One;
CheckCard(); CheckCard();
@ -89,27 +272,19 @@ void CMemoryCardDriver::MountCardFailed(CMemoryCardSys::ECardResult result)
} }
} }
void CMemoryCardDriver::CheckCard() void CMemoryCardDriver::Case27(ECardResult result)
{
CMemoryCardSys::ECardResult result = CMemoryCardSys::CheckCard(x0_cardPort);
if (result != CMemoryCardSys::ECardResult::CARD_RESULT_READY)
CheckCardFailed(result);
}
void CMemoryCardDriver::CheckCardFailed(CMemoryCardSys::ECardResult result)
{ {
switch (result) switch (result)
{ {
case CMemoryCardSys::ECardResult::CARD_RESULT_READY: case ECardResult::CARD_RESULT_READY:
x10_state = EState::Seven; x10_state = EState::Seven;
if (!GetCardFreeBytes()) if (!GetCardFreeBytes())
return; return;
if (CMemoryCardSys::GetSerialNo(x0_cardPort, x28_cardSerial) == if (CMemoryCardSys::GetSerialNo(x0_cardPort, x28_cardSerial) == ECardResult::CARD_RESULT_READY)
CMemoryCardSys::ECardResult::CARD_RESULT_READY)
return; return;
NoCardFound(); NoCardFound();
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_BROKEN: case ECardResult::CARD_RESULT_BROKEN:
x10_state = EState::Fourteen; x10_state = EState::Fourteen;
x14_error = EError::One; x14_error = EError::One;
break; break;
@ -118,32 +293,148 @@ void CMemoryCardDriver::CheckCardFailed(CMemoryCardSys::ECardResult result)
} }
} }
void CMemoryCardDriver::Case28(ECardResult result)
{
if (result == ECardResult::CARD_RESULT_READY)
{
x100_mcFileInfos[x194_fileIdx].first = 1;
if (x100_mcFileInfos[bool(x194_fileIdx)].first == 3)
{
x10_state = EState::Seventeen;
GoTo28();
}
else
{
x10_state = EState::Seven;
if (!GetCardFreeBytes())
return;
GoTo17();
}
}
else
HandleCardError(result, EState::Fifteen);
}
void CMemoryCardDriver::Case29(ECardResult result)
{
if (result == ECardResult::CARD_RESULT_READY)
{
auto& fileInfo = x100_mcFileInfos[x194_fileIdx];
ECardResult readRes = fileInfo.second.TryFileRead();
if (fileInfo.second.Close() != ECardResult::CARD_RESULT_READY)
{
NoCardFound();
return;
}
u32 fileIdx = bool(x194_fileIdx);
if (readRes == ECardResult::CARD_RESULT_READY)
{
x10_state = EState::One;
ReadFinished();
u32 fileId = x100_mcFileInfos[fileIdx].first;
if (fileId == 1)
CheckCardCapacity();
else
DeleteFile();
return;
}
if (readRes == ECardResult::CARD_RESULT_CRC_MISMATCH)
{
x100_mcFileInfos[x194_fileIdx].first = 3;
if (x100_mcFileInfos[fileIdx].first == 2)
{
x10_state = EState::Seven;
GoTo17();
}
else
{
x10_state = EState::Seventeen;
x14_error = EError::Nine;
}
}
}
else
HandleCardError(result, EState::Seventeen);
}
void CMemoryCardDriver::Case30(ECardResult result)
{
}
void CMemoryCardDriver::Case31(ECardResult result)
{
}
void CMemoryCardDriver::Case32(ECardResult result)
{
}
void CMemoryCardDriver::Case33(ECardResult result)
{
}
void CMemoryCardDriver::Case34(ECardResult result)
{
}
void CMemoryCardDriver::Case35(ECardResult result)
{
}
void CMemoryCardDriver::Case36(ECardResult result)
{
}
void CMemoryCardDriver::Case37(ECardResult result)
{
}
void CMemoryCardDriver::GoTo17()
{
}
void CMemoryCardDriver::GoTo28()
{
}
bool CMemoryCardDriver::GetCardFreeBytes() bool CMemoryCardDriver::GetCardFreeBytes()
{ {
CMemoryCardSys::ECardResult result = CMemoryCardSys::GetNumFreeBytes(x0_cardPort, ECardResult result = CMemoryCardSys::GetNumFreeBytes(x0_cardPort,
x18_cardFreeBytes, x18_cardFreeBytes,
x1c_cardFreeFiles); x1c_cardFreeFiles);
if (result == CMemoryCardSys::ECardResult::CARD_RESULT_READY) if (result == ECardResult::CARD_RESULT_READY)
return true; return true;
NoCardFound(); NoCardFound();
return false; return false;
} }
void CMemoryCardDriver::HandleCardError(CMemoryCardSys::ECardResult result, EState state) void CMemoryCardDriver::HandleCardError(ECardResult result, EState state)
{ {
switch (result) switch (result)
{ {
case CMemoryCardSys::ECardResult::CARD_RESULT_WRONGDEVICE: case ECardResult::CARD_RESULT_WRONGDEVICE:
x10_state = state; x10_state = state;
x14_error = EError::Four; x14_error = EError::Four;
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_NOCARD: case ECardResult::CARD_RESULT_NOCARD:
NoCardFound(); NoCardFound();
break; break;
case CMemoryCardSys::ECardResult::CARD_RESULT_IOERROR: case ECardResult::CARD_RESULT_IOERROR:
x10_state = state; x10_state = state;
x14_error = EError::Three; x14_error = EError::Three;
case CMemoryCardSys::ECardResult::CARD_RESULT_ENCODING: case ECardResult::CARD_RESULT_ENCODING:
x10_state = state; x10_state = state;
x14_error = EError::Two; x14_error = EError::Two;
break; break;
@ -153,9 +444,9 @@ void CMemoryCardDriver::HandleCardError(CMemoryCardSys::ECardResult result, ESta
void CMemoryCardDriver::Update() void CMemoryCardDriver::Update()
{ {
auto result = CMemoryCardSys::CardProbe(x0_cardPort); CMemoryCardSys::CardProbeResults result = CMemoryCardSys::CardProbe(x0_cardPort);
if (result.x0_error == CMemoryCardSys::ECardResult::CARD_RESULT_NOCARD) if (result.x0_error == ECardResult::CARD_RESULT_NOCARD)
{ {
if (x10_state != EState::Two) if (x10_state != EState::Two)
NoCardFound(); NoCardFound();
@ -163,23 +454,65 @@ void CMemoryCardDriver::Update()
return; return;
} }
if (x10_state == EState::TwentyFive)
{
FinishedLoading2();
static_cast<CMain*>(g_Main)->SetCardInserted(false);
return;
}
ECardResult resultCode = g_MemoryCardSys->GetResultCode(x0_cardPort);
bool cardInserted = false;
if (InCardInsertedRange(x10_state))
{
cardInserted = true;
switch (x10_state) switch (x10_state)
{ {
case EState::TwentySix: case EState::TwentySix:
Case26(resultCode);
break;
case EState::TwentySeven: case EState::TwentySeven:
Case27(resultCode);
break;
case EState::TwentyEight: case EState::TwentyEight:
Case28(resultCode);
break;
case EState::TwentyNine: case EState::TwentyNine:
Case29(resultCode);
break;
case EState::Thirty: case EState::Thirty:
Case30(resultCode);
break;
case EState::ThirtyOne: case EState::ThirtyOne:
Case31(resultCode);
break;
case EState::ThirtyTwo: case EState::ThirtyTwo:
Case32(resultCode);
break;
case EState::ThirtyThree: case EState::ThirtyThree:
Case33(resultCode);
break;
case EState::ThirtyFour: case EState::ThirtyFour:
Case34(resultCode);
break;
case EState::ThirtyFive: case EState::ThirtyFive:
Case35(resultCode);
break;
case EState::ThirtySix: case EState::ThirtySix:
Case36(resultCode);
break;
case EState::ThirtySeven: case EState::ThirtySeven:
Case37(resultCode);
break;
default: break; default: break;
} }
} }
static_cast<CMain*>(g_Main)->SetCardInserted(cardInserted);
}
} }
} }

View File

@ -2,6 +2,7 @@
#define __URDE_CMEMORYCARDDRIVER_HPP__ #define __URDE_CMEMORYCARDDRIVER_HPP__
#include "CMemoryCardSys.hpp" #include "CMemoryCardSys.hpp"
#include "CGameState.hpp"
namespace urde namespace urde
{ {
@ -23,6 +24,8 @@ public:
Twelve = 12, Twelve = 12,
Thirteen = 13, Thirteen = 13,
Fourteen = 14, Fourteen = 14,
Fifteen = 15,
Seventeen = 17,
TwentyFive = 26, TwentyFive = 26,
TwentySix = 26, TwentySix = 26,
TwentySeven = 27, TwentySeven = 27,
@ -53,14 +56,42 @@ public:
}; };
private: private:
struct SMemoryCardSlotInfo struct CARDFileInfo
{ {
CMemoryCardSys::EMemoryCardPort x0_cardPort; CMemoryCardSys::EMemoryCardPort x0_cardPort;
u32 x4_ = -1; int x4_fileNo = -1;
int x8_offset;
int xc_length;
u16 iBlock;
};
struct SFileInfo
{
CARDFileInfo x0_fileInfo;
std::string x14_name; std::string x14_name;
std::vector<u8> x24_; std::vector<u8> x24_saveFileData;
std::vector<u8> x34_; std::vector<u8> x34_saveData;
SMemoryCardSlotInfo(CMemoryCardSys::EMemoryCardPort cardPort, const std::string& name); SFileInfo(CMemoryCardSys::EMemoryCardPort cardPort, const std::string& name);
CMemoryCardSys::ECardResult Close();
CMemoryCardSys::EMemoryCardPort GetFileCardPort() const { return x0_fileInfo.x0_cardPort; }
int GetFileNo() const { return x0_fileInfo.x4_fileNo; }
CMemoryCardSys::ECardResult TryFileRead();
CMemoryCardSys::ECardResult FileRead();
CMemoryCardSys::ECardResult GetSaveDataOffset(u32& offOut);
};
struct SSaveHeader
{
u32 x0_;
u32 x4_[3];
};
struct SGameFileSlot
{
u8 x0_saveBuffer[940] = {};
CGameState::GameFileStateInfo x944_fileInfo;
SGameFileSlot();
void InitializeFromGameState();
}; };
CMemoryCardSys::EMemoryCardPort x0_cardPort; CMemoryCardSys::EMemoryCardPort x0_cardPort;
@ -71,16 +102,16 @@ private:
EError x14_error = EError::Zero; EError x14_error = EError::Zero;
s32 x18_cardFreeBytes = 0; s32 x18_cardFreeBytes = 0;
s32 x1c_cardFreeFiles = 0; s32 x1c_cardFreeFiles = 0;
u32 x20_ = 0; u32 x20_fileTime = 0;
u32 x24_ = 0; u32 x24_ = 0;
u64 x28_cardSerial = 0; u64 x28_cardSerial = 0;
u8 x30_[174]; u8 x30_systemData[174] = {};
std::unique_ptr<u8> xe4_[3]; std::unique_ptr<SGameFileSlot> xe4_fileSlots[3];
std::vector<std::pair<u32, SMemoryCardSlotInfo>> x100_mcSlotInfos; std::vector<std::pair<u32, SFileInfo>> x100_mcFileInfos;
u32 x194_ = -1; u32 x194_fileIdx = -1;
u32 x198_ = 0; u32 x198_ = 0;
bool x19c_ = false; bool x19c_ = false;
bool x19d_flag; bool x19d_doMergePersistent;
public: public:
CMemoryCardDriver(CMemoryCardSys::EMemoryCardPort cardPort, ResId saveBanner, CMemoryCardDriver(CMemoryCardSys::EMemoryCardPort cardPort, ResId saveBanner,
@ -89,12 +120,51 @@ public:
void FinishedLoading2(); void FinishedLoading2();
void NoCardFound(); void NoCardFound();
void MountCard(); void MountCard();
void MountCardFailed(CMemoryCardSys::ECardResult result);
void CheckCard(); void CheckCard();
void CheckCardFailed(CMemoryCardSys::ECardResult result);
CGameState::GameFileStateInfo* GetGameFileStateInfo(int idx);
static SSaveHeader LoadSaveHeader(CMemoryInStream& in);
static std::unique_ptr<SGameFileSlot> LoadSaveFile(CMemoryInStream& in);
void ReadFinished();
void MergePersistentOptions();
void DeleteFile();
void CheckCardCapacity();
void Case26(CMemoryCardSys::ECardResult result);
void Case27(CMemoryCardSys::ECardResult result);
void Case28(CMemoryCardSys::ECardResult result);
void Case29(CMemoryCardSys::ECardResult result);
void Case30(CMemoryCardSys::ECardResult result);
void Case31(CMemoryCardSys::ECardResult result);
void Case32(CMemoryCardSys::ECardResult result);
void Case33(CMemoryCardSys::ECardResult result);
void Case34(CMemoryCardSys::ECardResult result);
void Case35(CMemoryCardSys::ECardResult result);
void Case36(CMemoryCardSys::ECardResult result);
void Case37(CMemoryCardSys::ECardResult result);
void GoTo17();
void GoTo28();
bool GetCardFreeBytes(); bool GetCardFreeBytes();
void HandleCardError(CMemoryCardSys::ECardResult result, EState state); void HandleCardError(CMemoryCardSys::ECardResult result, EState state);
void Update(); void Update();
static bool InCardInsertedRange(EState v)
{
return v >= EState::TwentySix && v <= EState::ThirtySeven;
}
static bool InRange2(EState v)
{
if (v < EState::TwentyFive)
return false;
if (v == EState::TwentySeven)
return false;
if (v == EState::TwentyNine)
return false;
return true;
}
}; };
} }

View File

@ -57,22 +57,6 @@ bool CSaveUI::PumpLoad()
return true; return true;
} }
static bool InRange1(EState v)
{
return v >= EState::TwentySix && v <= EState::ThirtySeven;
}
static bool InRange2(EState v)
{
if (v < EState::TwentyFive)
return false;
if (v == EState::TwentySeven)
return false;
if (v == EState::TwentyNine)
return false;
return true;
}
CSaveUI::UIType CSaveUI::SelectUIType() const CSaveUI::UIType CSaveUI::SelectUIType() const
{ {
if (x6c_cardDriver->x10_state == EState::Two) if (x6c_cardDriver->x10_state == EState::Two)
@ -87,9 +71,9 @@ CSaveUI::UIType CSaveUI::SelectUIType() const
default: break; default: break;
} }
if (InRange1(x6c_cardDriver->x10_state)) if (CMemoryCardDriver::InCardInsertedRange(x6c_cardDriver->x10_state))
{ {
if (!InRange2(x6c_cardDriver->x10_state)) if (!CMemoryCardDriver::InRange2(x6c_cardDriver->x10_state))
return UIType::Two; return UIType::Two;
return UIType::One; return UIType::One;
} }
@ -156,7 +140,6 @@ void CSaveUI::ProcessUserInput(const CFinalInput& input)
void CSaveUI::StartGame(int idx) void CSaveUI::StartGame(int idx)
{ {
} }
void CSaveUI::EraseGame(int idx) void CSaveUI::EraseGame(int idx)
@ -178,7 +161,7 @@ CSaveUI::CSaveUI(u32 instIdx, u32 a, u32 b)
x38_strgMemoryCard = g_SimplePool->GetObj("STRG_MemoryCard"); x38_strgMemoryCard = g_SimplePool->GetObj("STRG_MemoryCard");
x44_frmeGenericMenu = g_SimplePool->GetObj("FRME_GenericMenu"); x44_frmeGenericMenu = g_SimplePool->GetObj("FRME_GenericMenu");
x6c_cardDriver = ConstructCardDriver(x0_instIdx / 32); x6c_cardDriver = ConstructCardDriver(x0_instIdx);
if (instIdx == 1) if (instIdx == 1)
{ {

View File

@ -174,9 +174,9 @@ u32 CSlideShow::SlideShowGalleryFlags()
ret |= 1; ret |= 1;
if (g_GameState->SystemOptions().GetLogScanCount() == 100) if (g_GameState->SystemOptions().GetLogScanCount() == 100)
ret |= 2; ret |= 2;
if (g_GameState->SystemOptions().PlayerBeatHardMode()) if (g_GameState->SystemOptions().GetPlayerBeatHardMode())
ret |= 4; ret |= 4;
if (g_GameState->SystemOptions().AllItemsCollected()) if (g_GameState->SystemOptions().GetAllItemsCollected())
ret |= 8; ret |= 8;
return ret; return ret;
} }