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