#include "CMemoryCardSys.hpp" #include "GameGlobalObjects.hpp" #include "CSimplePool.hpp" #include "CGameState.hpp" #include "GuiSys/CStringTable.hpp" #include "CCRC32.hpp" #include "Graphics/CTexture.hpp" namespace urde { CSaveWorldIntermediate::CSaveWorldIntermediate(ResId mlvl, ResId savw) : x0_mlvlId(mlvl), x8_savwId(savw) { if (savw == -1) x2c_dummyWorld = std::make_unique(mlvl, false); else x34_saveWorld = g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), savw}); } bool CSaveWorldIntermediate::InitializePump() { if (x2c_dummyWorld) { CDummyWorld& wld = *x2c_dummyWorld; if (!wld.ICheckWorldComplete()) return false; x4_strgId = wld.IGetStringTableAssetId(); x8_savwId = wld.IGetSaveWorldAssetId(); u32 areaCount = wld.IGetAreaCount(); xc_areaIds.reserve(areaCount); for (u32 i=0 ; iIGetAreaId()); } ResId mlvlId = wld.IGetWorldAssetId(); CWorldState& mlvlState = g_GameState->StateForWorld(mlvlId); x1c_defaultLayerStates = mlvlState.GetLayerState()->x0_areaLayers; 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; } return false; } CMemoryCardSys::CMemoryCardSys() { x0_hints = g_SimplePool->GetObj("HINT_Hints"); xc_memoryWorlds.reserve(16); x1c_worldInter.emplace(); x1c_worldInter->reserve(16); g_ResFactory->EnumerateNamedResources([&](const std::string& name, const SObjectTag& tag) -> bool { if (tag.type == FOURCC('MLVL')) { auto existingSearch = std::find_if(xc_memoryWorlds.cbegin(), xc_memoryWorlds.cend(), [&](const auto& wld) { return wld.first == tag.id; }); if (existingSearch == xc_memoryWorlds.cend()) { xc_memoryWorlds.emplace_back(tag.id, CSaveWorldMemory{}); x1c_worldInter->emplace_back(tag.id, -1); } } return true; }); } 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; } if (!x0_hints.IsLoaded() || !x0_hints.GetObj()) return false; return true; } 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; CSaveWorld& savw = *world.x34_saveWorld; wldMemOut.x8_areaCount = savw.GetAreaCount(); x20_scanStates.reserve(x20_scanStates.size() + savw.GetScans().size()); for (const CSaveWorld::SScanState& scan : savw.GetScans()) x20_scanStates.emplace_back(scan.x0_id, scan.x4_category); wldMemOut.x3c_saveWorld = std::move(world.x34_saveWorld); wldMemOut.x2c_worldName = g_SimplePool->GetObj(SObjectTag{FOURCC('STRG'), wldMemOut.x0_strgId}); } else done = false; } if (done) x1c_worldInter = std::experimental::nullopt; return false; } void CMemoryCardSys::CCardFileInfo::LockBannerToken(ResId bannerTxtr, CSimplePool& sp) { x3c_bannerTex = bannerTxtr; x40_bannerTok.emplace(sp.GetObj({FOURCC('TXTR'), bannerTxtr}, m_texParam)); } CMemoryCardSys::CCardFileInfo::Icon::Icon(ResId id, u32 speed, CSimplePool& sp, const CVParamTransfer& cv) : x0_id(id), x4_speed(speed), x8_tex(sp.GetObj({FOURCC('TXTR'), id}, cv)) {} void CMemoryCardSys::CCardFileInfo::LockIconToken(ResId iconTxtr, u32 speed, CSimplePool& sp) { x50_iconToks.emplace_back(iconTxtr, speed, sp, m_texParam); } u32 CMemoryCardSys::CCardFileInfo::CalculateBannerDataSize() const { u32 ret = 68; if (x3c_bannerTex != -1) { if ((*x40_bannerTok)->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3) ret = 6212; else 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; } } if (paletteTex) ret += 512; return ret; } u32 CMemoryCardSys::CCardFileInfo::CalculateTotalDataSize() const { return (CalculateBannerDataSize() + xf4_saveBuffer.size() + 8191) & ~8191; } void CMemoryCardSys::CCardFileInfo::BuildCardBuffer() { u32 bannerSz = CalculateBannerDataSize(); x104_cardBuffer.resize((bannerSz + xf4_saveBuffer.size() + 8191) & ~8191); CMemoryOutStream w(x104_cardBuffer.data(), x104_cardBuffer.size()); w.writeUint32Big(0); char comment[64]; strncpy(comment, x28_comment.data(), 64); w.writeBytes(comment, 64); WriteBannerData(w); WriteIconData(w); memmove(x104_cardBuffer.data() + bannerSz, xf4_saveBuffer.data(), xf4_saveBuffer.size()); reinterpret_cast(*x104_cardBuffer.data()) = hecl::SBig(CCRC32::Calculate(x104_cardBuffer.data() + 4, x104_cardBuffer.size() - 4)); xf4_saveBuffer.clear(); } void CMemoryCardSys::CCardFileInfo::WriteBannerData(CMemoryOutStream& out) const { if (x3c_bannerTex != -1) { const TLockedToken& tex = *x40_bannerTok; u32 bufSz; ETexelFormat fmt; std::unique_ptr palette; std::unique_ptr texels = tex->BuildMemoryCardTex(bufSz, fmt, palette); if (fmt == ETexelFormat::RGB5A3) out.writeBytes(texels.get(), 6144); else out.writeBytes(texels.get(), 3072); if (fmt == ETexelFormat::C8) out.writeBytes(palette.get(), 512); } } void CMemoryCardSys::CCardFileInfo::WriteIconData(CMemoryOutStream& out) const { std::unique_ptr palette; for (const Icon& icon : x50_iconToks) { u32 bufSz; ETexelFormat fmt; std::unique_ptr texels = icon.x8_tex->BuildMemoryCardTex(bufSz, fmt, palette); if (fmt == ETexelFormat::RGB5A3) out.writeBytes(texels.get(), 2048); else out.writeBytes(texels.get(), 1024); } if (palette) out.writeBytes(palette.get(), 512); } CMemoryCardSys::ECardResult CMemoryCardSys::CCardFileInfo::PumpCardTransfer() { if (x0_status == EStatus::Standby) return ECardResult::CARD_RESULT_READY; else if (x0_status == EStatus::Transferring) { ECardResult result = CMemoryCardSys::GetResultCode(GetCardPort()); if (result != ECardResult::CARD_RESULT_BUSY) x104_cardBuffer.clear(); if (result != ECardResult::CARD_RESULT_READY) return result; x0_status = EStatus::Done; CARDStat stat = {}; result = GetStatus(stat); if (result != ECardResult::CARD_RESULT_READY) return result; result = CMemoryCardSys::SetStatus(GetCardPort(), GetFileNo(), stat); if (result != ECardResult::CARD_RESULT_READY) return result; return ECardResult::CARD_RESULT_BUSY; } else { ECardResult result = CMemoryCardSys::GetResultCode(GetCardPort()); if (result == ECardResult::CARD_RESULT_READY) x0_status = EStatus::Standby; return result; } } CMemoryCardSys::ECardResult CMemoryCardSys::CCardFileInfo::GetStatus(CARDStat& stat) const { ECardResult result = CMemoryCardSys::GetStatus(GetCardPort(), GetFileNo(), stat); if (result != ECardResult::CARD_RESULT_READY) return result; stat.SetCommentAddr(4); stat.SetIconAddr(68); u32 bannerFmt; if (x3c_bannerTex != -1) { if ((*x40_bannerTok)->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3) bannerFmt = 2; else bannerFmt = 1; } else bannerFmt = 0; stat.SetBannerFormat(bannerFmt); int idx = 0; for (const Icon& icon : x50_iconToks) { stat.SetIconFormat(icon.x8_tex->GetMemoryCardTexelFormat() == ETexelFormat::RGB5A3 ? 2 : 1, idx); stat.SetIconSpeed(icon.x4_speed, idx); ++idx; } if (idx < 8) { stat.SetIconFormat(0, idx); stat.SetIconSpeed(0, idx); } return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::CCardFileInfo::CreateFile() { return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::CCardFileInfo::Write() { BuildCardBuffer(); //DCStoreRange(info.x104_cardBuffer.data(), info.x104_cardBuffer.size()); //CARDWriteAsync(&info.x4_info, info.x104_cardBuffer.data(), info.x104_cardBuffer.size(), 0, 0); return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::CCardFileInfo::Close() { EMemoryCardPort port = GetCardPort(); //CARDClose(port); x4_info.chan = port; return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::CardProbeResults CMemoryCardSys::CardProbe(EMemoryCardPort port) { return {}; } CMemoryCardSys::ECardResult CMemoryCardSys::MountCard(EMemoryCardPort port) { return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::CheckCard(EMemoryCardPort port) { return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::GetNumFreeBytes(EMemoryCardPort port, s32& freeBytes, s32& freeFiles) { return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::GetSerialNo(EMemoryCardPort port, u64& serialOut) { 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::SetStatus(EMemoryCardPort port, int fileNo, const CARDStat& stat) { 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; } CMemoryCardSys::ECardResult CMemoryCardSys::Rename(EMemoryCardPort port, const char* oldName, const char* newName) { return ECardResult::CARD_RESULT_READY; } CMemoryCardSys::ECardResult CMemoryCardSys::FormatCard(EMemoryCardPort port) { return ECardResult::CARD_RESULT_READY; } }