ctr_Repair/trunk/SkipFirstLaunch/idb/src/IDBi.cpp
N2614 0d5bf9a14e UIG製のコードを追加
git-svn-id: file:///Volumes/Transfer/gigaleak_20231201/2020-05-23%20-%20ctr.7z%20+%20svn_v1.068.zip/ctr/svn/ctr_Repair@651 385bec56-5757-e545-9c3a-d8741f4650f1
2012-02-22 02:25:50 +00:00

927 lines
29 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// IDBi.c : コンソール アプリケーションのエントリ ポイントを定義します。
//
#include "IDBi.h"
//#define DEBUG_SD 1 // この定義を有効にするとSDカード版になります(デバッグ用)
#include <nn/assert.h>
#include <string.h>
#include <nn/fs/fs_Api.h>
#include <nn/fs/fs_ApiSharedExtSaveData.h>
#include <nn/fs/fs_Parameters.h>
#include <nn/fs/CTR/MPCore/fs_FileSystemBase.h>
#include <nn/fs/fs_FileSystemBase.h>
#include <nn/fs/fs_FileSystem.h>
#include <nn/fs/fs_FileOutputStream.h>
#include <nn/fs/fs_FileInputStream.h>
#include <nn/fnd/fnd_DateTime.h>
#include <nn/fnd/fnd_TimeSpan.h>
#include <nn/CTR/CTR_ProgramId.h>
static bool sReadOnly = false; // ReadOnlyモードのときはtrue
static IDBi_TableFile * restrict spTable = NULL;
static IDBi_IconData * restrict spIconBuf = NULL; // データ読み書き用の一時的なアイコンバッファ。
static IDBi_IconData * restrict spTmpIconBuf = NULL; // 交換用の一時的なアイコンバッファ
// 使用頻度が高いので、未使用でもアロケートは常にしておく
static IDBi_DataFile * restrict spDataBuf = NULL; // オンメモリバージョンのバッファ、こちらを使用しているときは
// spIconBufはNULLになる。
static const char * const sArchiveName = "IDB";
#ifndef DEBUG_SD
static const bit32 sShardExtID = 0xF000000B; // アイコンデータベースID
#endif
static const bit64 sInvalidProgramID = 0xffffffffffffffff;
static const wchar_t * const sTableFileName= L"IDB:/idbt.dat";
static const wchar_t * const sDataFileName = L"IDB:/idb.dat";
// ファイル外部非公開関数宣言
static void IDBi_InitTable( void );
static IDBi_Index IDBi_Append( const IDB_Icon * restrict inIcon, IDBi_Division division );
static IDBi_Index IDBi_AppendCore( const IDB_Icon * restrict inIcon, IDBi_Division division );
static void IDBi_SetFromIndex( const IDB_Icon * restrict inIcon, IDBi_Index index );
static IDBi_Index IDBi_ExpandOwner( void );
static IDBi_Index IDBi_MoveToOwner( IDBi_Index index );
static IDBi_Index IDBi_UpdateLastAccess( const IDB_Key * restrict inKey ); // アクセス日時を上書き
static IDBi_Index IDBi_GetOldestIndex(IDBi_Division division );
static IDBi_Time IDBi_GetCurrentTime( void );
static IDBi_Division IDBi_GetDivision( const IDB_Key * restrict key );
static IDBi_Division IDBi_GetDivisionFromIndex( IDBi_Index index );
static IDBi_Index IDBi_GetEmptyIndex(IDBi_Division division);
static BOOL IDBi_IsOwnerDivisionFull( void );
static BOOL IDBi_IsFriendDivisionFull( void );
static void IDBi_Move( IDBi_Index dest, IDBi_Index src );
static void IDBi_Exchange( IDBi_Index dest, IDBi_Index src );
static void IDBi_ReadData(IDBi_Index index);
static void IDBi_WriteData(IDBi_Index index);
/////////////////////////////////////////////////////////////////////////////////
// 関数定義
/////////////////////////////////////////////////////////////////////////////////
// 初期化
void IDBi_Initialize( u8 * buffer, bool onMemory, bool readOnly )
{
NN_NULL_ASSERT( buffer );
NN_ASSERT( spTable == NULL );
NN_ASSERT( spIconBuf == NULL );
NN_ASSERT( spTmpIconBuf == NULL );
if ( buffer==NULL ) return;
if ( spTable != NULL ) return;
if ( spIconBuf != NULL ) return;
if( spTmpIconBuf != NULL ) return;
sReadOnly = readOnly;
spTable = (IDBi_TableFile *)buffer; // reinterpret_cast
spTmpIconBuf = (IDBi_IconData *)(buffer+sizeof(IDBi_TableFile)); // reinterpret_cast
if ( onMemory )
{
spDataBuf = (IDBi_DataFile *)(buffer+sizeof(IDBi_TableFile)+sizeof(IDBi_IconData) ); // reinterpret_cast
spIconBuf = NULL;
}else{
spIconBuf = (IDBi_IconData *)(buffer+sizeof(IDBi_TableFile)+sizeof(IDBi_IconData) ); // reinterpret_cast
spDataBuf = NULL;
}
nn::Result res;
#ifndef DEBUG_SD
NN_LOG("IDB::MountSharedExtSaveData start\n");
res = nn::fs::MountSharedExtSaveData( sArchiveName, sShardExtID );
NN_LOG("IDB::MountSharedExtSaveData done\n");
NN_ASSERT_RESULT( res );
#else
NN_LOG("IDB::MountSdmc start\n");
nn::fs::MountSdmc( sArchiveName );
NN_LOG("IDB::MountSdmc done\n");
#endif
// ファイルが存在しなかった場合は作成
nn::fs::FileOutputStream os;
bool fileCreated = false;
res = os.TryInitialize( sDataFileName, false );
if ( res.IsFailure() )
{
NN_LOG( "res = os.TryInitialize( sDataFileName, false ); failure\n" );
fileCreated = true;
NN_LOG("IDB::CreateFile start\n");
res = nn::fs::TryCreateFile( sDataFileName, sizeof(IDBi_DataFile) );
NN_LOG("IDB::CreateFile done\n");
if ( res.IsSuccess() )
{
res = os.TryInitialize( sDataFileName, false );
NN_ASSERT_RESULT( res );
}
}
if ( res.IsSuccess() )
{
s64 size = 0;
res = os.TryGetSize(&size);
if ( res.IsFailure() || fileCreated )
{
if ( spDataBuf )
{
NN_LOG("IDB::Format start (on memory)\n");
memset(spDataBuf, 0x0, sizeof(IDBi_DataFile ) );
s32 len;
res = os.TryWrite( &len, spDataBuf, sizeof(IDBi_DataFile ), false );
NN_LOG("IDB::Format done (on memory)\n");
}else{ // オンメモリでは無い場合のフォーマットはかなり遅い
NN_LOG("IDB::Format start (small memory) %d\n", IDBi_GetBufferSize(false) );
const s32 max = IDB_ICON_DATA_MAX * sizeof(IDBi_IconData );
const s32 chunk = IDBi_GetBufferSize(false);
s32 current = 0;
s32 len;
int i=0;
memset(buffer, 0x0, chunk );
while( current+chunk<max )
{
res = os.TryWrite( &len, buffer, chunk, false );
if ( (i++)%16==0 ) NN_LOG(".");
current+=chunk;
}
res = os.TryWrite( &len, buffer, max-current, false );
/* 本当にやりたいのはこっちです
// memset(spIconBuf, 0x0, sizeof(IDBi_IconData ) );
for ( int i=0; i<IDB_ICON_DATA_MAX; i++ )
{
s32 len;
res = os.TryWrite( &len, spIconBuf, sizeof(IDBi_IconData ), false );
if ( i%16==0 ) NN_LOG(".");
}
*/
NN_LOG("\nIDB::Format done (on memory)\n");
}
}
res = os.TryFlush();
os.Finalize();
}else{ // ここに来るのはありえないはず。あるとすればアプレットとアプリの競合(どちらかのバグ)
NN_ASSERT_RESULT( res );
}
IDBi_Load();
}
u8 * IDBi_Finalize( void )
{
NN_NULL_ASSERT( spTable );
NN_ASSERT( spIconBuf!=NULL || spDataBuf!=NULL );
if ( spTable==NULL ) return NULL;
if ( spIconBuf==NULL && spDataBuf==NULL ) return NULL;
if ( spTmpIconBuf == NULL ) return NULL;
u8 * ret = (u8 *)spTable; // reinterpret_cast
if ( !sReadOnly )
{
IDBi_Save();
}
nn::fs::Unmount(sArchiveName);
spTable = NULL;
spIconBuf = NULL;
spTmpIconBuf = NULL;
spDataBuf = NULL;
return ret;
}
u32 IDBi_GetBufferSize( bool onMemory )
{
if ( onMemory )
{
return sizeof( IDBi_TableFile ) + sizeof( IDBi_DataFile ) + sizeof( IDBi_IconData ); // テーブル + 全アイコンデータ + 交換用のアイコンバッファ
}else{
return sizeof( IDBi_TableFile ) + sizeof( IDBi_IconData ) + sizeof( IDBi_IconData ); // テーブル + アイコン1つ分の編集用バッファ + 交換用のアイコンバッファ
}
}
BOOL IDBi_IsInitialized( void )
{
return spTable!=NULL;
}
// 指定したアイコンを指定した場所に更新する、データが存在しない場合は追加する)
IDBi_Index IDBi_Update( IDB_Icon * inIcon, IDBi_Division division )
{
NN_NULL_ASSERT( spTable ); // Initialize済
NN_NULL_ASSERT( inIcon );
NN_ASSERT( !sReadOnly ); // ReadOnlyモードのときは呼べない
NN_ASSERT( inIcon->key.programID != 0 ); // 0は登録できません。
NN_ASSERT( inIcon->key.programID != nn::CTR::INVALID_PROGRAM_ID ); // nn::CTR::INVALID_PROGRAM_IDは登録できません。
if ( spTable==NULL ) return IDBi_INDEX_INVALID;
if ( inIcon==NULL ) return IDBi_INDEX_INVALID;
if ( sReadOnly ) return IDBi_INDEX_INVALID;
if ( inIcon->key.programID == 0 ) return IDBi_INDEX_INVALID; // 無効なIDが指定された時は何もしない
if ( inIcon->key.programID == nn::CTR::INVALID_PROGRAM_ID ) return IDBi_INDEX_INVALID;
if ( nn::CTR::IsTwlSystemApp( inIcon->key.programID ) ) return IDBi_INDEX_INVALID; // TwlSystemアプリの場合は登録しない
// アプレットのプログラムIDに対して下位ビットを無視するようにする
IDBi_MaskProgramID( &inIcon->key.programID );
IDBi_Division origDivision = IDBi_GetDivision( &inIcon->key );
NN_ASSERT( division!=IDBi_DIVISION_NONE ); // divisionが有効な値である
if ( division==IDBi_DIVISION_NONE ) return IDBi_INDEX_INVALID;
NN_LOG( "update time %d\n" , IDBi_GetCurrentTime() );
IDBi_Index ret = IDBi_INDEX_INVALID;
switch ( origDivision )
{
case IDBi_DIVISION_OWNER:
ret = IDBi_UpdateLastAccess( &inIcon->key );
break;
case IDBi_DIVISION_FRIEND:
if ( division== IDBi_DIVISION_FRIEND )
{
ret = IDBi_UpdateLastAccess( &inIcon->key );
}
else
{
ret = IDBi_MoveToOwner( IDBi_GetIndex( &inIcon->key ) );
}
break;
case IDBi_DIVISION_NONE:
ret = IDBi_Append( inIcon, division );
break;
}
return ret;
}
// データを取得する
// データが存在しなかった場合はFALSEを返す
BOOL IDBi_Select( IDB_Icon * outIcon, IDB_Key * key, BOOL updateLastAccess )
{
NN_NULL_ASSERT( spTable ); // Initialize済
NN_NULL_ASSERT( outIcon );
NN_NULL_ASSERT( key );
if ( spTable==NULL ) return FALSE;
if ( outIcon==NULL ) return FALSE;
if ( key ==NULL ) return FALSE;
if( key->programID == sInvalidProgramID ){ return FALSE; }
// アプレットのプログラムIDに対して下位ビットを無視するようにする
IDBi_MaskProgramID( &key->programID );
IDBi_Index index = IDBi_GetIndex( key );
if ( index == IDBi_INDEX_INVALID ) return FALSE;
return IDBi_SelectFromIndex( outIcon, index, updateLastAccess );
}
// データを取得する
// データが存在しなかった場合はFALSEを返す
BOOL IDBi_SelectFromIndex( IDB_Icon * outIcon, IDBi_Index index, BOOL updateLastAccess )
{
NN_NULL_ASSERT( spTable ); // Initialize済
NN_NULL_ASSERT( outIcon );
NN_ASSERT( 0<=index && index<IDB_ICON_DATA_MAX ); // indexは有効な値である
if ( spTable==NULL ) return FALSE;
if ( outIcon==NULL ) return FALSE;
if (!( 0<=index && index<IDB_ICON_DATA_MAX )) return FALSE;
if ( index == IDBi_INDEX_INVALID ) return FALSE;
IDBi_IconTable * const restrict pTable = &spTable->iconTable[index];
IDBi_IconData * restrict pData;
if ( spDataBuf )
{
pData = &spDataBuf->iconData[index];
}else{
IDBi_ReadData( index );
pData = spIconBuf;
}
if ( pTable->programID == sInvalidProgramID ) return FALSE;
if ( updateLastAccess )
{
pTable->lastAccess = IDBi_GetCurrentTime();
}
outIcon->key.programID = pTable->programID;
outIcon->key.remasterVersion = pTable->remasterVersion;
memset( outIcon->key.padding, 0, sizeof(outIcon->key.padding) );
memcpy( outIcon->info, pData->info, sizeof( pData->info ) );
memcpy( outIcon->rating, pData->rating, sizeof( pData->rating ) );
outIcon->region = (nn::CTR::SystemMenuDataRegion)pData->region;
outIcon->format = (IDB_IconFormat)pData->format;
// フォーマットが違っていても(TWL等)、あまりの部分は0で埋めているので問題ない
memcpy( outIcon->data, &pData->data, sizeof( IDB_IconData ) );
return TRUE;
}
// 指定したキーのデータが存在するかどうかを返す
// データが存在したらtrue、存在しない場合はfalseを返す
BOOL IDBi_IsContains( IDB_Key *key )
{
NN_NULL_ASSERT( spTable ); // Initialize済
if ( spTable==NULL ) return FALSE;
if ( key->programID == sInvalidProgramID ){ return FALSE; }
// アプレットのプログラムIDに対して下位ビットを無視するようにする
IDBi_MaskProgramID( &key->programID );
return IDBi_GetIndex( key ) != IDBi_INDEX_INVALID;
}
// データが存在する区分を返す
// 存在しなかった場合はIDBi_DIVISION_NONEを返す
IDBi_Division IDBi_GetDivision( const IDB_Key * restrict key )
{
NN_NULL_ASSERT( key );
if ( key==NULL ) return IDBi_DIVISION_NONE;
IDBi_Index index = IDBi_GetIndex( key );
return IDBi_GetDivisionFromIndex( index );
}
// データが存在する区分を返す
// 存在しなかった場合はIDBi_DIVISION_NONEを返す
IDBi_Division IDBi_GetDivisionFromIndex( IDBi_Index index )
{
if ( index == IDBi_INDEX_INVALID ) return IDBi_DIVISION_NONE;
NN_ASSERT( 0<=index && index<IDB_ICON_DATA_MAX ); // indexは有効な値である
if (!( 0<=index && index<IDB_ICON_DATA_MAX )) return IDBi_DIVISION_NONE;
if ( index<spTable->ownerDataCount ) return IDBi_DIVISION_OWNER;
else return IDBi_DIVISION_FRIEND;
}
// 空のインデックスを返す
// 空きが存在しなかった時はIDBi_INDEX_INVALIDを返す
IDBi_Index IDBi_GetEmptyIndex(IDBi_Division division)
{
int from = division == IDBi_DIVISION_OWNER ? 0 : spTable->ownerDataCount;
int to = division == IDBi_DIVISION_OWNER ? spTable->ownerDataCount : IDB_ICON_DATA_MAX;
int i;
for ( i=from; i<to; i++ )
{
if ( spTable->iconTable[i].programID == sInvalidProgramID ) return (IDBi_Index)i;
}
return IDBi_INDEX_INVALID;
}
// オーナー枠が満タンならTrueを返す
// (拡張すれば空きが存在する可能性もある)
BOOL IDBi_IsOwnerDivisionFull( void )
{
int i;
for ( i=0; i<spTable->ownerDataCount; i++ )
{
if ( spTable->iconTable[i].programID == sInvalidProgramID ) return FALSE;
}
return TRUE;
}
// フレンド枠が満タンならTrueを返す
BOOL IDBi_IsFriendDivisionFull( void )
{
int i;
for ( i=IDB_ICON_DATA_MAX-1; i>=spTable->ownerDataCount; i-- )
{
if ( spTable->iconTable[i].programID == sInvalidProgramID ) return FALSE;
}
return TRUE;
}
// 内部データを移動する
void IDBi_Move( IDBi_Index dest, IDBi_Index src )
{
NN_ASSERT( 0<=dest && dest<IDB_ICON_DATA_MAX ); // indexは有効な値である
NN_ASSERT( 0<= src && src<IDB_ICON_DATA_MAX ); // indexは有効な値である
if (!( 0<=dest && dest<IDB_ICON_DATA_MAX )) return;
if (!( 0<= src && src<IDB_ICON_DATA_MAX )) return;
if ( dest == IDBi_INDEX_INVALID ) return;
if ( src == IDBi_INDEX_INVALID ) return;
spTable->iconTable[dest] = spTable->iconTable[src];
//spTable->iconTable[dest].lastAccess = IDBi_GetCurrentTime();
if( spDataBuf == NULL )
{
IDBi_ReadData( src );
}else{
memcpy( &spDataBuf->iconData[ dest ], &spDataBuf->iconData[ src ], sizeof( IDBi_IconData ) );
}
IDBi_WriteData( dest );
spTable->iconTable[src].programID = sInvalidProgramID;
}
// 内部データを交換する
void IDBi_Exchange( IDBi_Index dest, IDBi_Index src )
{
NN_ASSERT( 0<=dest && dest<IDB_ICON_DATA_MAX ); // indexは有効な値である
NN_ASSERT( 0<= src && src<IDB_ICON_DATA_MAX ); // indexは有効な値である
NN_ASSERT( spTmpIconBuf != NULL );
if (!( 0<=dest && dest<IDB_ICON_DATA_MAX )) return;
if (!( 0<= src && src<IDB_ICON_DATA_MAX )) return;
if( spTmpIconBuf == NULL ) return;
if ( dest == IDBi_INDEX_INVALID ) return;
if ( src == IDBi_INDEX_INVALID ) return;
IDBi_IconTable tmpTable;
tmpTable = spTable->iconTable[dest];
spTable->iconTable[dest] = spTable->iconTable[src];
spTable->iconTable[src] = tmpTable;
spTable->iconTable[dest].lastAccess = IDBi_GetCurrentTime();
if( spDataBuf != NULL )
{
memcpy( spTmpIconBuf, &spDataBuf->iconData[ dest ], sizeof( IDBi_IconData ) );
memcpy( &spDataBuf->iconData[ dest ], &spDataBuf->iconData[ src ], sizeof( IDBi_IconData ) );
memcpy( &spDataBuf->iconData[ src ], spTmpIconBuf, sizeof( IDBi_IconData ) );
}
else if( spIconBuf != NULL )
{
IDBi_ReadData( dest );
memcpy( spTmpIconBuf, spIconBuf, sizeof( IDBi_IconData ) );
// src を dest へ
IDBi_ReadData( src );
IDBi_WriteData( dest );
// dest を src へ
memcpy( spIconBuf, spTmpIconBuf, sizeof( IDBi_IconData ) );
IDBi_WriteData( src );
}
}
///////////////////////////////////////////////////////////////////
// ファイル外部非公開関数
///////////////////////////////////////////////////////////////////
// テーブルのデータを初期化する
void IDBi_InitTable( void )
{
int i;
spTable->ownerDataCount = 0;
memset( spTable->padding, 0, sizeof(spTable->padding) );
for ( i=0; i<IDB_ICON_DATA_MAX; i++ )
{
IDBi_IconTable * restrict table = &spTable->iconTable[i];
table->programID = sInvalidProgramID; // 無効ID
table->lastAccess = 0; // IDBi_Time lastAccess : 最終アクセス日時
table->remasterVersion = 0; // ソフトバージョン
table->reserved0 = 0; // 予約(必ず0で埋める)
table->reserved1 = 0; // 予約(必ず0で埋める)
}
IDBi_Save();
}
// データを追加する場合の処理
// 可能であるのならオーナー枠の拡張などを行う
IDBi_Index IDBi_Append( const IDB_Icon * restrict inIcon, IDBi_Division division )
{
NN_NULL_ASSERT( inIcon );
if ( inIcon==NULL ) return IDBi_INDEX_INVALID;
switch ( division )
{
case IDBi_DIVISION_OWNER:
if ( !IDBi_IsOwnerDivisionFull() )
{
}else if ( spTable->ownerDataCount<IDB_OWNER_ICON_DATA_MAX )
{
IDBi_ExpandOwner();
}
break;
case IDBi_DIVISION_FRIEND:
break;
}
return IDBi_AppendCore( inIcon, division );
}
// オーナー枠にデータを追加する
// データが満タンの時は最も古いデータを削除する
// divisionがIDBi_DIVISION_FRIENDの時、
// moveToFriendによって追い出されたデータをフレンド枠に移動するか削除するかを指定できる
// それ以外のときはFALSE
IDBi_Index IDBi_AppendCore( const IDB_Icon * restrict inIcon, IDBi_Division division )
{
IDBi_Index index = IDBi_GetEmptyIndex(division);
if ( index!=IDBi_INDEX_INVALID )
{ // 空きがあったとき
IDBi_SetFromIndex( inIcon, index );
return index;
}else{ // 空きが無かった時
index = IDBi_GetOldestIndex( division );
if ( index==IDBi_INDEX_INVALID ) return index; // ここでIDBi_GetOldestIndexが返ることは無いはずだが、念のため
if ( division==IDBi_DIVISION_OWNER && !IDBi_IsFriendDivisionFull() )
{
// 追い出されたデータをフレンド枠に移動する
IDBi_Index fIndex = IDBi_GetEmptyIndex( IDBi_DIVISION_FRIEND );
IDBi_Move( fIndex, index );
IDBi_SetFromIndex( inIcon, index );
}else{
IDBi_SetFromIndex( inIcon, index );
}
return index;
}
}
// データを指定したindexに追加する
void IDBi_SetFromIndex( const IDB_Icon * restrict inIcon, IDBi_Index index )
{
NN_NULL_ASSERT( inIcon );
NN_ASSERT( 0<=index && index<IDB_ICON_DATA_MAX ); // indexは有効な値である
if ( inIcon==NULL ) return;
if (!( 0<=index && index<IDB_ICON_DATA_MAX )) return;
if ( index == IDBi_INDEX_INVALID ) return;
IDBi_IconTable * const restrict pTable = &spTable->iconTable[index];
pTable->programID = inIcon->key.programID;
pTable->remasterVersion = inIcon->key.remasterVersion;
pTable->reserved0 = 0;
pTable->reserved1 = 0;
pTable->lastAccess = IDBi_GetCurrentTime();
IDBi_IconData * restrict pData;
if ( spDataBuf )
{
pData = &spDataBuf->iconData[index];
}else{
pData = spIconBuf;
}
memcpy( pData->info, inIcon->info, sizeof( pData->info ) );
memcpy( pData->rating, inIcon->rating, sizeof( pData->rating ) );
pData->region = (u16)inIcon->region;
pData->format = (u8)inIcon->format;
switch ( pData->format )
{
case IDB_ICON_FORMAT_CTR:
memcpy( &pData->data, inIcon->data, sizeof( IDB_IconData ) );
break;
case IDB_ICON_FORMAT_TWL:
memcpy( &pData->data, inIcon->twlData, sizeof( nn::CTR::LegacyBannerAnime ) );
memset( ((u8*)&pData->data) + sizeof( nn::CTR::LegacyBannerAnime ), 0, sizeof( IDB_IconData )-sizeof( nn::CTR::LegacyBannerAnime ) );
break;
case IDB_ICON_FORMAT_NTR:
memcpy( &pData->data, inIcon->ntrData, sizeof( IDB_NTRIconDataRaw ) );
memset( ((u8*)&pData->data) + sizeof( IDB_NTRIconDataRaw ), 0, sizeof( IDB_IconData )-sizeof( IDB_NTRIconDataRaw ) );
break;
default:
NN_ASSERT(false);
return;
}
IDBi_WriteData( index );
}
// オーナー枠を拡張する
// 拡張したオーナー枠を返す
IDBi_Index IDBi_ExpandOwner( void )
{
// 拡張した際にフレンド枠のデータを破壊してしまう場合は追い出されたデータを移動する
if ( spTable->iconTable[spTable->ownerDataCount].programID != sInvalidProgramID )
{
//NN_LOG("%llx %llx\n", spTable->iconTable[spTable->ownerDataCount].programID, sInvalidProgramID );
IDBi_Index dropped = spTable->ownerDataCount;
IDBi_Index change;
change = IDBi_GetEmptyIndex( IDBi_DIVISION_FRIEND );
if( change != IDBi_INDEX_INVALID )
{
// 空きがあったのでそこに入れる
IDBi_Move( change, dropped );
}
else
{
// 空きがないのもっとも古い更新時刻のインデックを探す
change = IDBi_GetOldestIndex( IDBi_DIVISION_FRIEND );
if ( change!=IDBi_INDEX_INVALID ) // このルーチンでInvalidが返ってくる事は無いが、念のため
{
if ( spTable->iconTable[change].lastAccess < spTable->iconTable[dropped].lastAccess )
{
IDBi_Move( change, dropped );
}
else
{
// 追い出されたところを空きにする
spTable->iconTable[ dropped ].programID = sInvalidProgramID;
}
}
}
spTable->ownerDataCount++;
// NN_LOG("change:%d dropped:%d\n", change, dropped);
}else{
spTable->ownerDataCount++;
}
return spTable->ownerDataCount;
}
// フレンド枠のデータをオーナー枠に移動する
IDBi_Index IDBi_MoveToOwner( IDBi_Index src )
{
IDBi_Index dest = IDBi_GetEmptyIndex( IDBi_DIVISION_OWNER );
NN_ASSERT( spTable->ownerDataCount <= src && src < IDB_ICON_DATA_MAX ); // indexのデータはフレンド枠にある
if ( dest == IDBi_INDEX_INVALID )
{
// フレンド枠にあるものを移したいときなので、移動先の要素と交換する
if ( spTable->ownerDataCount<IDB_OWNER_ICON_DATA_MAX )
{
dest = spTable->ownerDataCount;
spTable->ownerDataCount++;
}
else
{
// 最も古いデータを交換
dest = IDBi_GetOldestIndex( IDBi_DIVISION_OWNER );
}
// NN_LOG("move to owner exchange src:%d dest:%d ownerDataCount:%d\n", src, dest, spTable->ownerDataCount);
IDBi_Exchange( dest, src );
}
else
{
// NN_LOG("move to owner copy src:%d dest:%d ownerDataCount:%d\n", src, dest, spTable->ownerDataCount);
IDBi_Move( dest, src );
spTable->iconTable[dest].lastAccess = IDBi_GetCurrentTime();
}
return dest;
}
// 最終アクセス日時を更新する
IDBi_Index IDBi_UpdateLastAccess( const IDB_Key * restrict inKey )
{
IDBi_Index index = IDBi_GetIndex( inKey );
if ( index == IDBi_INDEX_INVALID ) return IDBi_INDEX_INVALID;
NN_NULL_ASSERT( inKey );
if ( inKey==NULL ) return IDBi_INDEX_INVALID;
spTable->iconTable[index].lastAccess = IDBi_GetCurrentTime();
return index;
}
// 該当キーのインデックスを返す
// 存在しない時にはIDBi_INDEX_INVALIDを返す
IDBi_Index IDBi_GetIndex( const IDB_Key * restrict inKey )
{
int i;
NN_NULL_ASSERT( inKey );
if ( inKey==NULL ) return IDBi_INDEX_INVALID;
if ( inKey->remasterVersion == IDB_KEY_LATEST_VERSION )
{ // 最新バージョンモード
int latestVer = -1;
IDBi_Index latestIndex = IDBi_INDEX_INVALID;
for ( i=0; i<IDB_ICON_DATA_MAX; i++ )
{
if ( spTable->iconTable[i].programID ==inKey->programID )
{
if ( latestVer < spTable->iconTable[i].remasterVersion )
{
latestVer = spTable->iconTable[i].remasterVersion;
latestIndex = (IDBi_Index)i;
}
}
}
return latestIndex;
}else if ( inKey->remasterVersion == IDB_KEY_LATEST_VERSION_OWNER )
{ // オーナー枠優先モード
int latestScore = -1; // 多いほうを優先
IDBi_Index latestIndex = IDBi_INDEX_INVALID;
for ( i=0; i<IDB_ICON_DATA_MAX; i++ )
{
if ( spTable->iconTable[i].programID ==inKey->programID )
{
const int U16_MAX = 0xffff;
int iVer = spTable->iconTable[i].remasterVersion;
int iScore = IDBi_GetDivisionFromIndex(i)==IDBi_DIVISION_OWNER ? iVer+U16_MAX : iVer;
if ( latestScore < iScore )
{
latestScore = iScore;
latestIndex = (IDBi_Index)i;
}
}
}
return latestIndex;
}else{ // 完全一致モード
for ( i=0; i<IDB_ICON_DATA_MAX; i++ )
{
if ( spTable->iconTable[i].programID ==inKey->programID &&
spTable->iconTable[i].remasterVersion ==inKey->remasterVersion )
{
return (IDBi_Index)i;
}
}
return IDBi_INDEX_INVALID;
}
}
// 最もアクセス日時が遅いインデックスを返す
// データベースが空の時はIDBi_INDEX_INVALIDを返す
IDBi_Index IDBi_GetOldestIndex(IDBi_Division division )
{
int from = division == IDBi_DIVISION_OWNER ? 0 : spTable->ownerDataCount;
int to = division == IDBi_DIVISION_OWNER ? spTable->ownerDataCount : IDB_ICON_DATA_MAX;
int i;
u32 oldestTime = std::numeric_limits<u32>::max(); // U32_MAX;
int oldestIndex= IDBi_INDEX_INVALID;
NN_ASSERT( division!=IDBi_DIVISION_NONE );
if ( division==IDBi_DIVISION_NONE ) return IDBi_INDEX_INVALID;
for ( i=from; i<to; i++ )
{
if ( spTable->iconTable[i].programID==sInvalidProgramID ) continue;
if ( spTable->iconTable[i].lastAccess<oldestTime )
{
oldestIndex = i;
oldestTime =spTable->iconTable[i].lastAccess;
}
}
NN_LOG("IDBi_GetOldestIndex %d from %d to %d\n", oldestIndex, from, to );
return (IDBi_Index)oldestIndex;
}
// IDB形式の現在時間を取得する
IDBi_Time IDBi_GetCurrentTime( void )
{
nn::fnd::TimeSpan now = nn::fnd::DateTime::GetNow() - nn::fnd::DateTime();
return (IDBi_Time)now.GetSeconds();
}
// データをバッファにロードする
void IDBi_ReadData(IDBi_Index index)
{
if ( spIconBuf==NULL ) return; // オンメモリ版は常時ロード
if ( index == IDBi_INDEX_INVALID ) return;
nn::fs::FileInputStream is(sDataFileName);
is.Seek( index * sizeof(IDBi_IconData) ,nn::fs::POSITION_BASE_BEGIN );
is.Read( spIconBuf, sizeof(IDBi_IconData) );
is.Finalize();
}
// バッファのデータをファイルに書き込む
void IDBi_WriteData(IDBi_Index index)
{
NN_ASSERT( 0<=index && index<IDB_ICON_DATA_MAX ); // indexは有効な値である
if (!( 0<=index && index<IDB_ICON_DATA_MAX )) return;
if ( index == IDBi_INDEX_INVALID ) return;
nn::fs::FileOutputStream os(sDataFileName, false);
os.Seek( index * sizeof(IDBi_IconData) ,nn::fs::POSITION_BASE_BEGIN );
if ( spDataBuf )
{
os.Write( &spDataBuf->iconData[index], sizeof(IDBi_IconData) );
}else{
os.Write( spIconBuf, sizeof(IDBi_IconData) );
}
os.Finalize();
}
// 指定したインデックスのプログラムIDを取得する
nn::ProgramId IDBi_GetProgramID( const int index )
{
NN_ASSERT( 0<=index && index<IDB_ICON_DATA_MAX ); // indexは有効な値である
if (!( 0<=index && index<IDB_ICON_DATA_MAX )) return nn::CTR::INVALID_PROGRAM_ID;
if ( index == IDBi_INDEX_INVALID ) return nn::CTR::INVALID_PROGRAM_ID;
return spTable->iconTable[index].programID;
}
// IDBを保存する
void IDBi_Save(void)
{
nn::fs::FileOutputStream os;
nn::Result res = os.TryInitialize( sTableFileName, false );
if ( res.IsFailure() )
{
nn::fs::CreateFile( sTableFileName, sizeof(*spTable) );
res = os.TryInitialize( sTableFileName, false );
NN_RESULT_ASSERT(res);
if ( res.IsFailure() )
{
os.Finalize();
return;
}
}
s32 size;
res = os.TryWrite( &size, spTable, sizeof(*spTable) );
os.Finalize();
}
// IDBをロードする
void IDBi_Load(void)
{
nn::Result res;
nn::fs::FileInputStream is;
res = is.TryInitialize( sTableFileName );
if ( res.IsSuccess() )
{
s32 size;
res = is.TryRead( &size, spTable, sizeof(*spTable) );
is.Finalize();
}else{
NN_LOG("idb reset start\n");
IDBi_InitTable();
}
// オンメモリ版は全てをロードしておく
if ( spDataBuf )
{
nn::fs::FileInputStream dis;
res = dis.TryInitialize(sDataFileName);
if ( res.IsSuccess() )
{
s32 size;
res = dis.TryRead( &size, spDataBuf, sizeof(IDBi_DataFile) );
}
dis.Finalize();
}
}
// アプレットのプログラムID に対して下位8ビットにマスクをかける
void IDBi_MaskProgramID( nn::ProgramId* programID )
{
NN_NULL_ASSERT(programID);
if ( programID==NULL ) return;
if( !nn::CTR::IsTwlApp( *programID ) )
{
if( nn::CTR::GetCategoryOf( *programID ) == nn::CTR::PROGRAM_ID_CATEGORY_APPLET )
{
nn::ProgramId pid = *programID & IDBi_MASK_PROGRAM_ID;
*programID = pid;
}
}
}