diff --git a/build/libraries_sysmenu/sysmenu/ARM9/Makefile b/build/libraries_sysmenu/sysmenu/ARM9/Makefile index b219ec5a..d76f95c1 100644 --- a/build/libraries_sysmenu/sysmenu/ARM9/Makefile +++ b/build/libraries_sysmenu/sysmenu/ARM9/Makefile @@ -32,6 +32,9 @@ SRCS = sysmenu_lib.c \ device.c \ keys.c \ title.c \ + SaveDataChecker/lgy_SaveDataChecker.c \ + SaveDataChecker/lgy_VCK.c \ + SaveDataChecker/lgy_VCW.c \ fs_wram.c \ ../common/src/status.c \ ../common/src/pxi.c \ diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.c b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.c new file mode 100644 index 00000000..136abc67 --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.c @@ -0,0 +1,198 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_SaveDataChecker.cpp + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#include + +#include "lgy_SaveDataChecker.h" +#include "lgy_VCK.h" +#include "lgy_VCW.h" + +// title.c より定義を拝借 +#define WRAM_SIZE_FOR_FS MI_WRAM_SIZE_96KB +#ifdef USE_HYENA_COMPONENT +#define WRAM_SLOT_FOR_FS 5 +#else +#define WRAM_SLOT_FOR_FS 0 +#endif + +enum GameCodeList +{ + TARGET_ROM_VCK = 0, + TARGET_ROM_VCW, + TARGET_ROM_MAX +}GameCodeList; + +char *game_code_list[TARGET_ROM_MAX] = { "VCK", "VCW" }; + + +BOOL CheckBackupData( TitleProperty *rhs) +{ + u8 list_num; + char test[5]; + + { + char* n = (char*)&rhs->titleID; + test[0] = n[3]; + test[1] = n[2]; + test[2] = n[1]; + test[3] = n[0]; + test[4] = '\0'; + } + + for( list_num = 0; list_num < TARGET_ROM_MAX; list_num++) + { + if( 0 == STD_CompareNString( test, game_code_list[list_num], 3)) + { + switch( list_num) + { + case TARGET_ROM_VCK: + return checkVCK( rhs); + case TARGET_ROM_VCW: + if( test[3] == 'V') + { + return checkVCW( rhs, 0x00800354); + } + else if( test[3] == 'F') + { + return checkVCW( rhs, 0x00800355); + } + else if( test[3] == 'E') + { + return checkVCW( rhs, 0x00400810); + } + default: + return TRUE; + } + } + } + return TRUE; +} + + +void InitializeBackup( void) +{ + u16 lock_id; + + if( HOTSW_isEnableHotSW()) + { + HOTSW_InvalidHotSW(); + OS_TPrintf( "HOTSW is enable, so HOTSW_InvalidHotSW is done.\n"); + } + OS_TPrintf( "ok, HOTSW is not enable.\n"); + + if( !HOTSW_isFinalized()) + { + OS_TPrintf( "HOTSW is not Finalized!\n"); + } + else + { + OS_TPrintf( "ok, HOTSW is Finalized.\n"); + } + if( !HOTSW_isCardLoadCompleted()) + { + OS_TPrintf( "HOTSW is not card load completed!\n"); + } + else + { + OS_TPrintf( "ok, HOTSW is is card load completed.\n"); + } + + if( !CARD_IsAvailable()) + { + CARD_Init(); + OS_TPrintf( "CARD is not available, CARD_Init() is done.\n"); + } + if( !CARD_IsEnabled()) + { + CARD_Enable( TRUE); + OS_TPrintf( "CARD is not enable, CARD_Enable() is done.\n"); + } + + lock_id = (u16)OS_GetLockID(); + CARD_LockBackup( lock_id); + if( !CARD_IdentifyBackup( CARD_BACKUP_TYPE_EEPROM_64KBITS)) + { + OS_TPrintf( "CARD_IdentifyBackup failed.\n"); + } + CARD_UnlockBackup( lock_id); + OS_TPrintf( "InitializeBackup done.\n"); +} + +void FinalizeBackup( void) +{ +// while( 1) {}; +} + + +BOOL readEEPROM( u32 src, void *dst, u32 len) +{ + BOOL result; + u16 lock_id; + + lock_id = (u16)OS_GetLockID(); + CARD_LockBackup( lock_id); + /* CARD_ReadEeprom() は、 一時バッファ(CARDi_backup_cache_page_buf)に読み出したデータを + A9-CPUでdstへコピーしてくれるので、dstのメモリ空間はA7から見えなくても良い */ + result = CARD_ReadEeprom( src, dst, len); + if( !result) + { + OS_TPrintf( "CARD_ReadEeprom failed! err:%d\n", (int)CARD_GetResultCode()); + } + CARD_UnlockBackup( lock_id); + + DC_FlushRange( dst, len); + + return result; +} + +BOOL writeEEPROM( u32 dst, const void *src, u32 len) +{ + BOOL result; + u16 lock_id; + + DC_FlushRange( src, len); + + lock_id = (u16)OS_GetLockID(); + CARD_LockBackup( lock_id); + /* CARD_WriteEeprom() は、 src上のデータをA9-CPUで一時バッファ(CARDi_backup_cache_page_buf)に + コピーした上でデバイスに書き込んでいるので、srcのメモリ空間はA7から見えなくても良い */ + result = CARD_WriteEeprom( dst, src, len); + if( !result) + { + OS_TPrintf( "CARD_WriteEeprom failed! err:%d\n", (int)CARD_GetResultCode()); + } + CARD_UnlockBackup( lock_id); + return result; +} + +BOOL writeAndVerifyEEPROM( u32 dst, const void *src, u32 len) +{ + BOOL result; + u16 lock_id; + + DC_FlushRange( src, len); + + lock_id = (u16)OS_GetLockID(); + CARD_LockBackup( lock_id); + /* CARD_WriteAndVerifyEeprom() は、 src上のデータをA9-CPUで一時バッファ(CARDi_backup_cache_page_buf)に + コピーした上で処理しているので、srcのメモリ空間はA7から見えなくても良い */ + result = CARD_WriteAndVerifyEeprom( dst, src, len); + if( !result) + { + OS_TPrintf( "CARD_WriteAndVerifyEeprom failed! err:%d\n", (int)CARD_GetResultCode()); + } + CARD_UnlockBackup( lock_id); + return result; +} diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.h b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.h new file mode 100644 index 00000000..7ec64ca0 --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_SaveDataChecker.h @@ -0,0 +1,53 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_SaveDataChecker.cpp + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#ifndef __LGY_SAVEDATACHECKER_CPP__ +#define __LGY_SAVEDATACHECKER_CPP__ + +#include +#include + +BOOL CheckBackupData( TitleProperty *rhs); + +void InitializeBackup( void); +void FinalizeBackup( void); +BOOL readEEPROM( u32 src, void *dst, u32 len); +BOOL writeEEPROM( u32 dst, const void *src, u32 len); +BOOL writeAndVerifyEEPROM( u32 dst, const void *src, u32 len); + +/* +// cardnor_NspiIfApi.cpp +namespace nn { +namespace drivers { +namespace cardnor { +namespace CTR { +namespace ARM946ES{ + +void InitializeTwlBackup( void); +nn::Result ReadTwlEEPROM(u32 address, u32* buffer, size_t size); +nn::Result WriteTwlEEPROM(u32 address, const u32* buffer, size_t size); +void FinalizeTwlBackup( void); + +} // end of namespace ARM946ES +} // end of namespace CTR +} // end of namespace cardnor +} // end of namespace drivers +} // end of namespace nn +*/ + +//nn::Result ReadTwlEEPROM(u32 address, u32* buffer, size_t size); +//nn::Result WriteTwlEEPROM(u32 address, const u32* buffer, size_t size); + +#endif diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.c b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.c new file mode 100644 index 00000000..6906f57c --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.c @@ -0,0 +1,256 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_VCK.cpp + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#include +#include +#include "lgy_VCK.h" +#include "lgy_SaveDataChecker.h" + +// title.c より定義を拝借 +#define WRAM_SIZE_FOR_FS MI_WRAM_SIZE_96KB +#ifdef USE_HYENA_COMPONENT +#define WRAM_SLOT_FOR_FS 5 +#else +#define WRAM_SLOT_FOR_FS 0 +#endif + +#define VCK_BACKUP_READ_SIZE (896) +//NN_COMPILER_ASSERT(VCK_BACKUP_READ_SIZE >= sizeof(VCK_SlotHeader) + sizeof(VCK_ProfileSlotBody)); +//NN_COMPILER_ASSERT(VCK_BACKUP_READ_SIZE % 32 == 0); + +// checksum を算出する +static u16 getChecksum( void* data); +// データが工場出荷状態か破壊されていれば TRUE を返す +static BOOL isFactoryOrBrokenForVCK( TitleProperty* tp, void* data, u16* ret_checksum); +// データが正常範囲内に収まっていれば TRUE を返す +static BOOL checkEepromRangeForVCK( void* data); + +//using namespace nn::drivers::cardnor::CTR::ARM946ES; + + +typedef struct TitleID_num +{ + u32 Lo; + u32 Hi; +} +TitleID_num; + + + +// チェックAPI(VCK*タイトル起動前にこれを呼べばOK) +BOOL checkVCK( TitleProperty* tp) +{ + u32 i; + u16 tick; + u32 *data; + VCK_SlotHeader *header; + VCK_ProfileSlotBody *body; + u8 *body_u8; + u16 S_checksum = 0; + u16 B_checksum; + u32 calculatedSha1[ MATH_SHA1_DIGEST_SIZE / sizeof(u32)]; + MATHRandContext16 rc16; + u32 rseed; + + OS_TPrintf("check VCK backup start.\n"); + OS_TPrintf( "header size:0x%x, body size:0x%x\n", sizeof(VCK_SlotHeader), sizeof(VCK_ProfileSlotBody)); + + // EEPROM から読み出し + data = (u32*)(MI_AllocWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9)); + OS_TPrintf("Buffer:0x%x\n", data); + + InitializeBackup(); + readEEPROM( 0, (u32*)data, VCK_BACKUP_READ_SIZE);//sizeof(VCK_SlotHeader) + sizeof(VCK_ProfileSlotBody)が32Bytesの倍数でないため + header = (VCK_SlotHeader*)data; + body = (VCK_ProfileSlotBody*)((u8*)data + sizeof(VCK_SlotHeader)); + body_u8 = (u8*)body; + + // 工場出荷状態またはデータ破壊状態かどうか確認 + if( !isFactoryOrBrokenForVCK( tp, data, &S_checksum)) + { + if( checkEepromRangeForVCK( data)) // データ範囲を確認 + { + OS_TPrintf("VCK backup is correct.\n"); + FinalizeBackup(); + MI_FreeWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + return TRUE; + } + } + else + { + if( !S_checksum) + { + S_checksum = getChecksum( body); + } + } + + // checksumがBになるようにランダムな1箇所へ書き込み + tick = OS_GetTickLo(); +#if 1 + { + MATH_CalcSHA1( calculatedSha1, (const void*)data, sizeof(VCK_ProfileSlotBody)); + } +#endif + rseed = (u32)(OS_GetTick()) + *(u32*)(calculatedSha1); + MATH_InitRand16( &rc16, rseed); + for( i=0; im_uniqueIdentifier) - (header->m_slotCheckSum)))) + { + B_checksum -= body_u8[sizeof(VCK_ProfileSlotBody) - 1]; + body_u8[sizeof(VCK_ProfileSlotBody) - 1]++; + B_checksum += body_u8[sizeof(VCK_ProfileSlotBody) - 1]; + } + + // Backup デバイスへ反映 + if( writeAndVerifyEEPROM( 0, data, VCK_BACKUP_READ_SIZE)) + { + OS_TPrintf("launch ok.\n"); + OS_TPrintf("ckecksum:0x%x, B_checksum:0x%x\n", getChecksum(body), B_checksum); + FinalizeBackup(); + MI_FreeWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + return TRUE; + } + + OS_TPrintf("launch NG.\n"); + OS_TPrintf("S_checksum:0x%x, ckecksum:0x%x, B_checksum:0x%x\n", S_checksum, getChecksum(body), B_checksum); + FinalizeBackup(); + MI_FreeWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + return FALSE; +} + +// checksum を算出する +static u16 getChecksum( void* data) +{ + u8 *data_u8; + u16 checksum = 0; + u16 i; + + data_u8 = (u8*)data; + + for( i=0; ititleID); // TWL:0x56434B45, CTR:0x454B4356 + pIdentifier = (u8*)&(header->m_uniqueIdentifier); // 0x56434B45 + + /* 工場出荷状態のチェック */ +#if 1 + for( i=0; i<4; i++) + { + if( pTitle_id[i] != pIdentifier[i]) + { + OS_TPrintf("%s, factory.\n", __FUNCTION__); + return TRUE; + } + } + OS_TPrintf("%s, not factory.\n", __FUNCTION__); +#endif + + /* 破壊のチェック */ + checksum = 0; + for( i=0; im_uniqueIdentifier); + checksum += header->m_slotCheckSum; + if( checksum != 0xFFFF) + { + OS_TPrintf("%s, broken.\n", __FUNCTION__); + return TRUE; // broken + } + + OS_TPrintf("%s, not broken.\n", __FUNCTION__); + return FALSE; +} + +// データが正常範囲内に収まっていれば TRUE を返す +static BOOL checkEepromRangeForVCK( void* data) +{ + VCK_ProfileSlotBody *buf; + buf = (VCK_ProfileSlotBody*)((u8*)data + sizeof(VCK_SlotHeader)); + + if( buf->m_playerName[10] != '\0') + { + OS_TPrintf("%s, playerName is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( (buf->m_averageCookingTime < -1) || (buf->m_averageCookingTime > 2)) + { + OS_TPrintf("%s, cookingTime is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( (buf->m_culinaryLevel < -1) || (buf->m_culinaryLevel > 2)) + { + OS_TPrintf("%s, culinaryLevel is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( (buf->m_costImportance < -1) || (buf->m_costImportance > 2)) + { + OS_TPrintf("%s, costImportance is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( (buf->m_numberCalories < -1) || (buf->m_numberCalories > 2)) + { + OS_TPrintf("%s, numberCalories is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( buf->m_ROTD_index > 9) + { + OS_TPrintf("%s, ROTD_index is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( buf->m_uBirthDate_Day >0x1F) + { + OS_TPrintf("%s, birthdate_day is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( buf->m_uBirthDate_Month > 0x0C) + { + OS_TPrintf("%s, birthdate_month is out of range.\n", __FUNCTION__); + return FALSE; + } + else if( buf->m_numServings > 8) + { + OS_TPrintf("%s, numServings is out of range.\n", __FUNCTION__); + return FALSE; + } + OS_TPrintf("%s, good range.\n", __FUNCTION__); + return TRUE; +} diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.h b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.h new file mode 100644 index 00000000..8a2406a7 --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCK.h @@ -0,0 +1,159 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_VCK.h + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#ifndef __LGY_VCK_H__ +#define __LGY_VCK_H__ + +#include +#include + +enum E_CookingCoachSavegameSlot +{ + CCSS_ProfileSlot, // 0x0000〜0x0377 + CCSS_ShoppingListSlot, // 0x0378〜0x0BCF + CCSS_CommentSlot1, // 0x0BD0〜0x0F03 + CCSS_CommentSlot2, // 0x0F04〜0x1237 + CCSS_PhotoSlot, // 0x1238〜0x1F76 + CCSS_SlotCount +}; + +#define C_PROFILENAME_MAXLENGTH 11 // Include \0 character at the end +#define DATASAVE_MAX_INGREDIENTS 8 +#define DATASAVE_MAX_SHOPPINGLIST_CUSTOMITEMS 10 +#define DATASAVE_MAX_SHOPPINGLIST_RECIPEITEMS 75 +#define MAX_SHOPPING_LIST_RECIPES 6 +#define DATASAVE_NUM_CALENDARRECIPES 50 +#define DATASAVE_MAX_ROTD 10 +#define DATASAVE_NUM_RECIPECOMMENTS_PER_SLOT 7 +#define DATASAVE_RECIPECOMMENT_LENGTH 114 +//the favorites are stored in an array of u32 used as a bitfield. each bit represents +//ONE recipe. Thus, the maximum number of recipes that can be remembered in this array +//is expressed by DATASAVE_FAVORITE_RECIPES_MAXNUMRECIPES +#define DATASAVE_FAVORITE_RECIPES_NB_U32 10 +#define DATASAVE_FAVORITE_RECIPES_MAXNUMRECIPES (8*4*DATASAVE_FAVORITE_RECIPES_NB_U32) +#define QUANTITY_BUFFER_SIZE 20 + + +typedef struct VCK_SlotHeader +{ + u32 m_uniqueIdentifier; + u16 m_slotCheckSum; + u8 m_status; + u8 struct_padding; +} +VCK_SlotHeader; + +typedef struct CalendarRecipe +{ + u32 m_uDay; + u16 m_uRecipeID; + u8 m_recipeState; +} +CalendarRecipe; + +typedef struct RecipeOfTheDay +{ + u32 m_recipeID; + s32 m_dayProposed; +} +RecipeOfTheDay; + + +//EntryData is the base class for the two "slots" that will be saved individually.= +//the structs are defined in the base class just in case some of them must be used in +//both derived classes. +typedef struct VCK_ProfileSlotBody +{ + //TODO : optimize the size of the EntryData structure + char m_playerName[C_PROFILENAME_MAXLENGTH]; // 11(0x08〜0x12) + // 1(0x13) + s32 m_averageCookingTime; // 4(0x14〜0x17) [取り得る値は -1,0,1,2のどれか] + s32 m_culinaryLevel; // 4(0x18〜0x1B) [取り得る値は -1,0,1,2のどれか] + s32 m_costImportance; // 4(0x1C〜0x1F) [取り得る値は -1,0,1,2のどれか] + s32 m_numberCalories; // 4(0x20〜0x23) [取り得る値は -1,0,1,2のどれか] + u32 m_ingredientsExcluded[DATASAVE_MAX_INGREDIENTS]; // 32(0x24〜0x43) + u32 m_ingredientsPrefered[DATASAVE_MAX_INGREDIENTS]; // 32(0x44〜0x63) + CalendarRecipe m_calendarRecipes[DATASAVE_NUM_CALENDARRECIPES]; //400(0x64〜0x1F3) + u16 m_uNbCalendarRecipes; // 2(0x1F4〜0x1F5) + + RecipeOfTheDay m_recipeOfTheDay[DATASAVE_MAX_ROTD]; // 80(0x1F8〜0x247) + u32 m_ROTD_index; // 4(0x248〜0x24B) + + //---Music + s32 m_musicStyle; // (0x24C〜0x24F) 取り得る値は -1,0,1,2,3のどれか + u32 m_musicLocation; // (0x250〜0x253) 取り得る値は 0,1,2のどちらか + + //---FAVORITES BIT FIELDS + u32 m_favoriteRecipes[DATASAVE_FAVORITE_RECIPES_NB_U32]; // 40(0x254) + u32 m_firstSessionDateInDay; // 4(0x27C) + s32 m_lastSessionDateInDay; // 4(0x280) + + u8 m_uBirthDate_Day; // 1(0x284) [取り得る値は 1〜0x1F] + u8 m_uBirthDate_Month; // 1(0x285) [取り得る値は 1〜0x0C] + + // recipe quantity info (300 quantity info saved on 2 bits each) + u32 m_recipeQuantity[QUANTITY_BUFFER_SIZE]; // 80 + + + // save kitchen profile + u8 m_unitsUsed; // 1 + u8 m_numServings; // 1(0x2D9) 取り得る値は 2,4,6,8のどれか + + BOOL m_hasOven :1; // 0x2DA + BOOL m_hasMixer :1; + BOOL m_hasBBQ :1; + BOOL m_hasFoodProcessor :1; + + // tutorial completed? + BOOL m_tutorialSearchCompleted :1; + BOOL m_tutorialCriteriaSearchCompleted :1; + BOOL m_tutorialPrecookCompleted :1; + BOOL m_tutorialTimelineCompleted :1; + BOOL m_tutorialCookbookCompleted :1; // 0x2DB + BOOL m_tutorialShoppingCompleted :1; + BOOL m_tutorialManagementCompleted :1; + BOOL m_tutorialExtraInfoCompleted :1; + BOOL m_tutorialCalendarCompleted :1; + BOOL m_searchFirstDeleteCompleted :1; + // introduction completed? + BOOL m_introductionCompleted :1; + BOOL m_tutorialFoodProfileCompleted :1; + BOOL m_tutorialFoodProfileFirstDeleteCompleted :1; // 0x2DC + BOOL m_tutorialCookbookTimerCompleted :1; + + //---OPTIONS + //---Voice + BOOL m_vocalCommandsActivated :1; + BOOL m_narrationActivated :1; + BOOL m_birthdayRecipeIsLocked :1; + //---Welcome + BOOL m_forceShowBirthday :1; + BOOL m_plannedARecipe :1; + BOOL m_usedFavorites :1; + + u8 m_welcomeMsgAlreadyDisplayed; //BIT FIELDS with msg that must be display only once // 0x2DD + //check enum E_WelcomeMsg for bit order. + + u8 m_language; // 0x2DE + u8 padding[0x95]; + //CookingCoach::ShoppingListCustomItem m_shoppingListCustomItems[DATASAVE_MAX_SHOPPINGLIST_CUSTOMITEMS]; +} +VCK_ProfileSlotBody; + + +// チェックAPI(VCK*タイトル起動前にこれを呼べばOK) +BOOL checkVCK( TitleProperty* tp); + +#endif diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.c b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.c new file mode 100644 index 00000000..5fd5c50b --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.c @@ -0,0 +1,255 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_VCW.cpp + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#include +#include +#include "lgy_VCW.h" +#include "lgy_SaveDataChecker.h" + +// title.c より定義を拝借 +#define WRAM_SIZE_FOR_FS MI_WRAM_SIZE_96KB +#ifdef USE_HYENA_COMPONENT +#define WRAM_SLOT_FOR_FS 5 +#else +#define WRAM_SLOT_FOR_FS 0 +#endif + +#define VCW_BACKUP_READ_SIZE (7808) +//NN_COMPILER_ASSERT(VCW_BACKUP_READ_SIZE >= ((sizeof(VCW_SavegameHeader) + sizeof(VCW_BodyForCheck)) * 2)); +//NN_COMPILER_ASSERT(VCW_BACKUP_READ_SIZE % 32 == 0); + +/* VCW メソッド */ +static u16 VCW_HeaderChecksumToChecksum( VCW* vcw); +static void VCW_Initialize( VCW* vcw, void* buffer, u32 ggid); +static BOOL VCW_IsFactoryOrBroken( VCW* vcw); +static BOOL VCW_CheckRange( VCW* vcw); +static u16 VCW_Modify( VCW* vcw); +static BOOL VCW_IsModified( VCW* vcw); +static void VCW_UpdateChecksum( VCW* vcw); +static u16 VCW_GetChecksum( VCW* vcw); +static void VCW_Finalize( VCW* vcw); + +//using namespace nn::drivers::cardnor::CTR::ARM946ES; + + +// チェックAPI(VCW*タイトル起動前にこれを呼べばOK) +BOOL checkVCW( TitleProperty* tp, u32 ggid) +{ +#pragma unused( tp) + u32 i; + u32 *data; + u8 *buf; + u16 B_checksum[2]; + VCW vcw[2]; + + OS_TPrintf("check VCW backup start.\n"); + OS_TPrintf( "header size:0x%x, body size:0x%x\n", sizeof(VCW_SavegameHeader), sizeof(VCW_BodyForCheck)); + + // EEPROM から読み出し +// base = MI_AllocWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + data = (u32*)(MI_AllocWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9)); + OS_TPrintf("Buffer:0x%x\n", data); + + InitializeBackup(); + readEEPROM( 0, (u32*)data, VCW_BACKUP_READ_SIZE); + + buf = (u8*)data; + VCW_Initialize( &vcw[0], buf, ggid); + buf = (u8*)data + (sizeof(VCW_SavegameHeader) + sizeof(VCW_BodyForCheck)); + VCW_Initialize( &vcw[1], buf, ggid); + + + for( i=0; i<2; i++) + { + + // 工場出荷状態またはデータ破壊状態かどうか確認 + if( !VCW_IsFactoryOrBroken( &vcw[i])) + { + if( VCW_CheckRange( &vcw[i])) + { + OS_TPrintf("VCW backup is correct.\n"); + continue; + } + } + + // ランダムな位置に書き込み + B_checksum[i] = VCW_Modify( &vcw[i]); + } + + + // バッファに書き込みしたならデバイスへの書き込みテストが必要 + if( VCW_IsModified( &vcw[0]) || VCW_IsModified( &vcw[1])) + { + OS_TPrintf( "backup write testing..\n"); + if( !writeAndVerifyEEPROM( 0, data, VCW_BACKUP_READ_SIZE)) + { + OS_TPrintf("launch NG!\n"); + VCW_Finalize( &vcw[0]); + VCW_Finalize( &vcw[1]); + FinalizeBackup(); + MI_FreeWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + return FALSE; + } + } + + OS_TPrintf("launch ok.\n"); + VCW_Finalize( &vcw[0]); + VCW_Finalize( &vcw[1]); + FinalizeBackup(); + MI_FreeWramSlot( MI_WRAM_C, WRAM_SLOT_FOR_FS, WRAM_SIZE_FOR_FS, MI_WRAM_ARM9); + return TRUE; +} + + + + + +/* VCWメソッド*/ +static u16 VCW_HeaderChecksumToChecksum( VCW* vcw) +{ + return (u16)(0xFFFF - (vcw->header->uniqueIdentifier) - (vcw->header->checkSum)); +} + +static void VCW_Initialize( VCW* vcw, void* buffer, u32 ggid) +{ + vcw->buf = (u8*)buffer; + if( (u32)(vcw->buf) % 4) + { + OS_TPrintf( "Buffer alignment error!\n"); + while( 1) {}; + } + vcw->ggid = ggid; + vcw->header = (VCW_SavegameHeader*)(vcw->buf); + vcw->body = (VCW_BodyForCheck*)(vcw->buf + sizeof(VCW_SavegameHeader)); + vcw->B_checksum = VCW_HeaderChecksumToChecksum( vcw); +} + +/* 工場出荷状態かデータ破壊状態なら TRUE を返す */ +static BOOL VCW_IsFactoryOrBroken( VCW* vcw) +{ + u16 checksum; + + /**/ + VCW_UpdateChecksum( vcw); + + /* 工場出荷状態のチェック */ + if( vcw->ggid != vcw->header->uniqueIdentifier) // VCWV:0x00800354, VCWF:0x00800355, VCWE:0x00400810 + { + OS_TPrintf("%s, factory.\n", __FUNCTION__); + return TRUE; + } + OS_TPrintf("%s, not factory.\n", __FUNCTION__); + + /* 破壊のチェック */ + checksum = VCW_GetChecksum( vcw); + checksum += (u16)(vcw->ggid); + checksum += vcw->header->checkSum; + if( checksum != 0xFFFF) + { + OS_TPrintf("%s, broken.\n", __FUNCTION__); + return TRUE; // broken + } + + OS_TPrintf("%s, not broken.\n", __FUNCTION__); + return FALSE; +} + +// データが正常範囲内に収まっていれば TRUE を返す +static BOOL VCW_CheckRange( VCW* vcw) +{ + if( vcw->body->m_name[UBI_PLAYERINFO_NAME_LENGTH] != '\0') + { + OS_TPrintf("%s, playerName is out of range.\n", __FUNCTION__); + return FALSE; + } + OS_TPrintf("%s, good range.\n", __FUNCTION__); + return TRUE; +} + +/* ランダムに一箇所書き換え、書き換え後のchecksumを返す */ +static u16 VCW_Modify( VCW* vcw) +{ + u16 S_checksum_tmp; + u32 i; + u16 tick; + u8* body_u8 = (u8*)(vcw->body); + u32 calculatedSha1[ MATH_SHA1_DIGEST_SIZE /sizeof(u32)]; + MATHRandContext16 rc16; + u32 rseed; + + S_checksum_tmp = VCW_GetChecksum( vcw); + + // checksumがBになるようにランダムな1箇所へ書き込み + tick = OS_GetTickLo(); +#if 1 + { + MATH_CalcSHA1( calculatedSha1, (const void*)vcw->body, sizeof(VCW_BodyForCheck)); + } +#endif + rseed = (u32)(OS_GetTick()) + *(u32*)(calculatedSha1); + MATH_InitRand16( &rc16, rseed); + for( i=0; iB_checksum = VCW_GetChecksum( vcw); + // 書き込んだ後の checksum が、S_checksum でも マジコンヘッダのchecksum にもならないようにする + while( (vcw->B_checksum == S_checksum_tmp) || + ( vcw->B_checksum == VCW_HeaderChecksumToChecksum( vcw))) + { + vcw->B_checksum -= body_u8[sizeof(VCW_BodyForCheck) - 1]; + body_u8[sizeof(VCW_BodyForCheck) - 1]++; + vcw->B_checksum += body_u8[sizeof(VCW_BodyForCheck) - 1]; + } + OS_TPrintf( "S_checksum:0x%x, A_checksum:0x%x, B_checksum;0x%x\n", + S_checksum_tmp, + VCW_HeaderChecksumToChecksum( vcw), + vcw->B_checksum); + + return vcw->B_checksum; +} + +/* Modifyされたかどうかを返す */ +static BOOL VCW_IsModified( VCW* vcw) +{ + return ( vcw->B_checksum != VCW_HeaderChecksumToChecksum( vcw)); +} + +/* Checksum をアップデートする */ +static void VCW_UpdateChecksum( VCW* vcw) +{ + u32 i; + u8* body_u8 = (u8*)vcw->body; + + vcw->S_checksum = 0; + for( i=0; iS_checksum += body_u8[i]; + } +} + +/* Checksum を取得する */ +static u16 VCW_GetChecksum( VCW* vcw) +{ + return vcw->S_checksum; +} + +/* */ +static void VCW_Finalize( VCW* vcw) +{ +#pragma unused( vcw) +} + diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.h b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.h new file mode 100644 index 00000000..14f45131 --- /dev/null +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/SaveDataChecker/lgy_VCW.h @@ -0,0 +1,262 @@ +/*---------------------------------------------------------------------------* + Project: Horizon + File: lgy_VCW.h + + Copyright (C)2009-2011 Nintendo Co., Ltd. All rights reserved. + + These coded instructions, statements, and computer programs contain + proprietary information of Nintendo of America Inc. and/or Nintendo + Company Ltd., and are protected by Federal copyright law. They may + not be disclosed to third parties or copied or duplicated in any form, + in whole or in part, without the prior written consent of Nintendo. + + $Rev$ + *---------------------------------------------------------------------------*/ + +#ifndef __LGY_VCW_H__ +#define __LGY_VCW_H__ + +#include +#include + +#define UBI_PLAYERINFO_TUTORIALSEENFLAG_SIZE 3 +#define UBI_PLAYERINFO_NAME_LENGTH 12 +#define UBI_PLAYERINFO_UNLOCK_LETTERMASK_COUNT 40 //1 for each alphabet letter, including accented letters +#define UBI_PLAYERINFO_UNLOCK_LETTERMASK_SIZE (UBI_PLAYERINFO_UNLOCK_LETTERMASK_COUNT/4) +#define UBI_PLAYERINFO_DATAITEM_MAXCOUNT 2900 +#define UBI_PLAYERINFO_DATAITEM_SAVE_MAXSIZE (UBI_PLAYERINFO_DATAITEM_MAXCOUNT/2) +#define UBI_PLAYERINFO_THEMEDATAITEM_MAXCOUNT 160 +#define UBI_PLAYERINFO_THEMEDATAITEM_SAVE_MAXSIZE (UBI_PLAYERINFO_THEMEDATAITEM_MAXCOUNT/2) +// defines for MWC2 +#define UBI_PLAYERINFO_TOP_SCORE_NUM 4 +#define UBI_PLAYERINFO_LAST_GAME_SCORES_NUM 5 +#define UBI_PLAYERINFO_SESSION_STARS 100 // 100 is temp for now +#define UBI_PLAYERINFO_CROSSWORD_COUNT 20 +#define UBI_PLAYERINFO_CROSSWORD_NO_SCORE 0xF +#define UBI_PLAYERINFO_CROSSWORD_MIN_RANK 10 + +#define GRID_SIZE 11 + +enum E_GameIDs +{ + UBI_PLAYERINFO_GAMES_MISMATCH_ID = 0, + UBI_PLAYERINFO_GAMES_NETWORDER_ID, // 1 + UBI_PLAYERINFO_GAMES_QUOTES_ID, // 2 + UBI_PLAYERINFO_GAMES_IDIOMS_ID, // 3 + UBI_PLAYERINFO_GAMES_TRACE_ID, // 4 + UBI_PLAYERINFO_GAMES_RACE_ID, // 5 + UBI_PLAYERINFO_GAMES_WORDFALL_ID, // 6 + UBI_PLAYERINFO_GAMES_LADDER_ID, // 7 + UBI_PLAYERINFO_GAMES_CROSSWORD_ID, // 8 + UBI_PLAYERINFO_GAMES_HIDDENWORD_ID,// 9 + UBI_PLAYERINFO_GAMES_BLUFF_ID, // 10 + UBI_PLAYERINFO_GAMES_HANGMAN_ID, // 11 + UBI_PLAYERINFO_GAMES_MUMBLE_ID, // 12 + UBI_PLAYERINFO_GAMES_UNLOCK_ID, // 13 + UBI_PLAYERINFO_GAMES_NUM, // 14 + UBI_PLAYERINFO_GAMES_NONE_ID +}; + + +typedef struct VCW_SavegameHeader +{ + u32 uniqueIdentifier; + u16 checkSum; + u8 m_status; + u8 padTemp; +}VCW_SavegameHeader; + +struct ScoreInfo +{ + u16 m_score; //represents either stars or points, whatever + u32 m_time; // could be u16 +}; + +struct ActivePassiveData +{ + u16 m_activeWords; + u16 m_passiveWords; +}; + +struct UnlockGameData +{ + u16 m_availableGuesses; + u16 m_currentQuoteIndex; + u8 m_letterStateMask[UBI_PLAYERINFO_UNLOCK_LETTERMASK_SIZE]; + u8 m_pad[2]; //for 32-bit alignment +}; + +struct UnlockData +{ + u8 m_lockState; + u16 m_availableStars; +}; + +struct HiddenWordData +{ + char gridLetters[11][11]; + struct WordData + { + u16 m_posX : 4; + u16 m_posY : 4; + u16 m_length : 4; + u16 m_axis : 4; + }m_words[12]; + +// u8 wordsStartPos[12][2]; +// u8 wordsLenght[12]; +// u8 wordsAxis[12]; + u8 amountOfWords; + u16 flagWordsFound; // The last bit (15) is used to show the presence of a hidden word save game. + u32 timeElapsed; + u8 hintsUsed; +}; + +// Used for dynamically generated crosswords +struct CrosswordData +{ + u8 m_pregenGridId; + + u8 m_helpUsed : 1; + u8 m_saveIsValid : 1; + u8 m_timerIndex : 6; + + // Letter displayed in the grid cell, 1st bit is bool for Cheated ( or Not ). + char m_gridLetters[GRID_SIZE][GRID_SIZE]; // Upper Case Ascii value + + // Add timer data + u32 m_elapsedTime; // In miliseconds +}; + +#define MAX_WORD_COUNT 30 + +struct DynamicCrosswordData +{ + struct Word + { + u16 m_startPosX : 4; + u16 m_startPosY : 4; + u16 m_axis : 4; + u16 m_isSmallword : 4; + + u16 m_wordId; // Id of the word in dictionary + }; + + u8 m_wordCount; + + // Used for generated grids. + u16 m_words[MAX_WORD_COUNT]; +}; + +struct VocabularyData +{ + union StateBucket + { + struct + { + u8 m_state0 : 3; //word state (item 0) + u8 m_seen0 : 1; //seen flag (item 0) + u8 m_state1 : 3; //word state (item 1) + u8 m_seen1 : 1; //seen flag (item 1) + }; + u8 m_wholeBucket; + }StateBucket; + + u8 m_type; + u8 m_playerDl; + u8 m_selectionCount[4]; //LT_BasicCount + u32 m_newActiveCount; + u32 m_allTimeMaxActiveCount; + u8 m_lastAccuracy; + + union StateBucket* m_itemStates; +}; + +struct MainVocabularyData +{ + struct VocabularyData voc; + union StateBucket m_specificItemStates[UBI_PLAYERINFO_DATAITEM_SAVE_MAXSIZE]; //missing 2 for 32-bit alignment +}; + +struct ThemeVocabularyData +{ + struct VocabularyData voc; + union StateBucket m_specificItemStates[UBI_PLAYERINFO_THEMEDATAITEM_SAVE_MAXSIZE]; //missing 2 for 32-bit alignment +}; + + + +typedef struct PlayerData +{ + char m_name[UBI_PLAYERINFO_NAME_LENGTH+1]; // 13 (0x08〜0x0x14) + u8 m_dataState; // 1 (0x15) + u8 m_musicOff; // 1 (0x16) + u8 m_useWarmups; // 1 (0x17) + u8 m_lefty; // 1 (0x18) + u16 m_numSessions; // 2 (0x19〜0x1A) + u16 m_seenRealLifeChallenges; // 2 (0x1B〜0x1C) + f32 m_fReadingTimeMod; // 4 (0x1D〜 + f32 m_fReactionTimeModQuick; // 4 + f32 m_fReactionTimeModSegment; // 4 + + u8 m_sessionStars; // 1 + u8 m_sessionUnlockTry; // 1 + u8 m_personalProgressSeen; // 1 + u32 m_gameTracker; // 4 + u8 m_hsGamesPlayed; // 1 + u8 m_themeViewed; // 1 + u8 m_themeUnlocked; // 1 + u8 m_gameIntroViewed[UBI_PLAYERINFO_GAMES_NUM]; // 14 + u16 m_gameLastSessionPlayed[UBI_PLAYERINFO_GAMES_NUM]; // 28 + struct ScoreInfo m_topScores[UBI_PLAYERINFO_GAMES_NUM][UBI_PLAYERINFO_TOP_SCORE_NUM]; // + struct ScoreInfo m_lastScores[UBI_PLAYERINFO_GAMES_NUM][UBI_PLAYERINFO_LAST_GAME_SCORES_NUM]; + u8 m_crosswordRanks[UBI_PLAYERINFO_CROSSWORD_COUNT]; + + struct ActivePassiveData m_activePassiveTotals; + struct UnlockGameData m_unlockGameData; + struct UnlockData m_unlockData; + + struct MainVocabularyData m_mainVocabularyData; // +// struct ThemeVocabularyData m_themeVocabularyData[DICT_THEME_MAX_COUNT]; // + + struct HiddenWordData m_hiddenWordData; // 153 () + struct CrosswordData m_crossWordData; // 133 () + struct DynamicCrosswordData m_dynamicCrossWordData; // 65 (〜0xF37) +} +PlayerData; + + +typedef struct VCW_BodyForCheck +{ + char m_name[UBI_PLAYERINFO_NAME_LENGTH+1]; // 13 (0x08〜0x0x14) + u8 dummy[0xF38 - 8 - 13]; +} +VCW_BodyForCheck; +//NN_COMPILER_ASSERT(0xF38 == sizeof(VCW_SavegameHeader) + sizeof(VCW_BodyForCheck)); + +/**/ +BOOL checkVCW( TitleProperty* tp, u32 ggid); + + +/* TWLランチャにも移植するので Cでオブジェクトを定義する */ +typedef struct VCW +{ + u8* buf; + u32 ggid; + VCW_SavegameHeader *header; + VCW_BodyForCheck *body; + u16 S_checksum; + u16 B_checksum; +}VCW; + +//static void VCW_Initialize( VCW* vcw, void* buffer, u32 ggid); +//static bool VCW_IsFactoryOrBroken( VCW* vcw); +//static bool VCW_CheckRange( VCW* vcw); +//static u16 VCW_Modify( VCW* vcw); +//static bool VCW_IsModified( VCW* vcw); +//static void VCW_UpdateChecksum( VCW* vcw); +//static u16 VCW_GetChecksum( VCW* vcw); +//static void VCW_Finalize( VCW* vcw); +/* オブジェクトここまで */ + +#endif diff --git a/build/libraries_sysmenu/sysmenu/ARM9/src/title.c b/build/libraries_sysmenu/sysmenu/ARM9/src/title.c index bbb6fe0c..1834a202 100644 --- a/build/libraries_sysmenu/sysmenu/ARM9/src/title.c +++ b/build/libraries_sysmenu/sysmenu/ARM9/src/title.c @@ -24,6 +24,7 @@ #include "internal_api.h" #include "fs_wram.h" #include +#include "SaveDataChecker/lgy_SaveDataChecker.h" // define data----------------------------------------------------------------- @@ -1816,7 +1817,19 @@ static BOOL SYSMi_AuthenticateTitleCore( TitleProperty *pBootTitle) return SYSMi_AuthenticateTWLTitle( pBootTitle ); case LAUNCHER_BOOTTYPE_ROM: OS_TPrintf( "Authenticate :TWL_ROM start.\n" ); - return SYSMi_AuthenticateTWLTitle( pBootTitle ); + if( SYSMi_AuthenticateTWLTitle( pBootTitle )) + { + if( CheckBackupData( pBootTitle)) // バックアップデータのチェック + { + return TRUE; + } + else + { + UTL_SetFatalError(FATAL_ERROR_BACKUP_DATA_CHECK_FAILED); + return FALSE; + } + } + return FALSE; case LAUNCHER_BOOTTYPE_TEMP: OS_TPrintf( "Authenticate :TWL_TEMP start.\n" ); if (!hs->permit_landing_tmp_jump) diff --git a/include/sysmenu/util.h b/include/sysmenu/util.h index a20a68c4..91e15bff 100644 --- a/include/sysmenu/util.h +++ b/include/sysmenu/util.h @@ -85,6 +85,7 @@ typedef enum FatalErrorCode { FATAL_ERROR_TITLEID_COMPARE_FAILED_NTR = 50, // ブート要求されたNTRアプリと実際にロードしたアプリのTitleIDが不一致 FATAL_ERROR_DHT_PHASE3_FAILED = 51, // アプリのNTRホワイトリスト認証失敗(フェーズ3) FATAL_ERROR_DHT_PHASE4_FAILED = 52, // アプリのNTRホワイトリスト認証失敗(フェーズ4) + FATAL_ERROR_BACKUP_DATA_CHECK_FAILED = 53, // バックアップデータの検証失敗 FATAL_ERROR_MAX = 53 }FatalErrorCode;