/*---------------------------------------------------------------------------* Project: Horizon File: main.cpp Copyright (C)2010 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. *---------------------------------------------------------------------------*/ #include #include #include #include #include //#include #include #include #include //#include #include #include #include //#include #include "../../../common/savefile/savefile.h" #include "../../../common/savefile/savedata.h" #include "../../../common/savefile/membak.h" #include "./screen/screen.h" #include "../../../common/common.h" //ヘッダにフラグのみ、コメントアウトで旧版相当(SD非対応) #include "main.h" #ifdef CSM_FLAG_USE_SD #include "../../../common/savefile/exsave.h" #endif nn::fnd::ExpHeap appHeap; uptr heapForGx; //Gui gui; myResult errRes; //char strBuff[128]; //bool exist; SaveData savedata; nn::os::LightEvent ejectEvnt(false); nn::os::LightEvent insEvnt(false); #ifdef CSM_FLAG_USE_SD ExSave exsave; nn::os::LightEvent ejectEvntSd(false); nn::os::LightEvent insEvntSd(false); bool BkupSd = false;//バックアップ先がSDか(ファイルサイズ合計で選択) #endif MemBak memsave; //バックアップの状態 bool Active,Formatted; bool exActive,exFormatted; tArcInfo arcInfo,arcInfo2;//アーカイブ情報 tDcList dcList,dcList2;//ディレクトリ格納 //プロダクトコードの一致 bool isAgreePCode; //SDあり起動 bool alive_Sd; //Top画面エラー表示 int tmerr; //結果 typedef enum { SUCCESS ,ERROR ,CANCEL ,INSEXIT }RetCode; //挿抜イベント時に設定 typedef enum { InEx_None ,InEx_InsCard ,InEx_EjcCard ,InEx_InsSd ,InEx_EjcSd }tIsInsEject; //メッセージ用タグ enum eMesTag{ MT_Compair,//コンペア時 MT_ChkCard,//ユーザセーブの存在チェック時 MT_ChkBkup,//バックアップ先の存在チェック時 MT_CrtBkup,//保存先の作成 MT_FileNot,//ユーザセーブのマウント成功したがファイルが無い MT_Reading,//リード中 MT_Complate,//終了 MT_CardFormat,//ユーザセーブのフォーマット時 MT_FormatOnly,//フォーマットのみ行ったとき(ファイルなし時) MT_Writing,//ライト中 MT_CrtDir1,//ディレクトリ作成中その1 MT_CrtDir2,//その2(1,2を交互表示) MT_Copy1,// ファイルコピー MT_Copy2,//ファイルコピー MT_Failed,//失敗 MT_Break,//中断 MT_Success,//成功 MT_END }; tChar* MES_NULL = L""; //文字数(終端込) #define MESS_SIZE 32 //メッセージ格納 wchar_t Mess[MT_END*MESS_SIZE]; //メッセージ位置 int MessPos[MT_END]; #define MessTxt(i) (wchar_t*)&Mess[MessPos[i]] tIsInsEject isInsEject; extern u8 scr_evnt; s64 sSize; wchar_t wk_dir[20];//拡張セーブ上のリネームファイル用ディレクトリ int rename_n =0;//リネーム番号 wchar_t file_pathw[MAX_PATH_LENGTH]; wchar_t file_pathw2[MAX_PATH_LENGTH]; char file_path[MAX_PATH_LENGTH]; //ドライバの仕様で速度的に4バイトアラインした方がいい(SDK 0.14.23 時点) char fileBuffer[512] NN_ATTRIBUTE_ALIGN(4);//検証が512単位なので char fileBuffer_ex[512] NN_ATTRIBUTE_ALIGN(4); #define FILEBUFF_SIZE sizeof(fileBuffer) void WaitUI(); void PutError(TgtDev dev,int cd=0); RetCode Card2Sd(); RetCode Sd2Card(); void CheckSaveDataState(); void CheckExSaveState(); //------------------------------------------------------------ 挿抜チェック bool CheckInsExit() { if (ejectEvnt.TryWait()){ //CARD抜け CheckSaveDataState(); isInsEject = InEx_EjcCard; return true; } if (insEvnt.TryWait()){ //CARD挿入 CheckSaveDataState(); isInsEject = InEx_InsCard; return true; } #ifdef CSM_FLAG_USE_SD if(alive_Sd){ if (ejectEvntSd.TryWait()){ //SD抜け CheckExSaveState(); isInsEject = InEx_EjcSd; return true; } if (insEvntSd.TryWait()){ //SD挿入 CheckExSaveState(); isInsEject = InEx_InsSd; return true; } } #endif return false; } //---------------------------------------------------------------- 終了処理 void endfunc() { nn::fs::UnregisterCardEjectedEvent(); nn::fs::UnregisterCardInsertedEvent(); ejectEvnt.Finalize(); insEvnt.Finalize(); #ifdef CSM_FLAG_USE_SD if (alive_Sd){ nn::fs::UnregisterSdmcEjectedEvent(); nn::fs::UnregisterSdmcInsertedEvent(); ejectEvntSd.Finalize(); insEvntSd.Finalize(); } #endif savedata.Finalize(); memsave.Finalize(); ScrFinalize(); } //エラー停止 void failstop() { NN_LOG("fail %d,stop\n",errRes); while(1){ CheckSysBreak(); } } //エラー表示 tChar sts[64]; void PutError(TgtDev dev,int cd) { nn::Result res; switch (dev) { case TGT_CARD: res = savedata.LastNnResult;break; case TGT_MEM: res = memsave.LastNnResult;break; #ifdef CSM_FLAG_USE_SD case TGT_SD: res = exsave.LastNnResult;break; #endif default: dev = TGT_NONE;//GetDescriptエラー対策 } GetErrorStr(dev,res,cd,sts); scr_Status(sts,COLOR_RED); } //表示テキストの読込(eMessTagの順) //テキストはSJISやASCII不可、U-16で保存し文字列は改行(0a0d)で終える //BOM(先頭2バイトのゴミ ^^; )は付けない事 void LoadMessText(wchar_t* path) { const size_t ROMFS_BUFFER_SIZE = 1024*8; static char buffer[ROMFS_BUFFER_SIZE]; NN_LOG("reqsize = %d",nn::fs::GetRomRequiredMemorySize(8,8)); nn::fs::MountRom(4, 4, buffer, ROMFS_BUFFER_SIZE); nn::fs::FileReader Reader; if(Reader.TryInitialize(path).IsFailure()) { NN_LOG("fail to read file $s\n",path); endfunc(); } const u32 size = (u32)Reader.GetSize(); if (size > (MT_END*MESS_SIZE*2)) { NN_LOG("textfile:size over\n"); endfunc(); } Reader.Read(Mess,size); int i,n; for (i=0;i 512) || (size < 0))return false; savedata.SetPos(pos); if(savedata.Read(fileBuffer,size) != size ) { NN_LOG("%d\n",savedata.LastNnResult.GetDescription()); if(nn::fs::ResultVerificationFailed::Includes(savedata.LastNnResult)) {//検証エラー for (int i = 0; i < size; i++) fileBuffer[i] = 0;//0埋め }else return false; } return true; } //-------------------------------------------------- Verifi bool Verifi(bool flg) { myResult res; int ct,i,msize,rsize; s64 total=0,pos; //セーブデータのマウント res = savedata.Mount(); if(res != RESULT_OK){ //PutError(TGT_CARD); return false; } //バッファデバイスのマウント res = memsave.Mount(); if(res != RESULT_OK){ //PutError(TGT_MEM); return false; } //ディレクトリの確認 //空ディレクトリチェックするアプリあり(マリカ7) //ファイルなし状態ありえるので先に確認 if( dcList.num >0 ) { nn::fs::Directory dc; for(i=0;i=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount); if (savedata.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = savedata.FileSize; total += fsize; if (memsave.Open(file_pathw)==false) { res = RESULT_FAIL_OPENW; //savedata.Close(); break; } if (fsize != memsave.FileSize)//file size { res = RESULT_FAIL; break; } msize=fsize; if(msize <= FILEBUFF_SIZE ){//一回で読めるサイズならゲージは出さない scr_CountPerMax2(0,0,-1); } pos = 0; while(fsize) { if(msize > FILEBUFF_SIZE ){ scr_CountPerMax2(msize-fsize,msize,0); } if (fsize > FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if (ReadBlock(pos,rsize)==false) { res = RESULT_FAIL_READ; break; } pos += rsize; if(memsave.Read(fileBuffer_ex,rsize) != rsize ) {//メモリなので実行時バグ res = RESULT_FAIL; break; } //verifi for (i = 0;i arcInfo.FileEntry) { savedata.Unmount(); PutError(TGT_NONE,ERC_EXEC); return ERROR; } if (arcInfo.DirCount > arcInfo.DirEntry) { savedata.Unmount(); PutError(TGT_NONE,ERC_EXEC); return ERROR; } if(arcInfo.DirCount >= MAX_DCLIST) {//ディレクトリ数が多すぎ savedata.Unmount(); PutError(TGT_MEM,ERC_EXEC); return ERROR; } //メモリFSの予想占有サイズ sSize = arcInfo.total+arcInfo.FileCount*512; #ifdef CSM_FLAG_FORCE_SD return Card2Sd();//デバグ用、強制でSDへ //警告抑制 unreachable #pragma diag_suppress 111 #endif //簡易サイズチェック if (sSize > MEM_BKUP_SIZE) { #ifdef CSM_FLAG_USE_SD if (alive_Sd)return Card2Sd();//SD使用 #endif savedata.Unmount(); PutError(TGT_MEM,ERC_NOSPACE);//容量オーバ return ERROR; } #ifdef CSM_FLAG_USE_SD //本体メモリ使用 BkupSd = false; #endif //一時保存が存在したら消す scr_Status(MessTxt(MT_ChkBkup),COLOR_YELLO); if(memsave.IsExist()){ if(memsave.Delete()==false) { savedata.Unmount(); PutError(TGT_MEM); return ERROR; } } //一時保存先の作成 scr_Status(MessTxt(MT_CrtBkup),COLOR_YELLO); res = memsave.Create(arcInfo.DirEntry+1,arcInfo.FileEntry+1); if (res != RESULT_OK){ savedata.Unmount(); PutError(TGT_MEM); return ERROR; } if (arcInfo.FileCount == 0)//ファイルが無い { scr_Status(MessTxt(MT_FileNot),COLOR_YELLO); savedata.Unmount(); memsave.Unmount(); strcpy(arcInfo.Pcode,savedata.PrdCode); return SUCCESS;//フォーマットのみ }else{ scr_Status(MessTxt(MT_Reading),COLOR_SKY); savedata.ResetPath(); s64 fsize; res = RESULT_OK; while(res==RESULT_OK){ res = savedata.GetPath(file_pathw); if (res != RESULT_OK) { if (res == RESULT_DIR_LEVEL_OVER){ //PutError(ERC_DEV_CARD,ERC_DIRDEPTH); PutError(TGT_NONE,ERC_WORK); break; } if (res == RESULT_PATH_LENGTH_OVER){ //PutError(ERC_DEV_CARD,ERC_PATH); PutError(TGT_NONE,ERC_WORK); break; } res = RESULT_FAIL_OPEN; break; } if (file_pathw[0] == 0)break;//root then end if (ct>=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount); if (savedata.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = savedata.FileSize; total += fsize; if (memsave.OpenW(file_pathw,fsize)==false) { res = RESULT_FAIL_OPENW; //savedata.Close(); break; } //検証単位の512ずつ msize=fsize; pos = 0; while(fsize) { if(msize > FILEBUFF_SIZE * 2){ scr_CountPerMax2(msize-fsize,msize,0); } if (fsize>FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if (ReadBlock(pos,rsize)==false) { res = RESULT_FAIL_READ; break; } pos += rsize; if(memsave.Write(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_WRITE; break; } } savedata.Close(); memsave.CloseW(); //todo: ゲージ表示 ct++; } savedata.Close(); memsave.CloseW(); } if (res == RESULT_OK){ if (ct != arcInfo.FileCount)//ファイル数に満たないパス検索終了 {//ディレクトリ情報かパス取得で失敗してると思われる PutError(TGT_NONE,ERC_EXEC); return ERROR; } } savedata.Unmount(); //memsave.Commit(); Nand,SDはコミット入れる memsave.Unmount(); scr_CountPerMax(ct,arcInfo.FileCount); if(total>0)scr_CountPerMax2(0,0,total); //結果 switch(res) { case RESULT_OK: if (Verifi(false)) { scr_Status(MessTxt(MT_Complate),COLOR_WHITE); strcpy(arcInfo.Pcode,savedata.PrdCode); return SUCCESS; } PutError(TGT_CARD,ERC_VERIFI); break; case RESULT_FAIL_WRITE: case RESULT_FAIL_OPENW: PutError(TGT_MEM,ERC_WRITE); break; case RESULT_FAIL_READ: case RESULT_FAIL_OPEN: PutError(TGT_CARD,ERC_READ); break; default: PutError(TGT_NONE,ERC_EXEC); break; } memsave.Delete();//バックアップを削除 return ERROR; } bool mkdir; //---------------------------------------------------------------- to CARD RetCode Ctr2Card() { myResult res; int ct=0; int msize,rsize; s64 total = 0; scr_RestoreYesNo();//実行確認 WaitUI();//入力待ち if(isInsEject != InEx_None ){//挿抜? //isInsEject = InEx_None; return INSEXIT;//挿抜による中断 } if (scr_evnt != EVNT_YES)return CANCEL;//キャンセル scr_Restore();//画面表示 scr_Status(MessTxt(MT_ChkBkup),COLOR_YELLO); #ifdef CSM_FLAG_USE_SD if (BkupSd)return Sd2Card();//SD使用 #endif //一時保存のマウント res = memsave.Mount(); if(res != RESULT_OK){ PutError(TGT_MEM); return ERROR; } //セーブデータのフォーマット&マウント scr_Status(MessTxt(MT_CardFormat),COLOR_YELLO); res = savedata.Format(&arcInfo); if(res != RESULT_OK){ memsave.Unmount(); PutError(TGT_CARD); return ERROR; } //ディレクトリ作成 //空ディレクトリもチェックするゲームあるので(マリカ7) //ファイルの存在と関係なく作成 if(dcList.num >0) { int i,rev=0; for (i=0;i=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount); if (memsave.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = memsave.FileSize; total += fsize; if (savedata.OpenC(file_pathw,fsize,&mkdir)==false) { //ディレクトリは作成済みなので作成されたらエラー res = RESULT_FAIL_OPENW; break; } /* int rev = 0; while(1){ if (savedata.OpenC(file_pathw,fsize,&mkdir)==false) { if(mkdir)//ディレクトリのみ作成 {//深い場合に作成が多いと画面が止まるので rev++; if (rev & 1)scr_MessOnCount2(MessTxt(MT_CrtDir1)); else scr_MessOnCount2(MessTxt(MT_CrtDir2)); }else{ res = RESULT_FAIL_OPENW; //memsave.Close(); break; } }else break; } if (res != RESULT_OK)break; */ //メモリファイルで検証エラーはないので512ごとである必要は無いが //メモリ節約のため同じ方法をとる msize = fsize; int itvl = 0; while(fsize) { if(msize > FILEBUFF_SIZE * 2){ scr_CountPerMax2(msize-fsize,msize,0); if (++itvl & 1)scr_Status(MessTxt(MT_Copy1),COLOR_PARPL); else scr_Status(MessTxt(MT_Copy2),COLOR_PARPL); }else scr_MessOnCount2(MES_NULL); if (fsize>FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if(memsave.Read(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_READ; break; } if(savedata.Write(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_WRITE; break; } } savedata.CloseW(); memsave.Close(); ct++; } savedata.Close(); memsave.CloseW(); } savedata.Commit(); savedata.Unmount(); memsave.Unmount(); scr_CountPerMax(ct,arcInfo.FileCount); scr_CountPerMax2(0,0,total); //結果 switch(res){ case RESULT_OK: if (Verifi(true)){ if(memsave.Delete()){//バックアップを削除 scr_Status(MessTxt(MT_Complate),COLOR_WHITE); return SUCCESS; } } PutError(TGT_CARD,ERC_VERIFI); break; case RESULT_FAIL_WRITE: case RESULT_FAIL_OPENW: PutError(TGT_CARD,ERC_WRITE); break; case RESULT_FAIL_READ: case RESULT_FAIL_OPEN: PutError(TGT_MEM,ERC_READ); break; default: PutError(TGT_NONE,ERC_EXEC); break; } return ERROR; } //******************************************** for SD //パス長オーバのファイルはメモリ上に記録 //mem:/元ネーム <- リネーム名格納 //sd:/ワーク/リネーム名 <-データ格納 //20000ファイル程度まで #ifdef CSM_FLAG_USE_SD bool VerifiSd() { myResult res; int ct,i,msize,rsize; s64 total=0,pos; wchar_t ws[20];//"/"+16文字+"\0" //セーブデータのマウント res = savedata.Mount(); if(res != RESULT_OK){ //PutError(TGT_CARD); return false; } //出力デバイスのマウント res = exsave.Mount(); if(res != RESULT_OK){ //PutError(TGT_SD); return false; } memsave.Mount(); ct =0; if (arcInfo.FileCount == 0)//ファイルが無い { savedata.Unmount(); exsave.Unmount(); return true;//フォーマット一致のみ }else{ scr_Status(MessTxt(MT_Compair),COLOR_YELLO); savedata.ResetPath(); exsave.ResetPath(); s64 fsize; res = RESULT_OK; while(res==RESULT_OK){ if(CheckInsExit())break;//挿抜による中断 res = savedata.GetPath(file_pathw); if (res != RESULT_OK) { if (res == RESULT_DIR_LEVEL_OVER){ break; } if (res == RESULT_PATH_LENGTH_OVER){ break; } res = RESULT_FAIL_OPEN; break; } if (file_pathw[0] == 0)break;//root then end if (ct>=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } if(CheckInsExit())break;//挿抜による中断 scr_CountPerMax(ct,arcInfo.FileCount); if (savedata.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = savedata.FileSize; total += fsize; if (memsave.Open(file_pathw)) {//リネーム対象 memsave.Read((char*)ws,18);//リネーム名 ws[9]=0;ws[10]=0; wcscpy(file_pathw,wk_dir);//格納先ディレクトリ wcscat(file_pathw,ws);//パス変換 memsave.Close(); } if(CheckInsExit())break;//挿抜による中断 if (exsave.Open(file_pathw)==false) { res = RESULT_FAIL_OPENW; //savedata.Close(); break; } if (fsize != exsave.FileSize)//file size { res = RESULT_FAIL; break; } msize=fsize; if(msize <= FILEBUFF_SIZE ){//一回で読めるサイズならゲージは出さない scr_CountPerMax2(0,0,-1); } pos = 0; while(fsize) { if(msize > FILEBUFF_SIZE ){ scr_CountPerMax2(msize-fsize,msize,0); } if (fsize > FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if(CheckInsExit())break;//挿抜による中断 if (ReadBlock(pos,rsize)==false) { res = RESULT_FAIL_READ; break; } pos += rsize; if(CheckInsExit())break;//挿抜による中断 if(exsave.Read(fileBuffer_ex,rsize) != rsize ) { res = RESULT_FAIL; break; } for (i = 0;i=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount);//カウンタ表示 if (exsave.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = exsave.FileSize; total += fsize; if (savedata.CheckPathBit & cpb_ex ) {//違反パス名あり? if (CmpDirW(file_pathw,wk_dir)==1) {//リネームファイルなら元のパス名を探す memsave.ResetPath(); int i=0;bool b=false; ws[9]=0;ws[10]=0; while(i差替?? or メモリ破壊?? res = RESULT_FAIL; break; } } } int rev = 0; while(1){ if (savedata.OpenC(file_pathw,fsize,&mkdir)==false) { if(mkdir)//ディレクトリのみ作成 {//深い場合に作成が多いと画面が止まるので rev++; if (rev & 1)scr_MessOnCount2(MessTxt(MT_CrtDir1)); else scr_MessOnCount2(MessTxt(MT_CrtDir2)); }else{ res = RESULT_FAIL_OPENW; //exsave.Close(); break; } }else break; }//while if (res != RESULT_OK)break; msize = fsize; int itvl = 0; while(fsize) { if(msize > FILEBUFF_SIZE * 2){ scr_CountPerMax2(msize-fsize,msize,0); if (++itvl & 1)scr_Status(MessTxt(MT_Copy1),COLOR_PARPL); else scr_Status(MessTxt(MT_Copy2),COLOR_PARPL); }else scr_MessOnCount2(MES_NULL); if (fsize>FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if(exsave.Read(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_READ; break; } if(savedata.Write(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_WRITE; break; } }//while savedata.CloseW(); exsave.Close(); ct++; }//while savedata.Close(); exsave.CloseW(); if(res == RESULT_OK)savedata.Commit(); savedata.Unmount(); exsave.Unmount(); memsave.Unmount(); scr_CountPerMax(ct,arcInfo.FileCount); scr_CountPerMax2(0,0,total); //結果 TgtDev ercd; int ercc; switch(res){ case RESULT_OK: if (VerifiSd()){ memsave.Delete(); if(exsave.Delete()){//バックアップを削除 scr_Status(MessTxt(MT_Complate),COLOR_WHITE); return SUCCESS; } } ercd =TGT_CARD;ercc = ERC_VERIFI;//ベリファイエラー break; case RESULT_FAIL_WRITE: case RESULT_FAIL_OPENW: ercd = TGT_CARD;ercc = ERC_WRITE; break; case RESULT_FAIL_READ: case RESULT_FAIL_OPEN: ercd = TGT_SD;ercc = ERC_READ; break; default: ercd = TGT_NONE;ercc = ERC_EXEC; break; } return PutErrRetCode(ercd,ercc); } //Crad2Ctrより呼ばれる RetCode Card2Sd() { myResult res; int ct=0,msize,rsize; s64 total=0,fsize,pos; int i; wchar_t ws[20];//"/"+16文字+"\0" BkupSd = true; //拡張セーブが存在したら消す scr_Status(MessTxt(MT_ChkBkup),COLOR_YELLO); if(exsave.IsExist()){ if(exsave.Delete()==false) return PutErrRetCode(TGT_SD); } if(CheckInsExit())return INSEXIT;//抜けで"なし"判定の場合 //一時保存先の作成 scr_Status(MessTxt(MT_CrtBkup),COLOR_YELLO); //拡張セーブで違反になるパス名あり? if (savedata.CheckPathBit & cpb_ex ) { //該当ファイルを変名保存するディレクトリ追加 res = exsave.Create(arcInfo.DirEntry+1,arcInfo.FileEntry); }else res = exsave.Create(arcInfo.DirEntry,arcInfo.FileEntry); if (res != RESULT_OK) return PutErrRetCode(TGT_SD); if(CheckInsExit())return INSEXIT;//抜け //パラメータはバックアップ時 //Top画面開始時、挿入イベントで取得 if (arcInfo.FileCount == 0)//ファイルが無い { scr_Status(MessTxt(MT_FileNot),COLOR_YELLO); strcpy(arcInfo.Pcode,savedata.PrdCode); savedata.Unmount(); return SUCCESS;//フォーマットのみ } //簡易サイズチェック //SDのクラスタサイズやヘッダでメモリFSより多く占有する //セクタサイズ4K:2GB、32K:4GB 余裕を見ておく(ファイル数上限での最悪) //4GBデバイスの移行なら、8G以上のSDを使う fsize = exsave.GetFreeSize();//取得APIがマウント必須なのでCreate後でないとダメ //ヘッダ16k+4K(ファイル名+アライメント) ..SDセクタサイズ4Kの前提 sSize = arcInfo.total + arcInfo.FileCount*20480; if ((sSize > fsize) || (fsize ==0)) return PutErrRetCode(TGT_SD,ERC_NOSPACE); //メモリFS上に作成..ファイル名保存のため、中身はSD上のファイル名 //拡張セーブは最大パス長がユーザセーブより短いため超えるものは変名 //オリジナルファイル名で作成したファイルに変更後のファイル名を格納 //ワークの10MByteで1万ファイルくらい memsave.Delete(); memsave.Create(1,arcInfo.FileCount); memsave.Mount(); //read scr_Status(MessTxt(MT_Reading),COLOR_PARPL); savedata.ResetPath(); total=0;ct=0;rename_n=0; res = RESULT_OK; while(res==RESULT_OK){ res = savedata.GetPath(file_pathw); if (res != RESULT_OK) { if ((res != RESULT_DIR_LEVEL_OVER) && (res != RESULT_PATH_LENGTH_OVER)) res = RESULT_FAIL_OPEN; break; } if (file_pathw[0] == 0)break;//root then end if (ct>=arcInfo.FileCount) {//カウンタ壊れ、メモリフローの可能性 res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount); if (savedata.Open(file_pathw)==false) { res = RESULT_FAIL_OPEN; break; } fsize = savedata.FileSize; total += fsize; //パス名チェック if (CheckPathEx(file_pathw) == false) {//メモリFS上にファイル作成(パス名記録のみ目的) savedata.Close(); if (memsave.OpenW(file_pathw,512)==false) { res = RESULT_FAIL_OPENW; break; } //リネーム名の格納 swprintf(ws,16,L"/%8x",rename_n);//リネーム for (i=1;i<8;i++)if(ws[i]==L' ')ws[i]=L'0'; rename_n++; if(memsave.Write( (char*)ws,18) != 18)//wchar 9文字 { res = RESULT_WORK_OVER;//ワーク不足 break; } memsave.CloseW(); }else { int rev = 0; while(1){ if (exsave.OpenC(file_pathw,fsize,&mkdir)==false) { if(mkdir)//ディレクトリのみ作成 {//深い場合に作成が多いと画面が止まるので rev++; if (rev & 1)scr_MessOnCount2(MessTxt(MT_CrtDir1)); else scr_MessOnCount2(MessTxt(MT_CrtDir2)); }else{ res = RESULT_FAIL_OPENW; //exsave.Close(); break; } }else break; } if (res != RESULT_OK)break; msize = fsize; int itvl = 0; pos =0; while(fsize) { if(msize > FILEBUFF_SIZE * 2) { scr_CountPerMax2(msize-fsize,msize,0); if (++itvl & 1)scr_Status(MessTxt(MT_Copy1),COLOR_PARPL); else scr_Status(MessTxt(MT_Copy2),COLOR_PARPL); }else scr_MessOnCount2(MES_NULL); if (fsize>FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if (ReadBlock(pos,rsize)==false) { res = RESULT_FAIL_READ; break; } pos += rsize; if(exsave.Write(fileBuffer,rsize) != rsize ) { NN_LOG("desc = %d\n",savedata.LastNnResult.GetDescription()); res = RESULT_FAIL_WRITE; break; } }//fsize ct++; savedata.Close(); exsave.CloseW(); }//else }//while if ((res == RESULT_OK) && (savedata.CheckPathBit & cpb_ex)) {//違反パスありなら、リネームして保存 nn::Result result; nn::fnd::DateTime tm; //ディレクトリ作成..既存名を避けるため作成後に行う i = 0; while(i++ < 10 ){//試行回数(無駄?) tm = nn::fnd::DateTime::GetNow(); //退避先ディレクトリ名作成:SDK制限で16文字まで swprintf(file_pathw,3,L"%3d",tm.GetMilliSecond()); if (file_pathw[0]==L' ')file_pathw[0] = L'0'; if (file_pathw[1]==L' ')file_pathw[1] = L'0'; wcscpy(wk_dir,L"/cSm_");//通常、使われないであろう名前にする wcscat(wk_dir,file_pathw); wcscat(wk_dir,L"_zzz"); exsave.GetRootPath(file_pathw); wcscat(file_pathw,wk_dir); result = nn::fs::TryCreateDirectory(file_pathw); if (result.IsSuccess())break; if(CheckInsExit())break; } if (result.IsFailure()){ exsave.Unmount(); memsave.Unmount(); savedata.Unmount(); exsave.Delete();//バックアップを削除 memsave.Delete(); if(isInsEject != InEx_None )return INSEXIT; PutError(TGT_SD,ERC_WORK);//退避先が確保できない return ERROR; } memsave.ResetPath(); while(res==RESULT_OK){ res = memsave.GetPath(file_pathw);//リネーム対象ファイル名 if (file_pathw[0] == 0)break;//root then end if (ct>=arcInfo.FileCount) { res = RESULT_FAIL; break; } scr_CountPerMax(ct,arcInfo.FileCount); if (savedata.Open(file_pathw)==false)//ユーザセーブ開く { res = RESULT_FAIL_OPEN; break; } fsize = savedata.FileSize; //リネーム名取得 if (memsave.Open(file_pathw)==false) { res = RESULT_FAIL;//メモリからなので通常ありえない break; } if(memsave.Read((char*)ws,18)!=18)//wchar 9文字 { res = RESULT_FAIL;//メモリからなので通常ありえない break; } memsave.Close(); ws[9]=0;//終端 ws[10]=0; wcscpy(file_pathw2,wk_dir); wcscat(file_pathw2,ws); if (exsave.OpenC(file_pathw2,fsize,&mkdir)==false) {//エラーもしくは、退避先ディレクトリが無い res = RESULT_FAIL_OPENW; break; } msize = fsize; pos =0; while(fsize) { if (fsize>FILEBUFF_SIZE) { rsize = FILEBUFF_SIZE; fsize -= FILEBUFF_SIZE; }else{ rsize = fsize; fsize = 0; } if (ReadBlock(pos,rsize)==false) { res = RESULT_FAIL_READ; break; } pos += rsize; if(exsave.Write(fileBuffer,rsize) != rsize ) { res = RESULT_FAIL_WRITE; break; } }//while(fsize) ct++; savedata.Close(); exsave.CloseW(); memsave.Close(); }//while }//if savedata.Unmount(); exsave.Unmount(); memsave.Unmount(); scr_CountPerMax(ct,arcInfo.FileCount); scr_CountPerMax2(0,0,total); //結果 TgtDev ercd; int ercc; switch(res) { case RESULT_OK: if (VerifiSd()) { scr_Status(MessTxt(MT_Complate),COLOR_WHITE); strcpy(arcInfo.Pcode,savedata.PrdCode); return SUCCESS; } ercd = TGT_CARD;ercc = ERC_VERIFI; break; case RESULT_FAIL_WRITE: case RESULT_FAIL_OPENW: ercd = TGT_SD;ercc = ERC_WRITE;//ライト失敗 break; case RESULT_FAIL_READ: case RESULT_FAIL_OPEN: ercd = TGT_CARD;ercc = ERC_READ;//リード break; case RESULT_WORK_OVER: ercd = TGT_NONE;ercc = ERC_NOSPACE;//ワーク不足..違反フアイル多すぎ break; default: ercd = TGT_NONE;ercc = ERC_EXEC; break; } exsave.Delete();//バックアップを削除 memsave.Delete(); return PutErrRetCode(ercd,ercc); } #endif //--------------------------------------------------------------- セーブデータの情報取得 //呼ぶ前に tmerr のクリアを忘れない事 void CheckSaveDataState() { Formatted = false; Active = false; if (savedata.GetPrdCode()) { //NN_LOG("%d",savedata.LastNnResult.GetDescription()); Active = true;//カード if (savedata.IsExist())//マウントで確認 { Formatted=true; }else{ tmerr |= SDATA_ERRPUT_MEDIA; } } scr_PrdCode(savedata.PrdCode); //プロダクトコードのチェック isAgreePCode = strcmp(savedata.PrdCode,arcInfo.Pcode)==0; } void CheckExSaveState() { #ifdef CSM_FLAG_USE_SD if (alive_Sd)exActive = nn::fs::IsSdmcWritable(); else exActive = true; #else exActive = true; #endif if (exActive){ #ifdef CSM_FLAG_USE_SD if (BkupSd){ exFormatted = exsave.IsExist(); exsave.Unmount(); }else{ #endif exFormatted = memsave.IsExist(); memsave.Unmount(); #ifdef CSM_FLAG_USE_SD } #endif }else{ exFormatted = false; } //デバグ時は抜けてもクリアしない #ifndef CSM_FLAG_DEBUG if (exFormatted==false) arcInfo.Pcode[0] = 0; #endif scr_PrdCodeEx(arcInfo.Pcode); //プロダクトコードのチェック isAgreePCode = strcmp(savedata.PrdCode,arcInfo.Pcode)==0; } //SD挿抜 void Evt_SdInEx() { #ifndef CSM_FLAG_DEBUG ScrClr(); scr_TopMenu(Formatted,Active,exFormatted,exActive,tmerr); scr_Draw(); failstop();//停止 #else CheckExSaveState();//状態更新 scr_evnt = EVNT_INEX; #endif } //---------------------------------------------------------------- 入力待ち void WaitUI() { tmerr = 0; scr_Draw();//画面更新 scr_evnt = EVNT_NONE; while(scr_evnt == EVNT_NONE) { //nn::os::Thread::Yield();//スレッド実行 //scr_GetEvnt();//入力イベント CheckSysBreak();//中断処理 #ifdef CSM_FLAG_USE_SD //挿抜を検知したらアプリ終了待ちするようする if (ejectEvntSd.TryWait()){ isInsEject = InEx_EjcSd; Evt_SdInEx(); return; } if (insEvntSd.TryWait()){ isInsEject = InEx_InsSd; Evt_SdInEx(); return; } #endif //挿抜を検知したら中断して抜ける //トップ以外ではトップメニューへ戻るようする if (ejectEvnt.TryWait()){ isInsEject = InEx_EjcCard; CheckSaveDataState(); scr_evnt = EVNT_INEX; return; } if (insEvnt.TryWait()){ isInsEject = InEx_InsCard; CheckSaveDataState(); scr_evnt = EVNT_INEX; return; } nn::os::Thread::Yield();//スレッド実行..タッチ&ボタン取得 } ScrClr();//画面消去 nn::os::Thread::Sleep(nn::fnd::TimeSpan::FromMilliSeconds(500));//チャタ対策 } void InsExtErrPut(tIsInsEject n) { switch (n) { case InEx_EjcSd: case InEx_InsSd://SD insert or exit PutError(TGT_SD,ERC_PULLOUT); #ifndef CSM_FLAG_DEBUG failstop();//stop running #endif break; case InEx_EjcCard: PutError(TGT_CARD,ERC_DEVICE); break; case InEx_InsCard://素早い抜き差しでEjectイベント消える? PutError(TGT_CARD,ERC_PULLOUT); break; default://通常ありえない .. 実行時エラー PutError(TGT_NONE,ERC_EXEC); failstop();//stop running break; } } //---------------------- main loop -------------------------- void nnMain() { RetCode retc; //tColStr *colstr[4]; //colstr[2] = &mess_WhenExc; //colstr[3] = &mess_PoffSys; isInsEject = InEx_None; //NN_LOG("Start\n"); nn::os::Initialize(); nn::fs::Initialize(); //DEA-SUPにて推奨のフリーズ暫定対策:無線デーモンを停止 (2011.3.1 現在) //ただし、スリープ時は止まらない //心配なら本体スイッチで切っとく nn::ndm::Initialize(); nn::ndm::SuspendScheduler(); //中断処理の準備 InitSysBreak((uptr)endfunc); // グラフィックスライブラリの初期化は、以降で行わなければならない // 他、アプリケーションの初期化処理 nn::applet::DisableSleep();//スリープ非対応 #ifdef CSM_FLAG_USE_SD //起動時にSD確認 alive_Sd = nn::fs::IsSdmcWritable(); #else alive_Sd = false; #endif //Heap appHeap.Initialize(nn::os::GetDeviceMemoryAddress(), nn::os::GetDeviceMemorySize(), nn::os::ALLOCATE_OPTION_LINEAR); const u32 GxHeapSize = 0x800000; heapForGx = reinterpret_cast(appHeap.Allocate(GxHeapSize)); //画面初期化 //メッセージ用テキストの読み込み bool jp; // nn::cfg::CTR::Initialize(); // if (nn::cfg::GetLanguage() == nn::cfg::CFG_LANGUAGE_JAPANESE) // { // jp = true; // LoadMessText(L"rom:/lang_jp.mes"); //} //else{ jp = false; LoadMessText(L"rom:/lang_en.mes"); // } // nn::cfg::Finalize(); #ifdef CSM_FLAG_DEBUG //リリース品と区別するためタイトルに[debug]を付ける(非セキュア品の流出予防) if (ScrInitialize(heapForGx,GxHeapSize,jp,true) == false)finish(); #else if (ScrInitialize(heapForGx,GxHeapSize,jp,false) == false)finish(); #endif //挿抜イベント ejectEvnt.ClearSignal(); insEvnt.ClearSignal(); nn::fs::RegisterCardEjectedEvent(&ejectEvnt); nn::fs::RegisterCardInsertedEvent(&insEvnt); #ifdef CSM_FLAG_USE_SD if (alive_Sd){ ejectEvntSd.ClearSignal(); insEvntSd.ClearSignal(); nn::fs::RegisterSdmcEjectedEvent(&ejectEvntSd); nn::fs::RegisterSdmcInsertedEvent(&insEvntSd); //拡張セーブが存在したら消す if(exsave.IsExist())exsave.Delete(); } #endif // スリープ要求に対する返答を有効にする // また、蓋の状態チェックを行い蓋が閉じられているならスリープ要求が発生する //nn::applet::EnableSleep(true); extern u8 scr_evnt; u8 scr_evntbk; while(1) { tmerr = 0; CheckSaveDataState(); //セーブデータの状態取得 CheckExSaveState(); //同バックアップ if (isAgreePCode==false) tmerr |= SDATA_ERRPUT_PCODE; scr_TopMenu(Formatted,Active,exFormatted,exActive,tmerr); WaitUI();//入力待ち scr_evntbk = scr_evnt; switch(scr_evnt) { case EVNT_INEX: isInsEject= InEx_None; break; case EVNT_PUSH_B: case EVNT_PUSH_R: case EVNT_PUSH_Y: case EVNT_NONE: break; case EVNT_SEL_READ://**************************** リード NN_LOG("select read\n"); retc = Card2Ctr(); switch (retc){ case SUCCESS: //CheckExSaveState(); //状態の更新 scr_ResultQuit(MessTxt(MT_Success),COLOR_GREEN);//成功とQuitボタン break; case INSEXIT: //scr_InsExitQuit();//挿抜検知表示 & Quit ScrClr();//画面消去 scr_Backup(); InsExtErrPut(isInsEject); scr_ResultQuit(MessTxt(MT_Break),COLOR_RED);//エラーとQuitボタン break; case CANCEL: break; default://errors if (CheckInsExit())InsExtErrPut(isInsEject);//抜差イベント? scr_ResultQuit(MessTxt(MT_Failed),COLOR_RED);//エラーとQuitボタン break; } break; case EVNT_SEL_WRITE:// **************************** リストア NN_LOG("select write\n"); retc = Ctr2Card(); switch (retc){ case SUCCESS: //CheckSaveDataState(); //状態の更新 //CheckExSaveState(); scr_ResultQuit(MessTxt(MT_Success),COLOR_GREEN);//成功とQuitボタン break; case INSEXIT: //scr_InsExitQuit();//挿抜検知表示 & Quit ScrClr();//画面消去 scr_Restore(); InsExtErrPut(isInsEject); scr_ResultQuit(MessTxt(MT_Break),COLOR_RED);//エラーとQuitボタン case CANCEL: break; default://errors if (CheckInsExit())InsExtErrPut(isInsEject);//抜差イベント? scr_ResultQuit(MessTxt(MT_Failed),COLOR_RED);//エラーとQuitボタン break; } break; case EVNT_PUSH_LEFT_X:// ------------------------------------- LEFT + X //バックアップ削除 scr_DelConf();//実行確認 WaitUI(); if (scr_evnt != EVNT_YES)break; memsave.Delete(); #ifdef CSM_FLAG_USE_SD if(alive_Sd)exsave.Delete(); #endif break; default: failstop(); break; } if (((scr_evntbk == EVNT_SEL_WRITE)||(scr_evntbk == EVNT_SEL_READ)) && (retc != CANCEL)) { while(1){ WaitUI(); if (scr_evnt == EVNT_INEX)isInsEject = InEx_None; if ((scr_evnt==EVNT_QUIT) || (scr_evnt==EVNT_PUSH_B))break; } } }//while() } /*---------------------------------------------------------------------------* End of file *---------------------------------------------------------------------------*/