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