/*---------------------------------------------------------------------------* Project: TwlSDK - FS - libraries File: fs_file.c Copyright 2007-2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2010-07-02#$ $Rev: 11351 $ $Author: okubata_ryoma $ *---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include "../include/rom.h" #include "../include/util.h" #include "../include/command.h" #define FS_DEBUG_TRACE(...) (void)0 //#define FS_DEBUG_TRACE OS_TPrintf /*---------------------------------------------------------------------------*/ /* functions */ #if defined(FS_IMPLEMENT) /*---------------------------------------------------------------------------* Name: FSi_IsValidTransferRegion Description: Read/Write操作に使用するバッファが危険な範囲でないか簡易判定。 Arguments: buffer : 転送対象となるバッファ。 length : 転送サイズ。 Returns: posを1文字戻した参照位置か-1。 *---------------------------------------------------------------------------*/ static BOOL FSi_IsValidTransferRegion(const void *buffer, s32 length) { BOOL retval = FALSE; if (buffer == NULL) { OS_TWarning("specified transfer buffer is NULL.\n"); } else if (((u32)buffer >= HW_IOREG) && ((u32)buffer < HW_IOREG_END)) { OS_TWarning("specified transfer buffer is in I/O register %08X. (seems to be dangerous)\n", buffer); } else if (length < 0) { OS_TWarning("specified transfer size is minus. (%d)\n", length); } else { #if !defined(SDK_TWL) s32 mainmem_size = HW_MAIN_MEM_EX_SIZE; #else s32 mainmem_size = OS_IsRunOnTwl() ? HW_TWL_MAIN_MEM_EX_SIZE : HW_MAIN_MEM_EX_SIZE; #endif if (length > mainmem_size) { OS_TWarning("specified transfer size is over mainmemory-size. (%d)\n", length); } else { retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FSi_DecrementSjisPosition Description: Shift_JIS文字列の参照位置を1文字分戻す。 Arguments: str Shift_JIS文字列の先頭を指すポインタ。 pos 現在の文字列参照位置。(バイト単位) Returns: posを1文字戻した参照位置か-1。 *---------------------------------------------------------------------------*/ int FSi_DecrementSjisPosition(const char *str, int pos) { // まず1バイト分だけは確実に戻る。 int prev = --pos; // Shift_JISの文字区切りとなるのはシングルバイトか後続バイトであり // 先行バイトと後続バイトがマッピングを一部共有しているため、 // そこが先行バイトに見えるうちは文字種別が確定しないのでさらに戻る。 for (; (prev > 0) && STD_IsSjisLeadByte(str[prev - 1]); --prev) { } // "===b"のようにあいまいな後続バイトを持つ全角文字が並んでいたら // 2の倍数だけ余分に戻っているためこれを無視する。(2の剰余を取る) return pos - ((pos - prev) & 1); } /*---------------------------------------------------------------------------* Name: FSi_IncrementSjisPositionToSlash Description: Shift_JIS文字列の参照位置を ディレクトリ区切り文字か終端まで進める。 Arguments: str Shift_JIS文字列の先頭を指すポインタ。 pos 現在の文字列参照位置。(バイト単位) Returns: pos以降に現れる最初のディレクトリ区切りか終端の位置。 *---------------------------------------------------------------------------*/ int FSi_IncrementSjisPositionToSlash(const char *str, int pos) { while (str[pos] && !FSi_IsSlash((u8)str[pos])) { pos = FSi_IncrementSjisPosition(str, pos); } return pos; } /*---------------------------------------------------------------------------* Name: FSi_DecrementSjisPositionToSlash Description: Shift_JIS文字列の参照位置を ディレクトリ区切り文字か先頭まで戻す。 Arguments: str Shift_JIS文字列の先頭を指すポインタ。 pos 現在の文字列参照位置。(バイト単位) Returns: pos未満に現れる最初のディレクトリ区切りか-1。 *---------------------------------------------------------------------------*/ int FSi_DecrementSjisPositionToSlash(const char *str, int pos) { for (;;) { pos = FSi_DecrementSjisPosition(str, pos); if ((pos < 0) || FSi_IsSlash((u8)str[pos])) { break; } } return pos; } /*---------------------------------------------------------------------------* Name: FSi_TrimSjisTrailingSlash Description: Shift_JIS文字列の終端がディレクトリ区切り文字であれば除去。 Arguments: str Shift_JIS文字列。 Returns: 文字列長。 *---------------------------------------------------------------------------*/ int FSi_TrimSjisTrailingSlash(char *str) { int length = STD_GetStringLength(str); int lastpos = FSi_DecrementSjisPosition(str, length); if ((lastpos >= 0) && FSi_IsSlash((u8)str[lastpos])) { length = lastpos; str[length] = '\0'; } return length; } /*---------------------------------------------------------------------------* Name: FSi_DecrementUnicodePosition Description: Unicode文字列の参照位置を1文字分戻す。 Arguments: str Unicode文字列の先頭を指すポインタ。 pos 現在の文字列参照位置。(バイト単位) Returns: posを1文字戻した参照位置か-1。 *---------------------------------------------------------------------------*/ int FSi_DecrementUnicodePosition(const u16 *str, int pos) { // まず1文字分だけは確実に戻る。 int prev = --pos; // 有効なサロゲートペアだった場合はもう1文字戻る。 if ((pos > 0) && ((str[pos - 1] >= 0xD800) && (str[pos - 1] <= 0xDC00)) && ((str[pos - 0] >= 0xDC00) && (str[pos - 0] <= 0xE000))) { --pos; } return pos; } /*---------------------------------------------------------------------------* Name: FSi_DecrementUnicodePositionToSlash Description: Unicode文字列の参照位置を ディレクトリ区切り文字か先頭まで戻す。 Arguments: str Unicode文字列の先頭を指すポインタ。 pos 現在の文字列参照位置。(バイト単位) Returns: pos未満に現れる最初のディレクトリ区切りか-1。 *---------------------------------------------------------------------------*/ int FSi_DecrementUnicodePositionToSlash(const u16 *str, int pos) { for (;;) { pos = FSi_DecrementUnicodePosition(str, pos); if ((pos < 0) || FSi_IsUnicodeSlash(str[pos])) { break; } } return pos; } /*---------------------------------------------------------------------------* Name: FS_InitFile Description: FSFile構造体を初期化 Arguments: file FSFile構造体 Returns: None. *---------------------------------------------------------------------------*/ void FS_InitFile(FSFile *file) { SDK_NULL_ASSERT(file); { file->arc = NULL; file->userdata = NULL; file->next = NULL; OS_InitThreadQueue(file->queue); file->stat = 0; file->stat |= (FS_COMMAND_INVALID << FS_FILE_STATUS_CMD_SHIFT); file->argument = NULL; file->error = FS_RESULT_SUCCESS; } } /*---------------------------------------------------------------------------* Name: FS_CancelFile Description: 非同期コマンドのキャンセルを要求する Arguments: file ファイルハンドル Returns: None. *---------------------------------------------------------------------------*/ void FS_CancelFile(FSFile *file) { SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); { OSIntrMode bak_psr = OS_DisableInterrupts(); if (FS_IsBusy(file)) { file->stat |= FS_FILE_STATUS_CANCEL; file->arc->flag |= FS_ARCHIVE_FLAG_CANCELING; } (void)OS_RestoreInterrupts(bak_psr); } } /*---------------------------------------------------------------------------* Name: FS_CreateFile Description: ファイルを生成する Arguments: path パス名 mode アクセスモード Returns: ファイルが正常に生成されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CreateFile(const char *path, u32 permit) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForCreateFile arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->permit = permit; retval = FSi_SendCommand(file, FS_COMMAND_CREATEFILE, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_DeleteFile Description: ファイルを削除する Arguments: path パス名 Returns: ファイルが正常に削除されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_DeleteFile(const char *path) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForDeleteFile arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; retval = FSi_SendCommand(file, FS_COMMAND_DELETEFILE, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_RenameFile Description: ファイル名を変更する Arguments: src 変換元のファイル名 dst 変換先のファイル名 Returns: ファイル名が正常に変更されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_RenameFile(const char *src, const char *dst) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s->%s)\n", __FUNCTION__, src, dst); SDK_NULL_ASSERT(src); SDK_NULL_ASSERT(dst); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath_src[FS_ARCHIVE_FULLPATH_MAX + 1]; char relpath_dst[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid_src = 0; u32 baseid_dst = 0; FSArchive *arc_src = FS_NormalizePath(src, &baseid_src, relpath_src); FSArchive *arc_dst = FS_NormalizePath(dst, &baseid_dst, relpath_dst); if (arc_src != arc_dst) { OS_TWarning("cannot rename between defferent archives.\n"); } else { FSFile file[1]; FSArgumentForRenameFile arg[1]; FS_InitFile(file); file->arc = arc_src; file->argument = arg; arg->baseid_src = baseid_src; arg->relpath_src = relpath_src; arg->baseid_dst = baseid_dst; arg->relpath_dst = relpath_dst; retval = FSi_SendCommand(file, FS_COMMAND_RENAMEFILE, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_GetPathInfo Description: ファイルまたはディレクトリの情報を取得する Arguments: path パス名 info 情報の格納先 Returns: 処理結果 *---------------------------------------------------------------------------*/ BOOL FS_GetPathInfo(const char *path, FSPathInfo *info) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(path); SDK_NULL_ASSERT(info); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForGetPathInfo arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->info = info; retval = FSi_SendCommand(file, FS_COMMAND_GETPATHINFO, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_SetPathInfo Description: ファイル情報を設定する Arguments: path パス名 info 情報の格納先 Returns: 処理結果 *---------------------------------------------------------------------------*/ BOOL FS_SetPathInfo(const char *path, const FSPathInfo *info) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(path); SDK_NULL_ASSERT(info); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForSetPathInfo arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->info = (FSPathInfo*)info; //info->attributesのFATFS_PROPERTY_CTRL_MASKを落とすため retval = FSi_SendCommand(file, FS_COMMAND_SETPATHINFO, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_CreateDirectory Description: ディレクトリを生成する Arguments: path パス名 mode アクセスモード Returns: ディレクトリが正常に生成されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CreateDirectory(const char *path, u32 permit) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForCreateDirectory arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->permit = permit; retval = FSi_SendCommand(file, FS_COMMAND_CREATEDIRECTORY, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_DeleteDirectory Description: ディレクトリを削除する Arguments: path パス名 Returns: ディレクトリが正常に削除されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_DeleteDirectory(const char *path) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForDeleteDirectory arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; retval = FSi_SendCommand(file, FS_COMMAND_DELETEDIRECTORY, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_RenameDirectory Description: ディレクトリ名を変更する Arguments: src 変換元のディレクトリ名 dst 変換先のディレクトリ名 Returns: ディレクトリ名が正常に変更されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_RenameDirectory(const char *src, const char *dst) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s->%s)\n", __FUNCTION__, src, dst); SDK_NULL_ASSERT(src); SDK_NULL_ASSERT(dst); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath_src[FS_ARCHIVE_FULLPATH_MAX + 1]; char relpath_dst[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid_src = 0; u32 baseid_dst = 0; FSArchive *arc_src = FS_NormalizePath(src, &baseid_src, relpath_src); FSArchive *arc_dst = FS_NormalizePath(dst, &baseid_dst, relpath_dst); if (arc_src != arc_dst) { OS_TWarning("cannot rename between defferent archives.\n"); } else { FSFile file[1]; FSArgumentForRenameDirectory arg[1]; FS_InitFile(file); file->arc = arc_src; file->argument = arg; arg->baseid_src = baseid_src; arg->relpath_src = relpath_src; arg->baseid_dst = baseid_dst; arg->relpath_dst = relpath_dst; retval = FSi_SendCommand(file, FS_COMMAND_RENAMEDIRECTORY, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FSi_GetFullPath Description: 指定されたパスをフルパスへ変換する。 Arguments: dst 得られたフルパスを格納するバッファ。 FS_ARCHIVE_FULLPATH_MAX+1バイト以上でなければならない。 path ファイルまたはディレクトリのパス名。 Returns: フルパスが正常に取得できればTRUE *---------------------------------------------------------------------------*/ static BOOL FSi_GetFullPath(char *dst, const char *path) { FSArchive *arc = FS_NormalizePath(path, NULL, dst); if (arc) { const char *arcname = FS_GetArchiveName(arc); int m = STD_GetStringLength(arcname); int n = STD_GetStringLength(dst); (void)STD_MoveMemory(&dst[m + 2], &dst[0], (u32)n + 1); (void)STD_MoveMemory(&dst[0], arcname, (u32)m); dst[m + 0] = ':'; dst[m + 1] = '/'; } return (arc != NULL); } /*---------------------------------------------------------------------------* Name: FSi_ComplementDirectory Description: 指定されたパスに至る全ての親ディレクトリの存在を確認し、 存在しない階層は自動的に生成して補完する。 Arguments: path ファイルまたはディレクトリのパス名。 これより1階層上までのディレクトリを補完する。 autogen 自動生成された最上位ディレクトリを記録するバッファ。 FS_ARCHIVE_FULLPATH_MAX+1バイト以上でなければならない。 結果の成否にかかわらずautogenには結果が返され、 空文字列であればすでに全て存在していたことを示す。 Returns: ディレクトリが正常に生成されればTRUE *---------------------------------------------------------------------------*/ static BOOL FSi_ComplementDirectory(const char *path, char *autogen) { BOOL retval = FALSE; int root = 0; // いったんフルパス名に正規化。(スタック節約のためautogenを流用) char *tmppath = autogen; if (FSi_GetFullPath(tmppath, path)) { int length = STD_GetStringLength(tmppath); if (length > 0) { int pos = 0; FS_DEBUG_TRACE(" trying to complete \"%s\"\n", tmppath); // 終端の"/"は除去。 length = FSi_TrimSjisTrailingSlash(tmppath); // 最下層のエントリは無視。 length = FSi_DecrementSjisPositionToSlash(tmppath, length); // 存在する最深の階層を探索。 for (pos = length; pos >= 0;) { FSPathInfo info[1]; BOOL exists; tmppath[pos] = '\0'; exists = FS_GetPathInfo(tmppath, info); FS_DEBUG_TRACE(" - \"%s\" is%s existent (result:%d)\n", tmppath, exists ? "" : " not", FS_GetArchiveResultCode(tmppath)); tmppath[pos] = '/'; // エントリが存在しなければさらに上位の階層へ。 if (!exists) { pos = FSi_DecrementSjisPositionToSlash(tmppath, pos); } // 何か存在していればひとまずここで探索終了。 else { // 同名のファイルが存在している場合は確実に失敗。 if ((info->attributes & FS_ATTRIBUTE_IS_DIRECTORY) == 0) { pos = -1; } // 同名のディレクトリが存在していればその下層を自動生成する。 else { ++pos; } break; } } // 基点を求められたならばそれ以降の階層を繰り返し生成。 if (pos >= 0) { for (;;) { // 終端に達したら成功。 if (pos >= length) { retval = TRUE; break; } else { pos = FSi_IncrementSjisPositionToSlash(tmppath, pos); tmppath[pos] = '\0'; if (!FS_CreateDirectory(tmppath, FS_PERMIT_R | FS_PERMIT_W)) { break; } else { // 最上位の階層は記憶しておく。 if (root == 0) { FS_DEBUG_TRACE(" - we have created \"%s\" as root\n", tmppath); root = pos; } tmppath[pos++] = '/'; } } } } } } // 自動生成された最上位ディレクトリを記録。 autogen[root] = '\0'; return retval; } /*---------------------------------------------------------------------------* Name: FS_CreateFileAuto Description: 必要な中間ディレクトリも含めてファイルを生成する Arguments: path パス名 permit アクセスモード Returns: ファイルが正常に生成されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CreateFileAuto(const char *path, u32 permit) { BOOL result = FALSE; char autogen[FS_ARCHIVE_FULLPATH_MAX + 1]; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); if (FSi_ComplementDirectory(path, autogen)) { result = FS_CreateFile(path, permit); if (!result) { (void)FS_DeleteDirectoryAuto(autogen); } } return result; } /*---------------------------------------------------------------------------* Name: FS_DeleteFileAuto Description: 必要な中間ディレクトリも含めてファイルを削除する Arguments: path パス名 Returns: ファイルが正常に削除されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_DeleteFileAuto(const char *path) { FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); // 命名の統一性のために存在しているが実際には補完処理が不要。 return FS_DeleteFile(path); } /*---------------------------------------------------------------------------* Name: FS_RenameFileAuto Description: 必要な中間ディレクトリも含めてファイル名を変更する Arguments: src 変換元のファイル名 dst 変換先のファイル名 Returns: ファイル名が正常に変更されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_RenameFileAuto(const char *src, const char *dst) { BOOL result = FALSE; char autogen[FS_ARCHIVE_FULLPATH_MAX + 1]; FS_DEBUG_TRACE( "%s(%s->%s)\n", __FUNCTION__); if (FSi_ComplementDirectory(dst, autogen)) { result = FS_RenameFile(src, dst); if (!result) { (void)FS_DeleteDirectoryAuto(autogen); } } return result; } /*---------------------------------------------------------------------------* Name: FS_CreateDirectoryAuto Description: 必要な中間ディレクトリも含めてディレクトリを生成する Arguments: path 生成するディレクトリ名 permit アクセスモード Returns: ディレクトリが正常に生成されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CreateDirectoryAuto(const char *path, u32 permit) { BOOL result = FALSE; char autogen[FS_ARCHIVE_FULLPATH_MAX + 1]; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); if (FSi_ComplementDirectory(path, autogen)) { result = FS_CreateDirectory(path, permit); if (!result) { (void)FS_DeleteDirectoryAuto(autogen); } } return result; } /*---------------------------------------------------------------------------* Name: FS_DeleteDirectoryAuto Description: ディレクトリを再帰的に削除する Arguments: path パス名 Returns: ディレクトリが正常に削除されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_DeleteDirectoryAuto(const char *path) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s(%s)\n", __FUNCTION__, path); if (path && *path) { char tmppath[FS_ARCHIVE_FULLPATH_MAX + 1]; if (FSi_GetFullPath(tmppath, path)) { int pos; BOOL mayBeEmpty; int length = FSi_TrimSjisTrailingSlash(tmppath); FS_DEBUG_TRACE(" trying to force-delete \"%s\"\n", tmppath); mayBeEmpty = TRUE; for (pos = 0; pos >= 0;) { BOOL failure = FALSE; // まず直接このディレクトリを削除してみて成功するなら1階層上へ。 tmppath[length + pos] = '\0'; if (mayBeEmpty && (FS_DeleteDirectory(tmppath) || (FS_GetArchiveResultCode(tmppath) == FS_RESULT_ALREADY_DONE))) { FS_DEBUG_TRACE(" -> succeeded to delete \"%s\"\n", tmppath); pos = FSi_DecrementSjisPositionToSlash(&tmppath[length], pos); } else { // ディレクトリが開けるようなら全エントリを列挙。 FSFile dir[1]; FS_InitFile(dir); if (!FS_OpenDirectory(dir, tmppath, FS_FILEMODE_R)) { FS_DEBUG_TRACE(" -> failed to delete & open \"%s\"\n", tmppath); failure = TRUE; } else { FSDirectoryEntryInfo info[1]; tmppath[length + pos] = '/'; mayBeEmpty = TRUE; while (FS_ReadDirectory(dir, info)) { (void)STD_CopyString(&tmppath[length + pos + 1], info->longname); // ファイルが存在すれば削除。 if ((info->attributes & FS_ATTRIBUTE_IS_DIRECTORY) == 0) { if (!FS_DeleteFile(tmppath)) { FS_DEBUG_TRACE(" -> failed to delete file \"%s\"\n", tmppath); failure = TRUE; break; } FS_DEBUG_TRACE(" -> succeeded to delete \"%s\"\n", tmppath); } // "." または ".." なら無視。 else if ((STD_CompareString(info->longname, ".") == 0) || (STD_CompareString(info->longname, "..") == 0)) { } // 空でないディレクトリが存在すればさらに下層へ移動。 else if (!FS_DeleteDirectory(tmppath)) { pos += 1 + STD_GetStringLength(info->longname); mayBeEmpty = FALSE; break; } } (void)FS_CloseDirectory(dir); } } // 成功すべき操作さえ失敗した場合は処理を中止。(ALREADY_DONEなど) if (failure) { break; } } retval = (pos < 0); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_RenameDirectoryAuto Description: 必要な中間ディレクトリを自動生成してディレクトリ名を変更する Arguments: src 変換元のディレクトリ名 dst 変換先のディレクトリ名 Returns: ディレクトリ名が正常に変更されればTRUE *---------------------------------------------------------------------------*/ BOOL FS_RenameDirectoryAuto(const char *src, const char *dst) { BOOL result = FALSE; char autogen[FS_ARCHIVE_FULLPATH_MAX + 1]; FS_DEBUG_TRACE( "%s(%s->%s)\n", __FUNCTION__, src, dst); if (FSi_ComplementDirectory(dst, autogen)) { result = FS_RenameDirectory(src, dst); if (!result) { (void)FS_DeleteDirectoryAuto(autogen); } } return result; } /*---------------------------------------------------------------------------* Name: FS_GetArchiveResource Description: 指定したアーカイブのリソース情報を取得する Arguments: path アーカイブを特定できるパス名 resource 取得したリソース情報の格納先 Returns: リソース情報が正常に取得できればTRUE *---------------------------------------------------------------------------*/ BOOL FS_GetArchiveResource(const char *path, FSArchiveResource *resource) { BOOL retval = FALSE; SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArchive *arc = FS_NormalizePath(path, NULL, NULL); if (arc) { FSFile file[1]; FSArgumentForGetArchiveResource arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->resource = resource; retval = FSi_SendCommand(file, FS_COMMAND_GETARCHIVERESOURCE, TRUE); } } return retval; } /*---------------------------------------------------------------------------* Name: FSi_GetSpaceToCreateDirectoryEntries Description: ファイルの生成と同時に生成されるディレクトリエントリの容量を見積もる (パス中に存在するディレクトリも新規に作成することを想定する) Arguments: path 生成したいファイルのパス名。 bytesPerCluster ファイルシステム上のクラスタあたりのバイト数。 Returns: 容量 *---------------------------------------------------------------------------*/ u32 FSi_GetSpaceToCreateDirectoryEntries(const char *path, u32 bytesPerCluster) { static const u32 bytesPerEntry = 32UL; static const u32 longnamePerEntry = 13UL; // フルパスならスキームを除去しつつ各エントリを個別に判定。 const char *root = STD_SearchString(path, ":"); const char *current = (root != NULL) ? (root + 1) : path; u32 totalBytes = 0; u32 restBytesInCluster = 0; current += (*current == '/'); while (*current) { BOOL isShortName = FALSE; u32 entries = 0; // エントリ名の長さを算出。 u32 len = (u32)FSi_IncrementSjisPositionToSlash(current, 0); // 8.3形式で表現できるエントリ名かどうか判定。 #if 0 // (TWLで採用しているFATドライバは常に長いファイル名を保存するので // 実際にはここまで厳密な判定処理は必要なかった) { static const char *alnum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; static const char *special = "!#$%&'()*+-<>?@^_`{}~"; if ((len <= 8 + 1 + 3) && STD_SearchChar(alnum, current[0])) { u32 namelen = 0; u32 extlen = 0; u32 scanned = 0; for (; namelen < len; ++namelen) { char c = current[scanned + namelen]; if (!STD_SearchChar(alnum, c) && !STD_SearchChar(special, c)) { break; } } scanned += namelen; if ((scanned < len) && (current[scanned] == '.')) { ++scanned; for (; scanned + extlen < len; ++extlen) { char c = current[scanned + extlen]; if (!STD_SearchChar(alnum, c) && !STD_SearchChar(special, c)) { break; } } scanned += extlen; } if ((scanned == len) && (namelen <= 8) && (extlen <= 3)) { isShortName = TRUE; } } } #endif // 8.3形式でなければ長いファイル名のために追加エントリが必要。 if (!isShortName) { entries += ((len + longnamePerEntry - 1UL) / longnamePerEntry); } // いずれにせよ1エントリ分は常に消費したとみなす。 entries += 1; current += len; // すでに自分が生成したディレクトリのクラスタ余白で足りないなら // 超過分のエントリをクラスタ単位で消費する。 { int over = (int)(entries * bytesPerEntry - restBytesInCluster); if (over > 0) { totalBytes += MATH_ROUNDUP(over, bytesPerCluster); } } // さらに下層があるならディレクトリとして1クラスタ消費し、 // そのうち "." ".." の2エントリ分を引いたサイズが余白となる。 if (*current != '\0') { current += 1; totalBytes += bytesPerCluster; restBytesInCluster = bytesPerCluster - (2 * bytesPerEntry); } } return totalBytes; } /*---------------------------------------------------------------------------* Name: FS_HasEnoughSpaceToCreateFile Description: 指定したパス名とサイズを持つファイルが現在生成可能か判定する Arguments: resource 事前にFS_GetArchiveResource関数で取得したアーカイブ情報。 関数が成功すると、ファイルが消費する分だけ各サイズが減少する。 path 生成したいファイルのパス名。 size 生成したいファイルのサイズ。 Returns: 指定したパス名とサイズを持つファイルが生成可能であればTRUE *---------------------------------------------------------------------------*/ BOOL FS_HasEnoughSpaceToCreateFile(FSArchiveResource *resource, const char *path, u32 size) { BOOL retval = FALSE; u32 bytesPerCluster = resource->bytesPerSector * resource->sectorsPerCluster; if (bytesPerCluster != 0) { u32 needbytes = (FSi_GetSpaceToCreateDirectoryEntries(path, bytesPerCluster) + MATH_ROUNDUP(size, bytesPerCluster)); u32 needclusters = needbytes / bytesPerCluster; if (needclusters <= resource->availableClusters) { resource->availableClusters -= needclusters; resource->availableSize -= needbytes; retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_IsArchiveReady Description: 指定したアーカイブが現在使用可能か判定する Arguments: path "アーカイブ名:"で始まる絶対パス Returns: 指定したアーカイブが現在使用可能であればTRUE。 スロットに挿入されていないSDメモリカードアーカイブや まだインポートされていないセーブデータアーカイブならFALSE。 *---------------------------------------------------------------------------*/ BOOL FS_IsArchiveReady(const char *path) { FSArchiveResource resource[1]; return FS_GetArchiveResource(path, resource); } /*---------------------------------------------------------------------------* Name: FS_FlushFile Description: ファイルの変更内容をデバイスへ反映する Arguments: file ファイルハンドル Returns: 処理結果 *---------------------------------------------------------------------------*/ FSResult FS_FlushFile(FSFile *file) { FSResult retval = FS_RESULT_ERROR; SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsFile(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { (void)FSi_SendCommand(file, FS_COMMAND_FLUSHFILE, TRUE); retval = FS_GetResultCode(file); } return retval; } /*---------------------------------------------------------------------------* Name: FS_SetFileLength Description: ファイルサイズを変更する Arguments: file ファイルハンドル length 変更後のサイズ Returns: 処理結果 *---------------------------------------------------------------------------*/ FSResult FS_SetFileLength(FSFile *file, u32 length) { FSResult retval = FS_RESULT_ERROR; SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsFile(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForSetFileLength arg[1]; file->argument = arg; arg->length = length; (void)FSi_SendCommand(file, FS_COMMAND_SETFILELENGTH, TRUE); retval = FS_GetResultCode(file); } return retval; } /*---------------------------------------------------------------------------* Name: FS_GetPathName Description: 指定したハンドルのパス名を取得する Arguments: file ファイルまたはディレクトリ buffer パス格納先 length バッファサイズ Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_GetPathName(FSFile *file, char *buffer, u32 length) { BOOL retval = FALSE; SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file) || FS_IsDir(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForGetPath arg[1]; file->argument = arg; arg->is_directory = FS_IsDir(file); arg->buffer = buffer; arg->length = length; retval = FSi_SendCommand(file, FS_COMMAND_GETPATH, TRUE); } return retval; } /*---------------------------------------------------------------------------* Name: FS_GetPathLength Description: 指定したファイルまたはディレクトリのフルパス名の長さを取得 Arguments: file ファイルまたはディレクトリハンドル Returns: 成功すれば終端の'\0'も含めたパス名の長さ、失敗すれば-1 *---------------------------------------------------------------------------*/ s32 FS_GetPathLength(FSFile *file) { s32 retval = -1; if (FS_GetPathName(file, NULL, 0)) { retval = file->arg.getpath.total_len; } return retval; } /*---------------------------------------------------------------------------* Name: FS_ConvertPathToFileID Description: パス名からファイルIDを取得する Arguments: p_fileid FSFileIDの格納先 path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_ConvertPathToFileID(FSFileID *p_fileid, const char *path) { BOOL retval = FALSE; SDK_NULL_ASSERT(p_fileid); SDK_NULL_ASSERT(path); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSFile file[1]; FSArgumentForFindPath arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->target_is_directory = FALSE; if (FSi_SendCommand(file, FS_COMMAND_FINDPATH, TRUE)) { p_fileid->arc = arc; p_fileid->file_id = arg->target_id; retval = TRUE; } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_OpenFileDirect Description: アーカイブ内の領域を直接指定してファイルを開く Arguments: file ハンドル情報を保持するFSFile arc マップ元になるアーカイブ image_top ファイルイメージ先頭のオフセット image_bottom ファイルイメージ終端のオフセット id 任意に指定するファイルID Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenFileDirect(FSFile *file, FSArchive *arc, u32 image_top, u32 image_bottom, u32 id) { BOOL retval = FALSE; SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(arc); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(!FS_IsFile(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForOpenFileDirect arg[1]; file->arc = arc; file->argument = arg; arg->id = id; arg->top = image_top; arg->bottom = image_bottom; arg->mode = 0; retval = FSi_SendCommand(file, FS_COMMAND_OPENFILEDIRECT, TRUE); } return retval; } /*---------------------------------------------------------------------------* Name: FS_OpenFileFast Description: IDを指定してファイルを開く Arguments: file ハンドル情報を保持するFSFile id 開くべきファイルを指すFSFileID Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenFileFast(FSFile *file, FSFileID id) { BOOL retval = FALSE; SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(!FS_IsFile(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); if (id.arc) { FSArgumentForOpenFileFast arg[1]; file->arc = id.arc; file->argument = arg; arg->id = id.file_id; arg->mode = 0; retval = FSi_SendCommand(file, FS_COMMAND_OPENFILEFAST, TRUE); } return retval; } /*---------------------------------------------------------------------------* Name: FS_OpenFileEx Description: パス名を指定してファイルを開く Arguments: file FSFile構造体 path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenFileEx(FSFile *file, const char *path, u32 mode) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); // FS_FILEMODE_L に関する論理チェック。 // (creationモードで開きつつサイズ制限したら意味が無い) if (((mode & FS_FILEMODE_L) != 0) && ((mode & FS_FILEMODE_RW) == FS_FILEMODE_W)) { OS_TWarning("\"FS_FILEMODE_WL\" seems useless.\n" "(this means creating empty file and prohibiting any modifications)"); } { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSArgumentForOpenFile arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->mode = mode; if (FSi_SendCommand(file, FS_COMMAND_OPENFILE, TRUE)) { retval = TRUE; } else { file->arc = NULL; } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_CloseFile Description: ファイルを閉じる Arguments: file ファイルハンドル Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CloseFile(FSFile *file) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { retval = FSi_SendCommand(file, FS_COMMAND_CLOSEFILE, TRUE); } return retval; } /*---------------------------------------------------------------------------* Name: FS_GetSeekCacheSize Description: 高速逆シーク用のフルキャッシュに必要なバッファサイズを求める Arguments: path Returns: 成功すればサイズ、失敗すれば0 *---------------------------------------------------------------------------*/ u32 FS_GetSeekCacheSize(const char *path) { u32 retval = 0; // ファイルが存在するならサイズを取得する。 FSPathInfo info; if (FS_GetPathInfo(path, &info) && ((info.attributes & FS_ATTRIBUTE_IS_DIRECTORY) == 0)) { // 該当するアーカイブのFAT情報を取得。 FSArchiveResource resource; if (FS_GetArchiveResource(path, &resource)) { // 実際にFATベースのアーカイブであればキャッシュサイズを算出。 u32 bytesPerCluster = resource.sectorsPerCluster * resource.bytesPerSector; if (bytesPerCluster != 0) { static const u32 fatBits = 32; retval = (u32)((info.filesize + bytesPerCluster - 1) / bytesPerCluster) * ((fatBits + 4) / 8); // バッファ前後をキャッシュラインに整合させるため余白を追加。 retval += (u32)(HW_CACHE_LINE_SIZE * 2); } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_SetSeekCache Description: 高速逆シーク用のキャッシュバッファを割り当てる Arguments: file ファイルハンドル buf キャッシュバッファ buf_size キャッシュバッファサイズ Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_SetSeekCache(FSFile *file, void* buf, u32 buf_size) { FSArgumentForSetSeekCache arg[1]; BOOL retval = FALSE; SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file)); file->argument = arg; arg->buf = buf; arg->buf_size = buf_size; retval = FSi_SendCommand(file, FS_COMMAND_SETSEEKCACHE, TRUE); return retval; } /*---------------------------------------------------------------------------* Name: FS_GetFileLength Description: ファイルサイズを取得 Arguments: file ファイルハンドル Returns: ファイルサイズ *---------------------------------------------------------------------------*/ u32 FS_GetFileLength(FSFile *file) { u32 retval = 0; SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file)); // アーカイブプロシージャの場合はこの場で直接参照可能。 if (!FSi_GetFileLengthIfProc(file, &retval)) { FSArgumentForGetFileLength arg[1]; file->argument = arg; arg->length = 0; if (FSi_SendCommand(file, FS_COMMAND_GETFILELENGTH, TRUE)) { retval = arg->length; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_GetFilePosition Description: ファイルポインタの現在位置を取得 Arguments: file ファイルハンドル Returns: ファイルポインタの現在位置 *---------------------------------------------------------------------------*/ u32 FS_GetFilePosition(FSFile *file) { u32 retval = 0; SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file)); // アーカイブプロシージャの場合はこの場で直接参照可能。 if (!FSi_GetFilePositionIfProc(file, &retval)) { FSArgumentForGetFilePosition arg[1]; file->argument = arg; arg->position = 0; if (FSi_SendCommand(file, FS_COMMAND_GETFILEPOSITION, TRUE)) { retval = arg->position; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_SeekFile Description: ファイルポインタを移動 Arguments: file ファイルハンドル offset 移動量 origin 移動起点 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_SeekFile(FSFile *file, s32 offset, FSSeekFileMode origin) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file)); { FSArgumentForSeekFile arg[1]; file->argument = arg; arg->offset = (int)offset; arg->from = origin; retval = FSi_SendCommand(file, FS_COMMAND_SEEKFILE, TRUE); } return retval; } /*---------------------------------------------------------------------------* Name: FS_ReadFile Description: ファイルからデータを読み出し Arguments: file ファイルハンドル buffer 転送先バッファ length 読み出しサイズ Returns: 成功すれば実際に読み出したサイズ、失敗すれば-1 *---------------------------------------------------------------------------*/ s32 FS_ReadFile(FSFile *file, void *buffer, s32 length) { FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FSi_IsValidTransferRegion(buffer, length)); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file) && !FS_IsBusy(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForReadFile arg[1]; file->argument = arg; arg->buffer = buffer; arg->length = (u32)length; if (FSi_SendCommand(file, FS_COMMAND_READFILE, TRUE)) { length = (s32)arg->length; } else { if( ( file->error == FS_RESULT_INVALID_PARAMETER ) || ( file->error == FS_RESULT_ERROR ) ) { length = -1; //そもそもリードしなかった場合 }else{ length = (s32)arg->length; //リードを試みた場合は-1以上の値が入っている } } } return length; } /*---------------------------------------------------------------------------* Name: FS_ReadFileAsync Description: ファイルからデータを非同期的に読み出し Arguments: file ファイルハンドル buffer 転送先バッファ length 読み出しサイズ Returns: 成功すれば単にlengthと同じ値、失敗すれば-1 *---------------------------------------------------------------------------*/ s32 FS_ReadFileAsync(FSFile *file, void *buffer, s32 length) { FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FSi_IsValidTransferRegion(buffer, length)); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file) && !FS_IsBusy(file)); // アーカイブプロシージャの場合はこの場でサイズ補正 { u32 end, pos; if (FSi_GetFilePositionIfProc(file, &pos) && FSi_GetFileLengthIfProc(file, &end) && (pos + length > end)) { length = (s32)(end - pos); } } { FSArgumentForReadFile *arg = (FSArgumentForReadFile*)file->reserved2; file->argument = arg; arg->buffer = buffer; arg->length = (u32)length; (void)FSi_SendCommand(file, FS_COMMAND_READFILE, FALSE); } return length; } /*---------------------------------------------------------------------------* Name: FS_WriteFile Description: ファイルへデータを書き込み Arguments: file ファイルハンドル buffer 転送元バッファ length 書き込みサイズ Returns: 成功すれば実際に書き込んだサイズ、失敗すれば-1 *---------------------------------------------------------------------------*/ s32 FS_WriteFile(FSFile *file, const void *buffer, s32 length) { FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FSi_IsValidTransferRegion(buffer, length)); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file) && !FS_IsBusy(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForWriteFile arg[1]; file->argument = arg; arg->buffer = buffer; arg->length = (u32)length; if (FSi_SendCommand(file, FS_COMMAND_WRITEFILE, TRUE)) { length = (s32)arg->length; } else { if( file->error == FS_RESULT_INVALID_PARAMETER) { length = -1; //そもそもライトしなかった場合 }else{ length = (s32)arg->length; //ライトを試みた場合は-1以上の値が入っている } } } return length; } /*---------------------------------------------------------------------------* Name: FS_WriteFileAsync Description: ファイルへデータを非同期的に書き込み Arguments: file ファイルハンドル buffer 転送元バッファ length 書き込みサイズ Returns: 成功すれば単にlengthと同じ値、失敗すれば-1 *---------------------------------------------------------------------------*/ s32 FS_WriteFileAsync(FSFile *file, const void *buffer, s32 length) { SDK_NULL_ASSERT(file); SDK_ASSERT(FSi_IsValidTransferRegion(buffer, length)); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsFile(file) && !FS_IsBusy(file)); // アーカイブプロシージャの場合はこの場でサイズ補正 { u32 end, pos; if (FSi_GetFilePositionIfProc(file, &pos) && FSi_GetFileLengthIfProc(file, &end) && (pos + length > end)) { length = (s32)(end - pos); } } { FSArgumentForWriteFile *arg = (FSArgumentForWriteFile*)file->reserved2; file->argument = arg; arg->buffer = buffer; arg->length = (u32)length; (void)FSi_SendCommand(file, FS_COMMAND_WRITEFILE, FALSE); } return length; } /*---------------------------------------------------------------------------* Name: FS_OpenDirectory Description: ディレクトリハンドルを開く Arguments: file FSFile構造体 path パス名 mode アクセスモード Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenDirectory(FSFile *file, const char *path, u32 mode) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { char relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { FSArgumentForOpenDirectory arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->mode = mode; if (FSi_SendCommand(file, FS_COMMAND_OPENDIRECTORY, TRUE)) { retval = TRUE; } else { file->arc = NULL; } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_CloseDirectory Description: ディレクトリハンドルを閉じる Arguments: file FSFile構造体 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_CloseDirectory(FSFile *file) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsDir(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { if (FSi_SendCommand(file, FS_COMMAND_CLOSEDIRECTORY, TRUE)) { retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_ReadDirectory Description: ディレクトリのエントリを1個だけ読み進める Arguments: file FSFile構造体 info FSDirectoryEntryInfo構造体 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_ReadDirectory(FSFile *file, FSDirectoryEntryInfo *info) { BOOL retval = FALSE; SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(info); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsDir(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForReadDirectory arg[1]; file->argument = arg; arg->info = info; MI_CpuFill8(info, 0x00, sizeof(info)); info->id = FS_INVALID_FILE_ID; if (FSi_SendCommand(file, FS_COMMAND_READDIR, TRUE)) { retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_SeekDir Description: ディレクトリ位置を指定して開く Arguments: file FSFile構造体 pos FS_ReadDirやFS_TellDirで取得したディレクトリ位置 Returns: 成功したらTRUE *---------------------------------------------------------------------------*/ BOOL FS_SeekDir(FSFile *file, const FSDirPos *pos) { BOOL retval = FALSE; SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(pos); SDK_NULL_ASSERT(pos->arc); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArgumentForSeekDirectory arg[1]; arg->id = (u32)((pos->own_id << 0) | (pos->index << 16)); arg->position = pos->pos; file->arc = pos->arc; file->argument = arg; if (FSi_SendCommand(file, FS_COMMAND_SEEKDIR, TRUE)) { file->stat |= FS_FILE_STATUS_IS_DIR; retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FS_TellDir Description: ディレクトリハンドルから現在のディレクトリ位置を取得 Arguments: dir ディレクトリハンドル pos ディレクトリ位置の格納先 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_TellDir(const FSFile *dir, FSDirPos *pos) { BOOL retval = FALSE; SDK_NULL_ASSERT(dir); SDK_NULL_ASSERT(pos); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsDir(dir)); { *pos = dir->prop.dir.pos; retval = TRUE; } return retval; } /*---------------------------------------------------------------------------* Name: FS_RewindDir Description: ディレクトリハンドルの列挙位置を先頭へ戻す Arguments: dir ディレクトリハンドル Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_RewindDir(FSFile *dir) { BOOL retval = FALSE; SDK_NULL_ASSERT(dir); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsDir(dir)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSDirPos pos; pos.arc = dir->arc; pos.own_id = dir->prop.dir.pos.own_id; pos.pos = 0; pos.index = 0; retval = FS_SeekDir(dir, &pos); } return retval; } /*---------------------------------------------------------------------------* * Unicode support *---------------------------------------------------------------------------*/ enum { FS_UNICODE_CONVSRC_ASCII, FS_UNICODE_CONVSRC_SHIFT_JIS, FS_UNICODE_CONVSRC_UNICODE }; /*---------------------------------------------------------------------------* Name: FSi_CopySafeUnicodeString Description: バッファサイズを確認しつつ文字列をUnicodeとしてコピー。 Arguments: dst 転送先バッファ dstlen 転送先サイズ src 転送元バッファ srclen 転送先文字サイズ srctype 転送元の文字セット stickyFailure 転送元の切り捨てが起こったらFALSE Returns: 実際に格納された文字数。 *---------------------------------------------------------------------------*/ static int FSi_CopySafeUnicodeString(u16 *dst, int dstlen, const void *srcptr, int srclen, int srctype, BOOL *stickyFailure) { int srcpos = 0; int dstpos = 0; if (srctype == FS_UNICODE_CONVSRC_ASCII) { const char *src = (const char *)srcptr; int n = (dstlen - 1 < srclen) ? (dstlen - 1) : srclen; while ((dstpos < n) && src[srcpos]) { dst[dstpos++] = (u8)src[srcpos++]; } if ((srcpos < srclen) && src[srcpos]) { *stickyFailure = TRUE; } } else if (srctype == FS_UNICODE_CONVSRC_UNICODE) { const u16 *src = (const u16 *)srcptr; int n = (dstlen - 1 < srclen) ? (dstlen - 1) : srclen; while ((dstpos < n) && src[srcpos]) { dst[dstpos++] = src[srcpos++]; } if ((srcpos < srclen) && src[srcpos]) { *stickyFailure = TRUE; } } else if (srctype == FS_UNICODE_CONVSRC_SHIFT_JIS) { const char *src = (const char *)srcptr; srcpos = srclen; dstpos = dstlen - 1; (void)FSi_ConvertStringSjisToUnicode(dst, &dstpos, src, &srcpos, NULL); if ((srcpos < srclen) && src[srcpos]) { *stickyFailure = TRUE; } } dst[dstpos] = L'\0'; return dstpos; } /*---------------------------------------------------------------------------* Name: FSi_NormalizePathWtoW Description: Unicodeパスをアーカイブ名まで含むUnicodeフルパスへ変換。 Arguments: path 正規化されていないパス文字列 baseid 基準ディレクトリIDの格納先またはNULL relpath 変換後のパス名格納先またはNULL Returns: アーカイブポインタまたはNULL *---------------------------------------------------------------------------*/ FSArchive* FSi_NormalizePathWtoW(const u16 *path, u32*baseid, u16 *relpath); FSArchive* FSi_NormalizePathWtoW(const u16 *path, u32*baseid, u16 *relpath) { FSArchive *arc = NULL; int pathlen = 0; int pathmax = FS_ARCHIVE_FULLPATH_MAX + 1; BOOL stickyFailure = FALSE; // まずコマンド対象となるアーカイブを特定。 // 指定されたUnicodeパスが絶対パスならアーカイブを取得。 BOOL absolute = FALSE; int arcnameLen; for (arcnameLen = 0; arcnameLen < FS_ARCHIVE_NAME_LONG_MAX + 1; ++arcnameLen) { if (path[arcnameLen] == L'\0') { break; } else if (FSi_IsUnicodeSlash(path[arcnameLen])) { break; } else if (path[arcnameLen] == L':') { char arcname[FS_ARCHIVE_NAME_LONG_MAX + 1]; int j; for (j = 0; j < arcnameLen; ++j) { arcname[j] = (char)path[j]; } arcname[arcnameLen] = '\0'; arc = FS_FindArchive(arcname, arcnameLen); break; } } if (arc) { absolute = TRUE; *baseid = 0; } else { arc = FS_NormalizePath("", baseid, NULL); } if (arc) { // アーカイブがUnicode対応可能でなければここで失敗。 u32 caps = 0; (void)arc->vtbl->GetArchiveCaps(arc, &caps); if ((caps & FS_ARCHIVE_CAPS_UNICODE) == 0) { arc = NULL; } else { // 先頭にアーカイブ名を格納。 pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, FS_GetArchiveName(arc), FS_ARCHIVE_NAME_LONG_MAX, FS_UNICODE_CONVSRC_ASCII, &stickyFailure); pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, L":", 1, FS_UNICODE_CONVSRC_UNICODE, &stickyFailure); // 絶対パスならルート以下をそのまま連結。 if (absolute) { path += arcnameLen + 1 + FSi_IsUnicodeSlash(path[arcnameLen + 1]); } // カレントルートならルート以下を直接連結。 else if (FSi_IsUnicodeSlash(*path)) { path += 1; } // カレントディレクトリならShift_JIS->Unicode変換して連結。 else { pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, L"/", 1, FS_UNICODE_CONVSRC_UNICODE, &stickyFailure); pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, FS_GetCurrentDirectory(), FS_ENTRY_LONGNAME_MAX, FS_UNICODE_CONVSRC_SHIFT_JIS, &stickyFailure); } // 残りの部分を連結。 pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, L"/", 1, FS_UNICODE_CONVSRC_UNICODE, &stickyFailure); { // 特殊エントリ名に注意しつつ相対パスを正規化。 int curlen = 0; while (!stickyFailure) { u16 c = path[curlen]; if ((c != L'\0') && !FSi_IsUnicodeSlash(c)) { curlen += 1; } else { // 空ディレクトリは無視。 if (curlen == 0) { } // "." (カレントディレクトリ)は無視。 else if ((curlen == 1) && (path[0] == L'.')) { } // ".." (親ディレクトリ)はルートを上限として1階層上がる。 else if ((curlen == 2) && (path[0] == '.') && (path[1] == '.')) { if ((pathlen > 2) && (relpath[pathlen - 2] != L':')) { --pathlen; pathlen = FSi_DecrementUnicodePositionToSlash(relpath, pathlen) + 1; } } // それ以外はエントリ追加。 else { pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, path, curlen, FS_UNICODE_CONVSRC_UNICODE, &stickyFailure); if (c != L'\0') { pathlen += FSi_CopySafeUnicodeString(&relpath[pathlen], pathmax - pathlen, L"/", 1, FS_UNICODE_CONVSRC_UNICODE, &stickyFailure); } } if (c == L'\0') { break; } path += curlen + 1; curlen = 0; } } } relpath[pathlen] = L'\0'; } } return stickyFailure ? NULL : arc; } /*---------------------------------------------------------------------------* Name: FS_OpenFileExW Description: パス名を指定してファイルを開く Arguments: file FSFile構造体 path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenFileExW(FSFile *file, const u16 *path, u32 mode) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); // FS_FILEMODE_L に関する論理チェック。 // (creationモードで開きつつサイズ制限したら意味が無い) if (((mode & FS_FILEMODE_L) != 0) && ((mode & FS_FILEMODE_RW) == FS_FILEMODE_W)) { OS_TWarning("\"FS_FILEMODE_WL\" seems useless.\n" "(this means creating empty file and prohibiting any modifications)"); } { u16 relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FSi_NormalizePathWtoW(path, &baseid, relpath); // 現時点ではUnicodeを使用可能な最小の変更にとどめるために // ROMなどUnicode非対応のアーカイブではUnsupportedを返す。 if (!arc) { file->error = FS_RESULT_UNSUPPORTED; } else { FSArgumentForOpenFile arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = (char*)relpath; arg->mode = mode; file->stat |= FS_FILE_STATUS_UNICODE_MODE; if (FSi_SendCommand(file, FS_COMMAND_OPENFILE, TRUE)) { retval = TRUE; } else { file->arc = NULL; } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_OpenDirectoryW Description: ディレクトリハンドルを開く Arguments: file FSFile構造体 path パス名 mode アクセスモード Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenDirectoryW(FSFile *file, const u16 *path, u32 mode) { BOOL retval = FALSE; FS_DEBUG_TRACE( "%s\n", __FUNCTION__); SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { u16 relpath[FS_ARCHIVE_FULLPATH_MAX + 1]; u32 baseid = 0; FSArchive *arc = FSi_NormalizePathWtoW(path, &baseid, relpath); // 現時点ではUnicodeを使用可能な最小の変更にとどめるために // ROMなどUnicode非対応のアーカイブではUnsupportedを返す。 if (!arc) { file->error = FS_RESULT_UNSUPPORTED; } else { FSArgumentForOpenDirectory arg[1]; FS_InitFile(file); file->arc = arc; file->argument = arg; arg->baseid = baseid; arg->relpath = (char*)relpath; arg->mode = mode; file->stat |= FS_FILE_STATUS_UNICODE_MODE; if (FSi_SendCommand(file, FS_COMMAND_OPENDIRECTORY, TRUE)) { retval = TRUE; } else { file->arc = NULL; } } } return retval; } /*---------------------------------------------------------------------------* Name: FS_ReadDirectoryW Description: ディレクトリのエントリを1個だけ読み進める Arguments: file FSFile構造体 info FSDirectoryEntryInfo構造体 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_ReadDirectoryW(FSFile *file, FSDirectoryEntryInfoW *info) { BOOL retval = FALSE; SDK_NULL_ASSERT(file); SDK_NULL_ASSERT(info); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(FS_IsDir(file)); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { FSArchive *arc = file->arc; // 現在はUnicodeを使用できる最小の変更にとどめるため、 // ROMなどUnicode非対応のアーカイブではUnsupportedを返す。 u32 caps = 0; (void)arc->vtbl->GetArchiveCaps(arc, &caps); if ((caps & FS_ARCHIVE_CAPS_UNICODE) == 0) { file->error = FS_RESULT_UNSUPPORTED; } else { FSArgumentForReadDirectory arg[1]; file->argument = arg; arg->info = (FSDirectoryEntryInfo*)info; MI_CpuFill8(info, 0x00, sizeof(info)); info->id = FS_INVALID_FILE_ID; file->stat |= FS_FILE_STATUS_UNICODE_MODE; if (FSi_SendCommand(file, FS_COMMAND_READDIR, TRUE)) { retval = TRUE; } } } return retval; } /*---------------------------------------------------------------------------* * obsolete functions *---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------* Name: FSi_ConvertToDirEntry Description: FSDirectoryEntryInfo構造体からFSDirEntry構造体への変換 Arguments: entry 変換先のFSDirEntry構造体 arc 変換元の情報を取得したアーカイブ info 変換元のFSDirectoryEntryInfo構造体 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ static void FSi_ConvertToDirEntry(FSDirEntry *entry, FSArchive *arc, const FSDirectoryEntryInfo *info) { entry->name_len = info->longname_length; if (entry->name_len > sizeof(entry->name) - 1) { entry->name_len = sizeof(entry->name) - 1; } MI_CpuCopy8(info->longname, entry->name, entry->name_len); entry->name[entry->name_len] = '\0'; if (info->id == FS_INVALID_FILE_ID) { entry->is_directory = FALSE; entry->file_id.file_id = FS_INVALID_FILE_ID; entry->file_id.arc = NULL; } else if ((info->attributes & FS_ATTRIBUTE_IS_DIRECTORY) != 0) { entry->is_directory = TRUE; entry->dir_id.arc = arc; entry->dir_id.own_id = (u16)(info->id >> 0); entry->dir_id.index = (u16)(info->id >> 16); entry->dir_id.pos = 0; } else { entry->is_directory = FALSE; entry->file_id.file_id = info->id; entry->file_id.arc = arc; } } /*---------------------------------------------------------------------------* Name: FS_OpenFile Description: パス名を指定してファイルを開く Arguments: file FSFile構造体 path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_OpenFile(FSFile *file, const char *path) { return FS_OpenFileEx(file, path, FS_FILEMODE_R); } /*---------------------------------------------------------------------------* Name: FS_GetLength Description: ファイルサイズを取得 Arguments: file ファイルハンドル Returns: ファイルサイズ *---------------------------------------------------------------------------*/ u32 FS_GetLength(FSFile *file) { return FS_GetFileLength(file); } /*---------------------------------------------------------------------------* Name: FS_GetPosition Description: ファイルポインタの現在位置を取得 Arguments: file ファイルハンドル Returns: ファイルポインタの現在位置 *---------------------------------------------------------------------------*/ u32 FS_GetPosition(FSFile *file) { return FS_GetFilePosition(file); } /*---------------------------------------------------------------------------* Name: FS_FindDir Description: ディレクトリハンドルを開く Arguments: dir FSFile構造体 path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_FindDir(FSFile *dir, const char *path) { return FS_OpenDirectory(dir, path, FS_FILEMODE_R); } /*---------------------------------------------------------------------------* Name: FS_ReadDir Description: ディレクトリのエントリを1個だけ読み進める Arguments: file FSFile構造体 entry FSDirEntry構造体 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_ReadDir(FSFile *file, FSDirEntry *entry) { BOOL retval = FALSE; FSDirectoryEntryInfo info[1]; if (FS_ReadDirectory(file, info)) { FSi_ConvertToDirEntry(entry, FS_GetAttachedArchive(file), info); retval = TRUE; } return retval; } /*---------------------------------------------------------------------------* Name: FS_ChangeDir Description: カレントディレクトリを変更する Arguments: path パス名 Returns: 成功すればTRUE *---------------------------------------------------------------------------*/ BOOL FS_ChangeDir(const char *path) { return FS_SetCurrentDirectory(path); } /*---------------------------------------------------------------------------* Name: FS_GetFileInfo Description: ファイル情報を取得する Arguments: path パス名 info 情報の格納先 Returns: 処理結果 *---------------------------------------------------------------------------*/ FSResult FS_GetFileInfo(const char *path, FSFileInfo *info) { return FS_GetPathInfo(path, info) ? FS_RESULT_SUCCESS : FS_GetArchiveResultCode(path); } #endif /* FS_IMPLEMENT */ // 以下はFSライブラリ以外でも使用されるのでFS_IMPLEMENTの対象外。 // ARM7でもUnicode対応が必要になるのはTWL動作時のみなので拡張メモリへ配置。 #if defined(SDK_TWL) && defined(SDK_ARM7) #include #endif static const int FSiUnicodeBufferQueueMax = 4; static OSMessageQueue FSiUnicodeBufferQueue[1]; static OSMessage FSiUnicodeBufferQueueArray[FSiUnicodeBufferQueueMax]; static BOOL FSiUnicodeBufferQueueInitialized = FALSE; static u16 FSiUnicodeBufferTable[FSiUnicodeBufferQueueMax][FS_ARCHIVE_FULLPATH_MAX + 1]; /*---------------------------------------------------------------------------* Name: FSi_GetUnicodeBuffer Description: Unicode変換用の一時バッファを取得。 FSライブラリがShift_JISを変換するために使用される。 Arguments: src : Unicode変換の必要なShift_JIS文字列またはNULL Returns: 必要に応じてUTF16-LEへ変換された文字列バッファ *---------------------------------------------------------------------------*/ u16* FSi_GetUnicodeBuffer(const char *src) { u16 *retval = NULL; // 初回呼び出し時にバッファをメッセージキューへ追加。 OSIntrMode bak = OS_DisableInterrupts(); if (!FSiUnicodeBufferQueueInitialized) { int i; FSiUnicodeBufferQueueInitialized = TRUE; OS_InitMessageQueue(FSiUnicodeBufferQueue, FSiUnicodeBufferQueueArray, 4); for (i = 0; i < FSiUnicodeBufferQueueMax; ++i) { (void)OS_SendMessage(FSiUnicodeBufferQueue, FSiUnicodeBufferTable[i], OS_MESSAGE_BLOCK); } } (void)OS_RestoreInterrupts(bak); // メッセージキューからバッファを確保。(無ければここでブロッキング) (void)OS_ReceiveMessage(FSiUnicodeBufferQueue, (OSMessage*)&retval, OS_MESSAGE_BLOCK); if (src) { int dstlen = FS_ARCHIVE_FULLPATH_MAX; (void)FSi_ConvertStringSjisToUnicode(retval, &dstlen, src, NULL, NULL); retval[dstlen] = L'\0'; } return retval; } /*---------------------------------------------------------------------------* Name: FSi_ReleaseUnicodeBuffer Description: Unicode変換用の一時バッファを解放。 Arguments: buf : FSi_GetUnicodeBuffer()で確保したバッファ Returns: None. *---------------------------------------------------------------------------*/ void FSi_ReleaseUnicodeBuffer(const void *buf) { if (buf) { // 使用したバッファをメッセージキューへ返却。 (void)OS_SendMessage(FSiUnicodeBufferQueue, (OSMessage)buf, OS_MESSAGE_BLOCK); } } /*---------------------------------------------------------------------------* Name: FSi_ConvertStringSjisToUnicode Description: ShiftJIS文字列をUnicode文字列に変換。 扱うパス名が明らかにASCIIのみである場合など UnicodeとShiftJISの相互変換を簡略化できる場合は この関数をオーバーライドすることによって STDライブラリの標準処理がリンクされるのを防ぐことができる。 Arguments: dst 変換先バッファ. NULL を指定すると格納処理は無視される. dst_len 変換先バッファの最大文字数を格納して渡し, 実際に格納された文字数を受け取るポインタ. NULL を与えた場合は無視される. src 変換元バッファ. src_len 変換すべき最大文字数を格納して渡し, 実際に変換された文字数を受け取るポインタ. この指定よりも文字列終端の位置が優先される. 負の値を格納して渡すか NULL を与えた場合は 終端位置までの文字数を指定したとみなされる. callback 変換できない文字が現れた時に呼ばれるコールバック. NULLを指定した場合, 変換できない文字の位置で 変換処理を終了する. Returns: 変換処理の結果. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL STDResult FSi_ConvertStringSjisToUnicode(u16 *dst, int *dst_len, const char *src, int *src_len, STDConvertUnicodeCallback callback) __attribute__((never_inline)) { return STD_ConvertStringSjisToUnicode(dst, dst_len, src, src_len, callback); } /*---------------------------------------------------------------------------* Name: FSi_ConvertStringUnicodeToSjis Description: Unicode文字列をShiftJIS文字列に変換。 扱うパス名が明らかにASCIIのみである場合など UnicodeとShiftJISの相互変換を簡略化できる場合は この関数をオーバーライドすることによって STDライブラリの標準処理がリンクされるのを防ぐことができる。 Arguments: dst 変換先バッファ. NULL を指定すると格納処理は無視される. dst_len 変換先バッファの最大文字数を格納して渡し, 実際に格納された文字数を受け取るポインタ. NULL を与えた場合は無視される. src 変換元バッファ. src_len 変換すべき最大文字数を格納して渡し, 実際に変換された文字数を受け取るポインタ. この指定よりも文字列終端の位置が優先される. 負の値を格納して渡すか NULL を与えた場合は 終端位置までの文字数を指定したとみなされる. callback 変換できない文字が現れた時に呼ばれるコールバック. NULLを指定した場合, 変換できない文字の位置で 変換処理を終了する. Returns: 変換処理の結果. *---------------------------------------------------------------------------*/ SDK_WEAK_SYMBOL STDResult FSi_ConvertStringUnicodeToSjis(char *dst, int *dst_len, const u16 *src, int *src_len, STDConvertSjisCallback callback) __attribute__((never_inline)) { return STD_ConvertStringUnicodeToSjis(dst, dst_len, src, src_len, callback); } #if defined(SDK_TWL) && defined(SDK_ARM7) #include #endif