1 /*---------------------------------------------------------------------------*
2   Project:  TwlSDK - FS - libraries
3   File:     fs_archive.c
4 
5   Copyright 2007-2009 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law. They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2009-06-04#$
14   $Rev: 10698 $
15   $Author: okubata_ryoma $
16 
17  *---------------------------------------------------------------------------*/
18 
19 
20 #include <nitro/fs.h>
21 
22 #include <nitro/mi/memory.h>
23 #include <nitro/std.h>
24 
25 
26 #include "../include/util.h"
27 #include "../include/command.h"
28 
29 
30 #if defined(FS_IMPLEMENT)
31 
32 
33 /*---------------------------------------------------------------------------*/
34 /* Constants */
35 
36 // Enable when internally supporting long archive names
37 #define FS_SUPPORT_LONG_ARCNAME
38 
39 
40 /*---------------------------------------------------------------------------*/
41 /* Variables */
42 
43 // Archive list registered to the file system
44 static FSArchive   *arc_list = NULL;
45 
46 // Current directory
47 static FSDirPos     current_dir_pos;
48 static char         current_dir_path[FS_ENTRY_LONGNAME_MAX];
49 
50 #if defined(FS_SUPPORT_LONG_ARCNAME)
51 // Static buffer for internal support of long archive names
52 #define FS_LONG_ARCNAME_LENGTH_MAX  15
53 #define FS_LONG_ARCNAME_TABLE_MAX   16
54 static char FSiLongNameTable[FS_LONG_ARCNAME_TABLE_MAX][FS_LONG_ARCNAME_LENGTH_MAX + 1];
55 #endif
56 
57 
58 /*---------------------------------------------------------------------------*/
59 /* functions */
60 
FSi_GetArchiveChain(void)61 FSArchive* FSi_GetArchiveChain(void)
62 {
63     return arc_list;
64 }
65 
66 /*---------------------------------------------------------------------------*
67   Name:         FSi_IsEventCommand
68 
69   Description:  Determine whether the command is an event notification
70 
71   Arguments:    command          Command type
72 
73   Returns:      TRUE when command is event notification
74  *---------------------------------------------------------------------------*/
FSi_IsEventCommand(FSCommandType command)75 static BOOL FSi_IsEventCommand(FSCommandType command)
76 {
77     return
78         ((command == FS_COMMAND_ACTIVATE) ||
79         (command == FS_COMMAND_IDLE) ||
80         (command == FS_COMMAND_SUSPEND) ||
81         (command == FS_COMMAND_RESUME) ||
82         (command == FS_COMMAND_MOUNT) ||
83         (command == FS_COMMAND_UNMOUNT) ||
84         (command == FS_COMMAND_INVALID));
85 }
86 
87 /*---------------------------------------------------------------------------*
88   Name:         FSi_EndCommand
89 
90   Description:  Completes the command and returns if there is an idle thread.
91 
92   Arguments:    file             Completed command
93                 ret:              Command result value
94 
95   Returns:      None.
96  *---------------------------------------------------------------------------*/
FSi_EndCommand(FSFile * file,FSResult ret)97 static void FSi_EndCommand(FSFile *file, FSResult ret)
98 {
99     OSIntrMode bak_psr = OS_DisableInterrupts();
100     // Delete command from the list
101     FSArchive *const arc = file->arc;
102     if (arc)
103     {
104         FSFile    **pp = &arc->list;
105         for (; *pp; pp = &(*pp)->next)
106         {
107             if (*pp == file)
108             {
109                 *pp = file->next;
110                 break;
111             }
112         }
113         file->next = NULL;
114     }
115     // Store results
116     {
117         FSCommandType   command = FSi_GetCurrentCommand(file);
118         if (!FSi_IsEventCommand(command))
119         {
120             arc->command = command;
121             arc->result = ret;
122         }
123         file->error = ret;
124         file->stat &= ~(FS_FILE_STATUS_CANCEL | FS_FILE_STATUS_BUSY |
125                         FS_FILE_STATUS_BLOCKING | FS_FILE_STATUS_OPERATING |
126                         FS_FILE_STATUS_ASYNC_DONE | FS_FILE_STATUS_UNICODE_MODE);
127     }
128     // Notify if there is an idling thread
129     OS_WakeupThread(file->queue);
130     (void)OS_RestoreInterrupts(bak_psr);
131 }
132 
133 
134 // The following two temporarily publicized for procedures
135 // 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.
136 //
137 
FSi_WaitForArchiveCompletion(FSFile * file,FSResult result)138 FSResult FSi_WaitForArchiveCompletion(FSFile *file, FSResult result)
139 {
140     if (result == FS_RESULT_PROC_ASYNC)
141     {
142         FSi_WaitConditionOn(&file->stat, FS_FILE_STATUS_ASYNC_DONE, file->queue);
143         file->stat &= ~FS_FILE_STATUS_ASYNC_DONE;
144         result = file->error;
145     }
146     return result;
147 }
148 
149 /*---------------------------------------------------------------------------*
150   Name:         FSi_InvokeCommand
151 
152   Description:  Issues a command to an archive.
153                 Command list management is resolved before calling this function
154 
155   Arguments:    file             FSFile structure that stores the argument
156                 Command: Command ID
157 
158   Returns:      The processing result for the command.
159  *---------------------------------------------------------------------------*/
FSi_InvokeCommand(FSFile * file,FSCommandType command)160 static FSResult FSi_InvokeCommand(FSFile *file, FSCommandType command)
161 {
162     FSResult            result = FS_RESULT_UNSUPPORTED;
163     FSArchive   * const arc = file->arc;
164 
165     {
166         const void   *(*table) = (const void*)arc->vtbl;
167         // Undefined command out of range
168         if ((command < 0) || (command >= FS_COMMAND_MAX))
169         {
170             OS_TWarning("undefined command (%d)\n", command);
171             result = FS_RESULT_UNSUPPORTED;
172         }
173         // Call by restoring the argument if it is not a command whose implementation was abandoned by the driver side
174         else if (table[command] == NULL)
175         {
176             result = FS_RESULT_UNSUPPORTED;
177         }
178         else
179         {
180 #define FS_DECLARE_ARGUMENT_(type)                              \
181             type *arg = (type*) file->argument;                 \
182             (void)arg
183 
184 #define FS_INVOKE_METHOD_(command, ...)                         \
185             do                                                  \
186             {                                                   \
187                 FS_DECLARE_ARGUMENT_(FSArgumentFor ## command); \
188                 result = arc->vtbl->command(__VA_ARGS__);       \
189             }                                                   \
190             while(0)
191 #define FS_NOTIFY_EVENT_(command, ...)                          \
192             do                                                  \
193             {                                                   \
194                 FS_DECLARE_ARGUMENT_(FSArgumentFor ## command); \
195                 (void)arc->vtbl->command(__VA_ARGS__);          \
196                 return FS_RESULT_SUCCESS;                       \
197             }                                                   \
198             while(0)
199             switch (command)
200             {
201             case FS_COMMAND_READFILE:
202                 FS_INVOKE_METHOD_(ReadFile, arc, file, arg->buffer, &arg->length);
203                 break;
204             case FS_COMMAND_WRITEFILE:
205                 FS_INVOKE_METHOD_(WriteFile, arc, file, arg->buffer, &arg->length);
206                 break;
207             case FS_COMMAND_SEEKDIR:
208                 FS_INVOKE_METHOD_(SeekDirectory, arc, file, arg->id, arg->position);
209                 break;
210             case FS_COMMAND_READDIR:
211                 FS_INVOKE_METHOD_(ReadDirectory, arc, file, arg->info);
212                 break;
213             case FS_COMMAND_FINDPATH:
214                 FS_INVOKE_METHOD_(FindPath, arc, arg->baseid, arg->relpath, &arg->target_id, arg->target_is_directory);
215                 break;
216             case FS_COMMAND_GETPATH:
217                 FS_INVOKE_METHOD_(GetPath, arc, file, arg->is_directory, arg->buffer, &arg->length);
218                 break;
219             case FS_COMMAND_OPENFILEFAST:
220                 FS_INVOKE_METHOD_(OpenFileFast, arc, file, arg->id, arg->mode);
221                 break;
222             case FS_COMMAND_OPENFILEDIRECT:
223                 FS_INVOKE_METHOD_(OpenFileDirect, arc, file, arg->top, arg->bottom, &arg->id);
224                 break;
225             case FS_COMMAND_CLOSEFILE:
226                 FS_INVOKE_METHOD_(CloseFile, arc, file);
227                 break;
228             case FS_COMMAND_ACTIVATE:
229                 FS_NOTIFY_EVENT_(Activate, arc);
230                 break;
231             case FS_COMMAND_IDLE:
232                 FS_NOTIFY_EVENT_(Idle, arc);
233                 break;
234             case FS_COMMAND_SUSPEND:
235                 FS_NOTIFY_EVENT_(Suspend, arc);
236                 break;
237             case FS_COMMAND_RESUME:
238                 FS_NOTIFY_EVENT_(Resume, arc);
239                 break;
240             case FS_COMMAND_OPENFILE:
241                 FS_INVOKE_METHOD_(OpenFile, arc, file, arg->baseid, arg->relpath, arg->mode);
242                 break;
243             case FS_COMMAND_SEEKFILE:
244                 FS_INVOKE_METHOD_(SeekFile, arc, file, &arg->offset, arg->from);
245                 break;
246             case FS_COMMAND_GETFILELENGTH:
247                 FS_INVOKE_METHOD_(GetFileLength, arc, file, &arg->length);
248                 break;
249             case FS_COMMAND_GETFILEPOSITION:
250                 FS_INVOKE_METHOD_(GetFilePosition, arc, file, &arg->position);
251                 break;
252                 // From here the extended command
253             case FS_COMMAND_MOUNT:
254                 FS_NOTIFY_EVENT_(Mount, arc);
255                 break;
256             case FS_COMMAND_UNMOUNT:
257                 FS_NOTIFY_EVENT_(Unmount, arc);
258                 break;
259             case FS_COMMAND_GETARCHIVECAPS:
260                 FS_INVOKE_METHOD_(GetArchiveCaps, arc, &arg->caps);
261                 break;
262             case FS_COMMAND_CREATEFILE:
263                 FS_INVOKE_METHOD_(CreateFile, arc, arg->baseid, arg->relpath, arg->permit);
264                 break;
265             case FS_COMMAND_DELETEFILE:
266                 FS_INVOKE_METHOD_(DeleteFile, arc, arg->baseid, arg->relpath);
267                 break;
268             case FS_COMMAND_RENAMEFILE:
269                 FS_INVOKE_METHOD_(RenameFile, arc, arg->baseid_src, arg->relpath_src, arg->baseid_dst, arg->relpath_dst);
270                 break;
271             case FS_COMMAND_GETPATHINFO:
272                 FS_INVOKE_METHOD_(GetPathInfo, arc, arg->baseid, arg->relpath, arg->info);
273                 break;
274             case FS_COMMAND_SETPATHINFO:
275                 FS_INVOKE_METHOD_(SetPathInfo, arc, arg->baseid, arg->relpath, arg->info);
276                 break;
277             case FS_COMMAND_CREATEDIRECTORY:
278                 FS_INVOKE_METHOD_(CreateDirectory, arc, arg->baseid, arg->relpath, arg->permit);
279                 break;
280             case FS_COMMAND_DELETEDIRECTORY:
281                 FS_INVOKE_METHOD_(DeleteDirectory, arc, arg->baseid, arg->relpath);
282                 break;
283             case FS_COMMAND_RENAMEDIRECTORY:
284                 FS_INVOKE_METHOD_(RenameDirectory, arc, arg->baseid_src, arg->relpath_src, arg->baseid_dst, arg->relpath_dst);
285                 break;
286             case FS_COMMAND_GETARCHIVERESOURCE:
287                 FS_INVOKE_METHOD_(GetArchiveResource, arc, arg->resource);
288                 break;
289             case FS_COMMAND_FLUSHFILE:
290                 FS_INVOKE_METHOD_(FlushFile, arc, file);
291                 break;
292             case FS_COMMAND_SETFILELENGTH:
293                 FS_INVOKE_METHOD_(SetFileLength, arc, file, arg->length);
294                 break;
295             case FS_COMMAND_OPENDIRECTORY:
296                 FS_INVOKE_METHOD_(OpenDirectory, arc, file, arg->baseid, arg->relpath, arg->mode);
297                 break;
298             case FS_COMMAND_CLOSEDIRECTORY:
299                 FS_INVOKE_METHOD_(CloseDirectory, arc, file);
300                 break;
301             case FS_COMMAND_SETSEEKCACHE:
302                 FS_INVOKE_METHOD_(SetSeekCache, arc, file, arg->buf, arg->buf_size);
303                 break;
304             default:
305                 result = FS_RESULT_UNSUPPORTED;
306             }
307 #undef FS_DECLARE_ARGUMENT_
308 #undef FS_INVOKE_METHOD_
309 #undef FS_NOTIFY_EVENT_
310         }
311     }
312     // If this is a normal command with no event notification, return to satisfy conditions when issued
313     if (!FSi_IsEventCommand(command))
314     {
315         // Because there are many cases where the path name is incorrect and unsupported, if there is the possibility, a warning will be output.
316         //
317         if (result == FS_RESULT_UNSUPPORTED)
318         {
319             OS_TWarning("archive \"%s:\" cannot support command %d.\n", FS_GetArchiveName(arc), command);
320         }
321         // If necessary, apply blocking
322         if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0)
323         {
324             result = FSi_WaitForArchiveCompletion(file, result);
325         }
326         // Release here if completed while nobody is blocking
327         else if (result != FS_RESULT_PROC_ASYNC)
328         {
329             FSi_EndCommand(file, result);
330         }
331     }
332     return result;
333 }
334 
335 /*---------------------------------------------------------------------------*
336   Name:         FSi_NextCommand
337 
338   Description:  Archive selects the next command that should be processed.
339                 If an asynchronous command is selected, return that pointer.
340                 If anything other than NULL is returned, it is necessary to process at the source of the call.
341 
342   Arguments:    arc            Archive that gets the next command
343                 owner            TRUE if source of call already has command execution rights
344 
345   Returns:      The next command that must be processed here.
346  *---------------------------------------------------------------------------*/
FSi_NextCommand(FSArchive * arc,BOOL owner)347 static FSFile *FSi_NextCommand(FSArchive *arc, BOOL owner)
348 {
349     FSFile  *next = NULL;
350     // First, check all command cancel requests.
351     {
352         OSIntrMode bak_psr = OS_DisableInterrupts();
353         if ((arc->flag & FS_ARCHIVE_FLAG_CANCELING) != 0)
354         {
355             FSFile *p = arc->list;
356             arc->flag &= ~FS_ARCHIVE_FLAG_CANCELING;
357             while (p != NULL)
358             {
359                 FSFile *q = p->next;
360                 if (FS_IsCanceling(p))
361                 {
362                     FSi_EndCommand(p, FS_RESULT_CANCELED);
363                     if (!q)
364                     {
365                         q = arc->list;
366                     }
367                 }
368                 p = q;
369             }
370         }
371         (void)OS_RestoreInterrupts(bak_psr);
372     }
373     // Determine whether the next command can be executed
374     {
375         OSIntrMode  bak_psr = OS_DisableInterrupts();
376         if (((arc->flag & FS_ARCHIVE_FLAG_SUSPENDING) == 0) &&
377             ((arc->flag & FS_ARCHIVE_FLAG_SUSPEND) == 0) && arc->list)
378         {
379             // Startup here if archive is stopped
380             const BOOL  is_started = owner && ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0);
381             if (is_started)
382             {
383                 arc->flag |= FS_ARCHIVE_FLAG_RUNNING;
384             }
385             (void)OS_RestoreInterrupts(bak_psr);
386             if (is_started)
387             {
388                 (void)FSi_InvokeCommand(arc->list, FS_COMMAND_ACTIVATE);
389             }
390             bak_psr = OS_DisableInterrupts();
391             // Command at the top of the list gets execution rights
392             if (owner || is_started)
393             {
394                 next = arc->list;
395                 next->stat |= FS_FILE_STATUS_OPERATING;
396             }
397             // If command issuing source is blocking, transfer execution rights
398             if (owner && ((next->stat & FS_FILE_STATUS_BLOCKING) != 0))
399             {
400                 OS_WakeupThread(next->queue);
401                 next = NULL;
402             }
403             (void)OS_RestoreInterrupts(bak_psr);
404         }
405         // suspend or idle state
406         else
407         {
408             // If does not have execution rights, stop and go to idle state
409             if (owner)
410             {
411                 if ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) != 0)
412                 {
413                     FSFile  tmp;
414                     FS_InitFile(&tmp);
415                     tmp.arc = arc;
416                     arc->flag &= ~FS_ARCHIVE_FLAG_RUNNING;
417                     (void)FSi_InvokeCommand(&tmp, FS_COMMAND_IDLE);
418                 }
419                 // If suspend is executing, notify the source of the suspend function call
420                 if ((arc->flag & FS_ARCHIVE_FLAG_SUSPENDING) != 0)
421                 {
422                     arc->flag &= ~FS_ARCHIVE_FLAG_SUSPENDING;
423                     arc->flag |= FS_ARCHIVE_FLAG_SUSPEND;
424                     OS_WakeupThread(&arc->queue);
425                 }
426             }
427             (void)OS_RestoreInterrupts(bak_psr);
428         }
429     }
430     return next;
431 }
432 
433 /*---------------------------------------------------------------------------*
434   Name:         FSi_ExecuteAsyncCommand
435 
436   Description:  Executes an asynchronous command.
437                 The first call is made from the user thread with interrupts enabled.
438                 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().
439 
440 
441                 Therefore, it is necessary to be aware of the NotifyAsyncEnd() calling environment when archive processing switches between synchronous and asynchronous.
442 
443 
444   Arguments:    file           FSFile structure that stores the asynchronous command to execute.
445 
446   Returns:      None.
447  *---------------------------------------------------------------------------*/
FSi_ExecuteAsyncCommand(FSFile * file)448 static void FSi_ExecuteAsyncCommand(FSFile *file)
449 {
450     FSArchive   * const arc = file->arc;
451     while (file)
452     {
453         // If call source is waiting on completion, wake that up and leave it to the process
454         {
455             OSIntrMode bak_psr = OS_DisableInterrupts();
456             file->stat |= FS_FILE_STATUS_OPERATING;
457             if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0)
458             {
459                 OS_WakeupThread(file->queue);
460                 file = NULL;
461             }
462             (void)OS_RestoreInterrupts(bak_psr);
463         }
464         if (!file)
465         {
466             break;
467         }
468         // If processing is asynchronous, quit for the time being.
469         else if (FSi_InvokeCommand(file, FSi_GetCurrentCommand(file)) == FS_RESULT_PROC_ASYNC)
470         {
471             break;
472         }
473         // If the result is a synchronous completion, select the continuation here.
474         else
475         {
476             file = FSi_NextCommand(arc, TRUE);
477         }
478     }
479 }
480 
481 /*---------------------------------------------------------------------------*
482   Name:         FSi_ExecuteAsyncCommand
483 
484   Description:  Executes blocking command.
485 
486   Arguments:    file           FSFile structure that stores the command to execute.
487 
488   Returns:      None.
489  *---------------------------------------------------------------------------*/
FSi_ExecuteSyncCommand(FSFile * file)490 static void FSi_ExecuteSyncCommand(FSFile *file)
491 {
492     FSArchive   * const arc = file->arc;
493     FSResult            result;
494     // If necessary, wait for turn, then process the command
495     FSi_WaitConditionOn(&file->stat, FS_FILE_STATUS_OPERATING, file->queue);
496     result = FSi_InvokeCommand(file, FSi_GetCurrentCommand(file));
497     FSi_EndCommand(file, result);
498     // If there is an asynchronous type command to be processed here, execute instead
499     file = FSi_NextCommand(arc, TRUE);
500     if (file)
501     {
502         FSi_ExecuteAsyncCommand(file);
503     }
504 }
505 
506 /*---------------------------------------------------------------------------*
507   Name:         FSi_SendCommand
508 
509   Description:  Issues a command to an archive
510                 Adjust start timing and block here if synchronous
511 
512   Arguments:    file           FSFile structure in which the command argument has been specified.
513                 Command: Command ID
514                 blocking		TRUE if blocking
515 
516   Returns:      TRUE if the command is successful
517  *---------------------------------------------------------------------------*/
FSi_SendCommand(FSFile * file,FSCommandType command,BOOL blocking)518 BOOL FSi_SendCommand(FSFile *file, FSCommandType command, BOOL blocking)
519 {
520     BOOL                retval = FALSE;
521     FSArchive   * const arc = file->arc;
522     BOOL                owner = FALSE;
523 
524     if (FS_IsBusy(file))
525     {
526         OS_TPanic("specified file is now still proceccing previous command!");
527     }
528     if (!arc)
529     {
530         OS_TWarning("specified handle is not related by any archive\n");
531         file->error = FS_RESULT_INVALID_PARAMETER;
532         return FALSE;
533     }
534 
535     // Initialize command
536     file->error = FS_RESULT_BUSY;
537     file->stat &= ~(FS_FILE_STATUS_CMD_MASK << FS_FILE_STATUS_CMD_SHIFT);
538     file->stat |= (command << FS_FILE_STATUS_CMD_SHIFT);
539     file->stat |= FS_FILE_STATUS_BUSY;
540     file->next = NULL;
541     if (blocking)
542     {
543         file->stat |= FS_FILE_STATUS_BLOCKING;
544     }
545     // If unloading, cancel the process and if not, add to the end of the list
546     {
547         OSIntrMode          bak_psr = OS_DisableInterrupts();
548         if ((arc->flag & FS_ARCHIVE_FLAG_UNLOADING) != 0)
549         {
550             FSi_EndCommand(file, FS_RESULT_CANCELED);
551         }
552         else
553         {
554             FSFile    **pp;
555             for (pp = &arc->list; *pp; pp = &(*pp)->next)
556             {
557             }
558             *pp = file;
559         }
560         owner = (arc->list == file) && ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0);
561         (void)OS_RestoreInterrupts(bak_psr);
562     }
563     // If added to the list, check the command execution rights
564     if (file->error != FS_RESULT_CANCELED)
565     {
566         // If there is a command that should be processed here, execute that instead.
567         FSFile *next = FSi_NextCommand(arc, owner);
568         // If necessary, block here
569         if (blocking)
570         {
571             FSi_ExecuteSyncCommand(file);
572             retval = FS_IsSucceeded(file);
573         }
574         // Added to the list, so will be processed by someone
575         else
576         {
577             if (next != NULL)
578             {
579                 FSi_ExecuteAsyncCommand(next);
580             }
581             retval = TRUE;
582         }
583     }
584 
585     return retval;
586 }
587 
588 /*---------------------------------------------------------------------------*
589   Name:         FSi_EndArchive
590 
591   Description:  End all archives and release.
592 
593   Arguments:    None.
594 
595   Returns:      None.
596  *---------------------------------------------------------------------------*/
FSi_EndArchive(void)597 void FSi_EndArchive(void)
598 {
599     OSIntrMode bak_psr = OS_DisableInterrupts();
600     while (arc_list)
601     {
602         FSArchive *p_arc = arc_list;
603         arc_list = arc_list->next;
604         (void)FS_UnloadArchive(p_arc);
605         FS_ReleaseArchiveName(p_arc);
606     }
607     (void)OS_RestoreInterrupts(bak_psr);
608 }
609 
610 /*---------------------------------------------------------------------------*
611   Name:         FS_FindArchive
612 
613   Description:  Searches for archive name
614                 If there is no matching name, returns NULL
615 
616   Arguments:    name             Character string of the archive name to search for
617                 name_len         Length of name string
618 
619   Returns:      Pointer to the archive that was found, or NULL.
620  *---------------------------------------------------------------------------*/
FS_FindArchive(const char * name,int name_len)621 FSArchive *FS_FindArchive(const char *name, int name_len)
622 {
623     OSIntrMode  bak_psr = OS_DisableInterrupts();
624     FSArchive  *arc = arc_list;
625     for (; arc; arc = arc->next)
626     {
627         if (FS_IsArchiveLoaded(arc))
628         {
629             const char *arcname = FS_GetArchiveName(arc);
630             if ((STD_CompareNString(arcname, name, name_len) == 0) && (arcname[name_len] == '\0'))
631             {
632                 break;
633             }
634         }
635     }
636     (void)OS_RestoreInterrupts(bak_psr);
637     return arc;
638 }
639 
640 /*---------------------------------------------------------------------------*
641   Name:         FS_GetArchiveResultCode
642 
643   Description:  Gets latest error code for the specified archive.
644 
645   Arguments:    path_or_archive  Specify targeted archive
646                                  FSArchive structure or path string
647 
648   Returns:      Latest error code for the specified archive.
649                 If the corresponding archive does not exist, FS_RESULT_ERROR
650  *---------------------------------------------------------------------------*/
FS_GetArchiveResultCode(const void * path_or_archive)651 FSResult FS_GetArchiveResultCode(const void *path_or_archive)
652 {
653     OSIntrMode  bak_psr = OS_DisableInterrupts();
654     FSArchive  *arc = arc_list;
655     while (arc && (arc != (const FSArchive *)path_or_archive))
656     {
657         arc = arc->next;
658     }
659     if (!arc)
660     {
661         arc = FS_NormalizePath((const char *)path_or_archive, NULL, NULL);
662     }
663     (void)OS_RestoreInterrupts(bak_psr);
664     return arc ? arc->result : FS_RESULT_ERROR;
665 }
666 
667 /*---------------------------------------------------------------------------*
668   Name:         FS_GetCurrentDirectoryPath
669 
670   Description:  Gets current directory with path name.
671 
672   Arguments:    None.
673 
674   Returns:      String that represents current directory
675  *---------------------------------------------------------------------------*/
FS_GetCurrentDirectory(void)676 const char *FS_GetCurrentDirectory(void)
677 {
678     return current_dir_path;
679 }
680 
681 /*---------------------------------------------------------------------------*
682   Name:         FS_SetCurrentDirectory
683 
684   Description:  Changes the current directory
685 
686   Arguments:    path        Path name
687 
688   Returns:      If successful, TRUE.
689  *---------------------------------------------------------------------------*/
FS_SetCurrentDirectory(const char * path)690 BOOL FS_SetCurrentDirectory(const char *path)
691 {
692     BOOL        retval = FALSE;
693     FSArchive  *arc = NULL;
694     u32         baseid = 0;
695     char        relpath[FS_ENTRY_LONGNAME_MAX];
696 
697     SDK_NULL_ASSERT(path);
698     SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ);
699 
700     // Get normalized path name
701     arc = FS_NormalizePath(path, &baseid, relpath);
702     if (arc)
703     {
704         // In any case, the setting was successful
705         current_dir_pos.arc = arc;
706         current_dir_pos.own_id = 0;
707         current_dir_pos.index = 0;
708         current_dir_pos.pos = 0;
709         (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path));
710         // If possible, try to get directory ID
711         if (arc->vtbl->FindPath != NULL)
712         {
713             FSFile                  dir[1];
714             FSArgumentForFindPath   arg[1];
715             FS_InitFile(dir);
716             dir->arc = arc;
717             dir->argument = arg;
718             arg->baseid = baseid;
719             arg->relpath = relpath;
720             arg->target_is_directory = TRUE;
721             if (FSi_SendCommand(dir, FS_COMMAND_FINDPATH, TRUE))
722             {
723                 current_dir_pos.own_id = (u16)arg->target_id;
724                 (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path));
725             }
726         }
727         retval = TRUE;
728     }
729     return retval;
730 }
731 
732 /*---------------------------------------------------------------------------*
733   Name:         FSi_CopySafeString
734 
735   Description:  Checks buffer size and copies character string
736 
737   Arguments:    dst               Transfer destination buffer
738                 dstlen        Transfer destination size
739                 src               Transfer source buffer.
740                 srclen        Transfer destination size
741                 stickyFailure FALSE, if truncated at transfer origin
742 
743   Returns:      Archive pointer or NULL
744  *---------------------------------------------------------------------------*/
FSi_CopySafeString(char * dst,int dstlen,const char * src,int srclen,BOOL * stickyFailure)745 static int FSi_CopySafeString(char *dst, int dstlen, const char *src, int srclen, BOOL *stickyFailure)
746 {
747     int     i;
748     int     n = (dstlen - 1 < srclen) ? (dstlen - 1) : srclen;
749     for (i = 0; (i < n) && src[i]; ++i)
750     {
751         dst[i] = src[i];
752     }
753     if ((i < srclen) && src[i])
754     {
755         *stickyFailure = TRUE;
756     }
757     dst[i] = '\0';
758     return i;
759 }
760 
761 /*---------------------------------------------------------------------------*
762   Name:         FS_NormalizePath
763 
764   Description:  Normalizes to format of (Standard Directory ID) + (Path Name) considering the current directory in the specified path.
765 
766 
767 
768   Arguments:    path:        Non-normalized path string.
769                 baseid      Standard directory ID storage destination or NULL
770                 relpath     Path name storage destination after conversion or NULL
771 
772   Returns:      Archive pointer or NULL
773  *---------------------------------------------------------------------------*/
FS_NormalizePath(const char * path,u32 * baseid,char * relpath)774 FSArchive* FS_NormalizePath(const char *path, u32 *baseid, char *relpath)
775 {
776     FSArchive  *arc = NULL;
777     int         pathlen = 0;
778     int         pathmax = FS_ENTRY_LONGNAME_MAX;
779     BOOL        stickyFailure = FALSE;
780     // Reevaluate when current directory is not specified
781     if (current_dir_pos.arc == NULL)
782     {
783         current_dir_pos.arc = arc_list;
784         current_dir_pos.own_id = 0;
785         current_dir_pos.pos = 0;
786         current_dir_pos.index = 0;
787         current_dir_path[0] = '\0';
788     }
789     // With "/path" make the current archive route the base
790     if (FSi_IsSlash((u8)*path))
791     {
792         arc = current_dir_pos.arc;
793         ++path;
794         if (baseid)
795         {
796             *baseid = 0;
797         }
798     }
799     else
800     {
801         int     i;
802         for (i = 0; ; i = FSi_IncrementSjisPosition(path, i))
803         {
804             u32     c = (u8)path[i];
805             // With "(path/)*path" the relevant path from the current position
806             if (!c || FSi_IsSlash(c))
807             {
808                 arc = current_dir_pos.arc;
809                 if (baseid)
810                 {
811                     *baseid = current_dir_pos.own_id;
812                 }
813                 if(relpath)
814                 {
815                     // If directory is retained only by the path name, link it.
816                     if ((current_dir_pos.own_id == 0) && (current_dir_path[0] != '\0'))
817                     {
818                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
819                                                       current_dir_path, FS_ENTRY_LONGNAME_MAX, &stickyFailure);
820                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
821                                                       "/", 1, &stickyFailure);
822                     }
823                 }
824                 break;
825             }
826             // With "arc:/path," the full path
827             else if (c == ':')
828             {
829                 arc = FS_FindArchive(path, i);
830                 if (!arc)
831                 {
832                     OS_Warning("archive \"%*s\" is not found.", i, path);
833                 }
834                 path += i + 1;
835                 if (FSi_IsSlash((u8)*path))
836                 {
837                     ++path;
838                 }
839                 if (baseid)
840                 {
841                     *baseid = 0;
842                 }
843                 break;
844             }
845         }
846     }
847     if(relpath)
848     {
849         // Be careful of special entry names and normalize relative path
850         int     curlen = 0;
851         while (!stickyFailure)
852         {
853             char    c = path[curlen];
854             if ((c != '\0') && !FSi_IsSlash((u8)c))
855             {
856                 curlen += STD_IsSjisCharacter(&path[curlen]) ? 2 : 1;
857             }
858             else
859             {
860                 // Ignore empty directory
861                 if (curlen == 0)
862                 {
863                 }
864                 // Ignore "." (current directory)
865                 else if ((curlen == 1) && (path[0] == '.'))
866                 {
867                 }
868                 // ".." (Parent Directory) raises the root one level as the upper limit
869                 else if ((curlen == 2) && (path[0] == '.') && (path[1] == '.'))
870                 {
871                     if (pathlen > 0)
872                     {
873                         --pathlen;
874                     }
875                     pathlen = FSi_DecrementSjisPositionToSlash(relpath, pathlen) + 1;
876                 }
877                 // Add entry for anything else
878                 else
879                 {
880                     pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
881                                                   path, curlen, &stickyFailure);
882                     if (c != '\0')
883                     {
884                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
885                                                       "/", 1, &stickyFailure);
886                     }
887                 }
888                 if (c == '\0')
889                 {
890                     break;
891                 }
892                 path += curlen + 1;
893                 curlen = 0;
894             }
895         }
896         relpath[pathlen] = '\0';
897         pathlen = FSi_TrimSjisTrailingSlash(relpath);
898     }
899     return stickyFailure ? NULL : arc;
900 }
901 
902 /*---------------------------------------------------------------------------*
903   Name:         FS_InitArchive
904 
905   Description:  Initialize archive structure
906 
907   Arguments:    p_arc          Archive to initialize.
908 
909   Returns:      None.
910  *---------------------------------------------------------------------------*/
FS_InitArchive(FSArchive * p_arc)911 void FS_InitArchive(FSArchive *p_arc)
912 {
913     SDK_NULL_ASSERT(p_arc);
914     MI_CpuClear8(p_arc, sizeof(FSArchive));
915     OS_InitThreadQueue(&p_arc->queue);
916 }
917 
918 /*---------------------------------------------------------------------------*
919   Name:         FS_RegisterArchiveName
920 
921   Description:  Registers the archive name in the file system and associates it.
922                 The archive itself is not yet loaded into the file system.
923                 The archive name  "rom" is reserved by the file system.
924 
925   Arguments:    p_arc            Archive to associate with the name.
926                 name             String of the name to register
927                 name_len         Length of name string
928 
929   Returns:      None.
930  *---------------------------------------------------------------------------*/
FS_RegisterArchiveName(FSArchive * p_arc,const char * name,u32 name_len)931 BOOL FS_RegisterArchiveName(FSArchive *p_arc, const char *name, u32 name_len)
932 {
933     BOOL    retval = FALSE;
934 
935     SDK_ASSERT(FS_IsAvailable());
936     SDK_NULL_ASSERT(p_arc);
937     SDK_NULL_ASSERT(name);
938 
939     {
940         OSIntrMode bak_intr = OS_DisableInterrupts();
941         if (!FS_FindArchive(name, (s32)name_len))
942         {
943             // adds to the end of the list
944             FSArchive **pp;
945             for (pp = &arc_list; *pp; pp = &(*pp)->next)
946             {
947             }
948             *pp = p_arc;
949             // With the current specifications, the archive has a maximum of 3 characters
950             if (name_len <= FS_ARCHIVE_NAME_LEN_MAX)
951             {
952                 p_arc->name.pack = 0;
953                 (void)STD_CopyLString(p_arc->name.ptr, name, (int)(name_len + 1));
954             }
955             else
956             {
957 #if defined(FS_SUPPORT_LONG_ARCNAME)
958             // Feature for expanding the limit of the number of characters in an archive name
959             // It is necessary to allocate external memory to support without changing the FSArchive structure size
960             //
961                 if (name_len <= FS_LONG_ARCNAME_LENGTH_MAX)
962                 {
963                     int i;
964                     for (i = 0; ; ++i)
965                     {
966                         if (i >= FS_LONG_ARCNAME_TABLE_MAX)
967                         {
968                             OS_TPanic("failed to allocate memory for long archive-name(%.*s)!", name_len, name);
969                         }
970                         else if (FSiLongNameTable[i][0] == '\0')
971                         {
972                             (void)STD_CopyLString(FSiLongNameTable[i], name, (int)(name_len + 1));
973                             p_arc->name.pack = (u32)FSiLongNameTable[i];
974                             break;
975                         }
976                     }
977                 }
978 #endif
979                 // Cannot register archive names that are too long
980                 else
981                 {
982                     OS_TPanic("too long archive-name(%.*s)!", name_len, name);
983                 }
984             }
985             p_arc->flag |= FS_ARCHIVE_FLAG_REGISTER;
986             retval = TRUE;
987         }
988         (void)OS_RestoreInterrupts(bak_intr);
989     }
990     return retval;
991 }
992 
993 /*---------------------------------------------------------------------------*
994   Name:         FS_ReleaseArchiveName
995 
996   Description:  Release registered archive names.
997                 They must be unloaded from the file system.
998 
999   Arguments:    p_arc            Archive with name to release.
1000 
1001   Returns:      None.
1002  *---------------------------------------------------------------------------*/
FS_ReleaseArchiveName(FSArchive * p_arc)1003 void FS_ReleaseArchiveName(FSArchive *p_arc)
1004 {
1005     SDK_ASSERT(FS_IsAvailable());
1006     SDK_NULL_ASSERT(p_arc);
1007 
1008 	if(p_arc == arc_list)
1009     {
1010         OS_TPanic("[file-system] cannot modify \"rom\" archive.\n");
1011     }
1012 
1013     if (p_arc->name.pack)
1014     {
1015         OSIntrMode bak_psr = OS_DisableInterrupts();
1016         // Cut from the list.
1017         FSArchive **pp;
1018         for (pp = &arc_list; *pp; pp = &(*pp)->next)
1019         {
1020             if(*pp == p_arc)
1021             {
1022                 *pp = (*pp)->next;
1023                 break;
1024             }
1025         }
1026 #if defined(FS_SUPPORT_LONG_ARCNAME)
1027         // Deallocate buffer to the system if there is a long archive name
1028         if (p_arc->name.ptr[3] != '\0')
1029         {
1030             ((char *)p_arc->name.pack)[0] = '\0';
1031         }
1032 #endif
1033         p_arc->name.pack = 0;
1034         p_arc->next = NULL;
1035         p_arc->flag &= ~FS_ARCHIVE_FLAG_REGISTER;
1036         // Deallocate if it is the current archive
1037         if (current_dir_pos.arc == p_arc)
1038         {
1039             current_dir_pos.arc = NULL;
1040         }
1041         (void)OS_RestoreInterrupts(bak_psr);
1042     }
1043 }
1044 
1045 /*---------------------------------------------------------------------------*
1046   Name:         FS_GetArchiveName
1047 
1048   Description:  Acquire archive name
1049 
1050   Arguments:    p_arc            Archive whose name to get.
1051 
1052   Returns:      Archive name registered in the file system
1053  *---------------------------------------------------------------------------*/
FS_GetArchiveName(const FSArchive * arc)1054 const char *FS_GetArchiveName(const FSArchive *arc)
1055 {
1056 #if defined(FS_SUPPORT_LONG_ARCNAME)
1057     return (arc->name.ptr[3] != '\0') ? (const char *)arc->name.pack : arc->name.ptr;
1058 #else
1059     return arc->name.ptr;
1060 #endif
1061 }
1062 
1063 /*---------------------------------------------------------------------------*
1064   Name:         FS_MountArchive
1065 
1066   Description:  Mounts the archive
1067 
1068   Arguments:    arc              Archive to mount
1069                 userdata         User defined variable that associates with an archive
1070                 vtbl             Command interface
1071                 reserved         Reserved for future use  (Always specify 0)
1072 
1073 
1074   Returns:      TRUE if mounting is successful
1075  *---------------------------------------------------------------------------*/
FS_MountArchive(FSArchive * arc,void * userdata,const FSArchiveInterface * vtbl,u32 reserved)1076 BOOL FS_MountArchive(FSArchive *arc, void *userdata,
1077                      const FSArchiveInterface *vtbl, u32 reserved)
1078 {
1079     (void)reserved;
1080     SDK_ASSERT(FS_IsAvailable());
1081     SDK_NULL_ASSERT(arc);
1082     SDK_ASSERT(!FS_IsArchiveLoaded(arc));
1083     // Initialize archive with new specifications
1084     arc->userdata = userdata;
1085     arc->vtbl = vtbl;
1086     // Issue mounting notification event
1087     {
1088         FSFile  tmp[1];
1089         FS_InitFile(tmp);
1090         tmp->arc = arc;
1091         (void)FSi_InvokeCommand(tmp, FS_COMMAND_MOUNT);
1092     }
1093     arc->flag |= FS_ARCHIVE_FLAG_LOADED;
1094     return TRUE;
1095 }
1096 
1097 /*---------------------------------------------------------------------------*
1098   Name:         FS_UnmountArchive
1099 
1100   Description:  Unmounts the archive
1101 
1102   Arguments:    arc              Archive to unmount
1103 
1104   Returns:      TRUE if unmounting is successful
1105  *---------------------------------------------------------------------------*/
FS_UnmountArchive(FSArchive * arc)1106 BOOL    FS_UnmountArchive(FSArchive *arc)
1107 {
1108     SDK_ASSERT(FS_IsAvailable());
1109     SDK_NULL_ASSERT(arc);
1110 
1111     {
1112         OSIntrMode bak_psr = OS_DisableInterrupts();
1113         // Ignore if it is not loaded.
1114         if (FS_IsArchiveLoaded(arc))
1115         {
1116             // Cancel all idling commands
1117             {
1118                 BOOL    bak_state = FS_SuspendArchive(arc);
1119                 FSFile *file = arc->list;
1120                 arc->flag |= FS_ARCHIVE_FLAG_UNLOADING;
1121                 while (file)
1122                 {
1123                     FSFile *next = file->next;
1124                     FSi_EndCommand(file, FS_RESULT_CANCELED);
1125                     file = next;
1126                 }
1127                 arc->list = NULL;
1128                 if (bak_state)
1129                 {
1130                     (void)FS_ResumeArchive(arc);
1131                 }
1132             }
1133             // Issue unmounting notification event
1134             {
1135                 FSFile  tmp[1];
1136                 FS_InitFile(tmp);
1137                 tmp->arc = arc;
1138                 (void)FSi_InvokeCommand(tmp, FS_COMMAND_UNMOUNT);
1139             }
1140             arc->flag &= ~(FS_ARCHIVE_FLAG_CANCELING |
1141                            FS_ARCHIVE_FLAG_LOADED | FS_ARCHIVE_FLAG_UNLOADING);
1142         }
1143         (void)OS_RestoreInterrupts(bak_psr);
1144     }
1145     return TRUE;
1146 
1147 }
1148 
1149 /*---------------------------------------------------------------------------*
1150   Name:         FS_SuspendArchive
1151 
1152   Description:  Stops archive processing mechanism itself.
1153                 If a process is currently executing, waits for it to complete.
1154 
1155   Arguments:    p_arc          Archive to suspend.
1156 
1157   Returns:      TRUE if it was not in a suspended state before the call.
1158  *---------------------------------------------------------------------------*/
FS_SuspendArchive(FSArchive * p_arc)1159 BOOL FS_SuspendArchive(FSArchive *p_arc)
1160 {
1161     BOOL    retval = FALSE;
1162 
1163     SDK_ASSERT(FS_IsAvailable());
1164     SDK_NULL_ASSERT(p_arc);
1165 
1166     {
1167         OSIntrMode bak_psr = OS_DisableInterrupts();
1168         retval = !FS_IsArchiveSuspended(p_arc);
1169         if (retval)
1170         {
1171             if ((p_arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0)
1172             {
1173                 p_arc->flag |= FS_ARCHIVE_FLAG_SUSPEND;
1174             }
1175             else
1176             {
1177                 p_arc->flag |= FS_ARCHIVE_FLAG_SUSPENDING;
1178                 FSi_WaitConditionOff(&p_arc->flag, FS_ARCHIVE_FLAG_SUSPENDING, &p_arc->queue);
1179             }
1180         }
1181         (void)OS_RestoreInterrupts(bak_psr);
1182     }
1183     return retval;
1184 }
1185 
1186 /*---------------------------------------------------------------------------*
1187   Name:         FS_ResumeArchive
1188 
1189   Description:  Resumes the suspended archive processing.
1190 
1191   Arguments:    arc              Archive to reopen
1192 
1193   Returns:      TRUE if it was not in a suspended state before the call.
1194  *---------------------------------------------------------------------------*/
FS_ResumeArchive(FSArchive * arc)1195 BOOL FS_ResumeArchive(FSArchive *arc)
1196 {
1197     BOOL    retval;
1198     SDK_ASSERT(FS_IsAvailable());
1199     SDK_NULL_ASSERT(arc);
1200     {
1201         OSIntrMode bak_irq = OS_DisableInterrupts();
1202         retval = !FS_IsArchiveSuspended(arc);
1203         if (!retval)
1204         {
1205             arc->flag &= ~FS_ARCHIVE_FLAG_SUSPEND;
1206         }
1207         (void)OS_RestoreInterrupts(bak_irq);
1208     }
1209     {
1210         FSFile *file = NULL;
1211         file = FSi_NextCommand(arc, TRUE);
1212         if (file)
1213         {
1214             FSi_ExecuteAsyncCommand(file);
1215         }
1216     }
1217     return retval;
1218 }
1219 
1220 
1221 /*---------------------------------------------------------------------------*
1222   Name:         FS_NotifyArchiveAsyncEnd
1223 
1224   Description:  To notify that asynchronous archive processing is complete,
1225                 Call from the archive implentation side
1226 
1227   Arguments:    arc           Archive for which to notify completion.
1228                 ret              Processing result.
1229 
1230   Returns:      None.
1231  *---------------------------------------------------------------------------*/
FS_NotifyArchiveAsyncEnd(FSArchive * arc,FSResult ret)1232 void FS_NotifyArchiveAsyncEnd(FSArchive *arc, FSResult ret)
1233 {
1234     FSFile     *file = arc->list;
1235     if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0)
1236     {
1237         OSIntrMode bak_psr = OS_DisableInterrupts();
1238         file->stat |= FS_FILE_STATUS_ASYNC_DONE;
1239         file->error = ret;
1240         OS_WakeupThread(file->queue);
1241         (void)OS_RestoreInterrupts(bak_psr);
1242     }
1243     else
1244     {
1245         FSi_EndCommand(file, ret);
1246         file = FSi_NextCommand(arc, TRUE);
1247         if (file)
1248         {
1249             FSi_ExecuteAsyncCommand(file);
1250         }
1251     }
1252 }
1253 
1254 
1255 /*---------------------------------------------------------------------------*
1256   Name:         FS_WaitAsync
1257 
1258   Description:  wait for end of asynchronous function and return result.
1259 
1260   Arguments:    file to wait
1261 
1262   Returns:      if succeeded, TRUE.
1263  *---------------------------------------------------------------------------*/
FS_WaitAsync(FSFile * file)1264 BOOL FS_WaitAsync(FSFile *file)
1265 {
1266     SDK_NULL_ASSERT(file);
1267     SDK_ASSERT(FS_IsAvailable());
1268     SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ);
1269 
1270     {
1271         BOOL    is_owner = FALSE;
1272         OSIntrMode bak_psr = OS_DisableInterrupts();
1273         if (FS_IsBusy(file))
1274         {
1275             // Take on processing here if an unprocessed asynchronous mode.
1276             is_owner = !(file->stat & (FS_FILE_STATUS_BLOCKING | FS_FILE_STATUS_OPERATING));
1277             if (is_owner)
1278             {
1279                 file->stat |= FS_FILE_STATUS_BLOCKING;
1280             }
1281         }
1282         (void)OS_RestoreInterrupts(bak_psr);
1283         if (is_owner)
1284         {
1285             FSi_ExecuteSyncCommand(file);
1286         }
1287         else
1288         {
1289             FSi_WaitConditionOff(&file->stat, FS_FILE_STATUS_BUSY, file->queue);
1290         }
1291     }
1292 
1293     return FS_IsSucceeded(file);
1294 }
1295 
1296 
1297 #endif /* FS_IMPLEMENT */
1298