2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/CMemoryCardSys.hpp"
|
|
|
|
|
|
|
|
#include "Runtime/CCRC32.hpp"
|
|
|
|
#include "Runtime/CGameState.hpp"
|
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
|
|
|
#include "Runtime/Graphics/CTexture.hpp"
|
|
|
|
#include "Runtime/GuiSys/CStringTable.hpp"
|
2021-02-26 05:07:41 +00:00
|
|
|
#include <hecl/CVar.hpp>
|
|
|
|
#include <hecl/CVarManager.hpp>
|
2016-09-25 01:58:20 +00:00
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2021-02-26 05:07:41 +00:00
|
|
|
namespace {
|
2016-12-28 08:51:28 +00:00
|
|
|
using ECardResult = kabufuda::ECardResult;
|
|
|
|
|
2016-12-28 21:39:38 +00:00
|
|
|
static kabufuda::SystemString g_CardImagePaths[2] = {};
|
2019-09-04 06:11:04 +00:00
|
|
|
static kabufuda::Card g_CardStates[2] = {kabufuda::Card{"GM8E", "01"}, kabufuda::Card{"GM8E", "01"}};
|
2018-12-08 05:30:43 +00:00
|
|
|
// static kabufuda::ECardResult g_OpResults[2] = {};
|
2021-02-26 05:07:41 +00:00
|
|
|
hecl::CVar* mc_dolphinAPath = nullptr;
|
|
|
|
hecl::CVar* mc_dolphinBPath = nullptr;
|
|
|
|
} // namespace
|
2018-12-08 05:30:43 +00:00
|
|
|
CSaveWorldIntermediate::CSaveWorldIntermediate(CAssetId mlvl, CAssetId savw) : x0_mlvlId(mlvl), x8_savwId(savw) {
|
|
|
|
if (!savw.IsValid())
|
|
|
|
x2c_dummyWorld = std::make_unique<CDummyWorld>(mlvl, false);
|
|
|
|
else
|
|
|
|
x34_saveWorld = g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), savw});
|
2017-01-09 03:44:00 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CSaveWorldIntermediate::InitializePump() {
|
|
|
|
if (x2c_dummyWorld) {
|
|
|
|
CDummyWorld& wld = *x2c_dummyWorld;
|
|
|
|
if (!wld.ICheckWorldComplete())
|
|
|
|
return false;
|
2017-01-09 03:44:00 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
x4_strgId = wld.IGetStringTableAssetId();
|
|
|
|
x8_savwId = wld.IGetSaveWorldAssetId();
|
2020-03-21 04:12:13 +00:00
|
|
|
const u32 areaCount = wld.IGetAreaCount();
|
2017-02-08 06:48:43 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
xc_areaIds.reserve(areaCount);
|
|
|
|
for (u32 i = 0; i < areaCount; ++i) {
|
|
|
|
const IGameArea* area = wld.IGetAreaAlways(i);
|
2020-10-21 22:55:45 +00:00
|
|
|
xc_areaIds.emplace_back(area->IGetAreaSaveId());
|
2017-02-08 06:48:43 +00:00
|
|
|
}
|
2017-05-14 19:58:44 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CAssetId mlvlId = wld.IGetWorldAssetId();
|
|
|
|
CWorldState& mlvlState = g_GameState->StateForWorld(mlvlId);
|
|
|
|
x1c_defaultLayerStates = mlvlState.GetLayerState()->x0_areaLayers;
|
2016-09-30 22:43:19 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
x34_saveWorld = g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), x8_savwId});
|
|
|
|
x2c_dummyWorld.reset();
|
|
|
|
} else {
|
|
|
|
if (!x34_saveWorld)
|
|
|
|
return true;
|
|
|
|
if (x34_saveWorld.IsLoaded() && x34_saveWorld.GetObj())
|
|
|
|
return true;
|
|
|
|
}
|
2016-09-30 22:43:19 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return false;
|
2016-09-30 22:43:19 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CMemoryCardSys::HasSaveWorldMemory(CAssetId wldId) const {
|
|
|
|
auto existingSearch = std::find_if(xc_memoryWorlds.cbegin(), xc_memoryWorlds.cend(),
|
|
|
|
[&](const auto& wld) { return wld.first == wldId; });
|
|
|
|
return existingSearch != xc_memoryWorlds.cend();
|
2018-06-08 21:00:18 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
const CSaveWorldMemory& CMemoryCardSys::GetSaveWorldMemory(CAssetId wldId) const {
|
|
|
|
auto existingSearch = std::find_if(xc_memoryWorlds.cbegin(), xc_memoryWorlds.cend(),
|
|
|
|
[&](const auto& wld) { return wld.first == wldId; });
|
|
|
|
return existingSearch->second;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CMemoryCardSys::CMemoryCardSys() {
|
2021-02-26 05:07:41 +00:00
|
|
|
mc_dolphinAPath = hecl::CVarManager::instance()->findOrMakeCVar(
|
|
|
|
"memcard.PathA"sv, "Path to the memory card image for SlotA"sv, ""sv,
|
|
|
|
(hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::System | hecl::CVar::EFlags::ModifyRestart));
|
|
|
|
mc_dolphinBPath = hecl::CVarManager::instance()->findOrMakeCVar(
|
|
|
|
"memcard.PathB"sv, "Path to the memory card image for SlotB"sv, ""sv,
|
|
|
|
(hecl::CVar::EFlags::Archive | hecl::CVar::EFlags::System | hecl::CVar::EFlags::ModifyRestart));
|
2018-12-08 05:30:43 +00:00
|
|
|
x0_hints = g_SimplePool->GetObj("HINT_Hints");
|
|
|
|
xc_memoryWorlds.reserve(16);
|
|
|
|
x1c_worldInter.emplace();
|
|
|
|
x1c_worldInter->reserve(16);
|
|
|
|
|
2019-10-01 07:38:03 +00:00
|
|
|
std::vector<CAssetId> orderedMLVLs;
|
2018-12-08 05:30:43 +00:00
|
|
|
orderedMLVLs.reserve(16);
|
|
|
|
g_ResFactory->EnumerateNamedResources([&](std::string_view name, const SObjectTag& tag) -> bool {
|
2019-10-01 07:38:03 +00:00
|
|
|
if (tag.type == FOURCC('MLVL'))
|
|
|
|
orderedMLVLs.emplace_back(tag.id);
|
2018-12-08 05:30:43 +00:00
|
|
|
return true;
|
|
|
|
});
|
2019-10-01 07:38:03 +00:00
|
|
|
std::sort(orderedMLVLs.begin(), orderedMLVLs.end());
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
for (const auto& mlvl : orderedMLVLs) {
|
2019-10-01 07:38:03 +00:00
|
|
|
if (!HasSaveWorldMemory(mlvl)) {
|
|
|
|
xc_memoryWorlds.emplace_back(mlvl, CSaveWorldMemory{});
|
|
|
|
x1c_worldInter->emplace_back(mlvl, CAssetId{});
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
x30_scanCategoryCounts.resize(6);
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
bool CMemoryCardSys::InitializePump() {
|
|
|
|
if (!x1c_worldInter) {
|
|
|
|
for (const auto& world : xc_memoryWorlds) {
|
|
|
|
const CSaveWorldMemory& wld = world.second;
|
|
|
|
if (!wld.GetWorldName())
|
|
|
|
continue;
|
|
|
|
if (!wld.GetWorldName().IsLoaded() || !wld.GetWorldName().GetObj())
|
|
|
|
return false;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2020-01-21 04:00:40 +00:00
|
|
|
return !(!x0_hints.IsLoaded() || !x0_hints.GetObj());
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
bool done = true;
|
|
|
|
for (CSaveWorldIntermediate& world : *x1c_worldInter) {
|
|
|
|
if (world.InitializePump()) {
|
|
|
|
if (!world.x34_saveWorld)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
auto existingSearch = std::find_if(xc_memoryWorlds.begin(), xc_memoryWorlds.end(),
|
|
|
|
[&](const auto& test) { return test.first == world.x0_mlvlId; });
|
|
|
|
CSaveWorldMemory& wldMemOut = existingSearch->second;
|
|
|
|
wldMemOut.x4_savwId = world.x8_savwId;
|
|
|
|
wldMemOut.x0_strgId = world.x4_strgId;
|
|
|
|
wldMemOut.xc_areaIds = world.xc_areaIds;
|
|
|
|
wldMemOut.x1c_defaultLayerStates = world.x1c_defaultLayerStates;
|
|
|
|
|
2021-06-05 05:27:52 +00:00
|
|
|
CWorldSaveGameInfo& savw = *world.x34_saveWorld;
|
2018-12-08 05:30:43 +00:00
|
|
|
wldMemOut.x8_areaCount = savw.GetAreaCount();
|
|
|
|
|
|
|
|
x20_scanStates.reserve(x20_scanStates.size() + savw.GetScans().size());
|
2021-06-05 05:27:52 +00:00
|
|
|
for (const CWorldSaveGameInfo::SScanState& scan : savw.GetScans()) {
|
2020-04-23 11:13:18 +00:00
|
|
|
const auto scanStateIter = std::find_if(x20_scanStates.cbegin(), x20_scanStates.cend(), [&](const auto& test) {
|
2018-12-08 05:30:43 +00:00
|
|
|
return test.first == scan.x0_id && test.second == scan.x4_category;
|
|
|
|
});
|
2020-04-23 11:13:18 +00:00
|
|
|
if (scanStateIter == x20_scanStates.cend()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
x20_scanStates.emplace_back(scan.x0_id, scan.x4_category);
|
|
|
|
++x30_scanCategoryCounts[int(scan.x4_category)];
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
wldMemOut.x3c_saveWorld = std::move(world.x34_saveWorld);
|
|
|
|
wldMemOut.x2c_worldName = g_SimplePool->GetObj(SObjectTag{FOURCC('STRG'), wldMemOut.x0_strgId});
|
|
|
|
} else
|
|
|
|
done = false;
|
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (done) {
|
2021-02-26 05:07:41 +00:00
|
|
|
std::sort(x20_scanStates.begin(), x20_scanStates.end(),
|
|
|
|
[&](const auto& a, const auto& b) { return a.first < b.first; });
|
2019-06-12 02:05:17 +00:00
|
|
|
x1c_worldInter = std::nullopt;
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return false;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2020-10-20 09:40:02 +00:00
|
|
|
std::pair<CAssetId, TAreaId> CMemoryCardSys::GetAreaAndWorldIdForSaveId(s32 saveId) const {
|
2021-02-26 05:07:41 +00:00
|
|
|
for (const auto& [mlvl, saveWorld] : xc_memoryWorlds) {
|
|
|
|
for (TAreaId areaId = 0; areaId < saveWorld.xc_areaIds.size(); ++areaId) {
|
|
|
|
if (saveWorld.xc_areaIds[areaId] == saveId) {
|
|
|
|
return {mlvl, areaId};
|
|
|
|
}
|
2020-10-20 09:40:02 +00:00
|
|
|
}
|
2021-02-26 05:07:41 +00:00
|
|
|
}
|
2020-10-20 09:40:02 +00:00
|
|
|
|
2021-02-26 05:07:41 +00:00
|
|
|
return {{}, kInvalidAreaId};
|
2020-10-20 09:40:02 +00:00
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::CCardFileInfo::LockBannerToken(CAssetId bannerTxtr, CSimplePool& sp) {
|
|
|
|
x3c_bannerTex = bannerTxtr;
|
|
|
|
x40_bannerTok.emplace(sp.GetObj({FOURCC('TXTR'), bannerTxtr}, m_texParam));
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CMemoryCardSys::CCardFileInfo::Icon::Icon(CAssetId id, kabufuda::EAnimationSpeed speed, CSimplePool& sp,
|
|
|
|
const CVParamTransfer& cv)
|
|
|
|
: x0_id(id), x4_speed(speed), x8_tex(sp.GetObj({FOURCC('TXTR'), id}, cv)) {}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::CCardFileInfo::LockIconToken(CAssetId iconTxtr, kabufuda::EAnimationSpeed speed, CSimplePool& sp) {
|
|
|
|
x50_iconToks.emplace_back(iconTxtr, speed, sp, m_texParam);
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 CMemoryCardSys::CCardFileInfo::CalculateBannerDataSize() const {
|
|
|
|
u32 ret = 68;
|
|
|
|
if (x3c_bannerTex.IsValid()) {
|
|
|
|
if ((*x40_bannerTok)->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3)
|
|
|
|
ret = 6212;
|
2016-12-23 06:41:39 +00:00
|
|
|
else
|
2018-12-08 05:30:43 +00:00
|
|
|
ret = 3652;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool paletteTex = false;
|
|
|
|
for (const Icon& icon : x50_iconToks) {
|
|
|
|
if (icon.x8_tex->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3)
|
|
|
|
ret += 2048;
|
|
|
|
else {
|
|
|
|
ret += 1024;
|
|
|
|
paletteTex = true;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (paletteTex)
|
|
|
|
ret += 512;
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return ret;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 CMemoryCardSys::CCardFileInfo::CalculateTotalDataSize() const {
|
|
|
|
return (CalculateBannerDataSize() + xf4_saveBuffer.size() + 8191) & ~8191;
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::CCardFileInfo::BuildCardBuffer() {
|
|
|
|
u32 bannerSz = CalculateBannerDataSize();
|
|
|
|
x104_cardBuffer.resize((bannerSz + xf4_saveBuffer.size() + 8191) & ~8191);
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
CMemoryOutStream w(x104_cardBuffer.data(), x104_cardBuffer.size());
|
|
|
|
w.writeUint32Big(0);
|
|
|
|
char comment[64];
|
2021-04-05 17:31:00 +00:00
|
|
|
std::memset(comment, 0, std::size(comment));
|
|
|
|
std::strncpy(comment, x28_comment.data(), std::size(comment) - 1);
|
2018-12-08 05:30:43 +00:00
|
|
|
w.writeBytes(comment, 64);
|
|
|
|
WriteBannerData(w);
|
|
|
|
WriteIconData(w);
|
|
|
|
memmove(x104_cardBuffer.data() + bannerSz, xf4_saveBuffer.data(), xf4_saveBuffer.size());
|
|
|
|
reinterpret_cast<u32&>(*x104_cardBuffer.data()) =
|
|
|
|
hecl::SBig(CCRC32::Calculate(x104_cardBuffer.data() + 4, x104_cardBuffer.size() - 4));
|
2017-02-04 03:46:12 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
xf4_saveBuffer.clear();
|
2016-12-18 04:16:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::CCardFileInfo::WriteBannerData(CMemoryOutStream& out) const {
|
|
|
|
if (x3c_bannerTex.IsValid()) {
|
|
|
|
const TLockedToken<CTexture>& tex = *x40_bannerTok;
|
|
|
|
u32 bufSz;
|
|
|
|
ETexelFormat fmt;
|
|
|
|
std::unique_ptr<u8[]> palette;
|
|
|
|
std::unique_ptr<u8[]> texels = tex->BuildMemoryCardTex(bufSz, fmt, palette);
|
2016-12-28 21:51:33 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (fmt == ETexelFormat::RGB5A3)
|
|
|
|
out.writeBytes(texels.get(), 6144);
|
|
|
|
else
|
|
|
|
out.writeBytes(texels.get(), 3072);
|
2016-12-28 08:51:28 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (fmt == ETexelFormat::C8)
|
|
|
|
out.writeBytes(palette.get(), 512);
|
|
|
|
}
|
2016-12-28 08:51:28 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::CCardFileInfo::WriteIconData(CMemoryOutStream& out) const {
|
|
|
|
std::unique_ptr<u8[]> palette;
|
|
|
|
for (const Icon& icon : x50_iconToks) {
|
|
|
|
u32 bufSz;
|
|
|
|
ETexelFormat fmt;
|
|
|
|
std::unique_ptr<u8[]> texels = icon.x8_tex->BuildMemoryCardTex(bufSz, fmt, palette);
|
2016-12-28 08:51:28 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
if (fmt == ETexelFormat::RGB5A3)
|
|
|
|
out.writeBytes(texels.get(), 2048);
|
|
|
|
else
|
|
|
|
out.writeBytes(texels.get(), 1024);
|
|
|
|
}
|
|
|
|
if (palette)
|
|
|
|
out.writeBytes(palette.get(), 512);
|
2016-12-18 04:16:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::CCardFileInfo::PumpCardTransfer() {
|
|
|
|
if (x0_status == EStatus::Standby)
|
2016-12-24 05:23:50 +00:00
|
|
|
return ECardResult::READY;
|
2018-12-08 05:30:43 +00:00
|
|
|
else if (x0_status == EStatus::Transferring) {
|
|
|
|
ECardResult result = CMemoryCardSys::GetResultCode(GetCardPort());
|
|
|
|
if (result != ECardResult::BUSY)
|
|
|
|
x104_cardBuffer.clear();
|
|
|
|
if (result != ECardResult::READY)
|
|
|
|
return result;
|
|
|
|
x0_status = EStatus::Done;
|
|
|
|
kabufuda::CardStat stat = {};
|
|
|
|
result = GetStatus(stat);
|
|
|
|
if (result != ECardResult::READY)
|
|
|
|
return result;
|
|
|
|
result = CMemoryCardSys::SetStatus(m_handle.slot, m_handle.getFileNo(), stat);
|
|
|
|
if (result != ECardResult::READY)
|
|
|
|
return result;
|
|
|
|
return ECardResult::BUSY;
|
|
|
|
} else {
|
|
|
|
ECardResult result = CMemoryCardSys::GetResultCode(GetCardPort());
|
|
|
|
if (result == ECardResult::READY)
|
|
|
|
x0_status = EStatus::Standby;
|
|
|
|
return result;
|
|
|
|
}
|
2016-12-18 04:16:04 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::CCardFileInfo::GetStatus(kabufuda::CardStat& stat) const {
|
|
|
|
ECardResult result = CMemoryCardSys::GetStatus(m_handle.slot, m_handle.getFileNo(), stat);
|
|
|
|
if (result != ECardResult::READY)
|
|
|
|
return result;
|
2016-12-18 04:16:04 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
stat.SetCommentAddr(4);
|
|
|
|
stat.SetIconAddr(68);
|
2016-12-20 21:51:50 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
kabufuda::EImageFormat bannerFmt;
|
|
|
|
if (x3c_bannerTex.IsValid()) {
|
|
|
|
if ((*x40_bannerTok)->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3)
|
|
|
|
bannerFmt = kabufuda::EImageFormat::RGB5A3;
|
|
|
|
else
|
|
|
|
bannerFmt = kabufuda::EImageFormat::C8;
|
|
|
|
} else
|
|
|
|
bannerFmt = kabufuda::EImageFormat::None;
|
|
|
|
stat.SetBannerFormat(bannerFmt);
|
2016-12-20 21:51:50 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
int idx = 0;
|
|
|
|
for (const Icon& icon : x50_iconToks) {
|
|
|
|
stat.SetIconFormat(icon.x8_tex->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3 ? kabufuda::EImageFormat::RGB5A3
|
|
|
|
: kabufuda::EImageFormat::C8,
|
|
|
|
idx);
|
|
|
|
stat.SetIconSpeed(icon.x4_speed, idx);
|
|
|
|
++idx;
|
|
|
|
}
|
|
|
|
if (idx < 8) {
|
|
|
|
stat.SetIconFormat(kabufuda::EImageFormat::None, idx);
|
|
|
|
stat.SetIconSpeed(kabufuda::EAnimationSpeed::End, idx);
|
|
|
|
}
|
2016-12-23 06:41:39 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
return ECardResult::READY;
|
2016-12-20 21:51:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::CCardFileInfo::CreateFile() {
|
|
|
|
return CMemoryCardSys::CreateFile(m_handle.slot, x18_fileName.c_str(), CalculateTotalDataSize(), m_handle);
|
2016-12-20 21:51:50 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::CCardFileInfo::WriteFile() {
|
|
|
|
BuildCardBuffer();
|
|
|
|
// DCStoreRange(x104_cardBuffer.data(), x104_cardBuffer.size());
|
|
|
|
x0_status = EStatus::Transferring;
|
|
|
|
return CMemoryCardSys::WriteFile(m_handle, x104_cardBuffer.data(), x104_cardBuffer.size(), 0);
|
2016-12-23 06:41:39 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::CCardFileInfo::CloseFile() { return CMemoryCardSys::CloseFile(m_handle); }
|
2021-06-02 15:06:22 +00:00
|
|
|
kabufuda::SystemString CMemoryCardSys::_GetDolphinCardPath(kabufuda::ECardSlot slot) {
|
|
|
|
return g_CardImagePaths[static_cast<u32>(slot)];
|
|
|
|
}
|
2016-12-28 08:51:28 +00:00
|
|
|
|
2021-02-26 05:07:41 +00:00
|
|
|
void CMemoryCardSys::_ResolveDolphinCardPath(const hecl::CVar* cv, kabufuda::ECardSlot slot) {
|
|
|
|
#if CARD_UCS2
|
|
|
|
if (cv != nullptr && cv->toWideLiteral().empty()) {
|
|
|
|
g_CardImagePaths[int(slot)] = ResolveDolphinCardPath(slot);
|
|
|
|
} else if (cv != nullptr) {
|
|
|
|
g_CardImagePaths[int(slot)] = cv->toWideLiteral();
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
if (cv != nullptr && cv->toLiteral().empty()) {
|
|
|
|
g_CardImagePaths[int(slot)] = ResolveDolphinCardPath(slot);
|
|
|
|
} else if (cv != nullptr) {
|
|
|
|
g_CardImagePaths[int(slot)] = cv->toLiteral();
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
kabufuda::ProbeResults CMemoryCardSys::CardProbe(kabufuda::ECardSlot port) {
|
2021-02-26 05:07:41 +00:00
|
|
|
_ResolveDolphinCardPath(mc_dolphinAPath, kabufuda::ECardSlot::SlotA);
|
|
|
|
_ResolveDolphinCardPath(mc_dolphinBPath, kabufuda::ECardSlot::SlotB);
|
2016-12-28 08:51:28 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
kabufuda::ProbeResults res = kabufuda::Card::probeCardFile(g_CardImagePaths[int(port)]);
|
|
|
|
// g_OpResults[int(port)] = res.x0_error;
|
|
|
|
return res;
|
2016-12-28 08:51:28 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
ECardResult CMemoryCardSys::MountCard(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (!card.open(g_CardImagePaths[int(port)]))
|
|
|
|
return ECardResult::NOCARD;
|
|
|
|
ECardResult result = card.getError();
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
if (result == ECardResult::READY)
|
2016-12-24 05:23:50 +00:00
|
|
|
return ECardResult::READY;
|
2018-12-08 05:30:43 +00:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::UnmountCard(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.commit();
|
|
|
|
// g_OpResults[int(port)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::CheckCard(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
ECardResult result = card.getError();
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::CreateFile(kabufuda::ECardSlot port, const char* name, u32 size, CardFileHandle& info) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
info.slot = port;
|
|
|
|
ECardResult result = card.createFile(name, size, info.handle);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::OpenFile(kabufuda::ECardSlot port, const char* name, CardFileHandle& info) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
info.slot = port;
|
|
|
|
ECardResult result = card.openFile(name, info.handle);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::FastOpenFile(kabufuda::ECardSlot port, int fileNo, CardFileHandle& info) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
info.slot = port;
|
|
|
|
ECardResult result = card.openFile(fileNo, info.handle);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::CloseFile(CardFileHandle& info) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(info.slot)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(info.slot)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.closeFile(info.handle);
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::ReadFile(CardFileHandle& info, void* buf, s32 length, s32 offset) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(info.slot)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(info.slot)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.seek(info.handle, offset, kabufuda::SeekOrigin::Begin);
|
|
|
|
card.asyncRead(info.handle, buf, length);
|
|
|
|
// g_OpResults[int(info.slot)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::WriteFile(CardFileHandle& info, const void* buf, s32 length, s32 offset) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(info.slot)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(info.slot)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.seek(info.handle, offset, kabufuda::SeekOrigin::Begin);
|
|
|
|
card.asyncWrite(info.handle, buf, length);
|
|
|
|
// g_OpResults[int(info.slot)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::GetNumFreeBytes(kabufuda::ECardSlot port, s32& freeBytes, s32& freeFiles) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.getFreeBlocks(freeBytes, freeFiles);
|
|
|
|
// g_OpResults[int(port)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::GetSerialNo(kabufuda::ECardSlot port, u64& serialOut) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
card.getSerial(serialOut);
|
|
|
|
// g_OpResults[int(port)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::GetResultCode(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
return card.getError();
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::GetStatus(kabufuda::ECardSlot port, int fileNo, CardStat& statOut) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ECardResult result = card.getStatus(fileNo, statOut);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::SetStatus(kabufuda::ECardSlot port, int fileNo, const CardStat& stat) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ECardResult result = card.setStatus(fileNo, stat);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::DeleteFile(kabufuda::ECardSlot port, const char* name) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ECardResult result = card.deleteFile(name);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::FastDeleteFile(kabufuda::ECardSlot port, int fileNo) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ECardResult result = card.deleteFile(fileNo);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::Rename(kabufuda::ECardSlot port, const char* oldName, const char* newName) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
ECardResult result = card.renameFile(oldName, newName);
|
|
|
|
// g_OpResults[int(port)] = result;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
ECardResult CMemoryCardSys::FormatCard(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
card.format(port);
|
|
|
|
if (CardResult err = card.getError()) {
|
|
|
|
// g_OpResults[int(port)] = err;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
// g_OpResults[int(port)] = ECardResult::READY;
|
|
|
|
return ECardResult::READY;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CMemoryCardSys::CommitToDisk(kabufuda::ECardSlot port) {
|
|
|
|
kabufuda::Card& card = g_CardStates[int(port)];
|
|
|
|
card.commit();
|
|
|
|
}
|
|
|
|
|
2021-06-02 15:06:22 +00:00
|
|
|
bool CMemoryCardSys::CreateDolphinCard(kabufuda::ECardSlot slot) {
|
|
|
|
kabufuda::SystemString path =
|
|
|
|
_CreateDolphinCard(slot, slot == kabufuda::ECardSlot::SlotA ? mc_dolphinAPath->hasDefaultValue()
|
|
|
|
: mc_dolphinBPath->hasDefaultValue());
|
2020-04-15 11:27:06 +00:00
|
|
|
if (CardProbe(slot).x0_error != ECardResult::READY) {
|
2021-06-02 15:06:22 +00:00
|
|
|
return false;
|
2020-04-15 11:27:06 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
MountCard(slot);
|
|
|
|
FormatCard(slot);
|
|
|
|
kabufuda::Card& card = g_CardStates[int(slot)];
|
|
|
|
card.waitForCompletion();
|
2021-06-02 15:06:22 +00:00
|
|
|
return true;
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 15:06:22 +00:00
|
|
|
void CMemoryCardSys::_ResetCVar(kabufuda::ECardSlot slot) {
|
2021-06-07 19:29:18 +00:00
|
|
|
switch (slot) {
|
2021-06-02 15:06:22 +00:00
|
|
|
case kabufuda::ECardSlot::SlotA:
|
|
|
|
mc_dolphinAPath->fromLiteral("");
|
|
|
|
break;
|
|
|
|
case kabufuda::ECardSlot::SlotB:
|
|
|
|
mc_dolphinBPath->fromLiteral("");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
void CMemoryCardSys::Shutdown() {
|
|
|
|
UnmountCard(kabufuda::ECardSlot::SlotA);
|
|
|
|
UnmountCard(kabufuda::ECardSlot::SlotB);
|
|
|
|
}
|
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|