1#include "Project/File/FileLoader.h"
2
3#include <filedevice/seadFileDeviceMgr.h>
4#include <resource/seadArchiveRes.h>
5#include <thread/seadDelegateThread.h>
6
7#include "Library/Base/StringUtil.h"
8#include "Library/File/FileUtil.h"
9#include "Library/Yaml/ByamlIter.h"
10#include "Project/ArchiveEntry.h"
11#include "Project/ArchiveHolder.h"
12#include "Project/File/FileLoaderThread.h"
13#include "Project/File/SoundItemEntry.h"
14#include "Project/File/SoundItemHolder.h"
15
16namespace al {
17static sead::FixedSafeString<256> sDeviceName{"main"};
18
19FileLoader::FileLoader(s32 threadPriority) {
20 mLoaderThread = new FileLoaderThread(threadPriority);
21 mArchiveHolder = new ArchiveHolder();
22 mSoundItemHolder = new SoundItemHolder();
23 mFileDevice = sead::FileDeviceMgr::instance()->findDevice(name: sDeviceName);
24}
25
26u32 getFileList(sead::FixedSafeString<256> out[], s32 outSize, const char* path,
27 bool isPathValid(const sead::DirectoryEntry&, const char*), const char* suffix) {
28 u32 entriesRead;
29 sead::FileDevice* mainFileDevice = sead::FileDeviceMgr::instance()->getMainFileDevice();
30 sead::DirectoryHandle handle;
31 sead::FileDevice* fileDevice = mainFileDevice->tryOpenDirectory(handle: &handle, path);
32
33 if (!fileDevice)
34 return 0;
35
36 u32 entryCount = 0;
37 sead::DirectoryEntry entry;
38 for (s32 i = 0; i < outSize; i++) {
39 entriesRead = 0;
40 fileDevice->tryReadDirectory(entriesRead: &entriesRead, handle: &handle, entries: &entry, entriesToRead: 1);
41 if (entriesRead == 0)
42 break;
43 if (isPathValid(entry, suffix)) {
44 out[entryCount] = entry.name;
45 entryCount++;
46 }
47 }
48
49 fileDevice->tryCloseDirectory(handle: &handle);
50 return entryCount;
51}
52
53static bool isDirectoryValid(const sead::DirectoryEntry& entry, const char* suffix) {
54 return entry.is_directory;
55}
56
57u32 FileLoader::listSubdirectories(sead::FixedSafeString<256> out[], s32 outSize,
58 const char* path) {
59 return getFileList(out, outSize, path, isPathValid: &isDirectoryValid, suffix: nullptr);
60}
61
62static bool isFileValid(const sead::DirectoryEntry& entry, const char* suffix) {
63 if (!suffix)
64 return !entry.is_directory;
65
66 s32 suffixLen = strlen(suffix);
67 if (entry.name.getBufferSize() < suffixLen)
68 return false;
69
70 const char* pathStr = entry.name.cstr();
71 const char* pathSuffixStart = strrchr(s: pathStr, c: '.');
72 const char* pathSuffix = "";
73 if (pathSuffixStart && pathSuffixStart != pathStr)
74 pathSuffix = pathSuffixStart + 1;
75
76 return strcasecmp(pathSuffix, suffix) == 0;
77}
78
79u32 FileLoader::listFiles(sead::FixedSafeString<256> out[], s32 outSize, const char* path,
80 const char* suffix) {
81 return getFileList(out, outSize, path, isPathValid: &isFileValid, suffix);
82}
83
84bool FileLoader::isExistFile(const sead::SafeString& path, sead::FileDevice* device) const {
85 sead::FileDevice* fileDevice = getFileDevice(path, device);
86
87 bool isExist = false;
88 fileDevice->tryIsExistFile(exists: &isExist, path);
89 return isExist;
90}
91
92sead::FileDevice* FileLoader::getFileDevice(const sead::SafeString& path,
93 sead::FileDevice* device) const {
94 return device ?: mFileDevice;
95}
96
97bool FileLoader::isExistArchive(const sead::SafeString& path, sead::FileDevice* device) const {
98 return isExistFile(path, device);
99}
100
101bool FileLoader::isExistDirectory(const sead::SafeString& path, sead::FileDevice* device) const {
102 sead::FileDevice* fileDevice = getFileDevice(path, device);
103
104 bool isExist = false;
105 fileDevice->tryIsExistDirectory(exists: &isExist, path);
106 return isExist;
107}
108
109u32 FileLoader::getFileSize(const sead::SafeString& path, sead::FileDevice* device) const {
110 sead::FileDevice* fileDevice = getFileDevice(path, device);
111
112 u32 fileSize = 0;
113 fileDevice->tryGetFileSize(fileSize: &fileSize, path);
114 return fileSize;
115}
116
117u8* FileLoader::loadFile(const sead::SafeString& path, s32 alignment, sead::FileDevice* device) {
118 sead::FileDevice::LoadArg loadArg;
119 loadArg.alignment = alignment;
120 loadArg.path = path;
121
122 return getFileDevice(path, device)->tryLoad(arg&: loadArg);
123}
124
125bool FileLoader::tryLoadFileToBuffer(const sead::SafeString& path, u8* buffer, u32 bufferSize,
126 s32 alignment, sead::FileDevice* device) {
127 sead::FileDevice::LoadArg loadArg;
128 loadArg.path = path;
129 loadArg.buffer = buffer;
130 loadArg.buffer_size = bufferSize;
131 loadArg.buffer_size_alignment = alignment;
132
133 return getFileDevice(path, device)->tryLoad(arg&: loadArg) != nullptr;
134}
135
136sead::ArchiveRes* FileLoader::loadArchive(const sead::SafeString& path, sead::FileDevice* device) {
137 return loadArchiveLocal(path, suffix: nullptr, device);
138}
139
140sead::ArchiveRes* FileLoader::loadArchiveLocal(const sead::SafeString& path, const char* suffix,
141 sead::FileDevice* device) {
142 ArchiveEntry* entry = mArchiveHolder->tryFindEntry(fileName: path);
143 if (entry) {
144 if (entry->getFileState() != FileState::IsLoadDone)
145 entry->waitLoadDone();
146 } else if (!sead::ThreadMgr::instance()->isMainThread()) {
147 sead::Heap* heap = sead::HeapMgr::instance()->getCurrentHeap();
148 entry = requestLoadArchive(path, heap, device: getFileDevice(path, device));
149 entry->waitLoadDone();
150 } else {
151 sead::ResourceMgr::LoadArg loadArg;
152 loadArg.device = getFileDevice(path, device);
153 loadArg.path = path;
154 loadArg.load_data_alignment = calcFileAlignment(fileName: path);
155 loadArg.load_data_buffer_alignment = calcBufferSizeAlignment(fileName: path);
156 sead::Resource* resource =
157 sead::ResourceMgr::instance()->tryLoad(arg: loadArg, factory_name: suffix ? suffix : "sarc", decompressor: nullptr);
158 return sead::DynamicCast<sead::ArchiveRes>(obj: resource);
159 }
160
161 return entry->getArchiveRes();
162}
163
164sead::ArchiveRes* FileLoader::loadArchiveWithExt(const sead::SafeString& path, const char* suffix,
165 sead::FileDevice* device) {
166 return loadArchiveLocal(path, suffix, device);
167}
168
169bool FileLoader::tryRequestLoadArchive(const sead::SafeString& path, sead::Heap* heap,
170 sead::FileDevice* device) {
171 if (mArchiveHolder->tryFindEntry(fileName: path))
172 return false;
173 requestLoadArchive(path, heap, device);
174 return true;
175}
176
177ArchiveEntry* FileLoader::requestLoadArchive(const sead::SafeString& path, sead::Heap* heap,
178 sead::FileDevice* device) {
179 ArchiveEntry* entry =
180 mArchiveHolder->addNewLoadRequestEntry(fileName: path, heap, fileDevice: getFileDevice(path, device));
181 mLoaderThread->requestLoadFile(fileEntry: entry);
182 return entry;
183}
184
185bool FileLoader::loadSoundItem(u32 itemId, u32 unknown, IAudioResourceLoader* loader) {
186 SoundItemEntry* entry = mSoundItemHolder->tryFindEntry(itemId, resourceLoader: loader);
187 if (!entry) {
188 entry = requestLoadSoundItem(itemId, unknown, loader);
189 entry->waitLoadDone();
190 } else if (entry->getFileState() != FileState::IsLoadDone) {
191 entry->waitLoadDone();
192 }
193
194 return entry->isLoadSuccess();
195}
196
197SoundItemEntry* FileLoader::requestLoadSoundItem(u32 itemId, u32 unknown,
198 IAudioResourceLoader* loader) {
199 SoundItemEntry* entry = mSoundItemHolder->addNewLoadRequestEntry(itemId, unknown, resourceLoader: loader);
200 mLoaderThread->requestLoadFile(fileEntry: entry);
201 return entry;
202}
203
204bool FileLoader::tryRequestLoadSoundItem(u32 itemId, IAudioResourceLoader* loader) {
205 if (mSoundItemHolder->tryFindEntry(itemId, resourceLoader: loader))
206 return false;
207
208 requestLoadSoundItem(itemId, unknown: -1, loader);
209 return true;
210}
211
212void FileLoader::requestPreLoadFile(const ByamlIter& preLoadList, sead::Heap* heap,
213 IAudioResourceLoader* loader) {
214 if (mIsFilePreloaded)
215 return;
216
217 for (s32 i = 0; i < preLoadList.getSize(); i++) {
218 ByamlIter iter;
219 preLoadList.tryGetIterByIndex(iter: &iter, index: i);
220
221 const char* type;
222 iter.tryGetStringByKey(string: &type, key: "Type");
223
224 if (!isEqualString(str1: type, str2: "SoundItem") && isEqualString(str1: type, str2: "Archive")) {
225 const char* path;
226 iter.tryGetStringByKey(string: &path, key: "Path");
227 tryRequestLoadArchive(path, heap, device: nullptr);
228 }
229 }
230
231 mIsFilePreloaded = true;
232}
233
234void FileLoader::waitLoadDoneAllFile() {
235 mArchiveHolder->waitLoadDoneAll();
236}
237
238void FileLoader::clearAllEntry() {
239 mArchiveHolder->clearEntry();
240 mSoundItemHolder->clearEntry();
241 mIsFilePreloaded = false;
242}
243
244void FileLoader::setThreadPriority(s32 priority) {
245 mLoaderThread->getThread()->setPriority(priority);
246}
247
248} // namespace al
249