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