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