1 /*---------------------------------------------------------------------------*
2 Project: TwlSDK - FS - libraries
3 File: fs_romfat_default.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-12-08#$
14 $Rev: 9544 $
15 $Author: yosizaki $
16
17 *---------------------------------------------------------------------------*/
18
19
20 #include <nitro/fs.h>
21
22 #include <nitro/mi/memory.h>
23 #include <nitro/math/math.h>
24 #include <nitro/std.h>
25
26
27 #include "../include/util.h"
28 #include "../include/command.h"
29 #include "../include/rom.h"
30
31
32 #if defined(FS_IMPLEMENT)
33
34
35 /*---------------------------------------------------------------------------*/
36 /* Declarations */
37
38 // Synchronous system command blocking read structure
39 typedef struct FSiSyncReadParam
40 {
41 FSArchive *arc;
42 u32 pos;
43 }
44 FSiSyncReadParam;
45
46 // Bit and alignment macros
47 #define BIT_MASK(n) ((1 << (n)) - 1)
48
49
50 /*---------------------------------------------------------------------------*/
51 /* functions */
52
53 /*---------------------------------------------------------------------------*
54 Name: FSi_SeekAndReadSRL
55
56 Description: SRL access callback called from the ROM hash context
57
58 Arguments: userdata Any user-defined data
59 buffer Transfer destination buffer
60 offset Transfer source ROM offset
61 length Transfer size
62
63 Returns: Size of the transmission
64 *---------------------------------------------------------------------------*/
FSi_SeekAndReadSRL(void * userdata,void * buffer,u32 offset,u32 length)65 static int FSi_SeekAndReadSRL(void *userdata, void *buffer, u32 offset, u32 length)
66 {
67 FSFile *file = (FSFile*)userdata;
68 if (file)
69 {
70 (void)FS_SeekFile(file, (int)offset, FS_SEEK_SET);
71 (void)FS_ReadFile(file, buffer, (int)length);
72 }
73 return (int)length;
74 }
75
76 /*---------------------------------------------------------------------------*
77 Name: FSi_GetTemporaryFileContext
78
79 Description: Gets dummy FSFile structure temporarily used by commands that do not directly operate files or directories
80
81
82 Arguments: arc Associated archive
83
84 Returns: Pointer of the FSFile structure.
85 *---------------------------------------------------------------------------*/
FSi_GetTemporaryFileContext(FSArchive * arc)86 static FSFile* FSi_GetTemporaryFileContext(FSArchive *arc)
87 {
88 static FSFile tmp[1];
89 FS_InitFile(tmp);
90 tmp->arc = arc;
91 return tmp;
92 }
93
94 /*---------------------------------------------------------------------------*
95 Name: FSi_TranslateCommand
96
97 Description: Calls a user procedure or the default process and returns the result.
98 If a synchronous command returns an asynchronous response, wait for completion internally.
99 If an asynchronous command returns an asynchronous response, return it as is.
100
101 Arguments: p_file FSFile structure in which the command argument is stored
102 Command: Command ID
103 block TRUE if blocking
104
105 Returns: Command processing result.
106 *---------------------------------------------------------------------------*/
107 static FSResult FSi_TranslateCommand(FSFile *p_file, FSCommandType command, BOOL block);
108
109 /*---------------------------------------------------------------------------*
110 Name: FSi_ReadTable
111
112 Description: Execute synchronous Read inside synchronous-type command
113
114 Arguments: p Synchronous read structure
115 dst Read data storage destination buffer
116 len Number of bytes to read
117
118 Returns: Result of synchronous read
119 *---------------------------------------------------------------------------*/
FSi_ReadTable(FSiSyncReadParam * p,void * dst,u32 len)120 static FSResult FSi_ReadTable(FSiSyncReadParam * p, void *dst, u32 len)
121 {
122 FSResult result;
123 FSArchive * const arc = p->arc;
124 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
125
126
127 if (context->load_mem)
128 {
129 MI_CpuCopy8((const void *)p->pos, dst, len);
130 result = FS_RESULT_SUCCESS;
131 }
132 else
133 {
134 result = (*context->read_func) (arc, dst, p->pos, len);
135 result = FSi_WaitForArchiveCompletion(arc->list, result);
136 }
137 p->pos += len;
138 return result;
139 }
140
141 /*---------------------------------------------------------------------------*
142 Name: FSi_SeekDirDirect
143
144 Description: Directly moves to beginning of specified directory
145
146 Arguments: file Pointer that stores directory list
147 id Directory ID
148
149 Returns: None.
150 *---------------------------------------------------------------------------*/
FSi_SeekDirDirect(FSFile * file,u32 id)151 static void FSi_SeekDirDirect(FSFile *file, u32 id)
152 {
153 file->arg.seekdir.pos.arc = file->arc;
154 file->arg.seekdir.pos.own_id = (u16)id;
155 file->arg.seekdir.pos.index = 0;
156 file->arg.seekdir.pos.pos = 0;
157 (void)FSi_TranslateCommand(file, FS_COMMAND_SEEKDIR, TRUE);
158 }
159
160 /*---------------------------------------------------------------------------*
161 Name: FSi_SeekDirDefault
162
163 Description: Default implementation of SEEKDIR command
164
165 Arguments: file Handle to process command
166
167 Returns: Command result value
168 *---------------------------------------------------------------------------*/
FSi_SeekDirDefault(FSFile * file)169 static FSResult FSi_SeekDirDefault(FSFile *file)
170 {
171 FSResult result;
172 FSArchive * const arc = file->arc;
173 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
174
175 const FSDirPos * const arg = &file->arg.seekdir.pos;
176 FSArchiveFNT fnt;
177 FSiSyncReadParam param;
178 param.arc = arc;
179 param.pos = context->fnt + arg->own_id * sizeof(fnt);
180 result = FSi_ReadTable(¶m, &fnt, sizeof(fnt));
181 if (result == FS_RESULT_SUCCESS)
182 {
183 file->prop.dir.pos = *arg;
184 if ((arg->index == 0) && (arg->pos == 0))
185 {
186 file->prop.dir.pos.index = fnt.index;
187 file->prop.dir.pos.pos = context->fnt + fnt.start;
188 }
189 file->prop.dir.parent = (u32)(fnt.parent & BIT_MASK(12));
190 }
191 return result;
192 }
193
194 /*---------------------------------------------------------------------------*
195 Name: FSi_ReadDirDefault
196
197 Description: Default implementation of READDIR command
198
199 Arguments: file Handle to process command
200
201 Returns: Command result value
202 *---------------------------------------------------------------------------*/
FSi_ReadDirDefault(FSFile * file)203 static FSResult FSi_ReadDirDefault(FSFile *file)
204 {
205 FSResult result;
206 FSDirEntry *entry = file->arg.readdir.p_entry;
207 u8 len;
208
209 FSiSyncReadParam param;
210 param.arc = file->arc;
211 param.pos = file->prop.dir.pos.pos;
212
213 result = FSi_ReadTable(¶m, &len, sizeof(len));
214
215 if (result == FS_RESULT_SUCCESS)
216 {
217 entry->name_len = (u32)(len & BIT_MASK(7));
218 entry->is_directory = (u32)((len >> 7) & 1);
219 if (entry->name_len == 0)
220 {
221 result = FS_RESULT_FAILURE;
222 }
223 else
224 {
225 if (!file->arg.readdir.skip_string)
226 {
227 result = FSi_ReadTable(¶m, entry->name, entry->name_len);
228 if (result != FS_RESULT_SUCCESS)
229 {
230 return result;
231 }
232 entry->name[entry->name_len] = '\0';
233 }
234 else
235 {
236 param.pos += entry->name_len;
237 }
238 if (!entry->is_directory)
239 {
240 entry->file_id.arc = file->arc;
241 entry->file_id.file_id = file->prop.dir.pos.index;
242 ++file->prop.dir.pos.index;
243 }
244 else
245 {
246 u16 id;
247 result = FSi_ReadTable(¶m, &id, sizeof(id));
248 if (result == FS_RESULT_SUCCESS)
249 {
250 entry->dir_id.arc = file->arc;
251 entry->dir_id.own_id = (u16)(id & BIT_MASK(12));
252 entry->dir_id.index = 0;
253 entry->dir_id.pos = 0;
254 }
255 }
256 if (result == FS_RESULT_SUCCESS)
257 {
258 file->prop.dir.pos.pos = param.pos;
259 }
260 }
261 }
262
263 return result;
264 }
265
266 /*---------------------------------------------------------------------------*
267 Name: FSi_FindPathDefault
268
269 Description: Default implementation of FINDPATH command
270 (Uses SEEKDIR, READDIR commands)
271
272 Arguments: p_dir Handle to process command
273
274 Returns: Command result value
275 *---------------------------------------------------------------------------*/
FSi_FindPathDefault(FSFile * p_dir)276 static FSResult FSi_FindPathDefault(FSFile *p_dir)
277 {
278 const char *path = p_dir->arg.findpath.path;
279 const BOOL is_dir = p_dir->arg.findpath.find_directory;
280
281 // Path name search based on specified directory
282 p_dir->arg.seekdir.pos = p_dir->arg.findpath.pos;
283 (void)FSi_TranslateCommand(p_dir, FS_COMMAND_SEEKDIR, TRUE);
284 for (; *path; path += (*path ? 1 : 0))
285 {
286 // Extract the entry name targeted for the search
287 int name_len = FSi_IncrementSjisPositionToSlash(path, 0);
288 u32 is_directory = ((path[name_len] != '\0') || is_dir);
289 // If an illegal directory such as "//" is detected, failure
290 if (name_len == 0)
291 {
292 return FS_RESULT_INVALID_PARAMETER;
293 }
294 // Special directory specification
295 else if (*path == '.')
296 {
297 // "." is the current directory
298 if (name_len == 1)
299 {
300 path += 1;
301 continue;
302 }
303 // ".." is the parent directory
304 else if ((name_len == 2) & (path[1] == '.'))
305 {
306 if (p_dir->prop.dir.pos.own_id != 0)
307 {
308 FSi_SeekDirDirect(p_dir, p_dir->prop.dir.parent);
309 }
310 path += 2;
311 continue;
312 }
313 }
314 else if (*path == '*')
315 {
316 // "*" specifies a wildcard (Ignore this with the archive procedure)
317 break;
318 }
319 // If an entry name that is too long is detected, failure
320 if (name_len > FS_FILE_NAME_MAX)
321 {
322 return FS_RESULT_NO_ENTRY;
323 }
324 // Enumerate entries and sequentially compare
325 else
326 {
327 FSDirEntry etr;
328 p_dir->arg.readdir.p_entry = &etr;
329 p_dir->arg.readdir.skip_string = FALSE;
330 for (;;)
331 {
332 // If corresponding entry did not exist even when the end is reached, failure
333 if (FSi_TranslateCommand(p_dir, FS_COMMAND_READDIR, TRUE) != FS_RESULT_SUCCESS)
334 {
335 return FS_RESULT_NO_ENTRY;
336 }
337 // If there is no match, ingore
338 if ((is_directory == etr.is_directory) &&
339 (name_len == etr.name_len) && (FSi_StrNICmp(path, etr.name, (u32)name_len) == 0))
340 {
341 // If directory, move to the top again
342 if (is_directory)
343 {
344 path += name_len;
345 p_dir->arg.seekdir.pos = etr.dir_id;
346 (void)FSi_TranslateCommand(p_dir, FS_COMMAND_SEEKDIR, TRUE);
347 break;
348 }
349 // If it is a file and expecting a directory, failure
350 else if (is_dir)
351 {
352 return FS_RESULT_NO_ENTRY;
353 }
354 // Discover the file
355 else
356 {
357 *p_dir->arg.findpath.result.file = etr.file_id;
358 return FS_RESULT_SUCCESS;
359 }
360 }
361 }
362 }
363 }
364 // If file cannot be found, failure
365 if (!is_dir)
366 {
367 return FS_RESULT_NO_ENTRY;
368 }
369 else
370 {
371 *p_dir->arg.findpath.result.dir = p_dir->prop.dir.pos;
372 return FS_RESULT_SUCCESS;
373 }
374 }
375
376 /*---------------------------------------------------------------------------*
377 Name: FSi_GetPathDefault
378
379 Description: Default implementation of GETPATH command
380 (Uses SEEKDIR, READDIR commands)
381
382 Arguments: file Handle to process command
383
384 Returns: Command result value
385 *---------------------------------------------------------------------------*/
FSi_GetPathDefault(FSFile * file)386 static FSResult FSi_GetPathDefault(FSFile *file)
387 {
388 FSResult result;
389 FSArchive * const arc = file->arc;
390 FSFile *tmp = FSi_GetTemporaryFileContext(arc);
391 FSGetPathInfo *p_info = &file->arg.getpath;
392 FSDirEntry entry;
393 u32 dir_id;
394 u32 file_id;
395
396 enum
397 { INVALID_ID = 0x10000 };
398
399 if (FS_IsDir(file))
400 {
401 dir_id = file->prop.dir.pos.own_id;
402 file_id = INVALID_ID;
403 }
404 else
405 {
406 file_id = file->prop.file.own_id;
407 {
408 // Search for all directories from root
409 u32 pos = 0;
410 u32 num_dir = 0;
411 dir_id = INVALID_ID;
412 do
413 {
414 FSi_SeekDirDirect(tmp, pos);
415 // Get total directory count first time
416 if (!pos)
417 {
418 num_dir = tmp->prop.dir.parent;
419 }
420 tmp->arg.readdir.p_entry = &entry;
421 tmp->arg.readdir.skip_string = TRUE;
422 while (FSi_TranslateCommand(tmp, FS_COMMAND_READDIR, TRUE) == FS_RESULT_SUCCESS)
423 {
424 if (!entry.is_directory && (entry.file_id.file_id == file_id))
425 {
426 dir_id = tmp->prop.dir.pos.own_id;
427 break;
428 }
429 }
430 }
431 while ((dir_id == INVALID_ID) && (++pos < num_dir));
432 }
433 }
434
435 // FALSE if the corresponding directory cannot be found
436 if (dir_id == INVALID_ID)
437 {
438 p_info->total_len = 0;
439 result = FS_RESULT_NO_ENTRY;
440 }
441 else
442 {
443 // If path length not calculated, measure once this time
444 {
445 u32 id = dir_id;
446 // First, add "archive name:/"
447 const char *arcname = FS_GetArchiveName(arc);
448 u32 len = (u32)STD_GetStringLength(arcname);
449 len += 2;
450 // Move to standard directory
451 FSi_SeekDirDirect(tmp, id);
452 // If necessary, add file name. (Already obtained)
453 if (file_id != INVALID_ID)
454 {
455 len += entry.name_len;
456 }
457 // Trace back in order and add own name
458 if (id != 0)
459 {
460 do
461 {
462 // Move to parent and search self
463 FSi_SeekDirDirect(tmp, tmp->prop.dir.parent);
464 tmp->arg.readdir.p_entry = &entry;
465 tmp->arg.readdir.skip_string = TRUE;
466 while (FSi_TranslateCommand(tmp, FS_COMMAND_READDIR, TRUE) == FS_RESULT_SUCCESS)
467 {
468 if (entry.is_directory && (entry.dir_id.own_id == id))
469 {
470 len += entry.name_len + 1;
471 break;
472 }
473 }
474 id = tmp->prop.dir.pos.own_id;
475 }
476 while (id != 0);
477 }
478 // Save the currently calculated data
479 p_info->total_len = (u16)(len + 1);
480 p_info->dir_id = (u16)dir_id;
481 }
482
483 // Success if measuring path length is the objective
484 if (!p_info->buf)
485 {
486 result = FS_RESULT_SUCCESS;
487 }
488 // Failure if buffer length is insufficient
489 else if (p_info->buf_len < p_info->total_len)
490 {
491 result = FS_RESULT_NO_MORE_RESOURCE;
492 }
493 // Store the character string from the end
494 else
495 {
496 u8 *dst = p_info->buf;
497 u32 total = p_info->total_len;
498 u32 pos = 0;
499 u32 id = dir_id;
500 // First, add "archive name:/"
501 const char *arcname = FS_GetArchiveName(arc);
502 u32 len = (u32)STD_GetStringLength(arcname);
503 MI_CpuCopy8(arcname, dst + pos, len);
504 pos += len;
505 MI_CpuCopy8(":/", dst + pos, 2);
506 pos += 2;
507 // Move to standard directory
508 FSi_SeekDirDirect(tmp, id);
509 // If necessary, add file name.
510 if (file_id != INVALID_ID)
511 {
512 tmp->arg.readdir.p_entry = &entry;
513 tmp->arg.readdir.skip_string = FALSE;
514 while (FSi_TranslateCommand(tmp, FS_COMMAND_READDIR, TRUE) == FS_RESULT_SUCCESS)
515 {
516 if (!entry.is_directory && (entry.file_id.file_id == file_id))
517 {
518 break;
519 }
520 }
521 len = entry.name_len + 1;
522 MI_CpuCopy8(entry.name, dst + total - len, len);
523 total -= len;
524 }
525 else
526 {
527 // If directory, append only the end
528 dst[total - 1] = '\0';
529 total -= 1;
530 }
531 // Trace back in order and add own name
532 if (id != 0)
533 {
534 do
535 {
536 // Move to parent and search self
537 FSi_SeekDirDirect(tmp, tmp->prop.dir.parent);
538 tmp->arg.readdir.p_entry = &entry;
539 tmp->arg.readdir.skip_string = FALSE;
540 // Add "/"
541 dst[total - 1] = '/';
542 total -= 1;
543 // Search parent's children (previous self)
544 while (FSi_TranslateCommand(tmp, FS_COMMAND_READDIR, TRUE) == FS_RESULT_SUCCESS)
545 {
546 if (entry.is_directory && (entry.dir_id.own_id == id))
547 {
548 u32 len = entry.name_len;
549 MI_CpuCopy8(entry.name, dst + total - len, len);
550 total -= len;
551 break;
552 }
553 }
554 id = tmp->prop.dir.pos.own_id;
555 }
556 while (id != 0);
557 }
558 SDK_ASSERT(pos == total);
559 }
560 result = FS_RESULT_SUCCESS;
561 }
562 return result;
563 }
564
565 /*---------------------------------------------------------------------------*
566 Name: FSi_OpenFileFastDefault
567
568 Description: Default implementation of OPENFILEFAST command
569 (Uses OPENFILEDIRECT command)
570
571 Arguments: p_file Handle to process command
572
573 Returns: Command result value
574 *---------------------------------------------------------------------------*/
FSi_OpenFileFastDefault(FSFile * p_file)575 static FSResult FSi_OpenFileFastDefault(FSFile *p_file)
576 {
577 FSResult result;
578 FSArchive * const p_arc = p_file->arc;
579 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(p_arc);
580 const FSFileID *p_id = &p_file->arg.openfilefast.id;
581 const u32 index = p_id->file_id;
582
583 {
584 // Determine propriety of FAT accessing region
585 u32 pos = (u32)(index * sizeof(FSArchiveFAT));
586 if (pos >= context->fat_size)
587 {
588 result = FS_RESULT_NO_ENTRY;
589 }
590 else
591 {
592 // Obtain the image area of the specified file from FAT
593 FSArchiveFAT fat;
594 FSiSyncReadParam param;
595 param.arc = p_arc;
596 param.pos = context->fat + pos;
597 result = FSi_ReadTable(¶m, &fat, sizeof(fat));
598 if (result == FS_RESULT_SUCCESS)
599 {
600 // Directly open
601 p_file->arg.openfiledirect.top = fat.top;
602 p_file->arg.openfiledirect.bottom = fat.bottom;
603 p_file->arg.openfiledirect.index = index;
604 result = FSi_TranslateCommand(p_file, FS_COMMAND_OPENFILEDIRECT, TRUE);
605 }
606 }
607 }
608 return result;
609 }
610
611 /*---------------------------------------------------------------------------*
612 Name: FSi_OpenFileDirectDefault
613
614 Description: Default implementation of OPENFILEDIRECT command
615
616 Arguments: p_file Handle to process command
617
618 Returns: Command result value
619 *---------------------------------------------------------------------------*/
FSi_OpenFileDirectDefault(FSFile * p_file)620 static FSResult FSi_OpenFileDirectDefault(FSFile *p_file)
621 {
622 p_file->prop.file.top = p_file->arg.openfiledirect.top;
623 p_file->prop.file.pos = p_file->arg.openfiledirect.top;
624 p_file->prop.file.bottom = p_file->arg.openfiledirect.bottom;
625 p_file->prop.file.own_id = p_file->arg.openfiledirect.index;
626 return FS_RESULT_SUCCESS;
627 }
628
629 /*---------------------------------------------------------------------------*
630 Name: FSi_ReadFileDefault
631
632 Description: Default implementation of READFILE command
633
634 Arguments: file Handle to process command
635
636 Returns: Command result value
637 *---------------------------------------------------------------------------*/
FSi_ReadFileDefault(FSFile * file)638 static FSResult FSi_ReadFileDefault(FSFile *file)
639 {
640 FSArchive * const arc = file->arc;
641 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
642 const u32 pos = file->prop.file.pos;
643 const u32 len = file->arg.readfile.len;
644 void * const dst = file->arg.readfile.dst;
645 file->prop.file.pos += len;
646 return (*context->read_func) (arc, dst, pos, len);
647 }
648
649 /*---------------------------------------------------------------------------*
650 Name: FSi_WriteFileDefault
651
652 Description: Default implementation of WRITEFILE command
653
654 Arguments: file Handle to process command
655
656 Returns: Command result value
657 *---------------------------------------------------------------------------*/
FSi_WriteFileDefault(FSFile * file)658 static FSResult FSi_WriteFileDefault(FSFile *file)
659 {
660 FSArchive * const arc = file->arc;
661 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
662 const u32 pos = file->prop.file.pos;
663 const u32 len = file->arg.writefile.len;
664 const void * const src = file->arg.writefile.src;
665 file->prop.file.pos += len;
666 return (*context->write_func) (arc, src, pos, len);
667 }
668
669 /*---------------------------------------------------------------------------*
670 Name: FSi_IgnoredCommand
671
672 Description: Implements commands that do nothing by default
673
674 Arguments: file Handle to process command
675
676 Returns: Command result value
677 *---------------------------------------------------------------------------*/
FSi_IgnoredCommand(FSFile * file)678 static FSResult FSi_IgnoredCommand(FSFile *file)
679 {
680 (void)file;
681 return FS_RESULT_SUCCESS;
682 }
683
684 /*---------------------------------------------------------------------------*
685 Name: FSi_TranslateCommand
686
687 Description: Calls user procedure or default process
688 If a synchronous command returns an asynchronous response, wait for completion internally.
689 If an asynchronous command returns an asynchronous response, return it as is.
690
691 Arguments: file FSFile structure that stores the argument
692 Command: Command ID
693 block TRUE if blocking is necessary
694
695 Returns: The processing result for the command.
696 *---------------------------------------------------------------------------*/
FSi_TranslateCommand(FSFile * file,FSCommandType command,BOOL block)697 FSResult FSi_TranslateCommand(FSFile *file, FSCommandType command, BOOL block)
698 {
699 FSResult result = FS_RESULT_PROC_DEFAULT;
700 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(file->arc);
701 // Execute if it is a command that supports the procedure
702 if ((context->proc_flag & (1 << command)) != 0)
703 {
704 result = (*context->proc) (file, command);
705 switch (result)
706 {
707 case FS_RESULT_SUCCESS:
708 case FS_RESULT_FAILURE:
709 case FS_RESULT_UNSUPPORTED:
710 break;
711 case FS_RESULT_PROC_ASYNC:
712 // Control asynchronous process handling in detail later.
713 break;
714 case FS_RESULT_PROC_UNKNOWN:
715 // Unknown command, so switch to the default process from this time onward.
716 result = FS_RESULT_PROC_DEFAULT;
717 context->proc_flag &= ~(1 << command);
718 break;
719 }
720 }
721 // If not processed in the procedure, call default process
722 if (result == FS_RESULT_PROC_DEFAULT)
723 {
724 static FSResult (*const (default_table[])) (FSFile *) =
725 {
726 FSi_ReadFileDefault,
727 FSi_WriteFileDefault,
728 FSi_SeekDirDefault,
729 FSi_ReadDirDefault,
730 FSi_FindPathDefault,
731 FSi_GetPathDefault,
732 FSi_OpenFileFastDefault,
733 FSi_OpenFileDirectDefault,
734 FSi_IgnoredCommand,
735 FSi_IgnoredCommand,
736 FSi_IgnoredCommand,
737 FSi_IgnoredCommand,
738 FSi_IgnoredCommand,
739 };
740 if (command < sizeof(default_table) / sizeof(*default_table))
741 {
742 result = (*default_table[command])(file);
743 }
744 else
745 {
746 result = FS_RESULT_UNSUPPORTED;
747 }
748 }
749 // If necessary, block here
750 if (block)
751 {
752 result = FSi_WaitForArchiveCompletion(file, result);
753 }
754 return result;
755 }
756
757 /*---------------------------------------------------------------------------*
758 Name: FSi_ROMFAT_ReadFile
759
760 Description: The FS_COMMAND_READFILE command.
761
762 Arguments: arc: The calling archive
763 file: The target file
764 buffer: The memory to transfer to
765 length Transfer size
766
767 Returns: The processing result for the command.
768 *---------------------------------------------------------------------------*/
FSi_ROMFAT_ReadFile(FSArchive * arc,FSFile * file,void * buffer,u32 * length)769 static FSResult FSi_ROMFAT_ReadFile(FSArchive *arc, FSFile *file, void *buffer, u32 *length)
770 {
771 FSROMFATProperty *prop = (FSROMFATProperty*)FS_GetFileUserData(file);
772 const u32 pos = prop->file.pos;
773 const u32 rest = (u32)(prop->file.bottom - pos);
774 const u32 org = *length;
775 if (*length > rest)
776 {
777 *length = rest;
778 }
779 file->arg.readfile.dst = buffer;
780 file->arg.readfile.len_org = org;
781 file->arg.readfile.len = *length;
782 (void)arc;
783 return FSi_TranslateCommand(file, FS_COMMAND_READFILE, FALSE);
784 }
785
786 /*---------------------------------------------------------------------------*
787 Name: FSi_ROMFAT_WriteFile
788
789 Description: The FS_COMMAND_WRITEFILE command.
790
791 Arguments: arc: The calling archive
792 file: The target file
793 buffer: The memory to transfer from
794 length Transfer size
795
796 Returns: The processing result for the command.
797 *---------------------------------------------------------------------------*/
FSi_ROMFAT_WriteFile(FSArchive * arc,FSFile * file,const void * buffer,u32 * length)798 static FSResult FSi_ROMFAT_WriteFile(FSArchive *arc, FSFile *file, const void *buffer, u32 *length)
799 {
800 FSROMFATProperty *prop = (FSROMFATProperty*)FS_GetFileUserData(file);
801 const u32 pos = prop->file.pos;
802 const u32 rest = (u32)(prop->file.bottom - pos);
803 const u32 org = *length;
804 if (*length > rest)
805 {
806 *length = rest;
807 }
808 file->arg.writefile.src = buffer;
809 file->arg.writefile.len_org = org;
810 file->arg.writefile.len = *length;
811 (void)arc;
812 return FSi_TranslateCommand(file, FS_COMMAND_WRITEFILE, FALSE);
813 }
814
815 /*---------------------------------------------------------------------------*
816 Name: FSi_ROMFAT_SeekDirectory
817
818 Description: FS_COMMAND_SEEKDIR command
819
820 Arguments: arc: The calling archive
821 file: The target file
822 id Unique directory ID
823 position Indicate enumeration state
824
825 Returns: The processing result for the command.
826 *---------------------------------------------------------------------------*/
FSi_ROMFAT_SeekDirectory(FSArchive * arc,FSFile * file,u32 id,u32 position)827 static FSResult FSi_ROMFAT_SeekDirectory(FSArchive *arc, FSFile *file, u32 id, u32 position)
828 {
829 FSResult result;
830 FSROMFATCommandInfo *arg = &file->arg;
831 file->arc = arc;
832 arg->seekdir.pos.arc = arc;
833 arg->seekdir.pos.own_id = (u16)(id >> 0);
834 arg->seekdir.pos.index = (u16)(id >> 16);
835 arg->seekdir.pos.pos = position;
836 result = FSi_TranslateCommand(file, FS_COMMAND_SEEKDIR, TRUE);
837 if (result == FS_RESULT_SUCCESS)
838 {
839 FS_SetDirectoryHandle(file, arc, &file->prop);
840 }
841 return result;
842 }
843
844 /*---------------------------------------------------------------------------*
845 Name: FSi_ROMFAT_ReadDirectory
846
847 Description: The FS_COMMAND_READDIR command.
848
849 Arguments: arc: The calling archive
850 file: The target file
851 info: Location to save information
852
853 Returns: The processing result for the command.
854 *---------------------------------------------------------------------------*/
FSi_ROMFAT_ReadDirectory(FSArchive * arc,FSFile * file,FSDirectoryEntryInfo * info)855 static FSResult FSi_ROMFAT_ReadDirectory(FSArchive *arc, FSFile *file, FSDirectoryEntryInfo *info)
856 {
857 FSResult result;
858 FSDirEntry entry[1];
859 FSROMFATCommandInfo *arg = &file->arg;
860 arg->readdir.p_entry = entry;
861 arg->readdir.skip_string = FALSE;
862 result = FSi_TranslateCommand(file, FS_COMMAND_READDIR, TRUE);
863 if (result == FS_RESULT_SUCCESS)
864 {
865 info->shortname_length = 0;
866 info->longname_length = entry->name_len;
867 MI_CpuCopy8(entry->name, info->longname, info->longname_length);
868 info->longname[info->longname_length] = '\0';
869 if (entry->is_directory)
870 {
871 info->attributes = FS_ATTRIBUTE_IS_DIRECTORY;
872 info->id = (u32)((entry->dir_id.own_id << 0) | (entry->dir_id.index << 16));
873 info->filesize = 0;
874 }
875 else
876 {
877 info->attributes = 0;
878 info->id = entry->file_id.file_id;
879 info->filesize = 0;
880 // If a valid file ID, get file size information from FAT
881 {
882 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
883 u32 pos = (u32)(info->id * sizeof(FSArchiveFAT));
884 if (pos < context->fat_size)
885 {
886 FSArchiveFAT fat;
887 FSiSyncReadParam param;
888 param.arc = arc;
889 param.pos = context->fat + pos;
890 if (FSi_ReadTable(¶m, &fat, sizeof(fat)) == FS_RESULT_SUCCESS)
891 {
892 info->filesize = (u32)(fat.bottom - fat.top);
893 // If running in NTR mode, record with a flag that the TWL dedicated region is not read
894 if (FSi_IsUnreadableRomOffset(arc, fat.top))
895 {
896 info->attributes |= FS_ATTRIBUTE_IS_OFFLINE;
897 }
898 }
899 }
900 }
901 }
902 info->mtime.year = 0;
903 info->mtime.month = 0;
904 info->mtime.day = 0;
905 info->mtime.hour = 0;
906 info->mtime.minute = 0;
907 info->mtime.second = 0;
908 }
909 (void)arc;
910 return result;
911 }
912
913 /*---------------------------------------------------------------------------*
914 Name: FSi_ROMFAT_FindPath
915
916 Description: FS_COMMAND_FINDPATH command
917
918 Arguments: arc: The calling archive
919 base_dir_id The base directory ID (0 for the root)
920 path Search path
921 target_id ID storage destination
922 target_is_directory Storage destination of whether this is a directory
923
924 Returns: The processing result for the command.
925 *---------------------------------------------------------------------------*/
FSi_ROMFAT_FindPath(FSArchive * arc,u32 base_dir_id,const char * path,u32 * target_id,BOOL target_is_directory)926 static FSResult FSi_ROMFAT_FindPath(FSArchive *arc, u32 base_dir_id, const char *path, u32 *target_id, BOOL target_is_directory)
927 {
928 FSResult result;
929 union
930 {
931 FSFileID file;
932 FSDirPos dir;
933 }
934 id;
935 FSFile *tmp = FSi_GetTemporaryFileContext(arc);
936 tmp->arg.findpath.pos.arc = arc;
937 tmp->arg.findpath.pos.own_id = (u16)(base_dir_id >> 0);
938 tmp->arg.findpath.pos.index = 0;
939 tmp->arg.findpath.pos.pos = 0;
940 tmp->arg.findpath.path = path;
941 tmp->arg.findpath.find_directory = target_is_directory;
942 if (target_is_directory)
943 {
944 tmp->arg.findpath.result.dir = &id.dir;
945 }
946 else
947 {
948 tmp->arg.findpath.result.file = &id.file;
949 }
950 result = FSi_TranslateCommand(tmp, FS_COMMAND_FINDPATH, TRUE);
951 if (result == FS_RESULT_SUCCESS)
952 {
953 if (target_is_directory)
954 {
955 *target_id = id.dir.own_id;
956 }
957 else
958 {
959 *target_id = id.file.file_id;
960 }
961 }
962 return result;
963 }
964
965 /*---------------------------------------------------------------------------*
966 Name: FSi_ROMFAT_GetPath
967
968 Description: FS_COMMAND_GETPATH command.
969
970 Arguments: arc: The calling archive
971 file: The target file
972 is_directory False, if file is a file, TRUE if it is a directory
973 buffer Path storage destination
974 length Buffer size
975
976 Returns: The processing result for the command.
977 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetPath(FSArchive * arc,FSFile * file,BOOL is_directory,char * buffer,u32 * length)978 static FSResult FSi_ROMFAT_GetPath(FSArchive *arc, FSFile *file, BOOL is_directory, char *buffer, u32 *length)
979 {
980 FSResult result;
981 FSROMFATCommandInfo *arg = &file->arg;
982 arg->getpath.total_len = 0;
983 arg->getpath.dir_id = 0;
984 arg->getpath.buf = (u8 *)buffer;
985 arg->getpath.buf_len = *length;
986 result = FSi_TranslateCommand(file, FS_COMMAND_GETPATH, TRUE);
987 if (result == FS_RESULT_SUCCESS)
988 {
989 *length = arg->getpath.buf_len;
990 }
991 (void)arc;
992 (void)is_directory;
993 return result;
994 }
995
996 /*---------------------------------------------------------------------------*
997 Name: FSi_ROMFAT_OpenFileFast
998
999 Description: The FS_COMMAND_OPENFILEFAST command
1000
1001 Arguments: arc: The calling archive
1002 file: The target file
1003 id File ID
1004 mode: The access mode
1005
1006 Returns: The processing result for the command.
1007 *---------------------------------------------------------------------------*/
FSi_ROMFAT_OpenFileFast(FSArchive * arc,FSFile * file,u32 id,u32 mode)1008 static FSResult FSi_ROMFAT_OpenFileFast(FSArchive *arc, FSFile *file, u32 id, u32 mode)
1009 {
1010 FSResult result;
1011 FSROMFATCommandInfo *arg = &file->arg;
1012 arg->openfilefast.id.arc = arc;
1013 arg->openfilefast.id.file_id = id;
1014 result = FSi_TranslateCommand(file, FS_COMMAND_OPENFILEFAST, TRUE);
1015 if (result == FS_RESULT_SUCCESS)
1016 {
1017 FS_SetFileHandle(file, arc, &file->prop);
1018 }
1019 (void)mode;
1020 return result;
1021 }
1022
1023 /*---------------------------------------------------------------------------*
1024 Name: FSi_ROMFAT_OpenFileDirect
1025
1026 Description: The FS_COMMAND_OPENFILEDIRECT command.
1027
1028 Arguments: arc: The calling archive
1029 file: The target file
1030 top File top offset
1031 bottom File bottom offset
1032 id Storage destination for required file ID and results
1033
1034 Returns: The processing result for the command.
1035 *---------------------------------------------------------------------------*/
FSi_ROMFAT_OpenFileDirect(FSArchive * arc,FSFile * file,u32 top,u32 bottom,u32 * id)1036 static FSResult FSi_ROMFAT_OpenFileDirect(FSArchive *arc, FSFile *file, u32 top, u32 bottom, u32 *id)
1037 {
1038 FSResult result;
1039 FSROMFATCommandInfo *arg = &file->arg;
1040 arg->openfiledirect.top = top;
1041 arg->openfiledirect.bottom = bottom;
1042 arg->openfiledirect.index = *id;
1043 result = FSi_TranslateCommand(file, FS_COMMAND_OPENFILEDIRECT, TRUE);
1044 if (result == FS_RESULT_SUCCESS)
1045 {
1046 FS_SetFileHandle(file, arc, &file->prop);
1047 }
1048 return result;
1049 }
1050
1051 /*---------------------------------------------------------------------------*
1052 Name: FSi_ROMFAT_CloseFile
1053
1054 Description: The FS_COMMAND_CLOSEFILE command.
1055
1056 Arguments: arc: The calling archive
1057 file: The target file
1058
1059 Returns: The processing result for the command.
1060 *---------------------------------------------------------------------------*/
FSi_ROMFAT_CloseFile(FSArchive * arc,FSFile * file)1061 static FSResult FSi_ROMFAT_CloseFile(FSArchive *arc, FSFile *file)
1062 {
1063 FSResult result;
1064 result = FSi_TranslateCommand(file, FS_COMMAND_CLOSEFILE, TRUE);
1065 FS_DetachHandle(file);
1066 (void)arc;
1067 return result;
1068 }
1069
1070 /*---------------------------------------------------------------------------*
1071 Name: FSi_ROMFAT_Activate
1072
1073 Description: The FS_COMMAND_ACTIVATE command.
1074
1075 Arguments: arc: The calling archive
1076
1077 Returns: None.
1078 *---------------------------------------------------------------------------*/
FSi_ROMFAT_Activate(FSArchive * arc)1079 static void FSi_ROMFAT_Activate(FSArchive* arc)
1080 {
1081 (void)FSi_TranslateCommand(FSi_GetTemporaryFileContext(arc), FS_COMMAND_ACTIVATE, FALSE);
1082 }
1083
1084 /*---------------------------------------------------------------------------*
1085 Name: FSi_ROMFAT_Idle
1086
1087 Description: The FS_COMMAND_ACTIVATE command.
1088
1089 Arguments: arc: The calling archive
1090
1091 Returns: None.
1092 *---------------------------------------------------------------------------*/
FSi_ROMFAT_Idle(FSArchive * arc)1093 static void FSi_ROMFAT_Idle(FSArchive* arc)
1094 {
1095 (void)FSi_TranslateCommand(FSi_GetTemporaryFileContext(arc), FS_COMMAND_IDLE, FALSE);
1096 }
1097
1098 /*---------------------------------------------------------------------------*
1099 Name: FSi_ROMFAT_Suspend
1100
1101 Description: The FS_COMMAND_SUSPEND command.
1102
1103 Arguments: arc: The calling archive
1104
1105 Returns: None.
1106 *---------------------------------------------------------------------------*/
FSi_ROMFAT_Suspend(FSArchive * arc)1107 static void FSi_ROMFAT_Suspend(FSArchive* arc)
1108 {
1109 (void)FSi_TranslateCommand(FSi_GetTemporaryFileContext(arc), FS_COMMAND_SUSPEND, FALSE);
1110 }
1111
1112 /*---------------------------------------------------------------------------*
1113 Name: FSi_ROMFAT_Resume
1114
1115 Description: The FS_COMMAND_RESUME command.
1116
1117 Arguments: arc: The calling archive
1118
1119 Returns: None.
1120 *---------------------------------------------------------------------------*/
FSi_ROMFAT_Resume(FSArchive * arc)1121 static void FSi_ROMFAT_Resume(FSArchive* arc)
1122 {
1123 (void)FSi_TranslateCommand(FSi_GetTemporaryFileContext(arc), FS_COMMAND_RESUME, FALSE);
1124 }
1125
1126 /*---------------------------------------------------------------------------*
1127 Name: FSi_ROMFAT_OpenFile
1128
1129 Description: The FS_COMMAND_OPENFILE command.
1130
1131 Arguments: arc: The calling archive
1132 file: The target file
1133 baseid: The base directory (0 for the root)
1134 path : File path
1135 mode: The access mode
1136
1137 Returns: The processing result for the command.
1138 *---------------------------------------------------------------------------*/
FSi_ROMFAT_OpenFile(FSArchive * arc,FSFile * file,u32 baseid,const char * path,u32 mode)1139 static FSResult FSi_ROMFAT_OpenFile(FSArchive *arc, FSFile *file, u32 baseid, const char *path, u32 mode)
1140 {
1141 FSResult result;
1142 u32 fileid;
1143 result = FSi_ROMFAT_FindPath(arc, baseid, path, &fileid, FALSE);
1144 if (result == FS_RESULT_SUCCESS)
1145 {
1146 result = FSi_ROMFAT_OpenFileFast(arc, file, fileid, mode);
1147 }
1148 return result;
1149 }
1150
1151 /*---------------------------------------------------------------------------*
1152 Name: FSi_ROMFAT_SeekFile
1153
1154 Description: The FS_COMMAND_SEEKFILE command.
1155
1156 Arguments: arc: The calling archive
1157 file: The target file
1158 offset: The displacement and moved position
1159 from: The starting point to seek from
1160
1161 Returns: The processing result for the command.
1162 *---------------------------------------------------------------------------*/
FSi_ROMFAT_SeekFile(FSArchive * arc,FSFile * file,int * offset,FSSeekFileMode from)1163 static FSResult FSi_ROMFAT_SeekFile(FSArchive *arc, FSFile *file, int *offset, FSSeekFileMode from)
1164 {
1165 FSROMFATProperty *prop = (FSROMFATProperty*)FS_GetFileUserData(file);
1166 int pos = *offset;
1167 switch (from)
1168 {
1169 case FS_SEEK_SET:
1170 pos += prop->file.top;
1171 break;
1172 case FS_SEEK_CUR:
1173 default:
1174 pos += prop->file.pos;
1175 break;
1176 case FS_SEEK_END:
1177 pos += prop->file.bottom;
1178 break;
1179 }
1180 // Considered seek processing outside of range to have failed
1181 if ((pos < (int)prop->file.top) || (pos > (int)prop->file.bottom))
1182 {
1183 return FS_RESULT_INVALID_PARAMETER;
1184 }
1185 else
1186 {
1187 prop->file.pos = (u32)pos;
1188 *offset = pos;
1189 (void)arc;
1190 return FS_RESULT_SUCCESS;
1191 }
1192 }
1193
1194 /*---------------------------------------------------------------------------*
1195 Name: FSi_ROMFAT_GetFileLength
1196
1197 Description: The FS_COMMAND_GETFILELENGTH command.
1198
1199 Arguments: arc: The calling archive
1200 file: The target file
1201 length: Location to save the obtained size
1202
1203 Returns: The processing result for the command.
1204 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetFileLength(FSArchive * arc,FSFile * file,u32 * length)1205 static FSResult FSi_ROMFAT_GetFileLength(FSArchive *arc, FSFile *file, u32 *length)
1206 {
1207 FSROMFATProperty *prop = (FSROMFATProperty*)FS_GetFileUserData(file);
1208 *length = prop->file.bottom - prop->file.top;
1209 (void)arc;
1210 return FS_RESULT_SUCCESS;
1211 }
1212
1213 /*---------------------------------------------------------------------------*
1214 Name: FSi_ROMFAT_GetFilePosition
1215
1216 Description: The FS_COMMAND_GETFILEPOSITION command.
1217
1218 Arguments: arc: The calling archive
1219 file: The target file
1220 length Storage destination of gotten position
1221
1222 Returns: The processing result for the command.
1223 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetFilePosition(FSArchive * arc,FSFile * file,u32 * position)1224 static FSResult FSi_ROMFAT_GetFilePosition(FSArchive *arc, FSFile *file, u32 *position)
1225 {
1226 FSROMFATProperty *prop = (FSROMFATProperty*)FS_GetFileUserData(file);
1227 *position = prop->file.pos - prop->file.top;
1228 (void)arc;
1229 return FS_RESULT_SUCCESS;
1230 }
1231
1232 /*---------------------------------------------------------------------------*
1233 Name: FSi_ROMFAT_Unmount
1234
1235 Description: The FS_COMMAND_UNMOUNT command
1236
1237 Arguments: arc: The calling archive
1238
1239 Returns: None.
1240 *---------------------------------------------------------------------------*/
FSi_ROMFAT_Unmount(FSArchive * arc)1241 static void FSi_ROMFAT_Unmount(FSArchive *arc)
1242 {
1243 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1244 if (FS_IsArchiveTableLoaded(arc))
1245 {
1246 OS_TWarning("memory may leak. preloaded-table of archive \"%s\" (0x%08X)",
1247 FS_GetArchiveName(arc), context->load_mem);
1248 }
1249 context->base = 0;
1250 context->fat = 0;
1251 context->fat_size = 0;
1252 context->fnt = 0;
1253 context->fnt_size = 0;
1254 context->fat_bak = 0;
1255 context->fnt_bak = 0;
1256 }
1257
1258 /*---------------------------------------------------------------------------*
1259 Name: FSi_ROMFAT_GetArchiveCaps
1260
1261 Description: The FS_COMMAND_GETARCHIVECAPS command.
1262
1263 Arguments: arc: The calling archive
1264 caps: Location to save the device capability flag
1265
1266 Returns: The processing result for the command.
1267 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetArchiveCaps(FSArchive * arc,u32 * caps)1268 static FSResult FSi_ROMFAT_GetArchiveCaps(FSArchive *arc, u32 *caps)
1269 {
1270 (void)arc;
1271 *caps = 0;
1272 return FS_RESULT_SUCCESS;
1273 }
1274
1275 /*---------------------------------------------------------------------------*
1276 Name: FSi_ROMFAT_OpenDirectory
1277
1278 Description: The FS_COMMAND_OPENDIRECTORY command.
1279
1280 Arguments: arc: The calling archive
1281 file: The target file
1282 baseid: The base directory ID (0 for the root)
1283 path: The path
1284 mode: The access mode
1285
1286 Returns: The processing result for the command.
1287 *---------------------------------------------------------------------------*/
FSi_ROMFAT_OpenDirectory(FSArchive * arc,FSFile * file,u32 baseid,const char * path,u32 mode)1288 static FSResult FSi_ROMFAT_OpenDirectory(FSArchive *arc, FSFile *file, u32 baseid, const char *path, u32 mode)
1289 {
1290 FSResult result = FS_RESULT_ERROR;
1291 u32 id = 0;
1292 result = FSi_ROMFAT_FindPath(arc, baseid, path, &id, TRUE);
1293 if (result == FS_RESULT_SUCCESS)
1294 {
1295 result = FSi_ROMFAT_SeekDirectory(arc, file, id, 0);
1296 }
1297 (void)mode;
1298 return result;
1299 }
1300
1301 /*---------------------------------------------------------------------------*
1302 Name: FSi_ROMFAT_CloseDirectory
1303
1304 Description: The FS_COMMAND_CLOSEDIRECTORY command.
1305
1306 Arguments: arc: The calling archive
1307 file: The target file
1308
1309 Returns: The processing result for the command.
1310 *---------------------------------------------------------------------------*/
FSi_ROMFAT_CloseDirectory(FSArchive * arc,FSFile * file)1311 static FSResult FSi_ROMFAT_CloseDirectory(FSArchive *arc, FSFile *file)
1312 {
1313 FS_DetachHandle(file);
1314 (void)arc;
1315 return FS_RESULT_SUCCESS;
1316 }
1317
1318 /*---------------------------------------------------------------------------*
1319 Name: FSi_ROMFAT_GetPathInfo
1320
1321 Description: The FS_COMMAND_GETPATHINFO command.
1322
1323 Arguments: arc: The calling archive
1324 baseid: The base directory ID (0 for the root)
1325 path: The path
1326 info: Location to save file information
1327
1328 Returns: The processing result for the command.
1329 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetPathInfo(FSArchive * arc,u32 baseid,const char * path,FSPathInfo * info)1330 static FSResult FSi_ROMFAT_GetPathInfo(FSArchive *arc, u32 baseid, const char *path, FSPathInfo *info)
1331 {
1332 FSResult result = FS_RESULT_ERROR;
1333 u32 id = 0;
1334 MI_CpuFill8(info, 0x00, sizeof(*info));
1335 // The ratio is quite poor, so it is better to call the function separately
1336 if (FSi_ROMFAT_FindPath(arc, baseid, path, &id, TRUE) == FS_RESULT_SUCCESS)
1337 {
1338 info->attributes = FS_ATTRIBUTE_IS_DIRECTORY;
1339 info->id = id;
1340 result = FS_RESULT_SUCCESS;
1341 }
1342 else if (FSi_ROMFAT_FindPath(arc, baseid, path, &id, FALSE) == FS_RESULT_SUCCESS)
1343 {
1344 info->attributes = 0;
1345 info->id = id;
1346 info->filesize = 0;
1347 // If a valid file ID, get file size information from FAT
1348 {
1349 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1350 u32 pos = (u32)(id * sizeof(FSArchiveFAT));
1351 if (pos < context->fat_size)
1352 {
1353 FSArchiveFAT fat;
1354 FSiSyncReadParam param;
1355 param.arc = arc;
1356 param.pos = context->fat + pos;
1357 if (FSi_ReadTable(¶m, &fat, sizeof(fat)) == FS_RESULT_SUCCESS)
1358 {
1359 info->filesize = (u32)(fat.bottom - fat.top);
1360 // If running in NTR mode, record with a flag that the TWL dedicated region is not read
1361 if (FSi_IsUnreadableRomOffset(arc, fat.top))
1362 {
1363 info->attributes |= FS_ATTRIBUTE_IS_OFFLINE;
1364 }
1365 }
1366 }
1367 }
1368 result = FS_RESULT_SUCCESS;
1369 }
1370 // The NitroROM format archive prohibits writing by default
1371 info->attributes |= FS_ATTRIBUTE_IS_PROTECTED;
1372 return result;
1373 }
1374
1375 /*---------------------------------------------------------------------------*
1376 Name: FSi_ROMFAT_GetPathInfo
1377
1378 Description: The FATFS_COMMAND_GETARCHIVERESOURCE command.
1379
1380 Arguments: arc: The calling archive
1381 resource Storage destination of resource information
1382
1383 Returns: The processing result for the command.
1384 *---------------------------------------------------------------------------*/
FSi_ROMFAT_GetArchiveResource(FSArchive * arc,FSArchiveResource * resource)1385 static FSResult FSi_ROMFAT_GetArchiveResource(FSArchive *arc, FSArchiveResource *resource)
1386 {
1387 const CARDRomHeader *header = (const CARDRomHeader *)CARD_GetRomHeader();
1388 resource->bytesPerSector = 0;
1389 resource->sectorsPerCluster = 0;
1390 resource->totalClusters = 0;
1391 resource->availableClusters = 0;
1392 resource->totalSize = header->rom_size;
1393 resource->availableSize = 0;
1394 resource->maxFileHandles = 0x7FFFFFFF;
1395 resource->currentFileHandles = 0;
1396 resource->maxDirectoryHandles = 0x7FFFFFFF;
1397 resource->currentDirectoryHandles = 0;
1398 (void)arc;
1399 return FS_RESULT_SUCCESS;
1400 }
1401
1402 /*---------------------------------------------------------------------------*
1403 Name: FSi_ReadSRLCallback
1404
1405 Description: SRL file read callback
1406
1407 Arguments: p_arc FSArchive structure
1408 dst Transfer destination
1409 src Transfer source
1410 len Transfer size
1411
1412 Returns: Read process results
1413 *---------------------------------------------------------------------------*/
FSi_ReadSRLCallback(FSArchive * arc,void * buffer,u32 offset,u32 length)1414 static FSResult FSi_ReadSRLCallback(FSArchive *arc, void *buffer, u32 offset, u32 length)
1415 {
1416 CARDRomHashContext *hash = (CARDRomHashContext*)FS_GetArchiveBase(arc);
1417 CARD_ReadRomHashImage(hash, buffer, offset, length);
1418 return FS_RESULT_SUCCESS;
1419 }
1420
1421 /*---------------------------------------------------------------------------*
1422 Name: FSi_SRLArchiveProc
1423
1424 Description: SRL file archive procedure
1425
1426 Arguments: file FSFile structure that stores command information
1427 cmd: Command type.
1428
1429 Returns: Command processing result
1430 *---------------------------------------------------------------------------*/
FSi_SRLArchiveProc(FSFile * file,FSCommandType cmd)1431 static FSResult FSi_SRLArchiveProc(FSFile *file, FSCommandType cmd)
1432 {
1433 (void)file;
1434 switch (cmd)
1435 {
1436 case FS_COMMAND_WRITEFILE:
1437 return FS_RESULT_UNSUPPORTED;
1438 default:
1439 return FS_RESULT_PROC_UNKNOWN;
1440 }
1441 }
1442
1443 /*---------------------------------------------------------------------------*
1444 Name: FSi_MountSRLFile
1445
1446 Description: Mount the ROM file system that is included in the SRL file
1447
1448 Arguments: arc FSArchive structure used in mounting
1449 Names must have been registered
1450 file File targeted for mounting and that is already open
1451 Do not destroy this structure while mounting
1452 hash Hash context structure
1453
1454 Returns: TRUE if the process is successful
1455 *---------------------------------------------------------------------------*/
FSi_MountSRLFile(FSArchive * arc,FSFile * file,CARDRomHashContext * hash)1456 BOOL FSi_MountSRLFile(FSArchive *arc, FSFile *file, CARDRomHashContext *hash)
1457 {
1458 BOOL retval = FALSE;
1459 static CARDRomHeaderTWL header[1] ATTRIBUTE_ALIGN(32);
1460 if (file &&
1461 (FS_SeekFileToBegin(file) &&
1462 (FS_ReadFile(file, header, sizeof(header)) == sizeof(header))))
1463 {
1464 // For security, NAND applications require a hash table
1465 // When a ROM called NITRO compatible mode but will use the file system appears in the future, study usage policy at that time
1466 //
1467 if ((((const u8 *)header)[0x1C] & 0x01) != 0)
1468 {
1469 FSResult (*proc)(FSFile*, FSCommandType) = FSi_SRLArchiveProc;
1470 FSResult (*read)(FSArchive*, void*, u32, u32) = FSi_ReadSRLCallback;
1471 FSResult (*write)(FSArchive*, const void*, u32, u32) = NULL;
1472 FS_SetArchiveProc(arc, proc, FS_ARCHIVE_PROC_WRITEFILE);
1473 // Access change is created in the order of arc -> hash -> files
1474 if (FS_LoadArchive(arc, (u32)hash,
1475 header->ntr.fat.offset, header->ntr.fat.length,
1476 header->ntr.fnt.offset, header->ntr.fnt.length,
1477 read, write))
1478 {
1479 // If it was content that differed from the ROM header of the system work region, the buffer allocated in advance by the CARDi_InitRom function is wasted
1480 //
1481 extern u8 *CARDiHashBufferAddress;
1482 extern u32 CARDiHashBufferLength;
1483 u32 len = CARD_CalcRomHashBufferLength(header);
1484 if (len > CARDiHashBufferLength)
1485 {
1486 u8 *lo = (u8 *)MATH_ROUNDUP((u32)OS_GetMainArenaLo(), 32);
1487 u8 *hi = (u8 *)MATH_ROUNDDOWN((u32)OS_GetMainArenaHi(), 32);
1488 if (&lo[len] > hi)
1489 {
1490 OS_TPanic("cannot allocate memory for ROM-hash from ARENA");
1491 }
1492 else
1493 {
1494 OS_SetMainArenaLo(&lo[len]);
1495 CARDiHashBufferAddress = lo;
1496 CARDiHashBufferLength = len;
1497 }
1498 }
1499 CARD_InitRomHashContext(hash, header,
1500 CARDiHashBufferAddress, CARDiHashBufferLength,
1501 FSi_SeekAndReadSRL, NULL, file);
1502 // Destroy the pointer so that there is no competition for use of the same buffer
1503 CARDiHashBufferAddress = NULL;
1504 CARDiHashBufferLength = 0;
1505 retval = TRUE;
1506 }
1507 }
1508 }
1509 return retval;
1510 }
1511
1512 /*---------------------------------------------------------------------------*
1513 Name: FSi_ReadMemCallback
1514
1515 Description: Default memory read callback
1516
1517 Arguments: p_arc: Archive on which to operate
1518 dst: Storage destination of the memory to read
1519 pos Read position
1520 size Size to read
1521
1522 Returns: Always FS_RESULT_SUCCESS
1523 *---------------------------------------------------------------------------*/
FSi_ReadMemCallback(FSArchive * p_arc,void * dst,u32 pos,u32 size)1524 static FSResult FSi_ReadMemCallback(FSArchive *p_arc, void *dst, u32 pos, u32 size)
1525 {
1526 MI_CpuCopy8((const void *)FS_GetArchiveOffset(p_arc, pos), dst, size);
1527 return FS_RESULT_SUCCESS;
1528 }
1529
1530 /*---------------------------------------------------------------------------*
1531 Name: FSi_WriteMemCallback
1532
1533 Description: Default memory write callback
1534
1535 Arguments: p_arc: Archive on which to operate
1536 dst: Reference destination of the memory to write
1537 pos: Write location
1538 size Size to write
1539
1540 Returns: Always FS_RESULT_SUCCESS
1541 *---------------------------------------------------------------------------*/
FSi_WriteMemCallback(FSArchive * p_arc,const void * src,u32 pos,u32 size)1542 static FSResult FSi_WriteMemCallback(FSArchive *p_arc, const void *src, u32 pos, u32 size)
1543 {
1544 MI_CpuCopy8(src, (void *)FS_GetArchiveOffset(p_arc, pos), size);
1545 return FS_RESULT_SUCCESS;
1546 }
1547
1548 static const FSArchiveInterface FSiArchiveProcInterface =
1549 {
1550 // Commands that are compatible with old specifications
1551 FSi_ROMFAT_ReadFile,
1552 FSi_ROMFAT_WriteFile,
1553 FSi_ROMFAT_SeekDirectory,
1554 FSi_ROMFAT_ReadDirectory,
1555 FSi_ROMFAT_FindPath,
1556 FSi_ROMFAT_GetPath,
1557 FSi_ROMFAT_OpenFileFast,
1558 FSi_ROMFAT_OpenFileDirect,
1559 FSi_ROMFAT_CloseFile,
1560 FSi_ROMFAT_Activate,
1561 FSi_ROMFAT_Idle,
1562 FSi_ROMFAT_Suspend,
1563 FSi_ROMFAT_Resume,
1564 // Items that are compatible with old specifications but were not commands
1565 FSi_ROMFAT_OpenFile,
1566 FSi_ROMFAT_SeekFile,
1567 FSi_ROMFAT_GetFileLength,
1568 FSi_ROMFAT_GetFilePosition,
1569 // Command extended by the new specifications (UNSUPPORTED if NULL)
1570 NULL, // Mount
1571 FSi_ROMFAT_Unmount,
1572 FSi_ROMFAT_GetArchiveCaps,
1573 NULL, // CreateFile
1574 NULL, // DeleteFile
1575 NULL, // RenameFile
1576 FSi_ROMFAT_GetPathInfo,
1577 NULL, // SetFileInfo
1578 NULL, // CreateDirectory
1579 NULL, // DeleteDirectory
1580 NULL, // RenameDirectory
1581 FSi_ROMFAT_GetArchiveResource,
1582 NULL, // 29UL
1583 NULL, // FlushFile
1584 NULL, // SetFileLength
1585 FSi_ROMFAT_OpenDirectory,
1586 FSi_ROMFAT_CloseDirectory,
1587 };
1588
1589 /*---------------------------------------------------------------------------*
1590 Name: FS_LoadArchive
1591
1592 Description: Loads the archive into the file system.
1593 Its name must already be registered on the archive list.
1594
1595 Arguments: arc Archive to load
1596 base Any u32 value that can be uniquely used.
1597 fat Starting offset of the FAT table
1598 fat_size Size of FAT table
1599 fat Starting offset of the FNT table
1600 fat_size Size of FNT table
1601 read_func Read access callback.
1602 write_func Write access callback
1603
1604 Returns: TRUE if archive is loaded correctly
1605 *---------------------------------------------------------------------------*/
FS_LoadArchive(FSArchive * arc,u32 base,u32 fat,u32 fat_size,u32 fnt,u32 fnt_size,FS_ARCHIVE_READ_FUNC read_func,FS_ARCHIVE_WRITE_FUNC write_func)1606 BOOL FS_LoadArchive(FSArchive *arc, u32 base,
1607 u32 fat, u32 fat_size,
1608 u32 fnt, u32 fnt_size,
1609 FS_ARCHIVE_READ_FUNC read_func, FS_ARCHIVE_WRITE_FUNC write_func)
1610 {
1611 BOOL retval = FALSE;
1612 SDK_ASSERT(FS_IsAvailable());
1613 SDK_NULL_ASSERT(arc);
1614 SDK_ASSERT(!FS_IsArchiveLoaded(arc));
1615
1616 // Initialize information for archive
1617 {
1618 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)arc->reserved2;
1619 context->base = base;
1620 context->fat_size = fat_size;
1621 context->fat = fat;
1622 context->fat_bak = fat;
1623 context->fnt_size = fnt_size;
1624 context->fnt = fnt;
1625 context->fnt_bak = fnt;
1626 context->read_func = read_func ? read_func : FSi_ReadMemCallback;
1627 context->write_func = write_func ? write_func : FSi_WriteMemCallback;
1628 context->load_mem = NULL;
1629 return FS_MountArchive(arc, context, &FSiArchiveProcInterface, 0);
1630 }
1631 return retval;
1632 }
1633
1634 /*---------------------------------------------------------------------------*
1635 Name: FS_UnloadArchive
1636
1637 Description: Unload the archive from the file system.
1638 Block until all currently running tasks are completed.
1639
1640 Arguments: arc Archive to unload
1641
1642 Returns: TRUE if archive is unloaded correctly.
1643 *---------------------------------------------------------------------------*/
FS_UnloadArchive(FSArchive * arc)1644 BOOL FS_UnloadArchive(FSArchive *arc)
1645 {
1646 return FS_UnmountArchive(arc);
1647 }
1648
1649 /*---------------------------------------------------------------------------*
1650 Name: FSi_GetFileLengthIfProc
1651
1652 Description: If the specified file is an archive procedure, gets the size.
1653
1654 Arguments: file File handle
1655 length Storage destination of the size
1656
1657 Returns: If the specified file is an archive procedure, returns its size
1658 *---------------------------------------------------------------------------*/
FSi_GetFileLengthIfProc(FSFile * file,u32 * length)1659 BOOL FSi_GetFileLengthIfProc(FSFile *file, u32 *length)
1660 {
1661 return (file->arc->vtbl == &FSiArchiveProcInterface) &&
1662 (FSi_ROMFAT_GetFileLength(file->arc, file, length) == FS_RESULT_SUCCESS);
1663 }
1664
1665 /*---------------------------------------------------------------------------*
1666 Name: FSi_GetFilePositionIfProc
1667
1668 Description: If the specified file is an archive procedure, gets the current position
1669
1670 Arguments: file File handle
1671 length Storage destination of the size
1672
1673 Returns: If the specified file is an archive procedure, returns its current position
1674 *---------------------------------------------------------------------------*/
FSi_GetFilePositionIfProc(FSFile * file,u32 * length)1675 BOOL FSi_GetFilePositionIfProc(FSFile *file, u32 *length)
1676 {
1677 return (file->arc->vtbl == &FSiArchiveProcInterface) &&
1678 (FSi_ROMFAT_GetFilePosition(file->arc, file, length) == FS_RESULT_SUCCESS);
1679 }
1680
1681 /*---------------------------------------------------------------------------*
1682 Name: FS_SetArchiveProc
1683
1684 Description: Sets the archive's user procedure.
1685 If proc == NULL or flags = 0,
1686 Merely invalidates the user procedure.
1687
1688 Arguments: arc Archive for which to set the user procedure.
1689 proc User procedure.
1690 flags Bit collection of commands that hook to procedures.
1691
1692 Returns: Always returns the total table size.
1693 *---------------------------------------------------------------------------*/
FS_SetArchiveProc(FSArchive * arc,FS_ARCHIVE_PROC_FUNC proc,u32 flags)1694 void FS_SetArchiveProc(FSArchive *arc, FS_ARCHIVE_PROC_FUNC proc, u32 flags)
1695 {
1696 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)arc->reserved2;
1697 if (!flags)
1698 {
1699 proc = NULL;
1700 }
1701 else if (!proc)
1702 {
1703 flags = 0;
1704 }
1705 context->proc = proc;
1706 context->proc_flag = flags;
1707 }
1708
1709 /*---------------------------------------------------------------------------*
1710 Name: FS_LoadArchiveTables
1711
1712 Description: Preloads archive FAT + FNT in memory.
1713 Only if within specified size, execute the load and return the required size.
1714
1715 Arguments: p_arc Archive that will preload table.
1716 p_mem Storage destination buffer for table data
1717 max_size p_mem size
1718
1719 Returns: Always returns the total table size.
1720 *---------------------------------------------------------------------------*/
FS_LoadArchiveTables(FSArchive * p_arc,void * p_mem,u32 max_size)1721 u32 FS_LoadArchiveTables(FSArchive *p_arc, void *p_mem, u32 max_size)
1722 {
1723 SDK_ASSERT(FS_IsAvailable());
1724 SDK_NULL_ASSERT(p_arc);
1725
1726 // If this is a table that has already been loaded, unload it
1727 if ((p_mem != NULL) && FS_IsArchiveTableLoaded(p_arc))
1728 {
1729 (void)FS_UnloadArchiveTables(p_arc);
1730 }
1731
1732 {
1733 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(p_arc);
1734 // The preload size is 32-byte aligned.
1735 u32 total_size = (u32)((context->fat_size + context->fnt_size + 32 + 31) & ~31);
1736 if (total_size <= max_size)
1737 {
1738 // If the size is sufficient, load into memory.
1739 u8 *p_cache = (u8 *)(((u32)p_mem + 31) & ~31);
1740 FSFile tmp;
1741 FS_InitFile(&tmp);
1742 // Sometimes the table cannot be read.
1743 // * In that case, nothing is done because the table could not be accessed originally.
1744 if (FS_OpenFileDirect(&tmp, p_arc, context->fat, context->fat + context->fat_size, (u32)~0))
1745 {
1746 if (FS_ReadFile(&tmp, p_cache, (s32)context->fat_size) < 0)
1747 {
1748 MI_CpuFill8(p_cache, 0x00, context->fat_size);
1749 }
1750 (void)FS_CloseFile(&tmp);
1751 }
1752 context->fat = (u32)p_cache;
1753 p_cache += context->fat_size;
1754 if (FS_OpenFileDirect(&tmp, p_arc, context->fnt, context->fnt + context->fnt_size, (u32)~0))
1755 {
1756 if (FS_ReadFile(&tmp, p_cache, (s32)context->fnt_size) < 0)
1757 {
1758 MI_CpuFill8(p_cache, 0x00, context->fnt_size);
1759 }
1760 (void)FS_CloseFile(&tmp);
1761 }
1762 context->fnt = (u32)p_cache;
1763 // Thereafter, preload memory will be used with table reads.
1764 context->load_mem = p_mem;
1765 p_arc->flag |= FS_ARCHIVE_FLAG_TABLE_LOAD;
1766 }
1767 return total_size;
1768 }
1769 }
1770
1771 /*---------------------------------------------------------------------------*
1772 Name: FS_UnloadArchiveTables
1773
1774 Description: Deallocates the archive's preloaded memory.
1775
1776 Arguments: arc Archive with preloaded memory to release.
1777
1778 Returns: Buffer given by the user as preloaded memory.
1779 *---------------------------------------------------------------------------*/
FS_UnloadArchiveTables(FSArchive * arc)1780 void *FS_UnloadArchiveTables(FSArchive *arc)
1781 {
1782 void *retval = NULL;
1783
1784 SDK_ASSERT(FS_IsAvailable());
1785 SDK_NULL_ASSERT(arc);
1786
1787 if (FS_IsArchiveLoaded(arc))
1788 {
1789 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1790 BOOL bak_stat = FS_SuspendArchive(arc);
1791 if (FS_IsArchiveTableLoaded(arc))
1792 {
1793 arc->flag &= ~FS_ARCHIVE_FLAG_TABLE_LOAD;
1794 retval = context->load_mem;
1795 context->fat = context->fat_bak;
1796 context->fnt = context->fnt_bak;
1797 context->load_mem = NULL;
1798 }
1799 if (bak_stat)
1800 {
1801 (void)FS_ResumeArchive(arc);
1802 }
1803 }
1804 return retval;
1805 }
1806
1807 /*---------------------------------------------------------------------------*
1808 Name: FS_GetArchiveBase
1809
1810 Description: Gets ROMFAT-type archive base offset
1811
1812 Arguments: arc ROMFAT-type archive
1813
1814 Returns: ROMFAT-type archive base offset
1815 *---------------------------------------------------------------------------*/
FS_GetArchiveBase(const struct FSArchive * arc)1816 u32 FS_GetArchiveBase(const struct FSArchive *arc)
1817 {
1818 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1819 return context->base;
1820 }
1821
1822 /*---------------------------------------------------------------------------*
1823 Name: FS_GetArchiveFAT
1824
1825 Description: Gets ROMFAT-type archive FAT offset
1826
1827 Arguments: arc ROMFAT-type archive
1828
1829 Returns: ROMFAT-type archive FAT offset
1830 *---------------------------------------------------------------------------*/
FS_GetArchiveFAT(const struct FSArchive * arc)1831 u32 FS_GetArchiveFAT(const struct FSArchive *arc)
1832 {
1833 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1834 return context->fat;
1835 }
1836
1837 /*---------------------------------------------------------------------------*
1838 Name: FS_GetArchiveFNT
1839
1840 Description: Gets ROMFAT-type archive FNT offset
1841
1842 Arguments: arc ROMFAT-type archive
1843
1844 Returns: ROMFAT-type archive FNT offset
1845 *---------------------------------------------------------------------------*/
FS_GetArchiveFNT(const struct FSArchive * arc)1846 u32 FS_GetArchiveFNT(const struct FSArchive *arc)
1847 {
1848 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1849 return context->fnt;
1850 }
1851
1852 /* Obtain specified position offset from the archive's base */
1853 /*---------------------------------------------------------------------------*
1854 Name: FS_GetArchiveOffset
1855
1856 Description: Gets specified position offset from the ROMFAT-type archive's base
1857
1858 Arguments: arc ROMFAT-type archive
1859
1860 Returns: Specified position offset with the archive base added
1861 *---------------------------------------------------------------------------*/
FS_GetArchiveOffset(const struct FSArchive * arc,u32 pos)1862 u32 FS_GetArchiveOffset(const struct FSArchive *arc, u32 pos)
1863 {
1864 FSROMFATArchiveContext *context = (FSROMFATArchiveContext*)FS_GetArchiveUserData(arc);
1865 return context->base + pos;
1866 }
1867
1868 /*---------------------------------------------------------------------------*
1869 Name: FS_IsArchiveTableLoaded
1870
1871 Description: Determines whether the ROMFAT-type archive has finished preloading the table.
1872
1873 Arguments: arc ROMFAT-type archive
1874
1875 Returns: TRUE if the table has been preloaded
1876 *---------------------------------------------------------------------------*/
FS_IsArchiveTableLoaded(volatile const struct FSArchive * arc)1877 BOOL FS_IsArchiveTableLoaded(volatile const struct FSArchive *arc)
1878 {
1879 return ((arc->flag & FS_ARCHIVE_FLAG_TABLE_LOAD) != 0);
1880 }
1881
1882 /*---------------------------------------------------------------------------*
1883 Name: FS_GetFileImageTop
1884
1885 Description: Gets the top offset of the file opened from the ROMFAT-type archive
1886
1887
1888 Arguments: file File handle
1889
1890 Returns: File top offset on the archive
1891 *---------------------------------------------------------------------------*/
FS_GetFileImageTop(const struct FSFile * file)1892 u32 FS_GetFileImageTop(const struct FSFile *file)
1893 {
1894 return file->prop.file.top;
1895 }
1896
1897 /*---------------------------------------------------------------------------*
1898 Name: FS_GetFileImageBottom
1899
1900 Description: Gets the bottom offset of the file opened from the ROMFAT-type archive
1901
1902
1903 Arguments: file File handle
1904
1905 Returns: File bottom offset on the archive
1906 *---------------------------------------------------------------------------*/
FS_GetFileImageBottom(const struct FSFile * file)1907 u32 FS_GetFileImageBottom(const struct FSFile *file)
1908 {
1909 return file->prop.file.bottom;
1910 }
1911
1912
1913 #endif /* FS_IMPLEMENT */
1914