1#include <basis/seadNew.h>
2#include <basis/seadRawPrint.h>
3#include <filedevice/seadFileDevice.h>
4#include <filedevice/seadFileDeviceMgr.h>
5#include <filedevice/seadPath.h>
6#include <heap/seadHeapMgr.h>
7#include <math/seadMathCalcCommon.h>
8
9namespace sead
10{
11bool FileHandle::close()
12{
13 if (!mOriginalDevice)
14 {
15 SEAD_ASSERT_MSG(false, "handle not opened");
16 return false;
17 }
18 return mOriginalDevice->close(handle: this);
19}
20
21bool FileHandle::tryClose()
22{
23 if (!mOriginalDevice)
24 {
25 SEAD_ASSERT_MSG(false, "handle not opened");
26 return false;
27 }
28 return mOriginalDevice->tryClose(handle: this);
29}
30
31bool FileHandle::flush()
32{
33 if (!mOriginalDevice)
34 {
35 SEAD_ASSERT_MSG(false, "handle not opened");
36 return false;
37 }
38 return mOriginalDevice->flush(handle: this);
39}
40
41bool FileHandle::tryFlush()
42{
43 if (!mOriginalDevice)
44 {
45 SEAD_ASSERT_MSG(false, "handle not opened");
46 return false;
47 }
48 return mOriginalDevice->tryFlush(handle: this);
49}
50
51u32 FileHandle::read(u8* outBuffer, u32 bytesToRead)
52{
53 if (!mDevice)
54 {
55 SEAD_ASSERT_MSG(false, "handle not opened");
56 return 0;
57 }
58 return mDevice->read(handle: this, data: outBuffer, size: bytesToRead);
59}
60
61bool FileHandle::tryRead(u32* actual_size, u8* data, u32 size)
62{
63 if (!mDevice)
64 {
65 SEAD_ASSERT_MSG(false, "handle not opened");
66 return false;
67 }
68 return mDevice->tryRead(bytesRead: actual_size, handle: this, outBuffer: data, bytesToRead: size);
69}
70
71u32 FileHandle::write(const u8* data, u32 size)
72{
73 if (!mDevice)
74 {
75 SEAD_ASSERT_MSG(false, "handle not opened");
76 return 0;
77 }
78 return mDevice->write(handle: this, data, size);
79}
80
81bool FileHandle::tryWrite(u32* actual_size, const u8* data, u32 size)
82{
83 if (!mDevice)
84 {
85 SEAD_ASSERT_MSG(false, "handle not opened");
86 return false;
87 }
88 return mDevice->tryWrite(bytesWritten: actual_size, handle: this, inBuffer: data, bytesToWrite: size);
89}
90
91bool FileHandle::seek(s32 offset, FileDevice::SeekOrigin origin)
92{
93 if (!mDevice)
94 {
95 SEAD_ASSERT_MSG(false, "handle not opened");
96 return false;
97 }
98 return mDevice->seek(handle: this, offset, origin);
99}
100
101bool FileHandle::trySeek(s32 offset, FileDevice::SeekOrigin origin)
102{
103 if (!mDevice)
104 {
105 SEAD_ASSERT_MSG(false, "handle not opened");
106 return false;
107 }
108 return mDevice->trySeek(handle: this, offset, origin);
109}
110
111u32 FileHandle::getCurrentSeekPos()
112{
113 if (!mDevice)
114 {
115 SEAD_ASSERT_MSG(false, "handle not opened");
116 return 0;
117 }
118 return mDevice->getCurrentSeekPos(handle: this);
119}
120
121bool FileHandle::tryGetCurrentSeekPos(u32* pos)
122{
123 if (!mDevice)
124 {
125 SEAD_ASSERT_MSG(false, "handle not opened");
126 return false;
127 }
128 return mDevice->tryGetCurrentSeekPos(seekPos: pos, handle: this);
129}
130
131u32 FileHandle::getFileSize()
132{
133 if (!mDevice)
134 {
135 SEAD_ASSERT_MSG(false, "handle not opened");
136 return 0;
137 }
138 return mDevice->getFileSize(handle: this);
139}
140
141bool FileHandle::tryGetFileSize(u32* size)
142{
143 if (!mDevice)
144 {
145 SEAD_ASSERT_MSG(false, "handle not opened");
146 return false;
147 }
148 return mDevice->tryGetFileSize(size, handle: this);
149}
150
151bool DirectoryHandle::close()
152{
153 if (!mOriginalDevice)
154 {
155 SEAD_ASSERT_MSG(false, "handle not opened");
156 return false;
157 }
158 return mOriginalDevice->closeDirectory(handle: this);
159}
160
161bool DirectoryHandle::tryClose()
162{
163 if (!mOriginalDevice)
164 {
165 SEAD_ASSERT_MSG(false, "handle not opened");
166 return false;
167 }
168 return mOriginalDevice->tryCloseDirectory(handle: this);
169}
170
171u32 DirectoryHandle::read(DirectoryEntry* entries, u32 count)
172{
173 if (!mDevice)
174 {
175 SEAD_ASSERT_MSG(false, "handle not opened");
176 return false;
177 }
178 return mDevice->readDirectory(handle: this, entries, num_entries: count);
179}
180
181bool DirectoryHandle::tryRead(u32* actual_count, DirectoryEntry* entries, u32 count)
182{
183 if (!mDevice)
184 {
185 SEAD_ASSERT_MSG(false, "handle not opened");
186 return false;
187 }
188 return mDevice->tryReadDirectory(entriesRead: actual_count, handle: this, entries, entriesToRead: count);
189}
190
191FileDevice::~FileDevice()
192{
193 if (FileDeviceMgr::instance() != NULL)
194 FileDeviceMgr::instance()->unmount(device: this);
195}
196
197void FileDevice::traceFilePath(const SafeString& path) const
198{
199 doTracePath_(path);
200}
201
202void FileDevice::traceDirectoryPath(const SafeString& path) const
203{
204 doTracePath_(path);
205}
206
207void FileDevice::resolveFilePath(BufferedSafeString* out, const SafeString& path) const
208{
209 doResolvePath_(out, path);
210}
211
212void FileDevice::resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const
213{
214 doResolvePath_(out, path);
215}
216
217bool FileDevice::isMatchDevice_(const HandleBase* handle) const
218{
219 return handle->mDevice == this;
220}
221
222u8* FileDevice::doLoad_(LoadArg& arg)
223{
224 if (arg.buffer && arg.buffer_size == 0)
225 {
226 SEAD_WARN("arg.buffer is specified, but arg.buffer_size is zero");
227 return nullptr;
228 }
229
230 if (arg.buffer_size_alignment % cBufferMinAlignment != 0)
231 {
232 SEAD_WARN(
233 "arg.buffer_size_alignment[%u] is not multipe of FileDevice::cBufferMinAlignment[%u]",
234 arg.buffer_size_alignment, cBufferMinAlignment);
235 return nullptr;
236 }
237
238 FileHandle handle;
239 if (!tryOpen(handle: &handle, path: arg.path, flag: FileDevice::cFileOpenFlag_ReadOnly, divSize: arg.div_size))
240 return nullptr;
241
242 u32 bytesToRead = arg.buffer_size;
243 if (!arg.buffer || arg.check_read_entire_file)
244 {
245 u32 fileSize = 0;
246 if (!tryGetFileSize(size: &fileSize, handle: &handle))
247 return nullptr;
248
249 if (fileSize == 0)
250 {
251 SEAD_WARN("file_size is zero.[%s]", arg.path.cstr());
252 return nullptr;
253 }
254
255 if (bytesToRead != 0)
256 {
257 if (bytesToRead < fileSize)
258 {
259 SEAD_WARN("arg.buffer_size[%u] is smaller than file size[%u]", bytesToRead,
260 fileSize);
261 return nullptr;
262 }
263
264 if (arg.buffer_size_alignment && bytesToRead % arg.buffer_size_alignment != 0)
265 {
266 SEAD_WARN("arg.buffer_size[%u] is not multipe of arg.buffer_size_alignment[%u]",
267 bytesToRead, arg.buffer_size_alignment);
268 return nullptr;
269 }
270 }
271 else
272 {
273 if (arg.buffer_size_alignment)
274 {
275 bytesToRead = Mathu::roundUp(x: fileSize, multNumber: arg.buffer_size_alignment);
276 }
277 else
278 {
279 bytesToRead = Mathi::roundUpPow2(val: fileSize, base: FileDevice::cBufferMinAlignment);
280 }
281 }
282 }
283
284 u8* buf = arg.buffer;
285 bool allocated = false;
286
287 if (buf == nullptr)
288 {
289 const s32 sign = (arg.alignment < 0) ? -1 : 1;
290 s32 alignment = Mathi::abs(x: arg.alignment);
291 alignment = sign * ((alignment < cBufferMinAlignment) ? cBufferMinAlignment : alignment);
292
293 Heap* heap = arg.heap;
294 if (!heap)
295 heap = HeapMgr::instance()->getCurrentHeap();
296
297 void* raw_buf = heap->tryAlloc(size: bytesToRead, alignment);
298 if (!raw_buf)
299 {
300 if (arg.assert_on_alloc_fail)
301 {
302 SEAD_ASSERT_MSG(false, "alloc size[%u] failed in heap[%s] for file[%s]",
303 bytesToRead, heap->getName().cstr(), arg.path.cstr());
304 }
305 return nullptr;
306 }
307 buf = new (raw_buf) u8[bytesToRead];
308 allocated = true;
309 }
310
311 u32 bytesRead = 0;
312 if (!tryRead(bytesRead: &bytesRead, handle: &handle, outBuffer: buf, bytesToRead))
313 {
314 if (allocated)
315 delete[] buf;
316 return nullptr;
317 }
318
319 if (!tryClose(handle: &handle))
320 {
321 if (allocated)
322 delete[] buf;
323 return nullptr;
324 }
325
326 arg.read_size = bytesRead;
327 arg.roundup_size = bytesToRead;
328 arg.need_unload = allocated;
329
330 return buf;
331}
332
333bool FileDevice::doSave_(FileDevice::SaveArg& arg)
334{
335 if (!arg.buffer)
336 {
337 SEAD_ASSERT_MSG(false, "arg.buffer must be set for save file[%s]", arg.path.cstr());
338 return false;
339 }
340
341 FileHandle handle;
342 if (!tryOpen(handle: &handle, path: arg.path, flag: cFileOpenFlag_WriteOnly))
343 return false;
344
345 const bool ret =
346 arg.buffer_size == 0 || tryWrite(bytesWritten: &arg.write_size, handle: &handle, inBuffer: arg.buffer, bytesToWrite: arg.buffer_size);
347
348 if (!tryClose(handle: &handle))
349 return false;
350
351 return ret;
352}
353
354void FileDevice::doTracePath_(const SafeString& path) const
355{
356 SEAD_DEBUG_PRINT("[%s] %s\n", mDriveName.cstr(), path.cstr());
357 FixedSafeString<512> out;
358 doResolvePath_(out: &out, path);
359 SEAD_DEBUG_PRINT(" -> %s\n", out.cstr());
360}
361
362void FileDevice::doResolvePath_(BufferedSafeString* out, const SafeString& path) const
363{
364 out->copy(src: path);
365}
366
367bool FileDevice::isAvailable() const
368{
369 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
370 if (!mPermission)
371 return false;
372
373 return doIsAvailable_();
374}
375
376u8* FileDevice::tryLoad(LoadArg& arg)
377{
378 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
379 if (!mPermission)
380 return NULL;
381
382 return doLoad_(arg);
383}
384
385bool FileDevice::trySave(FileDevice::SaveArg& arg)
386{
387 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
388 if (!mPermission)
389 return false;
390
391 return doSave_(arg);
392}
393
394FileDevice* FileDevice::tryOpen(FileHandle* handle, const SafeString& path, FileOpenFlag flag,
395 u32 divSize)
396{
397 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
398 if (!mPermission)
399 return nullptr;
400
401 if (handle == nullptr)
402 {
403 SEAD_ASSERT_MSG(false, "handle is null");
404 return nullptr;
405 }
406
407 setFileHandleDivSize_(handle, divSize);
408 FileDevice* device = doOpen_(handle, path, flag);
409 setHandleBaseFileDevice_(handle, device);
410 if (device)
411 setHandleBaseOriginalFileDevice_(handle, device: this);
412
413 return device;
414}
415
416bool FileDevice::tryClose(FileHandle* handle)
417{
418 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
419 if (!mPermission)
420 return false;
421
422 if (handle == nullptr)
423 {
424 SEAD_ASSERT_MSG(false, "handle is null");
425 return false;
426 }
427
428 if (!isMatchDevice_(handle))
429 {
430 SEAD_ASSERT_MSG(false, "handle device miss match");
431 return false;
432 }
433
434 bool closed = doClose_(handle);
435 if (closed)
436 {
437 setHandleBaseFileDevice_(handle, device: nullptr);
438 setHandleBaseOriginalFileDevice_(handle, device: nullptr);
439 }
440
441 return closed;
442}
443
444bool FileDevice::tryFlush(FileHandle* handle)
445{
446 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
447 if (!mPermission)
448 return false;
449
450 if (!handle)
451 {
452 SEAD_ASSERT_MSG(false, "handle is null");
453 return false;
454 }
455
456 if (!isMatchDevice_(handle))
457 {
458 SEAD_ASSERT_MSG(false, "handle device miss match");
459 return false;
460 }
461
462 return doFlush_(handle);
463}
464
465bool FileDevice::tryRemove(const SafeString& str)
466{
467 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
468 if (!mPermission)
469 return false;
470
471 return doRemove_(str);
472}
473
474bool FileDevice::tryRead(u32* bytesRead, FileHandle* handle, u8* outBuffer, u32 bytesToRead)
475{
476 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
477 if (!mPermission)
478 return false;
479
480 if (handle == nullptr)
481 {
482 SEAD_ASSERT_MSG(false, "handle is null");
483 return false;
484 }
485
486 if (!isMatchDevice_(handle))
487 {
488 SEAD_ASSERT_MSG(false, "handle device miss match");
489 return false;
490 }
491
492 if (outBuffer == nullptr)
493 {
494 SEAD_ASSERT_MSG(false, "buf is null");
495 return false;
496 }
497
498 if (handle->mDivSize == 0)
499 {
500 const bool ret = doRead_(bytesRead, handle, outBuffer, bytesToRead);
501 SEAD_ASSERT_MSG(!bytesRead || *bytesRead <= bytesToRead, "buffer overflow");
502 return ret;
503 }
504
505 u32 totalReadSize = 0;
506
507 do
508 {
509 u32 size =
510 (static_cast<s32>(bytesToRead) < handle->mDivSize) ? bytesToRead : handle->mDivSize;
511 u32 readSize = 0;
512
513 if (!doRead_(bytesRead: &readSize, handle, outBuffer, bytesToRead: size))
514 {
515 if (bytesRead != NULL)
516 *bytesRead = totalReadSize;
517
518 return false;
519 }
520
521 totalReadSize += readSize;
522 if (readSize < size)
523 break;
524
525 outBuffer += readSize;
526 bytesToRead -= size;
527 } while (bytesToRead != 0);
528
529 if (bytesRead != NULL)
530 *bytesRead = totalReadSize;
531
532 return true;
533}
534
535bool FileDevice::tryWrite(u32* bytesWritten, FileHandle* handle, const u8* inBuffer,
536 u32 bytesToWrite)
537{
538 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
539 if (!mPermission)
540 return false;
541
542 if (handle == nullptr)
543 {
544 SEAD_ASSERT_MSG(false, "handle is null");
545 return false;
546 }
547
548 if (inBuffer == nullptr)
549 {
550 SEAD_ASSERT_MSG(false, "buf is null");
551 return false;
552 }
553
554 if (!isMatchDevice_(handle))
555 {
556 SEAD_ASSERT_MSG(false, "handle device miss match");
557 return false;
558 }
559
560 return doWrite_(bytesWritten, handle, inBuffer, bytesToWrite);
561}
562
563bool FileDevice::trySeek(FileHandle* handle, s32 offset, FileDevice::SeekOrigin origin)
564{
565 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
566 if (!mPermission)
567 return false;
568
569 if (handle == nullptr)
570 {
571 SEAD_ASSERT_MSG(false, "handle is null");
572 return false;
573 }
574
575 if (!isMatchDevice_(handle))
576 {
577 SEAD_ASSERT_MSG(false, "handle device miss match");
578 return false;
579 }
580
581 return doSeek_(handle, offset, origin);
582}
583
584bool FileDevice::tryGetCurrentSeekPos(u32* seekPos, FileHandle* handle)
585{
586 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
587 if (!mPermission)
588 return false;
589
590 if (handle == NULL)
591 {
592 SEAD_ASSERT_MSG(false, "handle is null");
593 return false;
594 }
595
596 if (!isMatchDevice_(handle))
597 {
598 SEAD_ASSERT_MSG(false, "handle device miss match");
599 return false;
600 }
601
602 if (seekPos == NULL)
603 {
604 SEAD_ASSERT_MSG(false, "pos is null");
605 return false;
606 }
607
608 return doGetCurrentSeekPos_(seekPos, handle);
609}
610
611bool FileDevice::tryGetFileSize(u32* fileSize, const SafeString& path)
612{
613 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
614 if (!mPermission)
615 return false;
616
617 if (fileSize == NULL)
618 {
619 SEAD_ASSERT_MSG(false, "size is null");
620 return false;
621 }
622
623 return doGetFileSize_(fileSize, path);
624}
625
626bool FileDevice::tryGetFileSize(u32* size, FileHandle* handle)
627{
628 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
629 if (!mPermission)
630 return false;
631
632 if (handle == nullptr)
633 {
634 SEAD_ASSERT_MSG(false, "handle is null");
635 return false;
636 }
637
638 if (size == nullptr)
639 {
640 SEAD_ASSERT_MSG(false, "size is null");
641 return false;
642 }
643
644 return doGetFileSize_(fileSize: size, handle);
645}
646
647bool FileDevice::tryIsExistFile(bool* exists, const SafeString& path)
648{
649 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
650 if (!mPermission)
651 return false;
652
653 if (exists == NULL)
654 {
655 SEAD_ASSERT_MSG(false, "is_exist is null");
656 return false;
657 }
658
659 return doIsExistFile_(exists, path);
660}
661
662bool FileDevice::tryIsExistDirectory(bool* exists, const SafeString& path)
663{
664 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
665 if (!mPermission)
666 return false;
667
668 if (exists == NULL)
669 {
670 SEAD_ASSERT_MSG(false, "is_exist is null");
671 return false;
672 }
673
674 return doIsExistDirectory_(exists, path);
675}
676
677FileDevice* FileDevice::tryOpenDirectory(DirectoryHandle* handle, const SafeString& path)
678{
679 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
680 if (!mPermission)
681 return NULL;
682
683 if (handle == NULL)
684 {
685 SEAD_ASSERT_MSG(false, "handle is null");
686 return NULL;
687 }
688
689 FileDevice* device = doOpenDirectory_(handle, path);
690 setHandleBaseFileDevice_(handle, device);
691 if (device != NULL)
692 setHandleBaseOriginalFileDevice_(handle, device: this);
693
694 return device;
695}
696
697bool FileDevice::tryCloseDirectory(DirectoryHandle* handle)
698{
699 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
700 if (!mPermission)
701 return false;
702
703 if (handle == NULL)
704 {
705 SEAD_ASSERT_MSG(false, "handle is null");
706 return false;
707 }
708
709 if (!isMatchDevice_(handle))
710 {
711 SEAD_ASSERT_MSG(false, "handle device miss match");
712 return false;
713 }
714
715 bool closed = doCloseDirectory_(handle);
716 if (closed)
717 {
718 setHandleBaseFileDevice_(handle, NULL);
719 setHandleBaseOriginalFileDevice_(handle, NULL);
720 }
721
722 return closed;
723}
724
725bool FileDevice::tryReadDirectory(u32* entriesRead, DirectoryHandle* handle,
726 DirectoryEntry* entries, u32 entriesToRead)
727{
728 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
729 if (!mPermission)
730 return false;
731
732 if (handle == NULL)
733 {
734 SEAD_ASSERT_MSG(false, "handle is null");
735 return false;
736 }
737
738 if (!isMatchDevice_(handle))
739 {
740 SEAD_ASSERT_MSG(false, "handle device miss match");
741 return false;
742 }
743
744 u32 readCount = 0;
745 bool success = doReadDirectory_(entriesRead: &readCount, handle, entries, entriesToRead);
746
747 if (entriesRead != NULL)
748 *entriesRead = readCount;
749
750 if (readCount > entriesToRead)
751 {
752 SEAD_ASSERT_MSG(false, "buffer overflow");
753 return false;
754 }
755
756 return success;
757}
758
759bool FileDevice::tryMakeDirectory(const SafeString& path, u32 _)
760{
761 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
762 if (!mPermission)
763 return false;
764
765 return doMakeDirectory_(path, _);
766}
767
768bool FileDevice::tryMakeDirectoryWithParent(const SafeString& path, u32 x)
769{
770 SEAD_ASSERT_MSG(mPermission, "Device permission error.");
771 if (!mPermission)
772 return false;
773
774 bool exists = false;
775 if (!doIsExistDirectory_(exists: &exists, path))
776 return false;
777
778 if (exists)
779 return true;
780
781 FixedSafeString<512> dir_name;
782 int num_existing_parents = 1;
783 bool should_trim = true;
784 bool reached_end = !Path::getDirectoryName(name: &dir_name, path);
785 while (!reached_end)
786 {
787 exists = false;
788 if (!tryIsExistDirectory(exists: &exists, path: dir_name))
789 return false;
790
791 if (exists)
792 {
793 should_trim = false;
794 break;
795 }
796
797 reached_end = !Path::getDirectoryName(name: &dir_name, path: dir_name);
798 ++num_existing_parents;
799 }
800 if (should_trim)
801 dir_name.trim(trim_length: 0);
802
803 int num_path_components = 0;
804 auto counting_iterator = path.tokenBegin(delimiter: "/");
805 const auto end = path.tokenEnd(delimiter: "/");
806 for (; end != counting_iterator; ++counting_iterator)
807 ++num_path_components;
808
809 auto it = path.tokenBegin(delimiter: "/");
810 int num_levels_to_create = num_path_components - num_existing_parents;
811 for (; end != it; ++it)
812 {
813 if (num_levels_to_create >= 1)
814 {
815 --num_levels_to_create;
816 continue;
817 }
818
819 FixedSafeString<128> component;
820 it.get(out: &component);
821
822 if (dir_name != "")
823 dir_name.append(str: "/");
824
825 dir_name.append(str: component);
826
827 if (!tryMakeDirectory(path: dir_name, x))
828 return false;
829 }
830
831 return true;
832}
833
834s32 FileDevice::getLastRawError() const
835{
836 return doGetLastRawError_();
837}
838
839HandleBuffer& FileDevice::getHandleBaseHandleBuffer_(HandleBase* handle) const
840{
841 return handle->mHandleBuffer;
842}
843
844void FileDevice::setFileHandleDivSize_(FileHandle* handle, u32 divSize) const
845{
846 handle->mDivSize = divSize;
847}
848
849void FileDevice::setHandleBaseFileDevice_(HandleBase* handle, FileDevice* device) const
850{
851 handle->mDevice = device;
852}
853
854void FileDevice::setHandleBaseOriginalFileDevice_(HandleBase* handle, FileDevice* device) const
855{
856 handle->mOriginalDevice = device;
857}
858
859} // namespace sead
860