TwlIPL/build/libraries_sysmenu/dht/common/src/dht.c
mizu 1ef4117338 remmodel for SuperCard DSTWO
git-svn-id: file:///Users/lillianskinner/Downloads/platinum/twl/TwlIPL/branches/20130304_launcher_save_app_tosd_Remodel@3080 b08762b0-b915-fc4b-9d8c-17b2551a87ff
2013-03-04 07:51:48 +00:00

686 lines
21 KiB
C

/*---------------------------------------------------------------------------*
Project: TwlIPL - DHT
File: dht.c
Copyright 2007 Nintendo. 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.
$Date:: $
$Rev$
$Author$
*---------------------------------------------------------------------------*/
//ホワイトチェック時に読んだオーバレイをSDに吐く
#define MGCN_TEST_OVL_OUT 1
//#define MGCN_TEST_OVL_OUT_0_REPEAT 1
//OVTを1ページづつ読む
//#define MGCN_TEST_OVT_1PAGE 1
#include <twl.h>
#include <sysmenu/dht/dht.h>
#include <nitro/os/ARM9/cache.h>
/*
定義すると処理時間を表示する
*/
//#define PRINT_PROFILE
#ifdef PRINT_PROFILE
static int count;
static OSTick profile[0x10];
#define PROFILE_INIT() (count = 0)
#define PROFILE_COUNT() (profile[count++] = OS_GetTick())
#else
#define PROFILE_INIT() ((void)0)
#define PROFILE_COUNT() ((void)0)
#endif
static const u8 g_pubkey_DER[ 0xa2 ] = {
0x30, 0x81, 0x9f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01,
0x05, 0x00, 0x03, 0x81, 0x8d, 0x00, 0x30, 0x81, 0x89, 0x02, 0x81, 0x81, 0x00, 0xc7, 0xf4, 0x1d,
0x27, 0x3f, 0xe8, 0xae, 0x7f, 0x7c, 0xbc, 0x9a, 0xae, 0x09, 0x8d, 0x19, 0x26, 0x2e, 0x90, 0x04,
0x03, 0x13, 0x93, 0xbc, 0xb2, 0xe0, 0x8b, 0x1f, 0x85, 0x48, 0xf5, 0xf6, 0x94, 0x69, 0x3e, 0x05,
0x1b, 0x97, 0x85, 0x44, 0x6d, 0xa3, 0xcd, 0xa8, 0x01, 0xfe, 0xdc, 0x77, 0x5d, 0xd1, 0xb1, 0x36,
0x21, 0xfc, 0x80, 0xe8, 0xa6, 0x0e, 0xde, 0x59, 0x76, 0xca, 0x96, 0xcc, 0x87, 0x4c, 0xc3, 0x90,
0xc6, 0x3b, 0xc8, 0x17, 0x9d, 0x2d, 0xac, 0x45, 0xbc, 0xa7, 0x15, 0xb2, 0xe3, 0xd7, 0x76, 0xfa,
0x09, 0x8c, 0x55, 0x09, 0x22, 0x95, 0x4b, 0xe7, 0xde, 0xc0, 0x82, 0xf2, 0x02, 0x1a, 0x8a, 0x42,
0x38, 0x7f, 0xbb, 0x31, 0xd6, 0xa8, 0x36, 0xdc, 0x8d, 0x2c, 0x42, 0x56, 0x51, 0xc1, 0xa3, 0x30,
0x21, 0x30, 0xef, 0x06, 0x72, 0x0c, 0xa6, 0x55, 0xb7, 0x4f, 0x30, 0x35, 0x1b, 0x02, 0x03, 0x01,
0x00, 0x01
};
static const u8 hmac_key[] = DHT_HMAC_KEY;
static DHTReadFunc imageReadFunc;
static void* imageBuffer;
static u8* fatCache;
static int fatPage;
#ifdef MGCN_TEST_OVL_OUT
static s32 mgcn_ovl_len;
extern u32 mgcn_cfg; //制御データ: title.c/SYSMi_LoadTitleThreadFuncでSDから読まれる
u8* pmgcn_cfg = (u8*)&mgcn_cfg;
//+0
//phase2 OVL out enable
#define mgcn_cfg_flg_out 0x80
//Overlay size read/write enable
#define mgcn_cfg_flg_rf 1
#define mgcn_cfg_flg_wf 2
//+1
//オーバレイサイズで読む上限
#define mgcn_cfg_out_max 0xff
#endif
/*
自家製bsearch
*/
static void *bsearch(const void *key, const void *base,
size_t nmemb, size_t size,
int (*compar)(const void *, const void *))
{
u32 left = 0;
u32 right = nmemb;
if ( compar((u8*)base + size * (right - 1), key) < 0 )
{
return NULL;
}
if ( compar((u8*)base + size * left, key) > 0 )
{
return NULL;
}
while (left <= right)
{
u32 mid = (left + right) >> 1;
const void* data = (u8*)base + size * mid;
int result = compar(data, key);
//OS_TPrintf("left = %d, mid = %d, right = %d\n", left, mid, right);
if ( !result )
{
return (void*)data;
}
if ( result < 0 )
{
left = mid + 1;
}
else
{
right = mid - 1;
}
}
return NULL;
}
static int CompareGameCodeAndVersion(const void* a, const void* b)
{
return MI_CpuComp8(a, b, 5);
}
/*
データベースを読み込む (前準備)
*/
u32 DHT_GetDatabaseLength(const DHTFile* pDHT)
{
if ( pDHT->header.magic_code != DHT_MAGIC_CODE ) // magic codeチェック
{
OS_TPrintf("Invalid magic code (magic=0x%08X).\n", pDHT->header.magic_code);
return 0;
}
return sizeof(DHTHeader) + pDHT->header.nums * sizeof(DHTDatabase);
}
static BOOL DHT_CheckDatabase(const DHTFile* pDHT)
{
SVCSignHeapContext pool;
static u8 heap[4*1024]; // avoid stack overflow
u8 md1[20];
u8 md2[20];
s32 result;
// ファイル署名取り出し
SVC_InitSignHeap(&pool, heap, sizeof(heap));
SVC_DecryptSign(&pool, md1, pDHT->header.sign, &g_pubkey_DER[29]);
// ハッシュ計算
SVC_CalcSHA1(md2, DHT_GET_SIGN_TARGET_ADDR(&pDHT->header), DHT_GET_SIGN_TARGET_SIZE(&pDHT->header));
// 検証
result = SVC_CompareSHA1(md1, md2);
if ( !result )
{
OS_TPrintf("\n");
OS_TPrintfEx("SIGN = % 20B\n", md1);
OS_TPrintfEx("HASH = % 20B\n", md2);
OS_TPrintf("Signature is not valid.\n");
return FALSE;
}
return TRUE;
}
BOOL DHT_PrepareDatabase(DHTFile* pDHT, FSFile* fp)
{
s32 result;
s32 length;
PROFILE_INIT();
if ( fp )
{
// ヘッダ読み込み
PROFILE_COUNT();
result = FS_ReadFile(fp, &pDHT->header, sizeof(DHTHeader));
if ( result != sizeof(DHTHeader) )
{
OS_TPrintf("Cannot read the DHT header (result=%d).\n", result);
return FALSE;
}
// データベース読み込み
PROFILE_COUNT();
length = (s32)DHT_GetDatabaseLength(pDHT) - (s32)sizeof(DHTHeader); // ヘッダを除く
if ( length < 0 )
{
OS_TPrintf("Invalid DHT header.\n");
return FALSE;
}
result = FS_ReadFile(fp, pDHT->database, length);
if ( result != length )
{
OS_TPrintf("Cannot read the DHT database (result=%d).\n", result);
return FALSE;
}
}
else
{
PROFILE_COUNT();
PROFILE_COUNT();
}
// データベースの検証
PROFILE_COUNT();
result = DHT_CheckDatabase(pDHT);
// 結果報告
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("\nDone to prepare the database.\n");
OS_TPrintf("%10d msec for reading header.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
OS_TPrintf("%10d msec for reading database.\n", (int)OS_TicksToMilliSeconds(profile[2]-profile[1]));
OS_TPrintf("%10d msec for comparing hash.\n", (int)OS_TicksToMilliSeconds(profile[3]-profile[2]));
OS_TPrintf("\nTotal: %10d msec.\n", (int)OS_TicksToMilliSeconds(profile[3]-profile[0]));
#endif
return result;
}
/*
ROMヘッダに対応するデータベースを手に入れる
*/
const DHTDatabase* DHT_GetDatabase(const DHTFile* pDHT, const ROM_Header_Short* pROMHeader)
{
u8 data[5];
DHTDatabase* db;
PROFILE_INIT();
// 準備
PROFILE_COUNT();
MI_CpuCopy8( pROMHeader->game_code, data, 4 );
data[4] = pROMHeader->rom_version;
db = (DHTDatabase*)bsearch(data, pDHT->database, pDHT->header.nums, sizeof(DHTDatabase), CompareGameCodeAndVersion);
if ( !db )
{
OS_TPrintf("Cannot find the database.\n");
}
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("%10d msec for searching database.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
#endif
return db;
}
/*
ハッシュ計算 (1)
読み込み済みデータをチェックする
*/
void DHT_CheckHashPhase1Init(SVCHMACSHA1Context* ctx, const ROM_Header_Short* pROMHeader)
{
PROFILE_INIT();
PROFILE_COUNT();
// 準備
SVC_HMACSHA1Init(ctx, hmac_key, sizeof(hmac_key));
// ヘッダ
SVC_HMACSHA1Update(ctx, pROMHeader, DHT_DS_HEADER_SIZE);
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("\n%10d msec for scanning header.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
#endif
}
void DHT_CheckHashPhase1Update(SVCHMACSHA1Context* ctx, const void* ptr, s32 length)
{
PROFILE_INIT();
PROFILE_COUNT();
// ARM9 or ARM7 static
if ( length > 0 )
{
SVC_HMACSHA1Update(ctx, ptr, (u32)length);
}
// 結果報告
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("%10d msec for scanning %d bytes.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]), length);
#endif
}
BOOL DHT_CheckHashPhase1Final(SVCHMACSHA1Context* ctx, const u8 *hash)
{
u8 md[20];
BOOL result;
PROFILE_INIT();
PROFILE_COUNT();
SVC_HMACSHA1GetHash(ctx, md);
result = SVC_CompareSHA1(hash, md);
if ( !result )
{
OS_TPrintf("\n");
OS_TPrintfEx("DB = % 20B\n", hash);
OS_TPrintfEx("HASH = % 20B\n", md);
OS_TPrintf("%s: hash[0] is not valid.\n", __func__);
}
// 結果報告
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("%10d msec for comparing hash.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
#endif
return result;
}
BOOL DHT_CheckHashPhase1(const u8* hash, const ROM_Header_Short* pROMHeader, const void* pARM9, const void* pARM7)
{
SVCHMACSHA1Context ctx;
BOOL result;
PROFILE_INIT();
// 準備&ヘッダ
PROFILE_COUNT();
DHT_CheckHashPhase1Init(&ctx, pROMHeader);
// ARM9 Static
DHT_CheckHashPhase1Update(&ctx, pARM9, (s32)pROMHeader->main_size);
// ARM7 Static
DHT_CheckHashPhase1Update(&ctx, pARM7, (s32)pROMHeader->sub_size);
// 検証
result = DHT_CheckHashPhase1Final(&ctx, hash);
// 結果報告
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("\nDone to check the hash (phase 1).\n");
OS_TPrintf("\nTotal: %10d msec.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
#endif
return result;
}
/*
ハッシュ計算 (2)
対象領域の読み込みとチェックを行う
*/
static BOOL ImageHMACSHA1Update(SVCHMACSHA1Context* ctx, s32 offset, s32 length, void* arg)
{
if ( !imageBuffer || !imageReadFunc )
{
return FALSE;
}
#ifdef MGCN_TEST_OVL_OUT
{
s32 len;
if ((pmgcn_cfg[0] & mgcn_cfg_flg_rf) && mgcn_ovl_len)len = mgcn_ovl_len;//オーバレイサイズ: 512Kまで
else len = length;
if (len > 512*1024) len = 512*1024;//Hash計算時のワーク知らんので、ギリまで使うと不味いかも
//DC_InvalidateRange( (void*)imageBuffer,length );// マジコン調査
if ( !imageReadFunc(imageBuffer, offset, len, arg) )
{
return FALSE;
}
}
#else
if ( !imageReadFunc(imageBuffer, offset, length, arg) )
{
return FALSE;
}
#endif
SVC_HMACSHA1Update(ctx, imageBuffer, (u32)length);
return TRUE;
}
static BOOL GetOverlayInfo(int no, int fat_offset, int* pOffset, int* pLength, DHTReadFunc func, void* arg)
{
ROM_FAT *fat;
int page = (fat_offset + no * (s32)sizeof(ROM_FAT)) / DHT_FAT_PAGE_SIZE;
if ( !fatCache )
{
return FALSE;
}
if ( fatPage != page )
{
#ifdef MGCN_TEST_OVT_1PAGE
//毎回1ページ読み .. マジコン調査
if ( !func(fatCache, page * DHT_FAT_PAGE_SIZE, DHT_FAT_PAGE_SIZE, arg))
{
return FALSE;
}
#else
if ( fatPage + 1 == page ) // 1ページはキャッシュ済み
{
MI_CpuCopy8( &fatCache[DHT_FAT_PAGE_SIZE], &fatCache[0], DHT_FAT_PAGE_SIZE );
if ( !func(&fatCache[DHT_FAT_PAGE_SIZE], (page+1) * DHT_FAT_PAGE_SIZE, DHT_FAT_PAGE_SIZE, arg) )
{
return FALSE;
}
}
else // 通常は2ページ読み
{
if ( !func(fatCache, page * DHT_FAT_PAGE_SIZE, DHT_FAT_CACHE_SIZE, arg) )
{
return FALSE;
}
}
#endif
fatPage = page;
}
fat = (ROM_FAT*)(fatCache + fat_offset + no * sizeof(ROM_FAT) - page * DHT_FAT_PAGE_SIZE);
if ( pOffset )
{
*pOffset = (s32)fat->top.offset;
}
if ( pLength )
{
*pLength = (s32)(fat->bottom.offset - fat->top.offset);
}
return TRUE;
}
BOOL DHT_CheckHashPhase2(const u8* hash, const ROM_Header_Short* pROMHeader, DHTPhase2Work* work, DHTReadFunc func, void* arg)
{
imageBuffer = work->buffer;
imageReadFunc = func;
return DHT_CheckHashPhase2Ex(hash, pROMHeader, (DHTPhase2ExWork*)work->fatCache, func, ImageHMACSHA1Update, arg);
}
BOOL DHT_CheckHashPhase2Ex(const u8* hash, const ROM_Header_Short* pROMHeader, DHTPhase2ExWork* work, DHTReadFunc func, DHTReadFuncEx funcEx, void* arg)
{
int overlay_nums = (int)(pROMHeader->main_ovt_size / sizeof(ROM_OVT));
u8 md[20];
#ifdef MGCN_TEST_OVL_OUT
int ovl_len;
#endif
PROFILE_INIT();
if ( overlay_nums )
{
SVCHMACSHA1Context ctx;
int total_sectors;
int i;
if ( !func || !funcEx || !work )
{
return FALSE;
}
fatCache = work->fatCache;
fatPage = -2; // default value = out of range
// 準備
PROFILE_COUNT();
SVC_HMACSHA1Init(&ctx, hmac_key, sizeof(hmac_key));
#ifdef MGCN_TEST_OVL_OUT
//funcEx(=ImageHMACSHA1Update)で オーバレイ以外は引数サイズでリード
mgcn_ovl_len = 0;
#endif
// OVT
PROFILE_COUNT();
if ( !funcEx(&ctx, (s32)pROMHeader->main_ovt_offset, (s32)pROMHeader->main_ovt_size, arg) )
{
OS_TPrintf("Cannot calc HMAC-SHA1 for OVT.\n");
return FALSE;
}
// FAT
PROFILE_COUNT();
if ( !funcEx(&ctx, (s32)pROMHeader->fat_offset, overlay_nums * (s32)sizeof(ROM_FAT), arg) )
{
OS_TPrintf("Cannot calc HMAC-SHA1 for %d of FAT.\n", overlay_nums);
return FALSE;
}
/*
#ifdef MGCN_TEST_OVL_OUT
if (pmgcn_cfg[0] & mgcn_cfg_flg_out)
{
FSFile dest;
char filename[64];
STD_TSNPrintf( filename, 64, "sdmc:/OVLFAT_%d_%8x.dat",overlay_nums,(s32)pROMHeader->fat_offset);
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
FS_WriteFile( &dest, imageBuffer, overlay_nums * (s32)sizeof(ROM_FAT));
FS_CloseFile( &dest );
}
#endif
*/
// 各オーバーレイ
PROFILE_COUNT();
total_sectors = 0;
for (i = 0; i < overlay_nums; i++)
{
int max_sectors = (DHT_OVERLAY_MAX/512 - total_sectors) / (overlay_nums - i);
int offset;
int length;
if ( !GetOverlayInfo(i, (s32)pROMHeader->fat_offset, &offset, &length, func, arg) )
{
OS_TPrintf("Cannot get %d of overlay info.\n", i);
return FALSE;
}
#ifdef MGCN_TEST_OVL_OUT_0_REPEAT
if(i ==0)
{//繰り返しリード
FSFile dest;
SVCHMACSHA1Context ctx2;
char filename[64];
int j,len = (length + 511) / 512; // bytes -> sectors
if ( len > max_sectors ) len = max_sectors;
for ( j =0;j<2;j++){
STD_TSNPrintf( filename, 64, "sdmc:/OVL0_%d.dat",j);
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
DC_InvalidateRange( (void*)imageBuffer,length );
imageReadFunc(imageBuffer, offset,length,arg);
FS_WriteFile( &dest, imageBuffer,length );
FS_CloseFile( &dest );
//Hash後の状態
SVC_HMACSHA1Update(&ctx2, imageBuffer, (u32)len);
STD_TSNPrintf( filename, 64, "sdmc:/OVL0_%d_hs.dat",j);
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
FS_WriteFile( &dest, imageBuffer,length );
FS_CloseFile( &dest );
}
}
#endif
#ifdef MGCN_TEST_OVL_OUT
//オーバレイサイズを保存
ovl_len = length;
#endif
length = (length + 511) / 512; // bytes -> sectors
#ifdef MGCN_TEST_OVL_OUT
// Hash計算時にリードがチェックサイズ以下にならないようする
mgcn_ovl_len = length * 512;
#endif
if ( length > max_sectors )
{
length = max_sectors;
}
#ifdef MGCN_TEST_OVL_OUT
// 指定以上はチェックサイズに合わせる
if (i > pmgcn_cfg[1]) mgcn_ovl_len = length * 512;
#endif
if ( length < 0 || offset < sizeof(ROM_Header) )
{
return FALSE;
}
if ( !funcEx(&ctx, offset, length * 512, arg) )
{
OS_TPrintf("Cannot calc HMAC-SHA1 for %d of overlay.\n", i);
return FALSE;
}
#ifdef MGCN_TEST_OVL_OUT
//----------- マジコン調査
//オーバレイを吐く
if (pmgcn_cfg[0] & mgcn_cfg_flg_out)
{
FSFile dest;
char filename[64];
/*
int sz,len = length;
STD_TSNPrintf( filename, 64, "sdmc:/OVL%d_%8x_%8x.dat",i,offset,length );
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
while(len){
if (remain < DHT_OVERLAY_MAX )sz = len;
else sz = DHT_OVERLAY_MAX;
DC_InvalidateRange( (void*)imageBuffer,sz );
imageReadFunc(imageBuffer, offset, sz, arg);
FS_WriteFile( &dest, imageBuffer,sz );
len-=sz;
}
FS_CloseFile( &dest );
*/
if((pmgcn_cfg[0] & mgcn_cfg_flg_wf ) && (i <= pmgcn_cfg[1]))
{
STD_TSNPrintf( filename, 64, "sdmc:/OVL%d_%8x_%8x.dat",i,offset,ovl_len );
}else{
ovl_len = length*512;
STD_TSNPrintf( filename, 64, "sdmc:/CHK%d_%8x_%8x.dat",i,offset,ovl_len);
}
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
FS_WriteFile( &dest,imageBuffer,ovl_len );
FS_CloseFile( &dest );
}
#endif
/* -- origin
length = (length + 511) / 512; // bytes -> sectors
if ( length > max_sectors )
{
length = max_sectors;
}
if ( length < 0 || offset < sizeof(ROM_Header) )
{
OS_TPrintf("Broken FAT for %d of overlay.\n", i);
return FALSE;
}
if ( !funcEx(&ctx, offset, length * 512, arg) )
{
OS_TPrintf("Cannot calc HMAC-SHA1 for %d of overlay.\n", i);
return FALSE;
}
*/
total_sectors += length;
}
// 検証
PROFILE_COUNT();
SVC_HMACSHA1GetHash(&ctx, md);
}
else
{
PROFILE_COUNT();
PROFILE_COUNT();
PROFILE_COUNT();
PROFILE_COUNT();
PROFILE_COUNT();
MI_CpuClear8(md, sizeof(md));
}
if ( !SVC_CompareSHA1(md, hash) )
{
#ifdef MGCN_TEST_OVL_OUT
if (pmgcn_cfg[0] & mgcn_cfg_flg_out)
{
FSFile dest;
char filename[64];
STD_TSNPrintf( filename, 64, "sdmc:/hasherr");
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
FS_WriteFile( &dest,&hash,20 );
FS_WriteFile( &dest,&md,20 );
FS_CloseFile( &dest );
}
#endif
OS_TPrintf("\n");
OS_TPrintfEx("DB = % 20B\n", hash);
OS_TPrintfEx("HASH = % 20B\n", md);
OS_TPrintf("%s: hash[1] is not valid.\n", __func__);
return FALSE;
}
// 結果報告
#ifdef PRINT_PROFILE
PROFILE_COUNT();
OS_TPrintf("\nDone to check the hash (phase 2).\n");
OS_TPrintf("%10d msec for preparing hash.\n", (int)OS_TicksToMilliSeconds(profile[1]-profile[0]));
OS_TPrintf("%10d msec for scanning OVT.\n", (int)OS_TicksToMilliSeconds(profile[2]-profile[1]));
OS_TPrintf("%10d msec for scanning FAT.\n", (int)OS_TicksToMilliSeconds(profile[3]-profile[2]));
OS_TPrintf("%10d msec for scanning every overlays.\n", (int)OS_TicksToMilliSeconds(profile[4]-profile[3]));
OS_TPrintf("%10d msec for comparing hash.\n", (int)OS_TicksToMilliSeconds(profile[5]-profile[4]));
OS_TPrintf("\nTotal: %10d msec.\n", (int)OS_TicksToMilliSeconds(profile[5]-profile[0]));
#endif
#ifdef MGCN_TEST_OVL_OUT
if (pmgcn_cfg[0] & mgcn_cfg_flg_out)
{
FSFile dest;
char filename[64];
STD_TSNPrintf( filename, 64, "sdmc:/complate");
FS_InitFile( &dest );
(void)FS_CreateFile(filename, FS_PERMIT_W | FS_PERMIT_R);
FS_OpenFileEx( &dest, filename, FS_FILEMODE_W );
FS_CloseFile( &dest );
}
#endif
return TRUE;
}
void DHT_CheckHashPhase2ExUpdate(SVCHMACSHA1Context* ctx, const void* ptr, s32 length)
{
// ARM9 or ARM7 static
if ( length > 0 )
{
SVC_HMACSHA1Update(ctx, ptr, (u32)length);
}
}