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