/*---------------------------------------------------------------------------* Project: TwlSDK - FS - libraries File: fs_archive.c Copyright 2007-2008 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:: 2008-09-25#$ $Rev: 8647 $ $Author: yosizaki $ *---------------------------------------------------------------------------*/ #include #include #include #include "../include/util.h" #include "../include/command.h" #if defined(FS_IMPLEMENT) /*---------------------------------------------------------------------------*/ /* Constants */ // Enable when internally supporting long archive names #define FS_SUPPORT_LONG_ARCNAME /*---------------------------------------------------------------------------*/ /* Variables */ // Archive list registered to the file system static FSArchive *arc_list = NULL; // Current directory static FSDirPos current_dir_pos; static char current_dir_path[FS_ENTRY_LONGNAME_MAX]; #if defined(FS_SUPPORT_LONG_ARCNAME) // Static buffer for internal support of long archive names #define FS_LONG_ARCNAME_LENGTH_MAX 15 #define FS_LONG_ARCNAME_TABLE_MAX 16 static char FSiLongNameTable[FS_LONG_ARCNAME_TABLE_MAX][FS_LONG_ARCNAME_LENGTH_MAX + 1]; #endif /*---------------------------------------------------------------------------*/ /* functions */ FSArchive* FSi_GetArchiveChain(void) { return arc_list; } /*---------------------------------------------------------------------------* Name: FSi_IsEventCommand Description: Determine whether the command is an event notification Arguments: command Command type Returns: TRUE when command is event notification *---------------------------------------------------------------------------*/ static BOOL FSi_IsEventCommand(FSCommandType command) { return ((command == FS_COMMAND_ACTIVATE) || (command == FS_COMMAND_IDLE) || (command == FS_COMMAND_SUSPEND) || (command == FS_COMMAND_RESUME) || (command == FS_COMMAND_MOUNT) || (command == FS_COMMAND_UNMOUNT) || (command == FS_COMMAND_INVALID)); } /*---------------------------------------------------------------------------* Name: FSi_EndCommand Description: Completes the command and returns if there is an idle thread. Arguments: file Completed command ret: Command result value Returns: None. *---------------------------------------------------------------------------*/ static void FSi_EndCommand(FSFile *file, FSResult ret) { OSIntrMode bak_psr = OS_DisableInterrupts(); // Delete command from the list FSArchive *const arc = file->arc; if (arc) { FSFile **pp = &arc->list; for (; *pp; pp = &(*pp)->next) { if (*pp == file) { *pp = file->next; break; } } file->next = NULL; } // Store results { FSCommandType command = FSi_GetCurrentCommand(file); if (!FSi_IsEventCommand(command)) { arc->command = command; arc->result = ret; } file->error = ret; file->stat &= ~(FS_FILE_STATUS_CANCEL | FS_FILE_STATUS_BUSY | FS_FILE_STATUS_BLOCKING | FS_FILE_STATUS_OPERATING | FS_FILE_STATUS_ASYNC_DONE | FS_FILE_STATUS_UNICODE_MODE); } // Notify if there is an idling thread OS_WakeupThread(file->queue); (void)OS_RestoreInterrupts(bak_psr); } // The following two temporarily publicized for procedures // Commands recursively calling from the procedures are all synchronous, so it is acceptable for applying only the FSi_WaitForArchiveCompletion function by directly calling via the vtbl. // FSResult FSi_WaitForArchiveCompletion(FSFile *file, FSResult result) { if (result == FS_RESULT_PROC_ASYNC) { FSi_WaitConditionOn(&file->stat, FS_FILE_STATUS_ASYNC_DONE, file->queue); file->stat &= ~FS_FILE_STATUS_ASYNC_DONE; result = file->error; } return result; } /*---------------------------------------------------------------------------* Name: FSi_InvokeCommand Description: Issues a command to an archive. Command list management is resolved before calling this function Arguments: file FSFile structure that stores the argument Command: Command ID Returns: The processing result for the command. *---------------------------------------------------------------------------*/ static FSResult FSi_InvokeCommand(FSFile *file, FSCommandType command) { FSResult result = FS_RESULT_UNSUPPORTED; FSArchive * const arc = file->arc; { const void *(*table) = (const void*)arc->vtbl; // Undefined command out of range if ((command < 0) || (command >= FS_COMMAND_MAX)) { OS_TWarning("undefined command (%d)\n", command); result = FS_RESULT_UNSUPPORTED; } // Call by restoring the argument if it is not a command whose implementation was abandoned by the driver side else if (table[command] == NULL) { result = FS_RESULT_UNSUPPORTED; } else { #define FS_DECLARE_ARGUMENT_(type) \ type *arg = (type*) file->argument; \ (void)arg #define FS_INVOKE_METHOD_(command, ...) \ do \ { \ FS_DECLARE_ARGUMENT_(FSArgumentFor ## command); \ result = arc->vtbl->command(__VA_ARGS__); \ } \ while(0) #define FS_NOTIFY_EVENT_(command, ...) \ do \ { \ FS_DECLARE_ARGUMENT_(FSArgumentFor ## command); \ (void)arc->vtbl->command(__VA_ARGS__); \ return FS_RESULT_SUCCESS; \ } \ while(0) switch (command) { case FS_COMMAND_READFILE: FS_INVOKE_METHOD_(ReadFile, arc, file, arg->buffer, &arg->length); break; case FS_COMMAND_WRITEFILE: FS_INVOKE_METHOD_(WriteFile, arc, file, arg->buffer, &arg->length); break; case FS_COMMAND_SEEKDIR: FS_INVOKE_METHOD_(SeekDirectory, arc, file, arg->id, arg->position); break; case FS_COMMAND_READDIR: FS_INVOKE_METHOD_(ReadDirectory, arc, file, arg->info); break; case FS_COMMAND_FINDPATH: FS_INVOKE_METHOD_(FindPath, arc, arg->baseid, arg->relpath, &arg->target_id, arg->target_is_directory); break; case FS_COMMAND_GETPATH: FS_INVOKE_METHOD_(GetPath, arc, file, arg->is_directory, arg->buffer, &arg->length); break; case FS_COMMAND_OPENFILEFAST: FS_INVOKE_METHOD_(OpenFileFast, arc, file, arg->id, arg->mode); break; case FS_COMMAND_OPENFILEDIRECT: FS_INVOKE_METHOD_(OpenFileDirect, arc, file, arg->top, arg->bottom, &arg->id); break; case FS_COMMAND_CLOSEFILE: FS_INVOKE_METHOD_(CloseFile, arc, file); break; case FS_COMMAND_ACTIVATE: FS_NOTIFY_EVENT_(Activate, arc); break; case FS_COMMAND_IDLE: FS_NOTIFY_EVENT_(Idle, arc); break; case FS_COMMAND_SUSPEND: FS_NOTIFY_EVENT_(Suspend, arc); break; case FS_COMMAND_RESUME: FS_NOTIFY_EVENT_(Resume, arc); break; case FS_COMMAND_OPENFILE: FS_INVOKE_METHOD_(OpenFile, arc, file, arg->baseid, arg->relpath, arg->mode); break; case FS_COMMAND_SEEKFILE: FS_INVOKE_METHOD_(SeekFile, arc, file, &arg->offset, arg->from); break; case FS_COMMAND_GETFILELENGTH: FS_INVOKE_METHOD_(GetFileLength, arc, file, &arg->length); break; case FS_COMMAND_GETFILEPOSITION: FS_INVOKE_METHOD_(GetFilePosition, arc, file, &arg->position); break; // From here the extended command case FS_COMMAND_MOUNT: FS_NOTIFY_EVENT_(Mount, arc); break; case FS_COMMAND_UNMOUNT: FS_NOTIFY_EVENT_(Unmount, arc); break; case FS_COMMAND_GETARCHIVECAPS: FS_INVOKE_METHOD_(GetArchiveCaps, arc, &arg->caps); break; case FS_COMMAND_CREATEFILE: FS_INVOKE_METHOD_(CreateFile, arc, arg->baseid, arg->relpath, arg->permit); break; case FS_COMMAND_DELETEFILE: FS_INVOKE_METHOD_(DeleteFile, arc, arg->baseid, arg->relpath); break; case FS_COMMAND_RENAMEFILE: FS_INVOKE_METHOD_(RenameFile, arc, arg->baseid_src, arg->relpath_src, arg->baseid_dst, arg->relpath_dst); break; case FS_COMMAND_GETPATHINFO: FS_INVOKE_METHOD_(GetPathInfo, arc, arg->baseid, arg->relpath, arg->info); break; case FS_COMMAND_SETPATHINFO: FS_INVOKE_METHOD_(SetPathInfo, arc, arg->baseid, arg->relpath, arg->info); break; case FS_COMMAND_CREATEDIRECTORY: FS_INVOKE_METHOD_(CreateDirectory, arc, arg->baseid, arg->relpath, arg->permit); break; case FS_COMMAND_DELETEDIRECTORY: FS_INVOKE_METHOD_(DeleteDirectory, arc, arg->baseid, arg->relpath); break; case FS_COMMAND_RENAMEDIRECTORY: FS_INVOKE_METHOD_(RenameDirectory, arc, arg->baseid_src, arg->relpath_src, arg->baseid_dst, arg->relpath_dst); break; case FS_COMMAND_GETARCHIVERESOURCE: FS_INVOKE_METHOD_(GetArchiveResource, arc, arg->resource); break; case FS_COMMAND_FLUSHFILE: FS_INVOKE_METHOD_(FlushFile, arc, file); break; case FS_COMMAND_SETFILELENGTH: FS_INVOKE_METHOD_(SetFileLength, arc, file, arg->length); break; case FS_COMMAND_OPENDIRECTORY: FS_INVOKE_METHOD_(OpenDirectory, arc, file, arg->baseid, arg->relpath, arg->mode); break; case FS_COMMAND_CLOSEDIRECTORY: FS_INVOKE_METHOD_(CloseDirectory, arc, file); break; case FS_COMMAND_SETSEEKCACHE: FS_INVOKE_METHOD_(SetSeekCache, arc, file, arg->buf, arg->buf_size); break; default: result = FS_RESULT_UNSUPPORTED; } #undef FS_DECLARE_ARGUMENT_ #undef FS_INVOKE_METHOD_ #undef FS_NOTIFY_EVENT_ } } // If this is a normal command with no event notification, return to satisfy conditions when issued if (!FSi_IsEventCommand(command)) { // Because there are many cases where the path name is incorrect and unsupported, if there is the possibility, a warning will be output. // if (result == FS_RESULT_UNSUPPORTED) { OS_TWarning("archive \"%s:\" cannot support command %d.\n", FS_GetArchiveName(arc), command); } // If necessary, apply blocking if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0) { result = FSi_WaitForArchiveCompletion(file, result); } // Release here if completed while nobody is blocking else if (result != FS_RESULT_PROC_ASYNC) { FSi_EndCommand(file, result); } } return result; } /*---------------------------------------------------------------------------* Name: FSi_NextCommand Description: Archive selects the next command that should be processed. If an asynchronous command is selected, return that pointer. If anything other than NULL is returned, it is necessary to process at the source of the call. Arguments: arc Archive that gets the next command owner TRUE if source of call already has command execution rights Returns: The next command that must be processed here. *---------------------------------------------------------------------------*/ static FSFile *FSi_NextCommand(FSArchive *arc, BOOL owner) { FSFile *next = NULL; // First, check all command cancel requests. { OSIntrMode bak_psr = OS_DisableInterrupts(); if ((arc->flag & FS_ARCHIVE_FLAG_CANCELING) != 0) { FSFile *p = arc->list; arc->flag &= ~FS_ARCHIVE_FLAG_CANCELING; while (p != NULL) { FSFile *q = p->next; if (FS_IsCanceling(p)) { FSi_EndCommand(p, FS_RESULT_CANCELED); if (!q) { q = arc->list; } } p = q; } } (void)OS_RestoreInterrupts(bak_psr); } // Determine whether the next command can be executed { OSIntrMode bak_psr = OS_DisableInterrupts(); if (((arc->flag & FS_ARCHIVE_FLAG_SUSPENDING) == 0) && ((arc->flag & FS_ARCHIVE_FLAG_SUSPEND) == 0) && arc->list) { // Startup here if archive is stopped const BOOL is_started = ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0); if (is_started) { arc->flag |= FS_ARCHIVE_FLAG_RUNNING; } (void)OS_RestoreInterrupts(bak_psr); if (is_started) { (void)FSi_InvokeCommand(arc->list, FS_COMMAND_ACTIVATE); } bak_psr = OS_DisableInterrupts(); // Command at the top of the list gets execution rights if (owner || is_started) { next = arc->list; next->stat |= FS_FILE_STATUS_OPERATING; } // If command issuing source is blocking, transfer execution rights if (owner && ((next->stat & FS_FILE_STATUS_BLOCKING) != 0)) { OS_WakeupThread(next->queue); next = NULL; } (void)OS_RestoreInterrupts(bak_psr); } // suspend or idle state else { // If does not have execution rights, stop and go to idle state if (owner) { if ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) != 0) { FSFile tmp; FS_InitFile(&tmp); tmp.arc = arc; arc->flag &= ~FS_ARCHIVE_FLAG_RUNNING; (void)FSi_InvokeCommand(&tmp, FS_COMMAND_IDLE); } // If suspend is executing, notify the source of the suspend function call if ((arc->flag & FS_ARCHIVE_FLAG_SUSPENDING) != 0) { arc->flag &= ~FS_ARCHIVE_FLAG_SUSPENDING; arc->flag |= FS_ARCHIVE_FLAG_SUSPEND; OS_WakeupThread(&arc->queue); } } (void)OS_RestoreInterrupts(bak_psr); } } return next; } /*---------------------------------------------------------------------------* Name: FSi_ExecuteAsyncCommand Description: Executes an asynchronous command. The first call is made from the user thread with interrupts enabled. As long as the archive is operating synchronously, command processing will repeat here. If even a single asynchronous process occurs, the rest is performed with NotifyAsyncEnd(). Therefore, it is necessary to be aware of the NotifyAsyncEnd() calling environment when archive processing switches between synchronous and asynchronous. Arguments: file FSFile structure that stores the asynchronous command to execute. Returns: None. *---------------------------------------------------------------------------*/ static void FSi_ExecuteAsyncCommand(FSFile *file) { FSArchive * const arc = file->arc; while (file) { // If call source is waiting on completion, wake that up and leave it to the process { OSIntrMode bak_psr = OS_DisableInterrupts(); file->stat |= FS_FILE_STATUS_OPERATING; if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0) { OS_WakeupThread(file->queue); file = NULL; } (void)OS_RestoreInterrupts(bak_psr); } if (!file) { break; } // If processing is asynchronous, quit for the time being. else if (FSi_InvokeCommand(file, FSi_GetCurrentCommand(file)) == FS_RESULT_PROC_ASYNC) { break; } // If the result is a synchronous completion, select the continuation here. else { file = FSi_NextCommand(arc, TRUE); } } } /*---------------------------------------------------------------------------* Name: FSi_ExecuteAsyncCommand Description: Executes blocking command. Arguments: file FSFile structure that stores the command to execute. Returns: None. *---------------------------------------------------------------------------*/ static void FSi_ExecuteSyncCommand(FSFile *file) { FSArchive * const arc = file->arc; FSResult result; // If necessary, wait for turn, then process the command FSi_WaitConditionOn(&file->stat, FS_FILE_STATUS_OPERATING, file->queue); result = FSi_InvokeCommand(file, FSi_GetCurrentCommand(file)); FSi_EndCommand(file, result); // If there is an asynchronous type command to be processed here, execute instead file = FSi_NextCommand(arc, TRUE); if (file) { FSi_ExecuteAsyncCommand(file); } } /*---------------------------------------------------------------------------* Name: FSi_SendCommand Description: Issues a command to an archive Adjust start timing and block here if synchronous Arguments: file FSFile structure in which the command argument has been specified. Command: Command ID blocking TRUE if blocking Returns: TRUE if the command is successful *---------------------------------------------------------------------------*/ BOOL FSi_SendCommand(FSFile *file, FSCommandType command, BOOL blocking) { BOOL retval = FALSE; FSArchive * const arc = file->arc; if (FS_IsBusy(file)) { OS_TPanic("specified file is now still proceccing previous command!"); } if (!arc) { OS_TWarning("specified handle is not related by any archive\n"); file->error = FS_RESULT_INVALID_PARAMETER; return FALSE; } // Initialize command file->error = FS_RESULT_BUSY; file->stat &= ~(FS_FILE_STATUS_CMD_MASK << FS_FILE_STATUS_CMD_SHIFT); file->stat |= (command << FS_FILE_STATUS_CMD_SHIFT); file->stat |= FS_FILE_STATUS_BUSY; file->next = NULL; if (blocking) { file->stat |= FS_FILE_STATUS_BLOCKING; } // If unloading, cancel the process and if not, add to the end of the list { OSIntrMode bak_psr = OS_DisableInterrupts(); if ((arc->flag & FS_ARCHIVE_FLAG_UNLOADING) != 0) { FSi_EndCommand(file, FS_RESULT_CANCELED); } else { FSFile **pp; for (pp = &arc->list; *pp; pp = &(*pp)->next) { } *pp = file; } (void)OS_RestoreInterrupts(bak_psr); } // If added to the list, check the command execution rights if (file->error != FS_RESULT_CANCELED) { // If there is a command that should be processed here, execute that instead. FSFile *next = FSi_NextCommand(arc, FALSE); // If necessary, block here if (blocking) { FSi_ExecuteSyncCommand(file); retval = FS_IsSucceeded(file); } // Added to the list, so will be processed by someone else { if (next != NULL) { FSi_ExecuteAsyncCommand(next); } retval = TRUE; } } return retval; } /*---------------------------------------------------------------------------* Name: FSi_EndArchive Description: End all archives and release. Arguments: None. Returns: None. *---------------------------------------------------------------------------*/ void FSi_EndArchive(void) { OSIntrMode bak_psr = OS_DisableInterrupts(); while (arc_list) { FSArchive *p_arc = arc_list; arc_list = arc_list->next; (void)FS_UnloadArchive(p_arc); FS_ReleaseArchiveName(p_arc); } (void)OS_RestoreInterrupts(bak_psr); } /*---------------------------------------------------------------------------* Name: FS_FindArchive Description: Searches for archive name If there is no matching name, returns NULL Arguments: name Character string of the archive name to search for name_len Length of name string Returns: Pointer to the archive that was found, or NULL. *---------------------------------------------------------------------------*/ FSArchive *FS_FindArchive(const char *name, int name_len) { OSIntrMode bak_psr = OS_DisableInterrupts(); FSArchive *arc = arc_list; for (; arc; arc = arc->next) { if (FS_IsArchiveLoaded(arc)) { const char *arcname = FS_GetArchiveName(arc); if ((STD_CompareNString(arcname, name, name_len) == 0) && (arcname[name_len] == '\0')) { break; } } } (void)OS_RestoreInterrupts(bak_psr); return arc; } /*---------------------------------------------------------------------------* Name: FS_GetArchiveResultCode Description: Gets latest error code for the specified archive. Arguments: path_or_archive Specify targeted archive FSArchive structure or path string Returns: Latest error code for the specified archive. If the corresponding archive does not exist, FS_RESULT_ERROR *---------------------------------------------------------------------------*/ FSResult FS_GetArchiveResultCode(const void *path_or_archive) { OSIntrMode bak_psr = OS_DisableInterrupts(); FSArchive *arc = arc_list; while (arc && (arc != (const FSArchive *)path_or_archive)) { arc = arc->next; } if (!arc) { arc = FS_NormalizePath((const char *)path_or_archive, NULL, NULL); } (void)OS_RestoreInterrupts(bak_psr); return arc ? arc->result : FS_RESULT_ERROR; } /*---------------------------------------------------------------------------* Name: FS_GetCurrentDirectoryPath Description: Gets current directory with path name. Arguments: None. Returns: String that represents current directory *---------------------------------------------------------------------------*/ const char *FS_GetCurrentDirectory(void) { return current_dir_path; } /*---------------------------------------------------------------------------* Name: FS_SetCurrentDirectory Description: Changes the current directory Arguments: path Path name Returns: If successful, TRUE. *---------------------------------------------------------------------------*/ BOOL FS_SetCurrentDirectory(const char *path) { BOOL retval = FALSE; FSArchive *arc = NULL; u32 baseid = 0; char relpath[FS_ENTRY_LONGNAME_MAX]; SDK_NULL_ASSERT(path); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); // Get normalized path name arc = FS_NormalizePath(path, &baseid, relpath); if (arc) { // In any case, the setting was successful current_dir_pos.arc = arc; current_dir_pos.own_id = 0; current_dir_pos.index = 0; current_dir_pos.pos = 0; (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path)); // If possible, try to get directory ID if (arc->vtbl->FindPath != NULL) { FSFile dir[1]; FSArgumentForFindPath arg[1]; FS_InitFile(dir); dir->arc = arc; dir->argument = arg; arg->baseid = baseid; arg->relpath = relpath; arg->target_is_directory = TRUE; if (FSi_SendCommand(dir, FS_COMMAND_FINDPATH, TRUE)) { current_dir_pos.own_id = (u16)arg->target_id; (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path)); } } retval = TRUE; } return retval; } /*---------------------------------------------------------------------------* Name: FSi_CopySafeString Description: Checks buffer size and copies character string Arguments: dst Transfer destination buffer dstlen Transfer destination size src Transfer source buffer. srclen Transfer destination size stickyFailure FALSE, if truncated at transfer origin Returns: Archive pointer or NULL *---------------------------------------------------------------------------*/ static int FSi_CopySafeString(char *dst, int dstlen, const char *src, int srclen, BOOL *stickyFailure) { int i; int n = (dstlen - 1 < srclen) ? (dstlen - 1) : srclen; for (i = 0; (i < n) && src[i]; ++i) { dst[i] = src[i]; } if ((i < srclen) && src[i]) { *stickyFailure = TRUE; } dst[i] = '\0'; return i; } /*---------------------------------------------------------------------------* Name: FS_NormalizePath Description: Normalizes to format of (Standard Directory ID) + (Path Name) considering the current directory in the specified path. Arguments: path: Non-normalized path string. baseid Standard directory ID storage destination or NULL relpath Path name storage destination after conversion or NULL Returns: Archive pointer or NULL *---------------------------------------------------------------------------*/ FSArchive* FS_NormalizePath(const char *path, u32 *baseid, char *relpath) { FSArchive *arc = NULL; int pathlen = 0; int pathmax = FS_ENTRY_LONGNAME_MAX; BOOL stickyFailure = FALSE; // Reevaluate when current directory is not specified if (current_dir_pos.arc == NULL) { current_dir_pos.arc = arc_list; current_dir_pos.own_id = 0; current_dir_pos.pos = 0; current_dir_pos.index = 0; current_dir_path[0] = '\0'; } // With "/path" make the current archive route the base if (FSi_IsSlash((u8)*path)) { arc = current_dir_pos.arc; ++path; if (baseid) { *baseid = 0; } } else { int i; for (i = 0; ; i = FSi_IncrementSjisPosition(path, i)) { u32 c = (u8)path[i]; // With "(path/)*path" the relevant path from the current position if (!c || FSi_IsSlash(c)) { arc = current_dir_pos.arc; if (baseid) { *baseid = current_dir_pos.own_id; } if(relpath) { // If directory is retained only by the path name, link it. if ((current_dir_pos.own_id == 0) && (current_dir_path[0] != '\0')) { pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen, current_dir_path, FS_ENTRY_LONGNAME_MAX, &stickyFailure); pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen, "/", 1, &stickyFailure); } } break; } // With "arc:/path," the full path else if (c == ':') { arc = FS_FindArchive(path, i); if (!arc) { OS_Warning("archive \"%*s\" is not found.", i, path); } path += i + 1; if (FSi_IsSlash((u8)*path)) { ++path; } if (baseid) { *baseid = 0; } break; } } } if(relpath) { // Be careful of special entry names and normalize relative path int curlen = 0; while (!stickyFailure) { char c = path[curlen]; if ((c != '\0') && !FSi_IsSlash((u8)c)) { curlen += STD_IsSjisCharacter(&path[curlen]) ? 2 : 1; } else { // Ignore empty directory if (curlen == 0) { } // Ignore "." (current directory) else if ((curlen == 1) && (path[0] == '.')) { } // ".." (Parent Directory) raises the root one level as the upper limit else if ((curlen == 2) && (path[0] == '.') && (path[1] == '.')) { if (pathlen > 0) { --pathlen; } pathlen = FSi_DecrementSjisPositionToSlash(relpath, pathlen) + 1; } // Add entry for anything else else { pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen, path, curlen, &stickyFailure); if (c != '\0') { pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen, "/", 1, &stickyFailure); } } if (c == '\0') { break; } path += curlen + 1; curlen = 0; } } relpath[pathlen] = '\0'; pathlen = FSi_TrimSjisTrailingSlash(relpath); } return stickyFailure ? NULL : arc; } /*---------------------------------------------------------------------------* Name: FS_InitArchive Description: Initialize archive structure Arguments: p_arc Archive to initialize. Returns: None. *---------------------------------------------------------------------------*/ void FS_InitArchive(FSArchive *p_arc) { SDK_NULL_ASSERT(p_arc); MI_CpuClear8(p_arc, sizeof(FSArchive)); OS_InitThreadQueue(&p_arc->queue); } /*---------------------------------------------------------------------------* Name: FS_RegisterArchiveName Description: Registers the archive name in the file system and associates it. The archive itself is not yet loaded into the file system. The archive name "rom" is reserved by the file system. Arguments: p_arc Archive to associate with the name. name String of the name to register name_len Length of name string Returns: None. *---------------------------------------------------------------------------*/ BOOL FS_RegisterArchiveName(FSArchive *p_arc, const char *name, u32 name_len) { BOOL retval = FALSE; SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(p_arc); SDK_NULL_ASSERT(name); { OSIntrMode bak_intr = OS_DisableInterrupts(); if (!FS_FindArchive(name, (s32)name_len)) { // adds to the end of the list FSArchive **pp; for (pp = &arc_list; *pp; pp = &(*pp)->next) { } *pp = p_arc; // With the current specifications, the archive has a maximum of 3 characters if (name_len <= FS_ARCHIVE_NAME_LEN_MAX) { p_arc->name.pack = 0; (void)STD_CopyLString(p_arc->name.ptr, name, (int)(name_len + 1)); } else { #if defined(FS_SUPPORT_LONG_ARCNAME) // Feature for expanding the limit of the number of characters in an archive name // It is necessary to allocate external memory to support without changing the FSArchive structure size // if (name_len <= FS_LONG_ARCNAME_LENGTH_MAX) { int i; for (i = 0; ; ++i) { if (i >= FS_LONG_ARCNAME_TABLE_MAX) { OS_TPanic("failed to allocate memory for long archive-name(%.*s)!", name_len, name); } else if (FSiLongNameTable[i][0] == '\0') { (void)STD_CopyLString(FSiLongNameTable[i], name, (int)(name_len + 1)); p_arc->name.pack = (u32)FSiLongNameTable[i]; break; } } } #endif // Cannot register archive names that are too long else { OS_TPanic("too long archive-name(%.*s)!", name_len, name); } } p_arc->flag |= FS_ARCHIVE_FLAG_REGISTER; retval = TRUE; } (void)OS_RestoreInterrupts(bak_intr); } return retval; } /*---------------------------------------------------------------------------* Name: FS_ReleaseArchiveName Description: Release registered archive names. They must be unloaded from the file system. Arguments: p_arc Archive with name to release. Returns: None. *---------------------------------------------------------------------------*/ void FS_ReleaseArchiveName(FSArchive *p_arc) { SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(p_arc); if(p_arc == arc_list) { OS_TPanic("[file-system] cannot modify \"rom\" archive.\n"); } if (p_arc->name.pack) { OSIntrMode bak_psr = OS_DisableInterrupts(); // Cut from the list. FSArchive **pp; for (pp = &arc_list; *pp; pp = &(*pp)->next) { if(*pp == p_arc) { *pp = (*pp)->next; break; } } #if defined(FS_SUPPORT_LONG_ARCNAME) // Deallocate buffer to the system if there is a long archive name if (p_arc->name.ptr[3] != '\0') { ((char *)p_arc->name.pack)[0] = '\0'; } #endif p_arc->name.pack = 0; p_arc->next = NULL; p_arc->flag &= ~FS_ARCHIVE_FLAG_REGISTER; // Deallocate if it is the current archive if (current_dir_pos.arc == p_arc) { current_dir_pos.arc = NULL; } (void)OS_RestoreInterrupts(bak_psr); } } /*---------------------------------------------------------------------------* Name: FS_GetArchiveName Description: Acquire archive name Arguments: p_arc Archive whose name to get. Returns: Archive name registered in the file system *---------------------------------------------------------------------------*/ const char *FS_GetArchiveName(const FSArchive *arc) { #if defined(FS_SUPPORT_LONG_ARCNAME) return (arc->name.ptr[3] != '\0') ? (const char *)arc->name.pack : arc->name.ptr; #else return arc->name.ptr; #endif } /*---------------------------------------------------------------------------* Name: FS_MountArchive Description: Mounts the archive Arguments: arc Archive to mount userdata User defined variable that associates with an archive vtbl Command interface reserved Reserved for future use (Always specify 0) Returns: TRUE if mounting is successful *---------------------------------------------------------------------------*/ BOOL FS_MountArchive(FSArchive *arc, void *userdata, const FSArchiveInterface *vtbl, u32 reserved) { (void)reserved; SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(arc); SDK_ASSERT(!FS_IsArchiveLoaded(arc)); // Initialize archive with new specifications arc->userdata = userdata; arc->vtbl = vtbl; // Issue mounting notification event { FSFile tmp[1]; FS_InitFile(tmp); tmp->arc = arc; (void)FSi_InvokeCommand(tmp, FS_COMMAND_MOUNT); } arc->flag |= FS_ARCHIVE_FLAG_LOADED; return TRUE; } /*---------------------------------------------------------------------------* Name: FS_UnmountArchive Description: Unmounts the archive Arguments: arc Archive to unmount Returns: TRUE if unmounting is successful *---------------------------------------------------------------------------*/ BOOL FS_UnmountArchive(FSArchive *arc) { SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(arc); { OSIntrMode bak_psr = OS_DisableInterrupts(); // Ignore if it is not loaded. if (FS_IsArchiveLoaded(arc)) { // Cancel all idling commands { BOOL bak_state = FS_SuspendArchive(arc); FSFile *file = arc->list; arc->flag |= FS_ARCHIVE_FLAG_UNLOADING; while (file) { FSFile *next = file->next; FSi_EndCommand(file, FS_RESULT_CANCELED); file = next; } arc->list = NULL; if (bak_state) { (void)FS_ResumeArchive(arc); } } // Issue unmounting notification event { FSFile tmp[1]; FS_InitFile(tmp); tmp->arc = arc; (void)FSi_InvokeCommand(tmp, FS_COMMAND_UNMOUNT); } arc->flag &= ~(FS_ARCHIVE_FLAG_CANCELING | FS_ARCHIVE_FLAG_LOADED | FS_ARCHIVE_FLAG_UNLOADING); } (void)OS_RestoreInterrupts(bak_psr); } return TRUE; } /*---------------------------------------------------------------------------* Name: FS_SuspendArchive Description: Stops archive processing mechanism itself. If a process is currently executing, waits for it to complete. Arguments: p_arc Archive to suspend. Returns: TRUE if it was not in a suspended state before the call. *---------------------------------------------------------------------------*/ BOOL FS_SuspendArchive(FSArchive *p_arc) { BOOL retval = FALSE; SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(p_arc); { OSIntrMode bak_psr = OS_DisableInterrupts(); retval = !FS_IsArchiveSuspended(p_arc); if (retval) { if ((p_arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0) { p_arc->flag |= FS_ARCHIVE_FLAG_SUSPEND; } else { p_arc->flag |= FS_ARCHIVE_FLAG_SUSPENDING; FSi_WaitConditionOff(&p_arc->flag, FS_ARCHIVE_FLAG_SUSPENDING, &p_arc->queue); } } (void)OS_RestoreInterrupts(bak_psr); } return retval; } /*---------------------------------------------------------------------------* Name: FS_ResumeArchive Description: Resumes the suspended archive processing. Arguments: arc Archive to reopen Returns: TRUE if it was not in a suspended state before the call. *---------------------------------------------------------------------------*/ BOOL FS_ResumeArchive(FSArchive *arc) { BOOL retval; SDK_ASSERT(FS_IsAvailable()); SDK_NULL_ASSERT(arc); { OSIntrMode bak_irq = OS_DisableInterrupts(); retval = !FS_IsArchiveSuspended(arc); if (!retval) { arc->flag &= ~FS_ARCHIVE_FLAG_SUSPEND; } (void)OS_RestoreInterrupts(bak_irq); } { FSFile *file = NULL; file = FSi_NextCommand(arc, TRUE); if (file) { FSi_ExecuteAsyncCommand(file); } } return retval; } /*---------------------------------------------------------------------------* Name: FS_NotifyArchiveAsyncEnd Description: To notify that asynchronous archive processing is complete, Call from the archive implentation side Arguments: arc Archive for which to notify completion. ret Processing result. Returns: None. *---------------------------------------------------------------------------*/ void FS_NotifyArchiveAsyncEnd(FSArchive *arc, FSResult ret) { FSFile *file = arc->list; if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0) { OSIntrMode bak_psr = OS_DisableInterrupts(); file->stat |= FS_FILE_STATUS_ASYNC_DONE; file->error = ret; OS_WakeupThread(file->queue); (void)OS_RestoreInterrupts(bak_psr); } else { FSi_EndCommand(file, ret); file = FSi_NextCommand(arc, TRUE); if (file) { FSi_ExecuteAsyncCommand(file); } } } /*---------------------------------------------------------------------------* Name: FS_WaitAsync Description: wait for end of asynchronous function and return result. Arguments: file to wait Returns: if succeeded, TRUE. *---------------------------------------------------------------------------*/ BOOL FS_WaitAsync(FSFile *file) { SDK_NULL_ASSERT(file); SDK_ASSERT(FS_IsAvailable()); SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ); { BOOL is_owner = FALSE; OSIntrMode bak_psr = OS_DisableInterrupts(); if (FS_IsBusy(file)) { // Take on processing here if an unprocessed asynchronous mode. is_owner = !(file->stat & (FS_FILE_STATUS_BLOCKING | FS_FILE_STATUS_OPERATING)); if (is_owner) { file->stat |= FS_FILE_STATUS_BLOCKING; } } (void)OS_RestoreInterrupts(bak_psr); if (is_owner) { FSi_ExecuteSyncCommand(file); } else { FSi_WaitConditionOff(&file->stat, FS_FILE_STATUS_BUSY, file->queue); } } return FS_IsSucceeded(file); } #endif /* FS_IMPLEMENT */