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