/*! @file sysFile.cpp @brief ファイルアクセス */ //#include "sys.h" #include #include #include "sysFile.h" #include "sysHeap.h" #include #include #include #define cDefaultDeviceAlignment 4 namespace sys { const char* File::mMmenSysArcName = "SYS:"; const wchar_t* File::mMmenSysSaveDataPath = L"SYS:/Launcher.dat"; const char* File::mMsetSysArcName = "MSETSYS:"; const wchar_t* File::mMsetSysSaveDataPath[ File::eSysSaveDataTypeNum ] = { L"MSETSYS:/Mset.dat", L"MSETSYS:/MsetForBoss.dat", }; const char* File::mMsetExtArcName = "MSETEXT:"; const wchar_t* File::mMsetExtSaveDataPath[ File::eExtSaveDataTypeNum ] = { L"MSETEXT:/MsetExt.dat", }; #ifndef NW_RELEASE bool File::smDebugPrintFlag = true; #endif /* char型でのファイルの読み込み @param device デバイスメモリに読み込むかどうか @param prefix ファイルのパスに接頭フォルダ名をつけるかどうか */ u8* File::read( const char* fileName, u32* size, bool device, bool prefix ) { wchar_t name[128]; std::memset( name, 0, sizeof( name ) ); int i = 0; while( fileName[i] != '\0' ) { name[i] = fileName[i]; if( ++i > 128 ) break; } return read( name, size, device, prefix ); } /* wchar_t型でのファイルの読み込み @param device デバイスメモリに読み込むかどうか @param prefix ファイルのパスに接頭フォルダ名をつけるかどうか */ u8* File::read( const wchar_t* fileName, u32* size, bool device, bool prefix ) { nn::Result result; nn::fs::FileReader fileReader; u8* buffer = NULL; s64 fileSize = 0; s32 readSize = 0; wchar_t name[128]; std::memset( name, 0, sizeof( name ) ); if( prefix ) { // ファイルシステム内にこれを書くのは正直どうかと思う // resLoader側に移動するかも #ifdef NW_PLATFORM_CTR std::swprintf( name, 128, L"rom:/%ls", (wchar_t*)fileName ); #else std::swprintf( name, 128, L"data/%s", fileName ); #endif } else { std::swprintf( name, 128, fileName ); } result = fileReader.TryInitialize( name ); if( !result.IsSuccess() ) { debug_print_(); return NULL; } result = fileReader.TryGetSize( &fileSize ); if( !result.IsSuccess() || fileSize <= 0 ) { debug_print_(); fileReader.Finalize(); return NULL; } *size = static_cast( fileSize ); if( device ) buffer = new( Mem::getDeviceHeap(), cDefaultDeviceAlignment ) u8[ *size ]; else buffer = new( Mem::getMainHeap() ) u8[ *size ]; if( buffer == NULL ) { debug_print_(); fileReader.Finalize(); return NULL; } result = fileReader.TryRead( &readSize, buffer, *size ); if( !result.IsSuccess() ) { debug_print_(); fileReader.Finalize(); delete buffer; return NULL; } fileReader.Finalize(); return buffer; } /*! 圧縮ファイルのリード */ u8* File::readLZ( const char* fileName, u32* size ) { NN_PANIC("comment out function, %s:%d", __func__, __LINE__); NN_UNUSED_VAR(fileName); NN_UNUSED_VAR(size); return 0; #if 0 u8* comp_buf = read( fileName, size, false, false ); u32 arc_size = nn::cx::GetUncompressedSize( comp_buf ); u8* arc_buf = new( Mem::getDeviceHeap() ) u8[arc_size]; nn::cx::UncompressLZ( comp_buf, arc_buf ); *size = arc_size; delete []comp_buf; return arc_buf; #endif } /*! 共有データのマウント */ u8* File::mountShare( const char* name, nn::ProgramId tit_id, int data_num, int files, int dirs ) { NN_PANIC("comment out function, %s:%d", __func__, __LINE__); NN_UNUSED_VAR(name); NN_UNUSED_VAR(tit_id); NN_UNUSED_VAR(data_num); NN_UNUSED_VAR(files); NN_UNUSED_VAR(dirs); return NULL; #if 0 int size = nn::fs::GetContentRequiredMemorySize( nn::fs::MEDIA_TYPE_NAND, tit_id, data_num, files, dirs ); u8* buf = new( Mem::getMainHeap() ) u8[size]; nn::Result result = nn::fs::MountContent( name, nn::fs::MEDIA_TYPE_NAND, tit_id, data_num, files, dirs, buf, size ); if( result.IsFailure() ) { delete []buf; return NULL; } return buf; #endif } /*! MSET のシステムセーブデータの初期化 */ void File::initializeMsetSys() { nn::Result result = nn::fs::MountSystemSaveData( mMsetSysArcName, getMsetSaveDataId() ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { nn::fs::SystemSaveDataId saveDataId = getMsetSaveDataId(); // ファイルの最大数とディレクトリの最大数は念のため多めにとっておきます。 size_t maxFiles = 2; size_t maxDirectories = 1; bool isDuplicateAll = true; if( result <= nn::fs::ResultNotFound() ) { // 存在していないため、作成する必要があります。 result = nn::fs::CreateSystemSaveData( saveDataId, maxFiles, maxDirectories, eMsetSaveDataSize, isDuplicateAll ); NN_LOG("create mset system save data\n"); } else if( result <= nn::fs::ResultOperationDenied() ) { // 指定した ID のシステムセーブデータは、他のプロセスによってマウントされている可能性があります。 // それ以外の理由で、このエラーが発生することは、まずありません。 NN_ASSERT_RESULT( result ); } else if( result <= nn::fs::ResultNotFormatted() ) { // フォーマットを行なう必要があります。 result = nn::fs::DeleteSystemSaveData( saveDataId ); if( result.IsSuccess() ) { result = nn::fs::CreateSystemSaveData( saveDataId, maxFiles, maxDirectories, eMsetSaveDataSize, isDuplicateAll ); NN_LOG("create mset system save data\n"); } } else if( result <= nn::fs::ResultBadFormat() ) { // フォーマットを行なう必要があります。 result = nn::fs::DeleteSystemSaveData( saveDataId ); if( result.IsSuccess() ) { result = nn::fs::CreateSystemSaveData( saveDataId, maxFiles, maxDirectories, eMsetSaveDataSize, isDuplicateAll ); NN_LOG("create mset system save data\n"); } } else if( result <= nn::fs::ResultVerificationFailed() ) { // フォーマットを行なう必要があります。 result = nn::fs::DeleteSystemSaveData( saveDataId ); if( result.IsSuccess() ) { result = nn::fs::CreateSystemSaveData( saveDataId, maxFiles, maxDirectories, eMsetSaveDataSize, isDuplicateAll ); NN_LOG("create mset system save data\n"); } } else if( result <= nn::fs::ResultOperationDenied() ) { // このエラーが発生することは、まずありません。 // ただ、発生した場合にカードの挿し直しや本体の再起動で復帰する可能性があります。 nn::dbg::Panic(); } else { // 上記以外のエラーは発生しません。 nn::dbg::Panic(); } NN_DBG_PRINT_RESULT( result ); // フォーマット後の判定 if( result.IsFailure() ) { // セーブデータのフォーマットが失敗することはまずありません。 nn::dbg::Panic(); } else { // 再マウント result = nn::fs::MountSystemSaveData( mMsetSysArcName, saveDataId ); if( result.IsFailure() ) { // フォーマットに成功した後に、マウントが失敗することはまずありません。 nn::dbg::Panic(); } else { NN_LOG("mset system savedata mount\n"); } } } else { NN_LOG("mset system savedata mount\n"); } } /*! MSET のシステムセーブデータの終了処理 */ void File::finalizeMsetSys() { nn::fs::Unmount( mMsetSysArcName ); } /*! MSET の追加データをつくる必要があるかどうか */ int File::checkMsetExt() { nn::Result result = nn::fs::MountExtSaveData( mMsetExtArcName, getMsetExtSaveDataId() ); int ret = eExtResultSuccess; NN_LOG( "check ext save data\n" ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { if( result <= nn::fs::ResultNotFound() ) { if( result <= nn::fs::ResultMediaNotFound() ) { ret = eExtResultDefaultError; } else { ret = eExtResultNeedCreate; } } else if( result <= nn::fs::ResultNotFormatted() ) { ret = eExtResultNeedCreate; } else if( result <= nn::fs::ResultBadFormat() ) { ret = eExtResultDefaultError; } else if( result <= nn::fs::ResultVerificationFailed() ) { ret = eExtResultNeedCreate; } else if( result <= nn::fs::ResultOperationDenied() ) { ret = eExtResultDefaultError; } else { ret = eExtResultDefaultError; } } nn::fs::Unmount( mMsetExtArcName ); return ret; } /*! MSET の追加データの初期化 */ int File::initializeMsetExt() { nn::Result result = nn::fs::MountExtSaveData( mMsetExtArcName, getMsetExtSaveDataId() ); int ret = eExtResultSuccess; NN_LOG( "mount ext save data\n" ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { nn::fs::ExtSaveDataId saveDataId = getMsetExtSaveDataId(); u32 iconData = 0; size_t iconDataSize = sizeof( iconData ); // ファイルの最大数とディレクトリの最大数は念のため多めにとっておきます。 u32 maxDirectories = 1; u32 maxFiles = 2; if( result <= nn::fs::ResultNotFound() ) { if( result <= nn::fs::ResultMediaNotFound() ) { // SD カードが挿さっていません。 // (補足) 壊れた SD カードや SD カードではないメディアが挿されているときにも、このエラーが返ります。 // 但し、その場合は RegisterSdmcInsertedEvent で登録した挿入イベントはシグナルされます。 ret = eExtResultDefaultError; } else { // 指定した拡張セーブデータは存在していないので、作成する必要があります。 // (補足) 開発中は、指定した ID が正しいか確認をしてください。 result = nn::fs::CreateExtSaveData( saveDataId, &iconData, iconDataSize, maxDirectories, maxFiles); ret = eExtResultNeedCreate; } } else if( result <= nn::fs::ResultNotFormatted() ) { // 拡張セーブデータを作り直す必要があります。 // (理由) 拡張セーブデータの作成に失敗した状態です。 result = nn::fs::DeleteExtSaveData( saveDataId ); if( result.IsSuccess() ) { result = nn::fs::CreateExtSaveData( saveDataId, &iconData, iconDataSize, maxDirectories, maxFiles); } ret = eExtResultNeedCreate; } else if( result <= nn::fs::ResultBadFormat() ) { // SD カードをフォーマットする必要があります。 ret = eExtResultDefaultError; } else if( result <= nn::fs::ResultVerificationFailed() ) { // 拡張セーブデータを作り直す必要があります。 // (理由) セーブデータが壊れているか、改竄されています。 result = nn::fs::DeleteExtSaveData( saveDataId ); if( result.IsSuccess() ) { result = nn::fs::CreateExtSaveData( saveDataId, &iconData, iconDataSize, maxDirectories, maxFiles); } ret = eExtResultNeedCreate; } else if( result <= nn::fs::ResultOperationDenied() ) { if( result <= nn::fs::ResultWriteProtected() ) { // SD カードが書き込み禁止にされています。 // (補足) マウント時にデータの復旧シーケンスが実行されることがあり、そのときに発生します。 } else if( result <= nn::fs::ResultMediaAccessError() ) { // 接触不良等によるハードウェエア的な要因のときのみ、このエラーが返ります。 // そのときは、SD カードの挿し直し、本体の再起動などで復帰する可能性があります。 } else { // SD カード上のファイル、もしくはディレクトリが読み取り専用にされている可能性があります。 // (補足) マウント時にデータの復旧シーケンスが実行されることがあり、そのときに発生します。 } ret = eExtResultDefaultError; } else { // 上記以外は、想定外のエラーです。 // 但し、拡張セーブデータへのアクセスで、FATAL エラーは発生させないようにしてください。 ret = eExtResultDefaultError; } // 作成後の判定 if( result.IsFailure() ) { if( result <= nn::fs::ResultNotEnoughSpace() ) { // SD カードに必要な空き容量がありません。 ret = eExtResultNotFreeError; } else if( result <= nn::fs::ResultNotFormatted() ) { // エラーが発生し、作成が途中で中断されました。 ret = eExtResultDefaultError; } else if( result <= nn::fs::ResultOperationDenied() ) { if( result <= nn::fs::ResultWriteProtected() ) { // SD カードが書き込み禁止にされています。 } else if( result <= nn::fs::ResultMediaAccessError() ) { // 接触不良等によるハードウェエア的な要因のときのみ、このエラーが返ります。 // そのときは、リトライ処理、ゲームカードの挿し直し、本体の再起動などで復帰する可能性があります。 } else { // SD カード上のディレクトリが読み取り専用にされている可能性があります。 // (補足) マウント時にデータの復旧シーケンスが実行されることがあり、そのときに発生します。 } ret = eExtResultDefaultError; } else { // 上記以外は、想定外のエラーです。 // 但し、拡張セーブデータへのアクセスで、FATAL エラーは発生させないようにしてください。 // (補足) 開発中は、iconData, iconDataSize に不正な値を渡していないか確かめてください。 ret = eExtResultDefaultError; } } else { // 再マウント result = nn::fs::MountExtSaveData( mMsetExtArcName, getMsetExtSaveDataId() ); if( result.IsFailure() ) { // 始めの呼び出し時と同様。 // (補足) SD カードが抜かれない限り、作成直後にマウントが失敗することは、まずありません。 ret = eExtResultDefaultError; } } } return ret; } /*! MSET の追加データの終了処理 */ void File::finalizeMsetExt() { nn::fs::Unmount( mMsetExtArcName ); } /*! MSET のセーブデータ読み込み */ void File::readMsetFile( u8* pSaveData, const size_t size, int type ) { u32 readSize = 0; u8* pTmp = NULL; pTmp = sys::File::read( mMsetSysSaveDataPath[ type ], &readSize, false, false ); if( pTmp == NULL ) { switch( type ) { case eSysSaveDataTypeDsi: // ファイルがないので初期化 // 無効な programID で埋める std::memset( pSaveData, 0xFF, size ); break; case eSysSaveDataTypeBoss: std::memset( pSaveData, 0, size ); break; } } else { // 管理ファイルが読み込めた std::memcpy( pSaveData, pTmp, size ); delete pTmp; } } /*! MSET の拡張セーブデータ読み込み */ void File::readMsetExtFile( u8* pSaveData, const size_t size, int type ) { u32 readSize = 0; u8* pTmp = NULL; pTmp = sys::File::read( mMsetExtSaveDataPath[ type ], &readSize, false, false ); if( pTmp == NULL ) { // ファイルがないので初期化 // 無効な programID で埋める std::memset( pSaveData, 0xFF, size ); } else { // 管理ファイルが読み込めた std::memcpy( pSaveData, pTmp, size ); delete[] pTmp; } } /*! システムセーブデータのセーブ マウントは外で行ってください */ void File::saveMmenSys( const u8* saveData, const size_t size ) { nn::Result result; nn::fs::FileWriter fileWriter; // 自分で他のセーブデータへファイルをつくることはしない result = fileWriter.TryInitialize( mMmenSysSaveDataPath, false ); NN_LOG("mmen sys try initialize\n"); NN_DBG_PRINT_RESULT( result ); if( result.IsSuccess() ) { s32 out = 0; result = fileWriter.TryWrite( &out, saveData, size ); if( result.IsSuccess() ) { result = nn::fs::CommitSystemSaveData( mMmenSysArcName ); if( result.IsFailure() ) { // これがエラーになってたらパニックだ nn::dbg::Panic(); } else { NN_LOG( "save to %ls\n", mMmenSysSaveDataPath ); } } fileWriter.Finalize(); } } void File::saveMsetSys( const u8* saveData, const size_t size, int type ) { nn::Result result; nn::fs::FileOutputStream fileWriter; // なかった場合はファイル作成 result = fileWriter.TryInitialize( mMsetSysSaveDataPath[ type ], false ); if( result.IsFailure() ) { if( result <= nn::fs::ResultNotFound() ) { result = nn::fs::TryCreateFile( mMsetSysSaveDataPath[ type ], size ); if( result.IsFailure() ) { return; } else { result = fileWriter.TryInitialize( mMsetSysSaveDataPath[ type ], false ); if( result.IsFailure() ) { return; } } } else if(result <= nn::fs::ResultVerificationFailed()) { // 作り直し result = nn::fs::TryDeleteFile( mMsetSysSaveDataPath[ type ] ); if( result.IsFailure() ) { return; } else { result = nn::fs::TryCreateFile( mMsetSysSaveDataPath[ type ], size ); if( result.IsFailure() ) { return; } else { result = fileWriter.TryInitialize( mMsetSysSaveDataPath[ type ], false ); if( result.IsFailure() ) { return; } } } } else { return; } } s64 fSize = 0; result = fileWriter.TryGetSize( &fSize ); if( result.IsFailure() ) { fileWriter.Finalize(); return; } if( fSize != size ) { result = fileWriter.TrySetSize( size ); if( result.IsFailure() ) { fileWriter.Finalize(); return; } } s32 out = 0; result = fileWriter.TryWrite( &out, saveData, size ); if( result.IsFailure() ) { // このときどうしようもない fileWriter.Finalize(); return; } else { fileWriter.Finalize(); // コミットしなければ反映されない result = nn::fs::CommitSystemSaveData( mMsetSysArcName ); if( result.IsFailure() ) { // これがエラーになってたらパニックだ nn::dbg::Panic(); } NN_LOG( "save to %ls\n", mMsetSysSaveDataPath[ type ] ); } } /*! 拡張セーブデータのセーブ マウントは外で行ってください */ void File::saveMsetExt( const u8* saveData, const size_t size, int type ) { nn::Result result; nn::fs::FileOutputStream fileWriter; // なかった場合はファイル作成 result = fileWriter.TryInitialize( mMsetExtSaveDataPath[ type ], false ); NN_LOG("try save mset ext\n"); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { if( result <= nn::fs::ResultNotFound() ) { if( result <= nn::fs::ResultMediaNotFound() ) { return; } else { NN_LOG("try create mset ext\n"); result = nn::fs::TryCreateFile( mMsetExtSaveDataPath[ type ], size ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { if(result <= nn::fs::ResultNotFound()) { return; } else if(result <= nn::fs::ResultAlreadyExists()) { NN_LOG("try delete mset ext\n"); result = nn::fs::TryDeleteFile( mMsetExtSaveDataPath[ type ] ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { return; } else { NN_LOG("try create mset ext\n"); result = nn::fs::TryCreateFile( mMsetExtSaveDataPath[ type ], size ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { return; } else { result = fileWriter.TryInitialize( mMsetExtSaveDataPath[ type ], false ); if( result.IsFailure() ) { return; } } } } } } } else if(result <= nn::fs::ResultVerificationFailed()) { NN_LOG("try delete mset ext\n"); result = nn::fs::TryDeleteFile( mMsetExtSaveDataPath[ type ] ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { return; } else { NN_LOG("try create mset ext\n"); result = nn::fs::TryCreateFile( mMsetExtSaveDataPath[ type ], size ); NN_DBG_PRINT_RESULT( result ); if( result.IsFailure() ) { return; } else { result = fileWriter.TryInitialize( mMsetExtSaveDataPath[ type ], false ); if( result.IsFailure() ) { return; } } } } else { return; } } s64 fSize = 0; result = fileWriter.TryGetSize( &fSize ); if( result.IsFailure() ) { fileWriter.Finalize(); return; } if( fSize != size ) { result = fileWriter.TrySetSize( size ); if( result.IsFailure() ) { fileWriter.Finalize(); return; } } s32 out = 0; result = fileWriter.TryWrite( &out, saveData, size ); if( result.IsFailure() ) { // このときどうしようもない fileWriter.Finalize(); return; } else { fileWriter.Finalize(); NN_LOG( "save to %ls\n", mMsetExtSaveDataPath[ type ] ); } } /*! ファイル読み込み失敗デバッグ表示 */ void File::debug_print_() { #ifndef NW_RELEASE if( smDebugPrintFlag ) { NN_LOG("Fail fileRead "); /* int i = 0; while( name[i] != L'\0' ) { NN_LOG("%c", name[i]); if( ++i == 128 ) break; } */ NN_LOG("\n"); } #endif } //========================================================================== // システムセーブデータのID取得 //========================================================================== nn::fs::SystemSaveDataId File::getMmenSaveDataId() { nn::fs::SystemSaveDataId ret; switch( nn::cfg::CTR::GetRegion() ) { case nn::cfg::CTR::CFG_REGION_JAPAN: ret = 0x00020082; break; case nn::cfg::CTR::CFG_REGION_EUROPE: case nn::cfg::CTR::CFG_REGION_AUSTRALIA: ret = 0x00020098; break; case nn::cfg::CTR::CFG_REGION_CHINA: ret = 0x000200A1; break; case nn::cfg::CTR::CFG_REGION_KOREA: ret = 0x000200A9; break; case nn::cfg::CTR::CFG_REGION_TAIWAN: ret = 0x000200B1; break; case nn::cfg::CTR::CFG_REGION_AMERICA: default: ret = 0x0002008f; break; } return ret; } nn::fs::SystemSaveDataId File::getMsetSaveDataId() { nn::fs::SystemSaveDataId ret; switch( nn::cfg::CTR::GetRegion() ) { case nn::cfg::CTR::CFG_REGION_JAPAN: ret = 0x00020200; break; case nn::cfg::CTR::CFG_REGION_EUROPE: case nn::cfg::CTR::CFG_REGION_AUSTRALIA: ret = 0x00020220; break; case nn::cfg::CTR::CFG_REGION_CHINA: ret = 0x00020260; break; case nn::cfg::CTR::CFG_REGION_KOREA: ret = 0x00020270; break; case nn::cfg::CTR::CFG_REGION_TAIWAN: ret = 0x00020280; break; case nn::cfg::CTR::CFG_REGION_AMERICA: default: ret = 0x00020210; break; } return ret; } nn::fs::ExtSaveDataId File::getMsetExtSaveDataId() { nn::fs::ExtSaveDataId ret; switch( nn::cfg::CTR::GetRegion() ) { case nn::cfg::CTR::CFG_REGION_JAPAN: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET; break; case nn::cfg::CTR::CFG_REGION_EUROPE: case nn::cfg::CTR::CFG_REGION_AUSTRALIA: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET_EU; break; case nn::cfg::CTR::CFG_REGION_CHINA: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET_CN; break; case nn::cfg::CTR::CFG_REGION_KOREA: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET_KR; break; case nn::cfg::CTR::CFG_REGION_TAIWAN: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET_TW; break; case nn::cfg::CTR::CFG_REGION_AMERICA: default: ret = nn::CTR::PROGRAM_ID_UNIQUE_ID_MSET_US; break; } return ret; } }