Files
ss/src/toBeSorted/save_manager.cpp
T
2025-09-27 12:22:01 +02:00

1758 lines
54 KiB
C++

#include "toBeSorted/save_manager.h"
#include "common.h"
#include "d/d_cs_base.h"
#include "d/d_d2d.h"
#include "d/d_dvd_unk.h"
#include "d/d_heap.h"
#include "d/d_message.h"
#include "d/d_reset.h"
#include "d/d_s_boot.h"
#include "d/d_sc_game.h"
#include "d/d_sys.h"
#include "d/lyt/d_lyt_save_msg_window.h"
#include "d/lyt/d_lyt_system_window.h"
#include "d/snd/d_snd_small_effect_mgr.h"
#include "egg/core/eggHeap.h"
#include "m/m_pad.h"
#include "toBeSorted/arc_managers/layout_arc_manager.h"
#include "toBeSorted/file_manager.h"
#include "toBeSorted/nand_request_thread.h"
#include "toBeSorted/nand_result_tracker.h"
#include "rvl/NAND.h"
#include "rvl/TPL.h"
#include <cstring>
SaveMgr *SaveMgr::sInstance;
static const char sSaveFileName[] = "wiiking2.sav";
static const char sSkipDatFileName[] = "skip.dat";
static const char sBannerBinFileName[] = "banner.bin";
static const char sTmpBannerBinFileName[] = "/tmp/banner.bin";
#define FILE_PERMS (NAND_PERM_RUSR | NAND_PERM_WUSR | NAND_PERM_RGRP | NAND_PERM_WGRP)
const SaveMgr::StateFunc SaveMgr::sStateFuncs[STATE_MAX] = {
&SaveMgr::executeCheckForSave, &SaveMgr::executeCheckForFreeSpace, &SaveMgr::executeCreateFiles,
&SaveMgr::executeSaveBanner, &SaveMgr::executeLoadSave, &SaveMgr::executeWriteSave,
&SaveMgr::executeWriteSkipDat, &SaveMgr::executeCopySave, &SaveMgr::executeClearSelectedFile,
&SaveMgr::executeSave, &SaveMgr::executeDeleteAllData, &SaveMgr::executeError,
&SaveMgr::executeNandError,
};
SaveMgr::SaveMgr()
: mCheckIsFileRequest(nullptr),
mCheckRequest(nullptr),
mCreateRequest(nullptr),
mWriteRequest(nullptr),
mSaveBannerRequest(nullptr),
mLoadSaveRequest(nullptr),
mDeleteRequest(nullptr) {
sInstance = this;
mpWindow = nullptr;
}
// Not used but this spawns the weak dtors
SaveMgr::~SaveMgr() {
delete mpWindow;
}
void SaveMgr::create(EGG::Heap *heap) {
new (heap, 4) SaveMgr();
sInstance->init();
}
void SaveMgr::init() {
mCurrentState = STATE_MAX;
field_0x824 = 2;
mCheckForFreeSpaceResult = 0;
mCopyToFile = 0;
mCopyFromFile = 0;
mIsIdle = false;
field_0x837 = 0;
field_0x838 = 0;
field_0x839 = 0;
field_0x83A = 0;
field_0x83B = 0;
field_0x83C = 0;
field_0x83D = false;
field_0x83E = 0;
mDelayTimer = 0;
FileManager::GetInstance()->setField0xA840(1);
if (dCsBase_c::GetInstance() != nullptr) {
dCsBase_c::GetInstance()->setDrawDirectly(false);
}
if (mpWindow != nullptr) {
mpWindow->reset();
}
field_0x83F = 0;
}
void SaveMgr::execute() {
if (mCurrentState != STATE_MAX) {
StateFunc f = sStateFuncs[mCurrentState];
if (f != nullptr) {
(this->*f)();
}
}
}
void SaveMgr::draw() {
d2d::defaultSet();
if (mpWindow != nullptr) {
mpWindow->drawNow();
}
}
void SaveMgr::createSaveMsgWindow() {
if (mpWindow == nullptr) {
mpWindow = new dLytSaveMsgWindow_c();
mpWindow->build(false);
}
}
bool SaveMgr::checkForSave() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeCheckForSave();
return true;
}
bool SaveMgr::checkForFreeSpace() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeCheckForFreeSpace();
return true;
}
bool SaveMgr::createFiles() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeCreateFiles();
return true;
}
bool SaveMgr::loadSave() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeLoadSave();
return true;
}
bool SaveMgr::save(bool entranceT1LoadFlag, bool full) {
if (mCurrentState != STATE_MAX) {
return false;
}
if (FileManager::GetInstance()->get_0xA84C()) {
return false;
}
mIsIdle = false;
if (full == 1) {
FileManager::GetInstance()->saveT1SaveInfo(entranceT1LoadFlag);
} else {
FileManager::GetInstance()->setEntranceLoadFlagT1(entranceT1LoadFlag);
}
FileManager::GetInstance()->setFileTimes();
initializeSave();
field_0x83E = 0;
return true;
}
bool SaveMgr::saveAfterCredits() {
if (mCurrentState != STATE_MAX) {
return false;
}
if (FileManager::GetInstance()->get_0xA84C()) {
return false;
}
mIsIdle = false;
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(true);
FileManager::GetInstance()->setFileTimes();
FileManager::GetInstance()->saveAfterCredits();
initializeSave();
field_0x83E = 0;
return true;
}
bool SaveMgr::writeSkipDat() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeWriteSkipDat();
return true;
}
bool SaveMgr::copySave(u8 to, u8 from) {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
mCopyToFile = to;
mCopyFromFile = from;
initializeCopySave();
field_0x83F = 0;
return true;
}
bool SaveMgr::clearSelectedFile() {
if (mCurrentState != STATE_MAX) {
return false;
}
mIsIdle = false;
initializeClearSelectedFile();
field_0x83F = 0;
return true;
}
void SaveMgr::initializeCheckForSave() {
beginState(STATE_CHECK_FOR_SAVE);
mCheckIsFileRequest.checkIsFile(sSaveFileName);
field_0x839 = 0;
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(false);
}
void SaveMgr::executeCheckForSave() {
FileManager *fileManager = FileManager::GetInstance();
NandResultTracker *t = NandResultTracker::GetInstance();
switch (mStep) {
case 0:
if (mCheckIsFileRequest.isCompleted()) {
if (t->isFailure(mCheckIsFileRequest.getResult()) && mCheckIsFileRequest.finish()) {
return;
}
if (mCheckIsFileRequest.getCheckResult()) {
field_0x839 |= 2;
}
if (mCheckIsFileRequest.finish()) {
mCheckIsFileRequest.checkIsFile(sSkipDatFileName);
mStep = 1;
}
}
break;
case 1:
if (mCheckIsFileRequest.isCompleted()) {
if (t->isFailure(mCheckIsFileRequest.getResult()) && mCheckIsFileRequest.finish()) {
return;
}
if (mCheckIsFileRequest.getCheckResult()) {
field_0x839 |= 4;
}
if (mCheckIsFileRequest.finish()) {
mCheckIsFileRequest.checkIsFile(sBannerBinFileName);
mStep = 2;
}
}
break;
case 2:
if (mCheckIsFileRequest.isCompleted()) {
if (t->isFailure(mCheckIsFileRequest.getResult()) && mCheckIsFileRequest.finish()) {
return;
}
if (mCheckIsFileRequest.getCheckResult()) {
field_0x839 |= 1;
}
if (mCheckIsFileRequest.finish()) {
mStep = 3;
}
}
break;
case 3:
if (field_0x839 == (1 | 2) || field_0x839 == (1 | 2 | 4)) {
fileManager->setField0xA841(1);
if (field_0x839 == (1 | 2)) {
fileManager->setField0xA842(0);
} else {
fileManager->setField0xA842(1);
}
} else {
fileManager->setField0xA841(0);
fileManager->setField0xA842(0);
if (field_0x839) {
t->isFailure(NAND_RESULT_AUTHENTICATION);
initializeError();
return;
}
}
endState();
break;
}
}
void SaveMgr::initializeCheckForFreeSpace() {
beginState(STATE_CHECK_FOR_FREE_SPACE);
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(false);
mCheckRequest.check(7, 3);
mCheckForFreeSpaceResult = 0;
}
void SaveMgr::executeCheckForFreeSpace() {
if (mCheckRequest.isCompleted()) {
if (NandResultTracker::GetInstance()->isFailure(mCheckRequest.getResult()) && mCheckRequest.finish()) {
return;
}
u32 result = mCheckRequest.getCheckResult();
if ((result & (NAND_CHECK_TOO_MANY_APP_BLOCKS | NAND_CHECK_TOO_MANY_USER_BLOCKS)) != 0) {
if (mCheckRequest.finish()) {
mCheckForFreeSpaceResult = 1;
initializeNandError();
}
} else if ((result & (NAND_CHECK_TOO_MANY_APP_FILES | NAND_CHECK_TOO_MANY_USER_FILES)) != 0) {
if (mCheckRequest.finish()) {
mCheckForFreeSpaceResult = 2;
initializeNandError();
}
} else {
if (mCheckRequest.finish()) {
if (field_0x838 == 1 || field_0x83A == 1) {
initializeCreateFiles();
} else {
endState();
}
}
}
}
}
void SaveMgr::initializeCreateFiles() {
beginState(STATE_CREATE_FILES);
field_0x838 = 1;
FileManager::GetInstance()->setField0xA84D(1);
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(false);
}
void SaveMgr::executeCreateFiles() {
FileManager *fileMgr = FileManager::GetInstance();
switch (mStep) {
case 0: {
s32 numLines = 0;
if (field_0x83A != 0) {
if (field_0x837 == 1) {
// "Restoring save data. Please do not touch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_INIT_06", 0, 1, field_0x83E)) {
numLines = 2;
}
} else {
// "Creating save data again. Please do not touch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_INIT_08", 0, 1, field_0x83E)) {
numLines = 2;
}
}
} else {
// "Creating save data. Please do not touch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_INIT_02", 0, 1, field_0x83E)) {
numLines = 2;
}
}
if (numLines != 0) {
mDelayTimer = 0;
mStep++;
mpWindow->setNumLines(numLines);
}
break;
}
case 1: {
if (mDelayTimer < 30) {
mDelayTimer++;
} else {
mCreateRequest.create(sSaveFileName, FILE_PERMS, 0);
mDelayTimer = 0;
mStep++;
}
break;
}
case 2: {
if (!mCreateRequest.isCompleted()) {
return;
}
NANDResult status = mCreateRequest.getResult();
if (!mCreateRequest.finish()) {
return;
}
if (NandResultTracker::GetInstance()->isFailure(status)) {
return;
}
fileMgr->setField0xA841(1);
mStep++;
break;
}
case 3: {
mCreateRequest.create(sSkipDatFileName, FILE_PERMS, 0);
mStep++;
break;
}
case 4: {
if (!mCreateRequest.isCompleted()) {
return;
}
NANDResult status = mCreateRequest.getResult();
if (!mCreateRequest.finish()) {
return;
}
if (NandResultTracker::GetInstance()->isFailure(status)) {
return;
}
if (dScGame_c::GetInstance() != nullptr && dScGame_c::GetInstance()->mProfileName == fProfile::GAME) {
fileMgr->copySelectedFileSkipData();
}
fileMgr->setField0xA842(1);
initializeWriteSave();
mStep = 1; // Skips something in other state!
break;
}
}
mpWindow->execute();
}
void SaveMgr::initializeSaveBanner() {
beginState(STATE_SAVE_BANNER);
// "Zelda: Skyward Sword"
const wchar_t *title = dMessage_c::getTextMessageByLabel("SYS_ZELDA_01", true, nullptr, 0);
// Need to stash title as next call to getTextMessageByLabel will reuse internal buffer
// Missed optimization: Could've passed mTextBuf directly
for (s32 i = 0; i < 0x3FF; i++) {
mTextBuf[i] = title[i];
}
// "A story of Link and Zelda."
const wchar_t *subtitle = dMessage_c::getTextMessageByLabel("SYS_ZELDA_02", true, nullptr, 0);
void *data = LayoutArcManager::GetInstance()->getData("saveBannerU", "tmp/saveBanner.tpl");
mSaveBannerRequest.saveBanner(sTmpBannerBinFileName, (TPLPalette *)data, mTextBuf, subtitle, FILE_PERMS, 0);
FileManager::GetInstance()->setField0xA84D(1);
}
void SaveMgr::executeSaveBanner() {
dDvdUnk::FontUnk *fontUnk = dDvdUnk::FontUnk::GetInstance();
switch (mStep) {
case 0: {
mDelayTimer++;
if (!mSaveBannerRequest.isCompleted()) {
return;
}
FileManager::GetInstance()->setField0xA84D(0);
NANDResult result = mSaveBannerRequest.getResult();
bool bFailed = mSaveBannerRequest.hasFailed();
if (!mSaveBannerRequest.finish()) {
return;
}
if (NandResultTracker::GetInstance()->isFailure(result)) {
mStep = 20;
return;
}
if (bFailed) {
// retry
mStep = 10;
return;
}
mStep = 1;
break;
}
case 1: {
if (mDelayTimer < getFrameRate()) {
mDelayTimer++;
} else {
if (mpWindow->checkIsWait()) {
mDelayTimer = 0;
mStep++;
}
}
break;
}
case 2: {
if (mpWindow->getWillFinishOut() == 1) {
mStep++;
}
break;
}
case 3: {
bool b = false;
if (field_0x83A == 1) {
if (field_0x837 != 0) {
// "Save data is restored."
if (mpWindow->setProperties("SYS_INIT_07", 0, 1, field_0x83E) == true) {
b = true;
}
} else {
// "New save data has been created."
if (mpWindow->setProperties("SYS_INIT_09", 0, 1, field_0x83E) == true) {
b = true;
}
}
} else if (field_0x838 == 1) {
// "Save data for The Legend of Zelda: Skyward Sword has been created."
if (mpWindow->setProperties("SYS_INIT_03", 0, 1, field_0x83E) == true) {
b = true;
mpWindow->setNumLines(2);
}
} else if (field_0x83B == 1) {
// "Copying complete."
if (mpWindow->setProperties("SYS_COPY_02", 0, 1, field_0x83E) == true) {
b = true;
}
} else if (field_0x83C == 1) {
// "Deleting complete."
if (mpWindow->setProperties("SYS_DELETE_02", 0, 1, field_0x83E) == true) {
b = true;
}
} else {
// "Saved."
if (mpWindow->setProperties("SYS_SAVE_02", 0, 1, field_0x83E) == true) {
b = true;
}
}
if (b == 1) {
if (!dDvdUnk::FontUnk::GetInstance()->isDiskError()) {
dSndSmallEffectMgr_c::GetInstance()->playSound(SE_S_SAVE_FINISH);
}
mDelayTimer = 0;
mStep++;
}
break;
}
case 4: {
if (mDelayTimer < getFrameRate() + 30) {
mDelayTimer++;
} else {
if (mpWindow->checkIsWait() == true) {
mDelayTimer = 0;
mStep++;
}
}
break;
}
case 5: {
if (mpWindow->getWillFinishOut() == 1) {
if (fontUnk->isNandError() == 1) {
fontUnk->clearNandTrackerError();
}
field_0x838 = 0;
field_0x83A = 0;
field_0x837 = 1;
endState();
}
break;
}
case 10: {
// retry
initializeSaveBanner();
break;
}
case 20: {
if (mpWindow->checkIsWait()) {
mStep++;
}
break;
}
case 21: {
initializeError();
break;
}
}
mpWindow->execute();
}
void SaveMgr::initializeLoadSave() {
beginState(STATE_LOAD_SAVE);
mLoadSaveRequest.loadSave(sSaveFileName, sizeof(SavedSaveFiles), 0, nullptr);
}
void SaveMgr::executeLoadSave() {
FileManager *fileManager = FileManager::GetInstance();
switch (mStep) {
case 0: {
if (!mLoadSaveRequest.isCompleted()) {
return;
}
NANDResult result = mLoadSaveRequest.getResult();
NandResultTracker *tracker = NandResultTracker::GetInstance();
if (tracker->isFailure(result)) {
mLoadSaveRequest.remove();
if (mLoadSaveRequest.finish()) {
initializeError();
}
return;
}
if (mLoadSaveRequest.getBuf() != nullptr) {
fileManager->saveSaveData(&mLoadSaveRequest, false);
}
mLoadSaveRequest.remove();
if (!mLoadSaveRequest.finish()) {
return;
}
fileManager->checkFileStatus();
if (fileManager->isFileInvalid() == true) {
tracker->isFailure(NAND_RESULT_AUTHENTICATION);
initializeError();
} else if (fileManager->getField_0xA842() != 1) {
mStep = 10;
} else {
mStep = 1;
}
break;
}
case 1: {
mLoadSaveRequest.loadSave(sSkipDatFileName, 0x80, 0, nullptr);
mStep = 2;
break;
}
case 2: {
if (!mLoadSaveRequest.isCompleted()) {
return;
}
NANDResult result = mLoadSaveRequest.getResult();
NandResultTracker *tracker = NandResultTracker::GetInstance();
if (tracker->isFailure(result)) {
mLoadSaveRequest.remove();
if (mLoadSaveRequest.finish()) {
mStep = 30;
}
return;
}
if (mLoadSaveRequest.getBuf() != nullptr) {
fileManager->saveSaveData(&mLoadSaveRequest, true);
}
mLoadSaveRequest.remove();
if (mLoadSaveRequest.finish()) {
if (fileManager->checkSkipDataCRCs() == 1) {
mStep = 3;
} else {
fileManager->updateEmptyFiles();
field_0x837 = 1;
endState();
}
}
break;
}
case 3: {
for (int i = 0; i < 3; i++) {
fileManager->copyFileSkipData(i);
}
mStep++;
break;
}
case 4: {
fileManager->updateEmptyFiles();
field_0x837 = 1;
initializeWriteSkipDat();
break;
}
case 10: {
mCheckRequest.check(1, 1);
mStep++;
break;
}
case 11: {
if (!mCheckRequest.isCompleted()) {
return;
}
NANDResult result = mCheckRequest.getResult();
if (!NandResultTracker::GetInstance()->isFailure(result) || !mCheckRequest.finish()) {
u32 check = mCheckRequest.getCheckResult();
if (mCheckRequest.finish()) {
if (check) {
fileManager->updateEmptyFiles();
field_0x837 = 1;
endState();
} else {
mStep += 1;
}
}
}
break;
}
case 12: {
fileManager->setField0xA84D(1);
mCreateRequest.create(sSkipDatFileName, FILE_PERMS, 0);
mStep += 1;
break;
}
case 13: {
if (mCreateRequest.isCompleted()) {
NANDResult result = mCreateRequest.getResult();
if (mCreateRequest.finish()) {
if (!NandResultTracker::GetInstance()->isFailure(result)) {
for (int i = 0; i < 3; i++) {
fileManager->copyFileSkipData(i);
}
fileManager->setField0xA842(1);
mStep = 4;
}
}
}
break;
}
case 30: {
fileManager->setField0xA84D(1);
mDeleteRequest.doDelete(sSkipDatFileName);
mStep += 1;
break;
}
case 31: {
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (mDeleteRequest.finish()) {
initializeError();
}
} else {
if (mDeleteRequest.finish()) {
mStep = 10;
}
}
break;
}
}
}
void SaveMgr::initializeWriteSave() {
beginState(STATE_WRITE);
mDelayTimer = 0;
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(false);
FileManager::GetInstance()->setField0xA84D(1);
field_0x83D = true;
}
void SaveMgr::executeWriteSave() {
FileManager *fileManager = FileManager::GetInstance();
switch (mStep) {
case 0: {
bool anyAction = false;
if (field_0x83B == 1) {
// "Copying... Please do not touch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_COPY_01", 0, 1, field_0x83E)) {
anyAction = true;
}
} else if (field_0x83C == 1) {
// "Deleting... Please do not touch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_DELETE_01", 0, 1, field_0x83E)) {
anyAction = true;
}
} else {
// "Saving in progress. Please do not\\ntouch the POWER Button or RESET."
if (mpWindow->setProperties("SYS_SAVE_01", 0, 1, field_0x83E)) {
anyAction = true;
}
}
if (anyAction == true) {
mStep = 1;
mDelayTimer = 0;
mpWindow->setNumLines(2);
}
break;
}
case 1: {
if (mDelayTimer >= 30) {
fileManager->loadSaveData(&mWriteRequest, sSaveFileName, false);
mDelayTimer = 0;
mStep++;
} else {
mDelayTimer++;
}
break;
}
case 2: {
mDelayTimer++;
if (!mWriteRequest.isCompleted()) {
return;
}
NANDResult result = mWriteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (mWriteRequest.finish()) {
mStep = 10;
}
return;
}
if (mWriteRequest.failedWrite() == 1) {
if (mWriteRequest.finish()) {
mStep = 5;
}
return;
}
if (!mWriteRequest.finish()) {
return;
}
if (fileManager->getField_0xA842() == 1) {
mStep = 3;
} else {
mStep = 40;
}
break;
}
case 3: {
fileManager->loadSaveData(&mWriteRequest, sSkipDatFileName, true);
mStep = 4;
break;
}
case 4: {
mDelayTimer++;
if (!mWriteRequest.isCompleted()) {
return;
}
NANDResult result = mWriteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (mWriteRequest.finish()) {
mStep = 30;
}
return;
}
if (mWriteRequest.failedWrite() == 1) {
if (mWriteRequest.finish()) {
mStep = 6;
}
return;
}
if (!mWriteRequest.finish()) {
return;
}
mStep = 40;
break;
}
case 5: {
fileManager->loadSaveData(&mWriteRequest, sSaveFileName, false);
mStep = 2;
mDelayTimer = 0;
break;
}
case 6: {
fileManager->loadSaveData(&mWriteRequest, sSkipDatFileName, true);
mStep = 4;
mDelayTimer = 0;
break;
}
case 10: {
if (mpWindow->checkIsWait()) {
initializeError();
}
break;
}
case 20: {
if (mpWindow->checkIsWait()) {
mStep++;
}
break;
}
case 21: {
if (mpWindow->getWillFinishOut() == true) {
fileManager->checkFileStatus();
if (fileManager->isFileInvalid() == 1) {
NandResultTracker::GetInstance()->isFailure(NAND_RESULT_AUTHENTICATION);
initializeError();
return;
}
initializeSaveBanner();
}
break;
}
case 30: {
mDelayTimer++;
mDeleteRequest.doDelete(sSkipDatFileName);
mStep++;
break;
}
case 31: {
mDelayTimer++;
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (!mDeleteRequest.finish()) {
return;
}
mpWindow->checkIsWait();
initializeError();
return;
}
if (!mDeleteRequest.finish()) {
return;
}
fileManager->setField0xA842(0);
mStep++;
break;
}
case 32: {
mDelayTimer++;
mCheckRequest.check(1, 1);
mStep++;
break;
}
case 33: {
mDelayTimer++;
if (!mCheckRequest.isCompleted()) {
return;
}
NANDResult result = mCheckRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result) && mCheckRequest.finish()) {
mpWindow->checkIsWait();
initializeError();
} else {
u32 result = mCheckRequest.getCheckResult();
if (!mCheckRequest.finish()) {
return;
}
if (result) {
mStep = 40;
} else {
mStep++;
}
}
break;
}
case 34: {
mDelayTimer++;
mCreateRequest.create(sSkipDatFileName, FILE_PERMS, 0);
mStep++;
break;
}
case 35: {
mDelayTimer++;
if (!mCreateRequest.isCompleted()) {
return;
}
NANDResult result = mCreateRequest.getResult();
if (!mCreateRequest.finish()) {
return;
}
if (NandResultTracker::GetInstance()->isFailure(result)) {
mpWindow->checkIsWait();
initializeError();
return;
}
for (int i = 0; i < 3; i++) {
fileManager->copyFileSkipData(i);
}
mStep = 3;
fileManager->setField0xA842(1);
break;
}
case 40: {
fileManager->checkFileStatus();
if (fileManager->isFileInvalid() == true) {
NandResultTracker::GetInstance()->isFailure(NAND_RESULT_UNKNOWN);
mpWindow->checkIsWait();
initializeError();
return;
}
u32 oldTimer = mDelayTimer;
initializeSaveBanner();
mDelayTimer = oldTimer;
break;
}
}
mpWindow->execute();
}
void SaveMgr::initializeWriteSkipDat() {
beginState(STATE_WRITE_SKIP_DAT);
if (FileManager::GetInstance()->get_0xA84C() || FileManager::GetInstance()->getField_0xA842() != 1) {
mStep = 10;
} else {
FileManager::GetInstance()->setField0xA84D(1);
FileManager::GetInstance()->copySelectedFileSkipData();
FileManager::GetInstance()->loadSaveData(&mWriteRequest, sSkipDatFileName, true);
}
}
void SaveMgr::executeWriteSkipDat() {
FileManager *fileManager = FileManager::GetInstance();
switch (mStep) {
case 0: {
if (mWriteRequest.isCompleted()) {
FileManager::GetInstance()->setField0xA84D(0);
NANDResult result = mWriteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (mWriteRequest.finish()) {
mStep = 30;
}
} else {
if (mWriteRequest.failedWrite() == true) {
if (mWriteRequest.finish()) {
mStep = 1;
}
} else {
if (mWriteRequest.finish()) {
endState();
}
}
}
}
break;
}
case 1: {
fileManager->loadSaveData(&mWriteRequest, sSkipDatFileName, true);
FileManager::GetInstance()->setField0xA84D(1);
mStep = 0;
mDelayTimer = 0;
break;
}
case 10: {
fileManager->setField0xA84D(0);
endState();
break;
}
case 30: {
fileManager->setField0xA84D(1);
mDeleteRequest.doDelete(sSkipDatFileName);
mStep++;
break;
}
case 31: {
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result)) {
if (mDeleteRequest.finish()) {
initializeError();
}
} else {
if (mDeleteRequest.finish()) {
fileManager->setField0xA842(0);
mStep++;
}
}
break;
}
case 32: {
mCheckRequest.check(1, 1);
mStep++;
break;
}
case 33: {
if (mCheckRequest.isCompleted()) {
NANDResult result = mCheckRequest.getResult();
if (NandResultTracker::GetInstance()->isFailure(result) && mCheckRequest.finish()) {
initializeError();
} else {
u32 result = mCheckRequest.getCheckResult();
if (mCheckRequest.finish()) {
if (result) {
mStep = 10;
} else {
mStep++;
}
}
}
}
break;
}
case 34: {
mDelayTimer++;
mCreateRequest.create(sSkipDatFileName, FILE_PERMS, 0);
mStep++;
break;
}
case 35: {
if (!mCreateRequest.isCompleted()) {
return;
}
NANDResult result = mCreateRequest.getResult();
if (!mCreateRequest.finish()) {
return;
}
if (NandResultTracker::GetInstance()->isFailure(result)) {
initializeError();
return;
}
for (int i = 0; i < 3; i++) {
fileManager->copyFileSkipData(i);
}
fileManager->setField0xA842(1);
FileManager::GetInstance()->loadSaveData(&mWriteRequest, sSkipDatFileName, true);
mStep = 0;
break;
}
}
}
void SaveMgr::initializeCopySave() {
beginState(STATE_COPY_SAVE);
FileManager::GetInstance()->setField0xA840(0);
field_0x83B = 1;
}
void SaveMgr::executeCopySave() {
FileManager *fileManager = FileManager::GetInstance();
fileManager->copyFile(mCopyFromFile, mCopyToFile);
fileManager->setSelectedFileNum(mCopyToFile);
initializeWriteSave();
FileManager::GetInstance()->setField0xA840(1);
}
void SaveMgr::initializeClearSelectedFile() {
beginState(STATE_CLEAR_SELECTED_FILE);
FileManager::GetInstance()->setField0xA840(0);
field_0x83C = 1;
}
void SaveMgr::executeClearSelectedFile() {
FileManager::GetInstance()->clearFileA();
FileManager::GetInstance()->saveFileAToSelectedFile();
initializeWriteSave();
FileManager::GetInstance()->setField0xA840(1);
}
void SaveMgr::initializeSave() {
beginState(STATE_SAVE);
FileManager::GetInstance()->setField0xA840(0);
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(false);
FileManager::GetInstance()->setField_0xA843(1);
}
void SaveMgr::executeSave() {
FileManager *fileManager = FileManager::GetInstance();
switch (mStep) {
case 0: {
if (fileManager->get_0xA84C() != 1 && fileManager->getField_0xA843()) {
if (fileManager->getField_0xA841() == 1) {
FileManager::GetInstance()->unsetFileANewFile();
FileManager::GetInstance()->copyFileAToSelectedFile();
initializeWriteSave();
FileManager::GetInstance()->setField0xA840(1);
} else {
FileManager::GetInstance()->copyFileAToSelectedFile();
FileManager::GetInstance()->setField0xA840(1);
initializeCheckForFreeSpace();
field_0x838 = 1;
}
}
break;
}
}
}
void SaveMgr::initializeDeleteAllData() {
beginState(STATE_DELETE_ALL_DATA);
field_0x83A = 1;
}
void SaveMgr::executeDeleteAllData() {
NandResultTracker *tracker = NandResultTracker::GetInstance();
switch (mStep) {
case 0: {
FileManager::GetInstance()->setField0xA84D(1);
mDeleteRequest.doDelete(sBannerBinFileName);
if (field_0x837 != 1 || FileManager::GetInstance()->isFileInvalid() == 1) {
FileManager::GetInstance()->initBlankSaveFiles();
}
mStep = 1;
break;
}
case 1: {
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (tracker->isFailure(result)) {
if (mDeleteRequest.finish()) {
initializeError();
}
} else {
if (mDeleteRequest.finish()) {
mStep = 2;
}
}
break;
}
case 2: {
mDeleteRequest.doDelete(sSaveFileName);
mStep = 3;
break;
}
case 3: {
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (tracker->isFailure(result)) {
if (mDeleteRequest.finish()) {
initializeError();
}
} else {
if (mDeleteRequest.finish()) {
mStep = 4;
}
}
break;
}
case 4: {
mDeleteRequest.doDelete(sSkipDatFileName);
mStep = 5;
break;
}
case 5: {
if (!mDeleteRequest.isCompleted()) {
return;
}
NANDResult result = mDeleteRequest.getResult();
if (tracker->isFailure(result)) {
if (mDeleteRequest.finish()) {
initializeError();
}
} else {
if (mDeleteRequest.finish()) {
initializeCheckForFreeSpace();
}
}
break;
}
}
}
void SaveMgr::initializeError() {
FileManager::GetInstance()->setField0xA84D(0);
field_0x83D = false;
beginState(STATE_ERROR);
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(true);
field_0x83F = 0;
}
void SaveMgr::executeError() {
dLytSystemWindow_c *systemWindow = dLytSystemWindow_c::GetInstance();
FileManager *fileManager = FileManager::GetInstance();
switch (mStep) {
case 0: {
if (dDvdUnk::FontUnk::GetInstance()->getNandError() == NandResultTracker::ERR_CAT_SAVE_MGR) {
if (field_0x837 == true) {
// "The save data is corrupted. Save data will be restored."
if (systemWindow->setProperties("SYS_INIT_05", false, nullptr) == true) {
systemWindow->showMaybe();
mStep = 20;
}
} else if (field_0x838 == true) {
// "Save data could not be created. Try again?[1]Yes[2]No"
if (systemWindow->setProperties("SYS_INIT_04", false, nullptr) == true) {
systemWindow->showMaybe(4);
mStep = 1;
dCsBase_c::GetInstance()->setDrawDirectly(true);
}
} else {
// "The file cannot be used because nthe data is corrupted. All data saved up to this point will be
// lost. Create new save data?[1]Yes[2]No"
if (systemWindow->setProperties("SYS_NAND_07", false, nullptr) == true) {
systemWindow->showMaybe(4);
mStep = 1;
dCsBase_c::GetInstance()->setDrawDirectly(true);
}
}
}
break;
}
case 1: {
if (systemWindow->getField_0xE10() != 0 && mPad::g_currentCore->downTrigger(/* A */ 0x800) &&
systemWindow->getField_0xDE0() != 2) {
if (systemWindow->getField_0xDE0() == 0) {
if (systemWindow->fn_80152F60() == true) {
mStep = 2;
}
} else {
if (systemWindow->fn_80152F50() == true) {
mStep = 2;
}
}
}
break;
}
case 2: {
if (systemWindow->fn_80152F70() == true) {
if (systemWindow->getField_0xDE0() == false) {
mStep = 3;
} else {
initializeDeleteAllData();
dCsBase_c::GetInstance()->setDrawDirectly(false);
}
}
break;
}
case 3: {
if (dScBoot_c::GetInstance() != nullptr) {
// "You will not be able to save. Do you want to begin your game anyway? [1]Begin[2-]Quit"
if (systemWindow->setProperties("SYS_SAVE_10", false, nullptr) == true) {
systemWindow->showMaybe(4);
mpWindow->setNumLines(2);
mStep = 4;
}
} else {
// "It is not possible to save. Continue your adventure anyway? [1]Continue[2]Quit"
if (systemWindow->setProperties("SYS_SAVE_11", false, nullptr) == true) {
systemWindow->showMaybe(4);
mpWindow->setNumLines(2);
mStep = 4;
}
}
break;
}
case 4: {
if (mPad::g_currentCore->downTrigger(/* A */ 0x800) && systemWindow->getField_0xDE0() != 2) {
if (systemWindow->getField_0xDE0() == 0) {
if (systemWindow->fn_80152F60() == true) {
mStep = 5;
}
} else {
if (systemWindow->fn_80152F50()) {
mStep = 10;
}
}
}
break;
}
case 5: {
if (systemWindow->fn_80152F70() == true) {
mStep = 6;
}
break;
}
case 6: {
if (field_0x837 == true) {
// "The save data is corrupted. Save data will be restored."
if (systemWindow->setProperties("SYS_INIT_05", false, nullptr) == true) {
systemWindow->showMaybe(4);
mStep = 1;
}
} else {
// "The file cannot be used because nthe data is corrupted. All data saved up to this point will be
// lost. Create new save data?[1]Yes[2]No"
if (systemWindow->setProperties("SYS_NAND_07", false, nullptr) == true) {
systemWindow->showMaybe(4);
mStep = 1;
}
}
break;
}
case 10: {
if (systemWindow->fn_80152F70() == true) {
dDvdUnk::FontUnk::GetInstance()->clearNandTrackerError();
if (field_0x837 != true) {
fileManager->setField0xA84C(1);
}
dCsBase_c::GetInstance()->setDrawDirectly(false);
endState();
}
break;
}
case 20: {
if (systemWindow->fn_80152F70() == true) {
mStep++;
}
break;
}
case 21: {
if (systemWindow->getField_0xDFC() == true && mPad::g_currentCore->downTrigger(/* A */ 0x800) &&
systemWindow->fn_80152F50() == true) {
mStep++;
}
break;
}
case 22: {
if (systemWindow->fn_80152F70() == true) {
initializeDeleteAllData();
}
}
}
if (mpWindow != nullptr) {
mpWindow->execute();
}
}
void SaveMgr::initializeNandError() {
FileManager::GetInstance()->setField0xA84D(0);
field_0x83D = false;
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(true);
beginState(STATE_NAND_ERROR);
}
void SaveMgr::executeNandError() {
FileManager *fileManager = FileManager::GetInstance();
dLytSystemWindow_c *systemWindow = dLytSystemWindow_c::GetInstance();
switch (mStep) {
case 0: {
bool anyAction = false;
if (mCheckForFreeSpaceResult == 1) {
// "There is not enough available space in Wii system memory. Create 1 block of free space either by
// moving data to an SD Card or deleting data in the Wii console's Data Management screen."
if (systemWindow->setProperties("SYS_NAND_02", false, nullptr) == true) {
anyAction = true;
}
} else {
// "There is not enough available space in Wii system memory. Either move data to an SD Card or delete
// data in the Wii console's Data Management screen."
if (systemWindow->setProperties("SYS_NAND_03", false, nullptr) == true) {
anyAction = true;
}
}
if (anyAction) {
systemWindow->showMaybe();
mStep = 1;
}
break;
}
case 1: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 2;
}
break;
}
case 2: {
if (systemWindow->getField_0xDFC() == 1 && mPad::g_currentCore->downTrigger(/* A */ 0x800) &&
systemWindow->fn_80152F60() == 1) {
mStep = 3;
}
break;
}
case 3: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 4;
}
break;
}
case 4: {
// "Do you want to go to the Wii console's Data Management screen to edit data? [1]Yes[2]No"
if (systemWindow->setProperties("SYS_NAND_04", false, nullptr) == true) {
systemWindow->showMaybe(4);
dCsBase_c::GetInstance()->setDrawDirectly(true);
mStep = 5;
}
break;
}
case 5: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 6;
}
break;
}
case 6: {
if (systemWindow->getField_0xE10() != 0) {
if (!dReset::Manage_c::GetInstance()->FadeOutCalc() &&
!dReset::Manage_c::GetInstance()->IsFaderBlank() && mPad::getCore()->downTrigger(mPad::BUTTON_A) &&
systemWindow->getField_0xDE0() != 2 && systemWindow->fn_80152F60() == true) {
mStep = 7;
}
}
break;
}
case 7: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 8;
}
break;
}
case 8: {
if (systemWindow->getField_0xDE0() == 0) {
mStep = 9;
} else {
dCsBase_c::GetInstance()->setDrawDirectly(false);
dReset::Manage_c::GetInstance()->SetInteriorReturnDataManager();
endState();
mIsIdle = false;
}
break;
}
case 9: {
// "You will not be able to save. Do you want to begin your game anyway? [1]Begin[2-]Quit"
if (systemWindow->setProperties("SYS_SAVE_10", false, nullptr) == true) {
systemWindow->showMaybe(4);
// Why save mgs window and not system window?
mpWindow->setNumLines(2);
mStep = 10;
}
break;
}
case 10: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 11;
}
break;
}
case 11: {
if (systemWindow->getField_0xE10() != 0 && mPad::g_currentCore->downTrigger(/* A */ 0x800) &&
systemWindow->getField_0xDE0() != 2) {
if (systemWindow->getField_0xDE0() == 0) {
if (systemWindow->fn_80152F60() == true) {
mStep = 12;
}
} else {
if (systemWindow->fn_80152F50()) {
mStep = 13;
}
}
}
break;
}
case 12: {
if (systemWindow->fn_80152F70() == 1) {
mStep = 0;
}
break;
}
case 13: {
if (systemWindow->fn_80152F70() == 1) {
dCsBase_c::GetInstance()->setDrawDirectly(false);
dDvdUnk::FontUnk::GetInstance()->clearNandTrackerError();
fileManager->setField0xA84C(1);
endState();
}
break;
}
}
}
void SaveMgr::beginState(SaveMgrState_e state) {
mCurrentState = state;
field_0x824 = 2;
mStep = 0;
mDelayTimer = 0;
}
void SaveMgr::endState() {
mIsIdle = true;
FileManager::GetInstance()->setField0xA840(1);
field_0x83D = false;
dDvdUnk::FontUnk::GetInstance()->setNeedsPad(true);
field_0x83B = 0;
field_0x83C = 0;
mCurrentState = STATE_MAX;
field_0x83E = 0;
field_0x83F = 0;
}
s32 SaveMgr::getFrameRate() {
return 60 / dSys_c::getFrameRate();
}
NandRequestCheckIsFile::NandRequestCheckIsFile(const char *path) : mIsFile(0) {
mPath = path;
}
bool NandRequestCheckIsFile::execute() {
u8 type = 0;
mStatus = NANDGetType(mPath, &type);
if (mStatus == NAND_RESULT_OK && type == NAND_FILE_TYPE_FILE) {
mIsFile = 1;
} else {
mIsFile = 0;
}
return true;
}
bool NandRequestCheckIsFileHolder::checkIsFile(const char *path) {
if (mpRequest != nullptr) {
return false;
}
NandRequestCheckIsFile *req = new NandRequestCheckIsFile(path);
if (req == nullptr) {
return false;
}
NandRequestThread::GetInstance()->enqueueRequest(req);
mpRequest = req;
return true;
}
bool NandRequestCheckIsFileHolder::getCheckResult() const {
return static_cast<NandRequestCheckIsFile *>(mpRequest)->mIsFile;
}
NandRequestSaveBanner::NandRequestSaveBanner(
const char *filePath, TPLPalette *palette, const wchar_t *title, const wchar_t *subtitle, u8 perm, u8 attr
)
: NandRequestCreate(filePath, perm, attr),
mpPalette(palette),
mpTitle(title),
mpSubTitle(subtitle),
mFailed(false) {}
bool NandRequestSaveBanner::execute() {
if (!NandRequestCreate::execute()) {
return true;
}
char homeDir[64];
NANDFileInfo info;
mStatus = NANDOpen(mFilePath, &info, NAND_ACCESS_WRITE);
if (mStatus != NAND_RESULT_OK) {
return mStatus;
}
if (writeBannerTpl(&info) != 0x72A0) {
NANDResult res = NANDClose(&info);
if (res != NAND_RESULT_OK) {
mStatus = res;
}
mFailed = true;
return false;
}
mStatus = NANDClose(&info);
mStatus = NANDGetHomeDir(homeDir);
if (mStatus != NAND_RESULT_OK) {
return mStatus;
}
mStatus = NANDMove(mFilePath, homeDir);
return true;
}
#define NUM_BANNER_ICONS 1
u32 NandRequestSaveBanner::writeBannerTpl(NANDFileInfo *info) {
static NANDBanner sBanner;
NANDInitBanner(&sBanner, 0, getTitle(), getSubTitle());
TPLPalette *palette = getPalette();
TPLDescriptor *tpl = TPLGet(palette, 0);
std::memcpy(&sBanner.bannerTexture, tpl->texHeader->data, NAND_BANNER_TEXTURE_SIZE);
for (s32 i = 0; i < NUM_BANNER_ICONS;) {
TPLDescriptor *tpl = TPLGet(palette, i + 1);
std::memcpy(sBanner.iconTexture[0], tpl->texHeader->data, NAND_BANNER_ICON_TEXTURE_SIZE);
// ah yes
s32 idx = i++;
NAND_SET_ICON_SPEED(&sBanner, idx, 2);
}
NAND_SET_ICON_SPEED(&sBanner, NUM_BANNER_ICONS, 0);
// How is this calculated?
return NANDWrite(info, &sBanner, 0x72A0);
}
bool NandRequestSaveBannerHolder::saveBanner(
const char *filePath, TPLPalette *palette, const wchar_t *title, const wchar_t *subtitle, u8 perm, u8 attr
) {
if (mpRequest != nullptr) {
return false;
}
NandRequestSaveBanner *req = new NandRequestSaveBanner(filePath, palette, title, subtitle, perm, attr);
if (req == nullptr) {
return false;
}
NandRequestThread::GetInstance()->enqueueRequest(req);
mpRequest = req;
return true;
}
bool NandRequestSaveBannerHolder::hasFailed() const {
return static_cast<NandRequestSaveBanner *>(mpRequest)->mFailed;
}
NandRequestLoadSaveFile::NandRequestLoadSaveFile(const char *path, s32 readSize, s32 mountDirection, EGG::Heap *heap)
: mpHeap(heap), mpBuf(nullptr), mBufLen(0), mMountDirection(mountDirection), mReadSize(readSize), mUnused(false) {
mPath = path;
if (mpHeap == nullptr) {
mpHeap = dHeap::work2Heap.heap;
}
}
bool NandRequestLoadSaveFile::execute() {
NANDFileInfo info;
mStatus = NANDOpen(mPath, &info, NAND_ACCESS_READ);
if (mStatus != NAND_RESULT_OK) {
return true;
}
u32 length;
mStatus = NANDGetLength(&info, &length);
if (mStatus != NAND_RESULT_OK) {
mStatus = NAND_RESULT_AUTHENTICATION; // ??
NANDResult res = NANDClose(&info);
if (res != NAND_RESULT_OK) {
mStatus = res;
}
return true;
}
if (length != mReadSize) {
mStatus = NAND_RESULT_AUTHENTICATION; // ??
NANDResult res = NANDClose(&info);
if (res != NAND_RESULT_OK) {
mStatus = res;
}
return true;
}
s32 align = mMountDirection == 0 ? 0x20 : -0x20;
mpBuf = mpHeap->alloc(length, align);
if (mpBuf == nullptr) {
mStatus = NAND_RESULT_AUTHENTICATION; // ??
NANDResult res = NANDClose(&info);
if (res != NAND_RESULT_OK) {
mStatus = res;
}
return false;
}
mBufLen = length;
mStatus = NANDRead(&info, mpBuf, mBufLen);
NANDResult res = NANDClose(&info);
if (mStatus != mBufLen && mStatus >= NAND_RESULT_OK) {
mStatus = NAND_RESULT_AUTHENTICATION; // ??
} else if (mStatus != NAND_RESULT_ECC_CRIT && mStatus != NAND_RESULT_AUTHENTICATION) {
mStatus = res;
}
return true;
}
void NandRequestLoadSaveFile::remove() {
if (mpBuf != nullptr) {
mpHeap->free(mpBuf);
mpBuf = nullptr;
mBufLen = 0;
}
}
bool NandRequestLoadSaveFileHolder::loadSave(const char *path, s32 readSize, s32 mountDirection, EGG::Heap *heap) {
if (mpRequest != nullptr) {
return false;
}
NandRequestLoadSaveFile *req = new NandRequestLoadSaveFile(path, readSize, mountDirection, heap);
if (req == nullptr) {
return false;
}
NandRequestThread::GetInstance()->enqueueRequest(req);
mpRequest = req;
return true;
}
void *NandRequestLoadSaveFileHolder::getBuf() const {
return static_cast<NandRequestLoadSaveFile *>(mpRequest)->mpBuf;
}
u32 NandRequestLoadSaveFileHolder::getLen() const {
return static_cast<NandRequestLoadSaveFile *>(mpRequest)->mBufLen;
}
void NandRequestLoadSaveFileHolder::remove() {
static_cast<NandRequestLoadSaveFile *>(mpRequest)->remove();
}
NandRequestDelete::NandRequestDelete(const char *path) {
mPath = path;
}
bool NandRequestDelete::execute() {
mStatus = NANDDelete(mPath);
return true;
}
bool NandRequestDeleteHolder::doDelete(const char *path) {
if (mpRequest != nullptr) {
return false;
}
NandRequestDelete *req = new NandRequestDelete(path);
if (req == nullptr) {
return false;
}
NandRequestThread::GetInstance()->enqueueRequest(req);
mpRequest = req;
return true;
}