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-07-14#$
14   $Rev: 10906 $
15   $Author: yosizaki $
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:  Determines 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) && (arc != NULL))
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             // Start up 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_ExecuteSyncCommand
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     // If necessary, wait for turn, then process the command
493     FSi_WaitConditionChange(&file->stat, FS_FILE_STATUS_OPERATING, FS_FILE_STATUS_BUSY, file->queue);
494     // If you do not have the execution rights for some reason (for example, the command has already been cancelled), do nothing
495     if ((file->stat & FS_FILE_STATUS_OPERATING) != 0)
496     {
497         FSArchive   * const arc = file->arc;
498         FSResult            result;
499         result = FSi_InvokeCommand(file, FSi_GetCurrentCommand(file));
500         FSi_EndCommand(file, result);
501         // If there is an asynchronous type command to be processed here, execute instead
502         file = FSi_NextCommand(arc, TRUE);
503         if (file)
504         {
505             FSi_ExecuteAsyncCommand(file);
506         }
507     }
508 }
509 
510 /*---------------------------------------------------------------------------*
511   Name:         FSi_SendCommand
512 
513   Description:  Issues a command to an archive.
514                 Adjust start timing and block here if synchronous.
515 
516   Arguments:    file: FSFile structure in which the command argument has been specified
517                 Command: Command ID
518                 blocking: TRUE if blocking
519 
520   Returns:      TRUE if the command is successful.
521  *---------------------------------------------------------------------------*/
FSi_SendCommand(FSFile * file,FSCommandType command,BOOL blocking)522 BOOL FSi_SendCommand(FSFile *file, FSCommandType command, BOOL blocking)
523 {
524     BOOL                retval = FALSE;
525     FSArchive   * const arc = file->arc;
526     BOOL                owner = FALSE;
527 
528     if (FS_IsBusy(file))
529     {
530         OS_TPanic("specified file is now still proceccing previous command!");
531     }
532     if (!arc)
533     {
534         OS_TWarning("specified handle is not related by any archive\n");
535         file->error = FS_RESULT_INVALID_PARAMETER;
536         return FALSE;
537     }
538 
539     // Initialize command
540     file->error = FS_RESULT_BUSY;
541     file->stat &= ~(FS_FILE_STATUS_CMD_MASK << FS_FILE_STATUS_CMD_SHIFT);
542     file->stat |= (command << FS_FILE_STATUS_CMD_SHIFT);
543     file->stat |= FS_FILE_STATUS_BUSY;
544     file->next = NULL;
545     if (blocking)
546     {
547         file->stat |= FS_FILE_STATUS_BLOCKING;
548     }
549     // If unloading, cancel the process and if not, add to the end of the list
550     {
551         OSIntrMode          bak_psr = OS_DisableInterrupts();
552         if ((arc->flag & FS_ARCHIVE_FLAG_UNLOADING) != 0)
553         {
554             FSi_EndCommand(file, FS_RESULT_CANCELED);
555         }
556         else
557         {
558             FSFile    **pp;
559             for (pp = &arc->list; *pp; pp = &(*pp)->next)
560             {
561             }
562             *pp = file;
563         }
564         owner = (arc->list == file) && ((arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0);
565         (void)OS_RestoreInterrupts(bak_psr);
566     }
567     // If added to the list, check the command execution rights
568     if (file->error != FS_RESULT_CANCELED)
569     {
570         // If there is a command that should be processed here, execute that instead
571         FSFile *next = FSi_NextCommand(arc, owner);
572         // If necessary, block here
573         if (blocking)
574         {
575             FSi_ExecuteSyncCommand(file);
576             retval = FS_IsSucceeded(file);
577         }
578         // Added to the list, so will be processed by someone
579         else
580         {
581             if (next != NULL)
582             {
583                 FSi_ExecuteAsyncCommand(next);
584             }
585             retval = TRUE;
586         }
587     }
588 
589     return retval;
590 }
591 
592 /*---------------------------------------------------------------------------*
593   Name:         FSi_EndArchive
594 
595   Description:  End all archives and release.
596 
597   Arguments:    None.
598 
599   Returns:      None.
600  *---------------------------------------------------------------------------*/
FSi_EndArchive(void)601 void FSi_EndArchive(void)
602 {
603     OSIntrMode bak_psr = OS_DisableInterrupts();
604     while (arc_list)
605     {
606         FSArchive *p_arc = arc_list;
607         arc_list = arc_list->next;
608         (void)FS_UnloadArchive(p_arc);
609         FS_ReleaseArchiveName(p_arc);
610     }
611     (void)OS_RestoreInterrupts(bak_psr);
612 }
613 
614 /*---------------------------------------------------------------------------*
615   Name:         FS_FindArchive
616 
617   Description:  Searches for archive name.
618                 If there is no matching name, returns NULL.
619 
620   Arguments:    name: String of the archive name to search for
621                 name_len: Length of name string
622 
623   Returns:      Pointer to the archive that was found, or NULL.
624  *---------------------------------------------------------------------------*/
FS_FindArchive(const char * name,int name_len)625 FSArchive *FS_FindArchive(const char *name, int name_len)
626 {
627     OSIntrMode  bak_psr = OS_DisableInterrupts();
628     FSArchive  *arc = arc_list;
629     for (; arc; arc = arc->next)
630     {
631         if (FS_IsArchiveLoaded(arc))
632         {
633             const char *arcname = FS_GetArchiveName(arc);
634             if ((STD_CompareNString(arcname, name, name_len) == 0) && (arcname[name_len] == '\0'))
635             {
636                 break;
637             }
638         }
639     }
640     (void)OS_RestoreInterrupts(bak_psr);
641     return arc;
642 }
643 
644 /*---------------------------------------------------------------------------*
645   Name:         FS_GetArchiveResultCode
646 
647   Description:  Gets latest error code for the specified archive.
648 
649   Arguments:    path_or_archive: FSArchive structure or path string that indicates the target archive
650 
651 
652   Returns:      Latest error code for the specified archive.
653                 If the corresponding archive does not exist, FS_RESULT_ERROR.
654  *---------------------------------------------------------------------------*/
FS_GetArchiveResultCode(const void * path_or_archive)655 FSResult FS_GetArchiveResultCode(const void *path_or_archive)
656 {
657     OSIntrMode  bak_psr = OS_DisableInterrupts();
658     FSArchive  *arc = arc_list;
659     while (arc && (arc != (const FSArchive *)path_or_archive))
660     {
661         arc = arc->next;
662     }
663     if (!arc)
664     {
665         arc = FS_NormalizePath((const char *)path_or_archive, NULL, NULL);
666     }
667     (void)OS_RestoreInterrupts(bak_psr);
668     return arc ? arc->result : FS_RESULT_ERROR;
669 }
670 
671 /*---------------------------------------------------------------------------*
672   Name:         FS_GetCurrentDirectoryPath
673 
674   Description:  Gets current directory with path name.
675 
676   Arguments:    None.
677 
678   Returns:      String that represents current directory.
679  *---------------------------------------------------------------------------*/
FS_GetCurrentDirectory(void)680 const char *FS_GetCurrentDirectory(void)
681 {
682     return current_dir_path;
683 }
684 
685 /*---------------------------------------------------------------------------*
686   Name:         FS_SetCurrentDirectory
687 
688   Description:  Changes the current directory.
689 
690   Arguments:    path: Path name
691 
692   Returns:      TRUE if successful.
693  *---------------------------------------------------------------------------*/
FS_SetCurrentDirectory(const char * path)694 BOOL FS_SetCurrentDirectory(const char *path)
695 {
696     BOOL        retval = FALSE;
697     FSArchive  *arc = NULL;
698     u32         baseid = 0;
699     char        relpath[FS_ENTRY_LONGNAME_MAX];
700 
701     SDK_NULL_ASSERT(path);
702     SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ);
703 
704     // Get normalized path name
705     arc = FS_NormalizePath(path, &baseid, relpath);
706     if (arc)
707     {
708         // In any case, the setting was successful
709         current_dir_pos.arc = arc;
710         current_dir_pos.own_id = 0;
711         current_dir_pos.index = 0;
712         current_dir_pos.pos = 0;
713         (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path));
714         // If possible, try to get directory ID
715         if (arc->vtbl->FindPath != NULL)
716         {
717             FSFile                  dir[1];
718             FSArgumentForFindPath   arg[1];
719             FS_InitFile(dir);
720             dir->arc = arc;
721             dir->argument = arg;
722             arg->baseid = baseid;
723             arg->relpath = relpath;
724             arg->target_is_directory = TRUE;
725             if (FSi_SendCommand(dir, FS_COMMAND_FINDPATH, TRUE))
726             {
727                 current_dir_pos.own_id = (u16)arg->target_id;
728                 (void)STD_CopyLString(current_dir_path, relpath, sizeof(current_dir_path));
729             }
730         }
731         retval = TRUE;
732     }
733     return retval;
734 }
735 
736 /*---------------------------------------------------------------------------*
737   Name:         FSi_CopySafeString
738 
739   Description:  Checks buffer size and copies string.
740 
741   Arguments:    dst: Transfer destination buffer
742                 dstlen: Transfer destination size
743                 src: Transfer source buffer
744                 srclen: Transfer destination size
745                 stickyFailure: FALSE, if truncated at transfer origin
746 
747   Returns:      Archive pointer or NULL.
748  *---------------------------------------------------------------------------*/
FSi_CopySafeString(char * dst,int dstlen,const char * src,int srclen,BOOL * stickyFailure)749 static int FSi_CopySafeString(char *dst, int dstlen, const char *src, int srclen, BOOL *stickyFailure)
750 {
751     int     i;
752     int     n = (dstlen - 1 < srclen) ? (dstlen - 1) : srclen;
753     for (i = 0; (i < n) && src[i]; ++i)
754     {
755         dst[i] = src[i];
756     }
757     if ((i < srclen) && src[i])
758     {
759         *stickyFailure = TRUE;
760     }
761     dst[i] = '\0';
762     return i;
763 }
764 
765 /*---------------------------------------------------------------------------*
766   Name:         FS_NormalizePath
767 
768   Description:  Normalizes to format of (Standard Directory ID) + (Path Name) considering the current directory in the specified path.
769 
770 
771 
772   Arguments:    path: Non-normalized path string
773                 baseid: Standard directory ID storage destination or NULL
774                 relpath: Path name storage destination after conversion or NULL
775 
776   Returns:      Archive pointer or NULL.
777  *---------------------------------------------------------------------------*/
FS_NormalizePath(const char * path,u32 * baseid,char * relpath)778 FSArchive* FS_NormalizePath(const char *path, u32 *baseid, char *relpath)
779 {
780     FSArchive  *arc = NULL;
781     int         pathlen = 0;
782     int         pathmax = FS_ENTRY_LONGNAME_MAX;
783     BOOL        stickyFailure = FALSE;
784     // Reevaluate when current directory is not specified
785     if (current_dir_pos.arc == NULL)
786     {
787         current_dir_pos.arc = arc_list;
788         current_dir_pos.own_id = 0;
789         current_dir_pos.pos = 0;
790         current_dir_pos.index = 0;
791         current_dir_path[0] = '\0';
792     }
793     // With "/path" make the current archive route the base
794     if (FSi_IsSlash((u8)*path))
795     {
796         arc = current_dir_pos.arc;
797         ++path;
798         if (baseid)
799         {
800             *baseid = 0;
801         }
802     }
803     else
804     {
805         int     i;
806         for (i = 0; ; i = FSi_IncrementSjisPosition(path, i))
807         {
808             u32     c = (u8)path[i];
809             // With "(path/)*path" the relevant path from the current position
810             if (!c || FSi_IsSlash(c))
811             {
812                 arc = current_dir_pos.arc;
813                 if (baseid)
814                 {
815                     *baseid = current_dir_pos.own_id;
816                 }
817                 if(relpath)
818                 {
819                     // If directory is retained only by the path name, link it
820                     if ((current_dir_pos.own_id == 0) && (current_dir_path[0] != '\0'))
821                     {
822                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
823                                                       current_dir_path, FS_ENTRY_LONGNAME_MAX, &stickyFailure);
824                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
825                                                       "/", 1, &stickyFailure);
826                     }
827                 }
828                 break;
829             }
830             // With "arc:/path," the full path
831             else if (c == ':')
832             {
833                 arc = FS_FindArchive(path, i);
834                 if (!arc)
835                 {
836                     OS_TWarning("archive \"%*s\" is not found.", i, path);
837                 }
838                 path += i + 1;
839                 if (FSi_IsSlash((u8)*path))
840                 {
841                     ++path;
842                 }
843                 if (baseid)
844                 {
845                     *baseid = 0;
846                 }
847                 break;
848             }
849         }
850     }
851     if(relpath)
852     {
853         // Be careful of special entry names and normalize relative path
854         int     curlen = 0;
855         while (!stickyFailure)
856         {
857             char    c = path[curlen];
858             if ((c != '\0') && !FSi_IsSlash((u8)c))
859             {
860                 curlen += STD_IsSjisCharacter(&path[curlen]) ? 2 : 1;
861             }
862             else
863             {
864                 // Ignore empty directory
865                 if (curlen == 0)
866                 {
867                 }
868                 // Ignore "." (current directory)
869                 else if ((curlen == 1) && (path[0] == '.'))
870                 {
871                 }
872                 // ".." (Parent Directory) raises the root one level as the upper limit
873                 else if ((curlen == 2) && (path[0] == '.') && (path[1] == '.'))
874                 {
875                     if (pathlen > 0)
876                     {
877                         --pathlen;
878                     }
879                     pathlen = FSi_DecrementSjisPositionToSlash(relpath, pathlen) + 1;
880                 }
881                 // Add entry for anything else
882                 else
883                 {
884                     pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
885                                                   path, curlen, &stickyFailure);
886                     if (c != '\0')
887                     {
888                         pathlen += FSi_CopySafeString(&relpath[pathlen], pathmax - pathlen,
889                                                       "/", 1, &stickyFailure);
890                     }
891                 }
892                 if (c == '\0')
893                 {
894                     break;
895                 }
896                 path += curlen + 1;
897                 curlen = 0;
898             }
899         }
900         relpath[pathlen] = '\0';
901         pathlen = FSi_TrimSjisTrailingSlash(relpath);
902     }
903     return stickyFailure ? NULL : arc;
904 }
905 
906 /*---------------------------------------------------------------------------*
907   Name:         FS_InitArchive
908 
909   Description:  Initializes archive structure.
910 
911   Arguments:    p_arc: Archive to initialize
912 
913   Returns:      None.
914  *---------------------------------------------------------------------------*/
FS_InitArchive(FSArchive * p_arc)915 void FS_InitArchive(FSArchive *p_arc)
916 {
917     SDK_NULL_ASSERT(p_arc);
918     MI_CpuClear8(p_arc, sizeof(FSArchive));
919     OS_InitThreadQueue(&p_arc->queue);
920 }
921 
922 /*---------------------------------------------------------------------------*
923   Name:         FS_RegisterArchiveName
924 
925   Description:  Registers the archive name in the file system and associates it.
926                 The archive itself is not yet loaded into the file system.
927                 The archive name "rom" is reserved by the file system.
928 
929   Arguments:    p_arc: Archive to associate with the name
930                 name: String of the name to register
931                 name_len: Length of name string
932 
933   Returns:      None.
934  *---------------------------------------------------------------------------*/
FS_RegisterArchiveName(FSArchive * p_arc,const char * name,u32 name_len)935 BOOL FS_RegisterArchiveName(FSArchive *p_arc, const char *name, u32 name_len)
936 {
937     BOOL    retval = FALSE;
938 
939     SDK_ASSERT(FS_IsAvailable());
940     SDK_NULL_ASSERT(p_arc);
941     SDK_NULL_ASSERT(name);
942 
943     {
944         OSIntrMode bak_intr = OS_DisableInterrupts();
945         if (!FS_FindArchive(name, (s32)name_len))
946         {
947             // Adds to the end of the list
948             FSArchive **pp;
949             for (pp = &arc_list; *pp; pp = &(*pp)->next)
950             {
951             }
952             *pp = p_arc;
953             // With the current specifications, the archive has a maximum of 3 characters
954             if (name_len <= FS_ARCHIVE_NAME_LEN_MAX)
955             {
956                 p_arc->name.pack = 0;
957                 (void)STD_CopyLString(p_arc->name.ptr, name, (int)(name_len + 1));
958             }
959             else
960             {
961 #if defined(FS_SUPPORT_LONG_ARCNAME)
962             // Feature for expanding the limit of the number of characters in an archive name
963             // It is necessary to allocate external memory to support without changing the FSArchive structure size
964             //
965                 if (name_len <= FS_LONG_ARCNAME_LENGTH_MAX)
966                 {
967                     int i;
968                     for (i = 0; ; ++i)
969                     {
970                         if (i >= FS_LONG_ARCNAME_TABLE_MAX)
971                         {
972                             OS_TPanic("failed to allocate memory for long archive-name(%.*s)!", name_len, name);
973                         }
974                         else if (FSiLongNameTable[i][0] == '\0')
975                         {
976                             (void)STD_CopyLString(FSiLongNameTable[i], name, (int)(name_len + 1));
977                             p_arc->name.pack = (u32)FSiLongNameTable[i];
978                             break;
979                         }
980                     }
981                 }
982 #endif
983                 // Cannot register archive names that are too long
984                 else
985                 {
986                     OS_TPanic("too long archive-name(%.*s)!", name_len, name);
987                 }
988             }
989             p_arc->flag |= FS_ARCHIVE_FLAG_REGISTER;
990             retval = TRUE;
991         }
992         (void)OS_RestoreInterrupts(bak_intr);
993     }
994     return retval;
995 }
996 
997 /*---------------------------------------------------------------------------*
998   Name:         FS_ReleaseArchiveName
999 
1000   Description:  Release registered archive names.
1001                 They must be unloaded from the file system.
1002 
1003   Arguments:    p_arc: Archive with name to release
1004 
1005   Returns:      None.
1006  *---------------------------------------------------------------------------*/
FS_ReleaseArchiveName(FSArchive * p_arc)1007 void FS_ReleaseArchiveName(FSArchive *p_arc)
1008 {
1009     SDK_ASSERT(FS_IsAvailable());
1010     SDK_NULL_ASSERT(p_arc);
1011 
1012 	if(p_arc == arc_list)
1013     {
1014         OS_TPanic("[file-system] cannot modify \"rom\" archive.\n");
1015     }
1016 
1017     if (p_arc->name.pack)
1018     {
1019         OSIntrMode bak_psr = OS_DisableInterrupts();
1020         // Cut from the list
1021         FSArchive **pp;
1022         for (pp = &arc_list; *pp; pp = &(*pp)->next)
1023         {
1024             if(*pp == p_arc)
1025             {
1026                 *pp = (*pp)->next;
1027                 break;
1028             }
1029         }
1030 #if defined(FS_SUPPORT_LONG_ARCNAME)
1031         // Deallocate buffer to the system if there is a long archive name
1032         if (p_arc->name.ptr[3] != '\0')
1033         {
1034             ((char *)p_arc->name.pack)[0] = '\0';
1035         }
1036 #endif
1037         p_arc->name.pack = 0;
1038         p_arc->next = NULL;
1039         p_arc->flag &= ~FS_ARCHIVE_FLAG_REGISTER;
1040         // Deallocate if it is the current archive
1041         if (current_dir_pos.arc == p_arc)
1042         {
1043             current_dir_pos.arc = NULL;
1044         }
1045         (void)OS_RestoreInterrupts(bak_psr);
1046     }
1047 }
1048 
1049 /*---------------------------------------------------------------------------*
1050   Name:         FS_GetArchiveName
1051 
1052   Description:  Gets archive name.
1053 
1054   Arguments:    p_arc: Archive whose name to get
1055 
1056   Returns:      Archive name registered in the file system.
1057  *---------------------------------------------------------------------------*/
FS_GetArchiveName(const FSArchive * arc)1058 const char *FS_GetArchiveName(const FSArchive *arc)
1059 {
1060 #if defined(FS_SUPPORT_LONG_ARCNAME)
1061     return (arc->name.ptr[3] != '\0') ? (const char *)arc->name.pack : arc->name.ptr;
1062 #else
1063     return arc->name.ptr;
1064 #endif
1065 }
1066 
1067 /*---------------------------------------------------------------------------*
1068   Name:         FS_MountArchive
1069 
1070   Description:  Mounts the archive.
1071 
1072   Arguments:    arc: Archive to mount
1073                 userdata: User-defined variable that associates with an archive
1074                 vtbl: Command interface
1075                 reserved: Reserved for future use (always specify 0)
1076 
1077 
1078   Returns:      TRUE if mounting is successful.
1079  *---------------------------------------------------------------------------*/
FS_MountArchive(FSArchive * arc,void * userdata,const FSArchiveInterface * vtbl,u32 reserved)1080 BOOL FS_MountArchive(FSArchive *arc, void *userdata,
1081                      const FSArchiveInterface *vtbl, u32 reserved)
1082 {
1083     (void)reserved;
1084     SDK_ASSERT(FS_IsAvailable());
1085     SDK_NULL_ASSERT(arc);
1086     SDK_ASSERT(!FS_IsArchiveLoaded(arc));
1087     // Initialize archive with new specifications
1088     arc->userdata = userdata;
1089     arc->vtbl = vtbl;
1090     // Issue mounting notification event
1091     {
1092         FSFile  tmp[1];
1093         FS_InitFile(tmp);
1094         tmp->arc = arc;
1095         (void)FSi_InvokeCommand(tmp, FS_COMMAND_MOUNT);
1096     }
1097     arc->flag |= FS_ARCHIVE_FLAG_LOADED;
1098     return TRUE;
1099 }
1100 
1101 /*---------------------------------------------------------------------------*
1102   Name:         FS_UnmountArchive
1103 
1104   Description:  Unmounts the archive.
1105 
1106   Arguments:    arc: Archive to unmount
1107 
1108   Returns:      TRUE if unmounting is successful.
1109  *---------------------------------------------------------------------------*/
FS_UnmountArchive(FSArchive * arc)1110 BOOL    FS_UnmountArchive(FSArchive *arc)
1111 {
1112     SDK_ASSERT(FS_IsAvailable());
1113     SDK_NULL_ASSERT(arc);
1114 
1115     {
1116         OSIntrMode bak_psr = OS_DisableInterrupts();
1117         // Ignore if it is not loaded
1118         if (FS_IsArchiveLoaded(arc))
1119         {
1120             // Cancel all idling commands
1121             {
1122                 BOOL    bak_state = FS_SuspendArchive(arc);
1123                 FSFile *file = arc->list;
1124                 arc->flag |= FS_ARCHIVE_FLAG_UNLOADING;
1125                 while (file)
1126                 {
1127                     FSFile *next = file->next;
1128                     FSi_EndCommand(file, FS_RESULT_CANCELED);
1129                     file = next;
1130                 }
1131                 arc->list = NULL;
1132                 if (bak_state)
1133                 {
1134                     (void)FS_ResumeArchive(arc);
1135                 }
1136             }
1137             // Issue unmounting notification event
1138             {
1139                 FSFile  tmp[1];
1140                 FS_InitFile(tmp);
1141                 tmp->arc = arc;
1142                 (void)FSi_InvokeCommand(tmp, FS_COMMAND_UNMOUNT);
1143             }
1144             arc->flag &= ~(FS_ARCHIVE_FLAG_CANCELING |
1145                            FS_ARCHIVE_FLAG_LOADED | FS_ARCHIVE_FLAG_UNLOADING);
1146         }
1147         (void)OS_RestoreInterrupts(bak_psr);
1148     }
1149     return TRUE;
1150 
1151 }
1152 
1153 /*---------------------------------------------------------------------------*
1154   Name:         FS_SuspendArchive
1155 
1156   Description:  Stops archive processing mechanism itself.
1157                 If a process is currently executing, waits for it to complete.
1158 
1159   Arguments:    p_arc: Archive to suspend
1160 
1161   Returns:      TRUE if it was not in a suspended state before the call.
1162  *---------------------------------------------------------------------------*/
FS_SuspendArchive(FSArchive * p_arc)1163 BOOL FS_SuspendArchive(FSArchive *p_arc)
1164 {
1165     BOOL    retval = FALSE;
1166 
1167     SDK_ASSERT(FS_IsAvailable());
1168     SDK_NULL_ASSERT(p_arc);
1169 
1170     {
1171         OSIntrMode bak_psr = OS_DisableInterrupts();
1172         retval = !FS_IsArchiveSuspended(p_arc);
1173         if (retval)
1174         {
1175             if ((p_arc->flag & FS_ARCHIVE_FLAG_RUNNING) == 0)
1176             {
1177                 p_arc->flag |= FS_ARCHIVE_FLAG_SUSPEND;
1178             }
1179             else
1180             {
1181                 p_arc->flag |= FS_ARCHIVE_FLAG_SUSPENDING;
1182                 FSi_WaitConditionOff(&p_arc->flag, FS_ARCHIVE_FLAG_SUSPENDING, &p_arc->queue);
1183             }
1184         }
1185         (void)OS_RestoreInterrupts(bak_psr);
1186     }
1187     return retval;
1188 }
1189 
1190 /*---------------------------------------------------------------------------*
1191   Name:         FS_ResumeArchive
1192 
1193   Description:  Resumes the suspended archive processing.
1194 
1195   Arguments:    arc: Archive to reopen
1196 
1197   Returns:      TRUE if it was not in a suspended state before the call.
1198  *---------------------------------------------------------------------------*/
FS_ResumeArchive(FSArchive * arc)1199 BOOL FS_ResumeArchive(FSArchive *arc)
1200 {
1201     BOOL    retval;
1202     SDK_ASSERT(FS_IsAvailable());
1203     SDK_NULL_ASSERT(arc);
1204     {
1205         OSIntrMode bak_irq = OS_DisableInterrupts();
1206         retval = !FS_IsArchiveSuspended(arc);
1207         if (!retval)
1208         {
1209             arc->flag &= ~FS_ARCHIVE_FLAG_SUSPEND;
1210         }
1211         (void)OS_RestoreInterrupts(bak_irq);
1212     }
1213     {
1214         FSFile *file = NULL;
1215         file = FSi_NextCommand(arc, TRUE);
1216         if (file)
1217         {
1218             FSi_ExecuteAsyncCommand(file);
1219         }
1220     }
1221     return retval;
1222 }
1223 
1224 
1225 /*---------------------------------------------------------------------------*
1226   Name:         FS_NotifyArchiveAsyncEnd
1227 
1228   Description:  This is called from the archive implementation to send a notification when asynchronous archive processing is complete.
1229 
1230 
1231   Arguments:    arc: Archive for which to notify completion
1232                 ret: Processing result
1233 
1234   Returns:      None.
1235  *---------------------------------------------------------------------------*/
FS_NotifyArchiveAsyncEnd(FSArchive * arc,FSResult ret)1236 void FS_NotifyArchiveAsyncEnd(FSArchive *arc, FSResult ret)
1237 {
1238     FSFile     *file = arc->list;
1239     if ((file->stat & FS_FILE_STATUS_BLOCKING) != 0)
1240     {
1241         OSIntrMode bak_psr = OS_DisableInterrupts();
1242         file->stat |= FS_FILE_STATUS_ASYNC_DONE;
1243         file->error = ret;
1244         OS_WakeupThread(file->queue);
1245         (void)OS_RestoreInterrupts(bak_psr);
1246     }
1247     else
1248     {
1249         FSi_EndCommand(file, ret);
1250         file = FSi_NextCommand(arc, TRUE);
1251         if (file)
1252         {
1253             FSi_ExecuteAsyncCommand(file);
1254         }
1255     }
1256 }
1257 
1258 
1259 /*---------------------------------------------------------------------------*
1260   Name:         FS_WaitAsync
1261 
1262   Description:  Waits for end of asynchronous function and returns result.
1263 
1264   Arguments:    File to wait
1265 
1266   Returns:      If succeeded, TRUE.
1267  *---------------------------------------------------------------------------*/
FS_WaitAsync(FSFile * file)1268 BOOL FS_WaitAsync(FSFile *file)
1269 {
1270     SDK_NULL_ASSERT(file);
1271     SDK_ASSERT(FS_IsAvailable());
1272     SDK_ASSERT(OS_GetProcMode() != OS_PROCMODE_IRQ);
1273 
1274     {
1275         BOOL    is_owner = FALSE;
1276         OSIntrMode bak_psr = OS_DisableInterrupts();
1277         if (FS_IsBusy(file))
1278         {
1279             // Take on processing here if an unprocessed asynchronous mode
1280             is_owner = !(file->stat & (FS_FILE_STATUS_BLOCKING | FS_FILE_STATUS_OPERATING));
1281             if (is_owner)
1282             {
1283                 file->stat |= FS_FILE_STATUS_BLOCKING;
1284             }
1285         }
1286         (void)OS_RestoreInterrupts(bak_psr);
1287         if (is_owner)
1288         {
1289             FSi_ExecuteSyncCommand(file);
1290         }
1291         else
1292         {
1293             FSi_WaitConditionOff(&file->stat, FS_FILE_STATUS_BUSY, file->queue);
1294         }
1295     }
1296 
1297     return FS_IsSucceeded(file);
1298 }
1299 
1300 
1301 #endif /* FS_IMPLEMENT */
1302