mirror of
https://github.com/rvtr/ctr_Repair.git
synced 2025-10-31 13:51:08 -04:00
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
927 lines
29 KiB
C++
927 lines
29 KiB
C++
// 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に対して下位8ビットを無視するようにする
|
||
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に対して下位8ビットを無視するようにする
|
||
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に対して下位8ビットを無視するようにする
|
||
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;
|
||
}
|
||
}
|
||
}
|