diff --git a/Runtime/CBasics.hpp b/Runtime/CBasics.hpp index 91030ea84..5ed539f72 100644 --- a/Runtime/CBasics.hpp +++ b/Runtime/CBasics.hpp @@ -39,7 +39,8 @@ public: static OSTime ToWiiTime(std::chrono::system_clock::time_point time); static std::chrono::system_clock::time_point FromWiiTime(OSTime wiiTime); - static OSCalendarTime ToCalendarTime(OSTime time); + static OSCalendarTime ToCalendarTime(OSTime time) { return ToCalendarTime(FromWiiTime(time)); } + static OSCalendarTime ToCalendarTime(std::chrono::system_clock::time_point time); }; } diff --git a/Runtime/CBasicsPC.cpp b/Runtime/CBasicsPC.cpp index ccacdabfe..6e099827e 100644 --- a/Runtime/CBasicsPC.cpp +++ b/Runtime/CBasicsPC.cpp @@ -24,45 +24,88 @@ const char* CBasics::Stringize(const char* fmt, ...) const u64 CBasics::SECONDS_TO_2000 = 946684800LL; const u64 CBasics::TICKS_PER_SECOND = 60750000LL; +#ifndef _WIN32 +static struct tm* localtime_r(const time_t& time, struct tm& timeSt, long& gmtOff) +{ + auto ret = localtime_r(&time, &timeSt); + if (!ret) + return nullptr; + gmtOff = ret->tm_gmtoff; + return ret; +} +#else +static struct tm* localtime_r(const time_t& time, struct tm& timeSt, long& gmtOff) +{ + struct tm _gmSt; + auto reta = localtime_s(&timeSt, &time); + auto retb = gmtime_s(&_gmSt, &time); + if (reta || retb) + return nullptr; + gmtOff = mktime(&timeSt) - mktime(&_gmSt); + return &timeSt; +} +#endif + OSTime CBasics::ToWiiTime(std::chrono::system_clock::time_point time) { - time_t sysTime, tzDiff; - struct tm* gmTime; + auto sec = std::chrono::time_point_cast(time); + auto us = std::chrono::duration_cast((time - sec)).count(); + time_t sysTime = std::chrono::system_clock::to_time_t(sec); - sysTime = std::chrono::system_clock::to_time_t(time); - // Account for DST where needed - gmTime = localtime(&sysTime); - if (!gmTime) + struct tm _timeSt; + long gmtOff; + struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff); + if (!timeSt) return 0; - // Lazy way to get local time in sec - gmTime = gmtime(&sysTime); - tzDiff = sysTime - mktime(gmTime); - - return OSTime(TICKS_PER_SECOND * ((sysTime + tzDiff) - SECONDS_TO_2000)); + /* Returning local */ + return OSTime(TICKS_PER_SECOND * ((sysTime + gmtOff) - SECONDS_TO_2000) + + us * TICKS_PER_SECOND / 1000000); } std::chrono::system_clock::time_point CBasics::FromWiiTime(OSTime wiiTime) { - time_t time = SECONDS_TO_2000 + wiiTime / TICKS_PER_SECOND; + auto div = std::lldiv(SECONDS_TO_2000 + wiiTime, TICKS_PER_SECOND); + time_t time = time_t(div.quot); time_t sysTime = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); - // Account for DST where needed - struct tm* gmTime = localtime(&sysTime); - if (!gmTime) + struct tm _timeSt; + long gmtOff; + struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff); + if (!timeSt) return std::chrono::system_clock::from_time_t(0); - // Lazy way to get local time in sec - gmTime = gmtime(&sysTime); - time_t tzDiff = sysTime - mktime(gmTime); - - return std::chrono::system_clock::from_time_t(time - tzDiff); + /* Returning GMT */ + return std::chrono::system_clock::from_time_t(time - gmtOff) + + std::chrono::microseconds(div.rem * 1000000 / TICKS_PER_SECOND); } -OSCalendarTime CBasics::ToCalendarTime(OSTime time) +OSCalendarTime CBasics::ToCalendarTime(std::chrono::system_clock::time_point time) { - OSCalendarTime ret = {}; - /* TODO: Finsh */ + OSCalendarTime ret; + + auto sec = std::chrono::time_point_cast(time); + auto us = std::chrono::duration_cast((time - sec)).count(); + time_t sysTime = std::chrono::system_clock::to_time_t(sec); + struct tm _timeSt; + long gmtOff; + struct tm* timeSt = localtime_r(sysTime, _timeSt, gmtOff); + if (!timeSt) + return {}; + + ret.x0_sec = timeSt->tm_sec; + ret.x4_min = timeSt->tm_min; + ret.x8_hour = timeSt->tm_hour; + ret.xc_mday = timeSt->tm_mday; + ret.x10_mon = timeSt->tm_mon; + ret.x14_year = timeSt->tm_year + 1900; + ret.x18_wday = timeSt->tm_wday; + ret.x1c_yday = timeSt->tm_yday; + + auto div = std::ldiv(us, 1000); + ret.x20_msec = div.quot; + ret.x24_usec = div.rem; + return ret; } diff --git a/Runtime/CMemoryCardSys.cpp b/Runtime/CMemoryCardSys.cpp index 9c6dcbab2..08d54cc36 100644 --- a/Runtime/CMemoryCardSys.cpp +++ b/Runtime/CMemoryCardSys.cpp @@ -189,9 +189,9 @@ void CMemoryCardSys::CCardFileInfo::BuildCardBuffer() CMemoryOutStream w(x104_cardBuffer.data(), x104_cardBuffer.size()); w.writeUint32Big(0); - char name[64]; - strncpy(name, x28_name2.data(), 64); - w.writeBytes(name, 64); + 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()); diff --git a/Runtime/CMemoryCardSys.hpp b/Runtime/CMemoryCardSys.hpp index e46d0a5cc..582e6d370 100644 --- a/Runtime/CMemoryCardSys.hpp +++ b/Runtime/CMemoryCardSys.hpp @@ -172,8 +172,8 @@ public: EStatus x0_status = EStatus::Standby; CARDFileInfo x4_info; - std::string x18_name; - std::string x28_name2; + std::string x18_fileName; + std::string x28_comment; ResId x3c_bannerTex = -1; std::experimental::optional> x40_bannerTok; rstl::reserved_vector x50_iconToks; @@ -183,7 +183,7 @@ public: CVParamTransfer m_texParam = {new TObjOwnerParam(SBIG('OTEX'))}; CCardFileInfo(EMemoryCardPort port, const std::string& name) - : x4_info(port), x18_name(name) {} + : x4_info(port), x18_fileName(name) {} void LockBannerToken(ResId bannerTxtr, CSimplePool& sp); void LockIconToken(ResId iconTxtr, u32 speed, CSimplePool& sp); @@ -195,11 +195,18 @@ public: void BuildCardBuffer(); void WriteBannerData(CMemoryOutStream& out) const; void WriteIconData(CMemoryOutStream& out) const; + void SetComment(const std::string& c) { x28_comment = c; } ECardResult PumpCardTransfer(); ECardResult GetStatus(CARDStat& stat) const; ECardResult CreateFile(); ECardResult Write(); ECardResult Close(); + + CMemoryOutStream BeginMemoryOut(u32 sz) + { + xf4_saveBuffer.resize(sz); + return CMemoryOutStream(xf4_saveBuffer.data(), sz); + } }; static CardProbeResults CardProbe(EMemoryCardPort port); diff --git a/Runtime/MP1/CMemoryCardDriver.cpp b/Runtime/MP1/CMemoryCardDriver.cpp index a6c77708a..40cdfa57e 100644 --- a/Runtime/MP1/CMemoryCardDriver.cpp +++ b/Runtime/MP1/CMemoryCardDriver.cpp @@ -226,9 +226,9 @@ CGameState::GameFileStateInfo* CMemoryCardDriver::GetGameFileStateInfo(int idx) CMemoryCardDriver::SSaveHeader CMemoryCardDriver::LoadSaveHeader(CMemoryInStream& in) { SSaveHeader ret; - ret.x0_ = in.readUint32Big(); + ret.x0_version = in.readUint32Big(); for (int i=0 ; i<3 ; ++i) - ret.x4_[i] = in.readBool(); + ret.x4_savePresent[i] = in.readBool(); return ret; } @@ -655,10 +655,28 @@ void CMemoryCardDriver::GoTo29() Case29(result); } +void CMemoryCardDriver::GoTo31() +{ + x14_error = EError::Zero; + x10_state = EState::ThirtyOne; + if (x18_cardFreeBytes < 8192 || x1c_cardFreeFiles < 2) + { + x10_state = EState::Eighteen; + x14_error = EError::Five; + return; + } + + x198_fileInfo = std::make_unique(x0_cardPort, SaveFileNames[x194_fileIdx]); + InitializeFileInfo(); + ECardResult result = x198_fileInfo->CreateFile(); + if (result != ECardResult::CARD_RESULT_READY) + Case31(result); +} + void CMemoryCardDriver::GoTo32() { x14_error = EError::Zero; - x10_state = EState::Write; + x10_state = EState::ThirtyTwo; ECardResult result = x198_fileInfo->Write(); if (result != ECardResult::CARD_RESULT_READY) Case32(result); @@ -667,7 +685,7 @@ void CMemoryCardDriver::GoTo32() void CMemoryCardDriver::GoTo33() { x14_error = EError::Zero; - x10_state = EState::ThirtyThree; + x10_state = EState::FileBuild; ClearFileInfo(); if (x18_cardFreeBytes < 8192 || !x1c_cardFreeFiles) { @@ -686,7 +704,7 @@ void CMemoryCardDriver::GoTo33() void CMemoryCardDriver::GoTo34() { x14_error = EError::Zero; - x10_state = EState::ThirtyFour; + x10_state = EState::FileWrite; ECardResult result = x198_fileInfo->Write(); if (result != ECardResult::CARD_RESULT_READY) Case34(result); @@ -707,7 +725,7 @@ void CMemoryCardDriver::GoTo36() if (x194_fileIdx == 1) { x14_error = EError::Zero; - x10_state = EState::ThirtySix; + x10_state = EState::FileRename; ECardResult result = CMemoryCardSys::Rename(x0_cardPort, SaveFileNames[x194_fileIdx], SaveFileNames[!bool(x194_fileIdx)]); @@ -733,7 +751,31 @@ void CMemoryCardDriver::GoTo37() void CMemoryCardDriver::InitializeFileInfo() { ExportPersistentOptions(); - /* TODO: Finish */ + + OSCalendarTime time = CBasics::ToCalendarTime(std::chrono::system_clock::now()); + char timeString[32]; + snprintf(timeString, 32, "%02d.%02d.%02d %02d:%02d", + time.x10_mon + 1, time.xc_mday, time.x14_year % 100, + time.x8_hour, time.x4_min); + std::string comment("Metroid Prime "); + comment += timeString; + x198_fileInfo->SetComment(comment); + + x198_fileInfo->LockBannerToken(x4_saveBanner, *g_SimplePool); + x198_fileInfo->LockIconToken(x8_saveIcon0, 2, *g_SimplePool); + + CMemoryOutStream w = x198_fileInfo->BeginMemoryOut(3004); + + SSaveHeader header; + for (int i=0 ; i<3 ; ++i) + header.x4_savePresent[i] = xe4_fileSlots[i].operator bool(); + header.DoPut(w); + + w.writeBytes(x30_systemData, 174); + + for (int i=0 ; i<3 ; ++i) + if (xe4_fileSlots[i]) + xe4_fileSlots[i]->DoPut(w); } void CMemoryCardDriver::WriteBackupBuf() @@ -822,19 +864,19 @@ void CMemoryCardDriver::Update() case EState::ThirtyOne: Case31(resultCode); break; - case EState::Write: + case EState::ThirtyTwo: Case32(resultCode); break; - case EState::ThirtyThree: + case EState::FileBuild: Case33(resultCode); break; - case EState::ThirtyFour: + case EState::FileWrite: Case34(resultCode); break; case EState::ThirtyFive: Case35(resultCode); break; - case EState::ThirtySix: + case EState::FileRename: Case36(resultCode); break; case EState::CardFormat: diff --git a/Runtime/MP1/CMemoryCardDriver.hpp b/Runtime/MP1/CMemoryCardDriver.hpp index 144e7dec7..a4c5219e3 100644 --- a/Runtime/MP1/CMemoryCardDriver.hpp +++ b/Runtime/MP1/CMemoryCardDriver.hpp @@ -47,11 +47,11 @@ public: TwentyNine = 29, Thirty = 30, ThirtyOne = 31, - Write = 32, - ThirtyThree = 33, - ThirtyFour = 34, + ThirtyTwo = 32, + FileBuild = 33, + FileWrite = 34, ThirtyFive = 35, - ThirtySix = 36, + FileRename = 36, CardFormat = 37 }; @@ -98,8 +98,14 @@ private: struct SSaveHeader { - u32 x0_; - u32 x4_[3]; + u32 x0_version = 0; + bool x4_savePresent[3]; + void DoPut(CMemoryOutStream& out) const + { + out.writeUint32Big(x0_version); + for (int i=0 ; i<3 ; ++i) + out.writeBool(x4_savePresent[i]); + } }; struct SGameFileSlot @@ -109,6 +115,10 @@ private: SGameFileSlot(); SGameFileSlot(CMemoryInStream& in); void InitializeFromGameState(); + void DoPut(CMemoryOutStream& w) const + { + w.writeBytes(x0_saveBuffer, 940); + } }; CMemoryCardSys::EMemoryCardPort x0_cardPort; @@ -164,6 +174,7 @@ public: void GoTo17(); void GoTo28(); void GoTo29(); + void GoTo31(); void GoTo32(); void GoTo33(); void GoTo34(); diff --git a/Runtime/MP1/CSaveUI.cpp b/Runtime/MP1/CSaveUI.cpp index d7eb28f9f..2c9de2bc3 100644 --- a/Runtime/MP1/CSaveUI.cpp +++ b/Runtime/MP1/CSaveUI.cpp @@ -12,9 +12,84 @@ namespace MP1 { using EState = CMemoryCardDriver::EState; +using EError = CMemoryCardDriver::EError; + +void CSaveUI::ResetCardDriver() +{ + x92_ = false; + x6c_cardDriver.reset(); + bool flag = (x0_instIdx == 0 && !x90_needsDriverReset); + x6c_cardDriver = ConstructCardDriver(flag); + x6c_cardDriver->FinishedLoading(); + x10_uiType = UIType::Zero; + FinishedLoading(); +} CIOWin::EMessageReturn CSaveUI::Update(float dt) { + if (PumpLoad()) + return CIOWin::EMessageReturn::Normal; + + x50_loadedFrame->Update(dt); + x6c_cardDriver->Update(); + + if (x6c_cardDriver->x10_state == EState::RuntimeBackup) + { + if (x90_needsDriverReset) + { + ResetCardDriver(); + x90_needsDriverReset = false; + } + else + x80_iowRet = CIOWin::EMessageReturn::Exit; + } + else if (x6c_cardDriver->x10_state == EState::SelectCardFile && x10_uiType != UIType::Fourteen) + { + if (x6c_cardDriver->x28_cardSerial && x8_serial) + { + if (x93_secondaryInst) + { + x10_uiType = UIType::Fourteen; + x91_ = true; + } + else + { + x8_serial = x6c_cardDriver->x28_cardSerial; + x6c_cardDriver->GoTo17(); + } + } + } + else if (x6c_cardDriver->x10_state == EState::Ready) + { + if (x90_needsDriverReset) + x6c_cardDriver->GoTo33(); + } + + if (x80_iowRet != CIOWin::EMessageReturn::Normal) + return x80_iowRet; + + UIType oldTp = x10_uiType; + x10_uiType = SelectUIType(); + if (oldTp == x10_uiType || x91_) + FinishedLoading(); + + if (x6c_cardDriver->x10_state == EState::NoCard) + { + auto res = CMemoryCardSys::CardProbe(CMemoryCardSys::EMemoryCardPort::SlotA); + if (res.x0_error == CMemoryCardSys::ECardResult::CARD_RESULT_READY || + res.x0_error == CMemoryCardSys::ECardResult::CARD_RESULT_WRONGDEVICE) + ResetCardDriver(); + } + else if (x6c_cardDriver->x10_state == EState::CardFormatted) + { + ResetCardDriver(); + } + else if (x6c_cardDriver->x10_state == EState::Seventeen && + x6c_cardDriver->x14_error == EError::Eight) + { + x6c_cardDriver->GoTo31(); + } + return CIOWin::EMessageReturn::Normal; } @@ -152,8 +227,8 @@ void* CSaveUI::GetGameData(int idx) const return nullptr; } -CSaveUI::CSaveUI(u32 instIdx, u32 a, u32 b) -: x0_instIdx(instIdx), x8_a(a), xc_b(b) +CSaveUI::CSaveUI(u32 instIdx, u64 serial) +: x0_instIdx(instIdx), x8_serial(serial) { x14_txtrSaveBanner = g_SimplePool->GetObj("TXTR_SaveBanner"); x20_txtrSaveIcon0 = g_SimplePool->GetObj("TXTR_SaveIcon0"); diff --git a/Runtime/MP1/CSaveUI.hpp b/Runtime/MP1/CSaveUI.hpp index 12ae509ff..317850420 100644 --- a/Runtime/MP1/CSaveUI.hpp +++ b/Runtime/MP1/CSaveUI.hpp @@ -59,8 +59,7 @@ public: private: u32 x0_instIdx; - u32 x8_a; - u32 xc_b; + u64 x8_serial; UIType x10_uiType = UIType::Zero; TLockedToken x14_txtrSaveBanner; TLockedToken x20_txtrSaveIcon0; @@ -76,15 +75,17 @@ private: CGuiTextPane* x68_textpane_choice3; std::unique_ptr x6c_cardDriver; std::vector> x70_saveWorlds; - u32 x80_ = 0; + CIOWin::EMessageReturn x80_iowRet = CIOWin::EMessageReturn::Normal; u32 x84_navConfirmSfx = 1460; u32 x88_navMoveSfx = 1461; u32 x8c_navBackSfx = 1459; - bool x90_ = false; + bool x90_needsDriverReset = false; bool x91_ = false; bool x92_ = false; bool x93_secondaryInst; + void ResetCardDriver(); + public: static std::unique_ptr ConstructCardDriver(bool flag); CIOWin::EMessageReturn Update(float dt); @@ -101,7 +102,7 @@ public: void EraseGame(int idx); void* GetGameData(int idx) const; UIType GetUIType() const { return x10_uiType; } - CSaveUI(u32 inst, u32 a, u32 b); + CSaveUI(u32 inst, u64 serial); }; }