tp/src/m_Do/m_Do_MemCardRWmng.cpp

453 lines
14 KiB
C++

/**
* m_Do_MemCardRWmng.cpp
* Memcard Saving and Checksum
*/
#include "m_Do/m_Do_MemCardRWmng.h"
#include "m_Do/m_Do_MemCard.h"
#include "JSystem/JUtility/JUTTexture.h"
#include "d/d_com_inf_game.h"
#include "d/d_save.h"
#include <cstdio>
#if VERSION == VERSION_GCN_JPN
#define HEADER_TITLE "ゼルダの伝説 トワイライトプリンセス"
#define HEADER_COMMENT "%d月%d日のセーブデータです"
#elif PLATFORM_WII
#define HEADER_TITLE "The Legend of Zelda: TP"
#define HEADER_COMMENT "%d/%d Save Data"
#else
#define HEADER_TITLE "Zelda: Twilight Princess"
#define HEADER_COMMENT "%d/%d Save Data"
#endif
struct data_s {
int unk_0x0;
int data_version;
u8 data[(SAVEFILE_SIZE) + 0x38]; // unsure what the extra 0x38 is
u32 checksum;
};
static u8 sTmpBuf[SECTOR_SIZE * 2];
#if !PLATFORM_SHIELD
s32 mDoMemCdRWm_Store(CARDFileInfo* file, void* data, u32 length) {
mDoMemCdRWm_BuildHeader((mDoMemCdRWm_HeaderData*)sTmpBuf);
s32 ret = CARDWrite(file, sTmpBuf, sizeof(sTmpBuf), 0);
if (ret != CARD_RESULT_READY) {
return ret;
}
if (!mDoMemCdRWm_CheckCardStat(file)) {
memset(sTmpBuf, 0, sizeof(sTmpBuf));
ret = CARDWrite(file, sTmpBuf, SECTOR_SIZE, 0x4000);
if (ret != CARD_RESULT_READY) {
return ret;
}
ret = CARDWrite(file, sTmpBuf, SECTOR_SIZE, 0x6000);
if (ret != CARD_RESULT_READY) {
return ret;
}
}
memset(sTmpBuf, 0, sizeof(sTmpBuf));
data_s* tmp_data = (data_s*)sTmpBuf;
tmp_data->data_version = SAVEDATA_VERSION;
memcpy(tmp_data->data, data, length);
tmp_data->unk_0x0 = 0;
u32 checksum = mDoMemCdRWm_CalcCheckSum(tmp_data, sizeof(data_s) - 4);
tmp_data->checksum = checksum;
ret = CARDWrite(file, sTmpBuf, SECTOR_SIZE, 0x4000);
if (ret != CARD_RESULT_READY) {
return ret;
}
ret = CARDRead(file, sTmpBuf, SECTOR_SIZE, 0x4000);
if (ret != CARD_RESULT_READY) {
return ret;
}
if (checksum != mDoMemCdRWm_CalcCheckSum(sTmpBuf, sizeof(data_s) - 4)) {
return ret;
}
ret = CARDWrite(file, sTmpBuf, SECTOR_SIZE, 0x6000);
if (ret != CARD_RESULT_READY) {
return ret;
}
ret = CARDRead(file, sTmpBuf, SECTOR_SIZE, 0x6000);
if (ret != CARD_RESULT_READY) {
return ret;
}
if (checksum != mDoMemCdRWm_CalcCheckSum(sTmpBuf, sizeof(data_s) - 4)) {
return ret;
}
mDoMemCdRWm_SetCardStat(file);
return ret;
}
s32 mDoMemCdRWm_Restore(CARDFileInfo* file, void* data, u32 length) {
BOOL rewrite = FALSE;
data_s* saves = (data_s*)sTmpBuf;
data_s* backup_saves = (data_s*)(sTmpBuf + SECTOR_SIZE);
s32 ret = CARDRead(file, saves, SECTOR_SIZE, 0x4000);
if (ret != CARD_RESULT_READY) {
return ret;
}
BOOL save1_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 0]);
BOOL save2_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 1]);
BOOL save3_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 2]);
ret = CARDRead(file, backup_saves, SECTOR_SIZE, 0x6000);
if (ret != CARD_RESULT_READY) {
return ret;
}
BOOL backup1_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 0]);
BOOL backup2_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 1]);
BOOL backup3_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 2]);
if (!save1_valid && backup1_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 0], &backup_saves->data[SAVEDATA_SIZE * 0], SAVEDATA_SIZE);
rewrite = TRUE;
}
if (!save2_valid && backup2_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 1], &backup_saves->data[SAVEDATA_SIZE * 1], SAVEDATA_SIZE);
rewrite = TRUE;
}
if (!save3_valid && backup3_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 2], &backup_saves->data[SAVEDATA_SIZE * 2], SAVEDATA_SIZE);
rewrite = TRUE;
}
if (!mDoMemCdRWm_CheckCardStat(file)) {
return CARD_RESULT_FATAL_ERROR;
}
if (rewrite) {
ret = CARDWrite(file, saves, SECTOR_SIZE, 0x4000);
if (ret != CARD_RESULT_READY) {
return ret;
}
ret = CARDWrite(file, saves, SECTOR_SIZE, 0x6000);
if (ret != CARD_RESULT_READY) {
return ret;
}
}
memcpy(data, saves->data, length);
mDoMemCd_setDataVersion(saves->data_version);
mDoMemCd_setSerialNo();
return CARD_RESULT_READY;
}
#endif
#if PLATFORM_WII || PLATFORM_SHIELD
s32 mDoMemCdRWm_StoreNAND(NANDFileInfo* file, void* data, u32 length) {
s32 ret;
memset(sTmpBuf, 0, sizeof(sTmpBuf));
data_s* tmp_data = (data_s*)sTmpBuf;
tmp_data->unk_0x0 = 0;
tmp_data->data_version = SAVEDATA_VERSION;
memcpy(tmp_data->data, data, length);
u32 checksum = tmp_data->checksum = mDoMemCdRWm_CalcCheckSum(tmp_data, sizeof(data_s) - 4);
ret = NANDWrite(file, sTmpBuf, 0x2000);
if (ret != 0x2000) {
return ret;
}
NANDSeek(file, 0, 0);
ret = NANDRead(file, sTmpBuf, 0x2000);
if (ret != 0x2000) {
return ret;
}
if (checksum != mDoMemCdRWm_CalcCheckSum(sTmpBuf, sizeof(data_s) - 4)) {
return ret;
}
NANDSeek(file, 0x2000, 0);
ret = NANDWrite(file, sTmpBuf, 0x2000);
if (ret != 0x2000) {
return ret;
}
NANDSeek(file, 0x2000, 0);
ret = NANDRead(file, sTmpBuf, 0x2000);
if (ret != 0x2000) {
return ret;
}
if (checksum != mDoMemCdRWm_CalcCheckSum(sTmpBuf, sizeof(data_s) - 4)) {
return ret;
}
return NAND_RESULT_OK;
}
s32 mDoMemCdRWm_RestoreNAND(NANDFileInfo* file, void* data, u32 length) {
BOOL rewrite = FALSE;
data_s* saves = (data_s*)sTmpBuf;
data_s* backup_saves = (data_s*)(sTmpBuf + SECTOR_SIZE);
NANDSeek(file, 0, 0);
s32 ret = NANDRead(file, saves, 0x2000);
if (ret != 0x2000) {
return ret;
}
BOOL save1_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 0]);
BOOL save2_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 1]);
BOOL save3_valid = mDoMemCdRWm_TestCheckSumGameData(&saves->data[SAVEDATA_SIZE * 2]);
NANDSeek(file, 0x2000, 0);
ret = NANDRead(file, backup_saves, 0x2000);
if (ret != 0x2000) {
return ret;
}
BOOL backup1_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 0]);
BOOL backup2_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 1]);
BOOL backup3_valid = mDoMemCdRWm_TestCheckSumGameData(&backup_saves->data[SAVEDATA_SIZE * 2]);
if (!save1_valid && backup1_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 0], &backup_saves->data[SAVEDATA_SIZE * 0], SAVEDATA_SIZE);
rewrite = TRUE;
}
if (!save2_valid && backup2_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 1], &backup_saves->data[SAVEDATA_SIZE * 1], SAVEDATA_SIZE);
rewrite = TRUE;
}
if (!save3_valid && backup3_valid) {
memcpy(&saves->data[SAVEDATA_SIZE * 2], &backup_saves->data[SAVEDATA_SIZE * 2], SAVEDATA_SIZE);
rewrite = TRUE;
}
BOOL sp10 = FALSE;
if (!save1_valid && !backup1_valid &&
!save2_valid && !backup2_valid &&
!save3_valid && !backup3_valid)
{
sp10 = TRUE;
}
if (rewrite) {
NANDSeek(file, 0, 0);
ret = NANDWrite(file, saves, 0x2000);
if (ret != 0x2000) {
return ret;
}
NANDSeek(file, 0x2000, 0);
ret = NANDWrite(file, saves, 0x2000);
if (ret != 0x2000) {
return ret;
}
}
memcpy(data, saves->data, length);
mDoMemCd_setDataVersion(saves->data_version);
return NAND_RESULT_OK;
}
#endif
#if PLATFORM_WII || PLATFORM_SHIELD
s32 mDoMemCdRWm_StoreBannerNAND(NANDFileInfo* file) {
static NANDBanner info;
static wchar_t titleTxt[] = L"The Legend of Zelda:";
static wchar_t commentTxt[] = L"Twilight Princess";
u32 size;
s32 ret;
NANDInitBanner(&info, 0, (u16*)titleTxt, (u16*)commentTxt);
ResTIMG* banner_data = (ResTIMG*)dComIfGp_getCardIconResArchive()->getResource("zelda2_wii_banner.bti");
ResTIMG* icon_data = (ResTIMG*)dComIfGp_getCardIconResArchive()->getResource("zelda2_wii_icon.bti");
u8* banner_base_ptr = (u8*)banner_data;
u8* icon_base_ptr = (u8*)icon_data;
memcpy(info.bannerTexture, banner_base_ptr + banner_data->imageOffset, sizeof(info.bannerTexture));
memcpy(info.iconTexture, icon_base_ptr + icon_data->imageOffset, 0x1200);
dComIfGp_getCardIconResArchive()->removeResourceAll();
NANDSetIconSpeed(info, 0, NAND_STAT_SPEED_MIDDLE);
NANDSetIconSpeed(info, 1, NAND_STAT_SPEED_END);
size = 0x72A0;
ret = NANDWrite(file, &info, size);
if (ret != size) {
return ret;
}
return NAND_RESULT_OK;
}
#endif
static void mDoMemCdRWm_BuildHeader(mDoMemCdRWm_HeaderData* header) {
snprintf(header->mTitle, sizeof(header->mTitle), HEADER_TITLE);
OSCalendarTime time;
OSTicksToCalendarTime(OSGetTime(), &time);
#if VERSION == VERSION_GCN_PAL
switch (dComIfGs_getPalLanguage()) {
case dSv_player_config_c::LANGUAGE_ENGLISH:
snprintf(header->mComment, sizeof(header->mComment), "%d/%d Save Data", time.mon + 1, time.mday);
break;
case dSv_player_config_c::LANGUAGE_GERMAN:
snprintf(header->mComment, sizeof(header->mComment), "%d/%d Spielstand", time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_FRENCH:
snprintf(header->mComment, sizeof(header->mComment), "Donn%ces de jeu %d/%d", 0xE9, time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_SPANISH:
snprintf(header->mComment, sizeof(header->mComment), "Datos guardados el %d/%d", time.mday, time.mon + 1);
break;
case dSv_player_config_c::LANGUAGE_ITALIAN:
snprintf(header->mComment, sizeof(header->mComment), "Dati salvati: %d/%d", time.mday, time.mon + 1);
break;
}
#else
snprintf(header->mComment, sizeof(header->mComment), HEADER_COMMENT, time.mon + 1, time.mday);
#endif
ResTIMG* banner_data = (ResTIMG*)dComIfGp_getCardIconResArchive()->getResource("zelda2_gc_banner.bti");
ResTIMG* icon_data = (ResTIMG*)dComIfGp_getCardIconResArchive()->getResource("zelda2_gc_icon.bti");
memcpy(header->mBannerTexData, (u8*)banner_data + banner_data->imageOffset, (banner_data->numColors * 2) + 0xC00);
memcpy(header->mIconTexData0, (u8*)icon_data + icon_data->imageOffset, 0x400);
memcpy(header->mIconTexData1, (u8*)icon_data + icon_data->imageOffset, 0x400);
memcpy(header->mIconTexData2, (u8*)icon_data + icon_data->imageOffset, 0x400);
memcpy(header->mIconTexData3, (u8*)icon_data + icon_data->imageOffset, 0x400);
memcpy(header->mIconTexData4, (u8*)icon_data + icon_data->imageOffset, (icon_data->numColors * 2) + 0x400);
dComIfGp_getCardIconResArchive()->removeResourceAll();
}
static void mDoMemCdRWm_SetCardStat(CARDFileInfo* file) {
CARDStat stat;
mDoMemCd_getCardStatus(file->fileNo, &stat);
stat.iconAddr = 0;
stat.commentAddr = 0x2400;
CARDSetBannerFormat(&stat, CARD_STAT_BANNER_C8);
CARDSetIconAnim(&stat, CARD_STAT_ANIM_BOUNCE);
CARDSetIconFormat(&stat, 0, CARD_STAT_ICON_C8);
CARDSetIconFormat(&stat, 1, CARD_STAT_ICON_C8);
CARDSetIconFormat(&stat, 2, CARD_STAT_ICON_C8);
CARDSetIconFormat(&stat, 3, CARD_STAT_ICON_C8);
CARDSetIconFormat(&stat, 4, CARD_STAT_ICON_C8);
CARDSetIconFormat(&stat, 5, CARD_STAT_ICON_NONE);
CARDSetIconFormat(&stat, 6, CARD_STAT_ICON_NONE);
CARDSetIconFormat(&stat, 7, CARD_STAT_ICON_NONE);
CARDSetIconSpeed(&stat, 0, CARD_STAT_SPEED_FAST);
CARDSetIconSpeed(&stat, 1, CARD_STAT_SPEED_FAST);
CARDSetIconSpeed(&stat, 2, CARD_STAT_SPEED_FAST);
CARDSetIconSpeed(&stat, 3, CARD_STAT_SPEED_FAST);
CARDSetIconSpeed(&stat, 4, CARD_STAT_SPEED_FAST);
CARDSetIconSpeed(&stat, 5, CARD_STAT_SPEED_END);
CARDSetIconSpeed(&stat, 6, CARD_STAT_SPEED_END);
CARDSetIconSpeed(&stat, 7, CARD_STAT_SPEED_END);
mDoMemCd_setCardStatus(file->fileNo, &stat);
}
static BOOL mDoMemCdRWm_CheckCardStat(CARDFileInfo* file) {
CARDStat stat;
mDoMemCd_getCardStatus(file->fileNo, &stat);
if (stat.iconAddr != 0 || stat.commentAddr != 0x2400
|| CARDGetBannerFormat(&stat) != CARD_STAT_BANNER_C8
|| CARDGetIconAnim(&stat) != CARD_STAT_ANIM_BOUNCE
|| CARDGetIconFormat(&stat, 0) != CARD_STAT_ICON_C8
|| CARDGetIconFormat(&stat, 1) != CARD_STAT_ICON_C8
|| CARDGetIconFormat(&stat, 2) != CARD_STAT_ICON_C8
|| CARDGetIconFormat(&stat, 3) != CARD_STAT_ICON_C8
|| CARDGetIconFormat(&stat, 4) != CARD_STAT_ICON_C8
|| CARDGetIconFormat(&stat, 5) != CARD_STAT_ICON_NONE
|| CARDGetIconFormat(&stat, 6) != CARD_STAT_ICON_NONE
|| CARDGetIconFormat(&stat, 7) != CARD_STAT_ICON_NONE
|| CARDGetIconSpeed(&stat, 0) != CARD_STAT_SPEED_FAST
|| CARDGetIconSpeed(&stat, 1) != CARD_STAT_SPEED_FAST
|| CARDGetIconSpeed(&stat, 2) != CARD_STAT_SPEED_FAST
|| CARDGetIconSpeed(&stat, 3) != CARD_STAT_SPEED_FAST
|| CARDGetIconSpeed(&stat, 4) != CARD_STAT_SPEED_FAST
|| CARDGetIconSpeed(&stat, 5) != CARD_STAT_SPEED_END
|| CARDGetIconSpeed(&stat, 6) != CARD_STAT_SPEED_END
|| CARDGetIconSpeed(&stat, 7) != CARD_STAT_SPEED_END)
{
return FALSE;
}
return TRUE;
}
static u32 mDoMemCdRWm_CalcCheckSum(void* data, u32 size) {
int i;
u16 high, low;
high = low = 0;
u16* d;
for (i = 0, d = (u16*)data; i < size / 2; i++, d++) {
high += *d;
low += ~*d;
}
return high << 16 | low;
}
static u64 mDoMemCdRWm_CalcCheckSumGameData(void* data, u32 size) {
int i;
u32 high, low;
high = low = 0;
u8* d;
for (i = 0, d = (u8*)data; i < size; i++, d++) {
high += *d;
low += ~*d;
}
u64 tmp = high;
return tmp << 32 | low;
}
BOOL mDoMemCdRWm_TestCheckSumGameData(void* data) {
u64 checksum;
u8* file_ptr = (u8*)data;
checksum = mDoMemCdRWm_CalcCheckSumGameData(data, (SAVEDATA_SIZE - sizeof(u64)));
return checksum == *(u64*)(file_ptr + (SAVEDATA_SIZE - sizeof(u64)));
}
void mDoMemCdRWm_SetCheckSumGameData(u8* data, u8 dataNum) {
u8* file_ptr = data + (dataNum * SAVEDATA_SIZE);
u64 checksum = mDoMemCdRWm_CalcCheckSumGameData(file_ptr, (SAVEDATA_SIZE - sizeof(u64)));
*(u64*)(file_ptr + (SAVEDATA_SIZE - sizeof(u64))) = checksum;
}