1#ifdef cafe
2#include <cafe.h>
3#include <nn/save.h>
4#endif // cafe
5
6#ifdef NNSDK
7#include <nn/fs/fs_mount.h>
8#include <nn/fs/fs_rom.h>
9#include <nn/fs/fs_save.h>
10#endif
11
12#include <basis/seadNew.h>
13#include <basis/seadRawPrint.h>
14#include <devenv/seadEnvUtil.h>
15#include <filedevice/seadFileDeviceMgr.h>
16#include <filedevice/seadPath.h>
17#include <heap/seadHeapMgr.h>
18
19namespace sead
20{
21SEAD_SINGLETON_DISPOSER_IMPL(FileDeviceMgr)
22
23FileDeviceMgr::FileDeviceMgr()
24{
25 if (HeapMgr::sInstancePtr == NULL)
26 {
27 SEAD_ASSERT_MSG(false, "FileDeviceMgr need HeapMgr");
28 return;
29 }
30
31 Heap* const heap = HeapMgr::instance()->findContainHeap(this);
32 mount_(heap);
33
34 mMainFileDevice = new (heap) MainFileDevice(heap);
35 mount(mMainFileDevice);
36
37 mDefaultFileDevice = mMainFileDevice;
38}
39
40FileDeviceMgr::~FileDeviceMgr()
41{
42 if (mMainFileDevice != NULL)
43 {
44 delete mMainFileDevice;
45 mMainFileDevice = NULL;
46 }
47 unmount_();
48}
49
50void FileDeviceMgr::mount_([[maybe_unused]] Heap* heap)
51{
52#ifdef cafe
53 FSInit();
54 FSAddClient(&client, FS_RET_NO_ERROR);
55
56 FSStateChangeParams changeParams = {
57 .userCallback = stateChangeCallback_, .userContext = NULL, .ioMsgQueue = NULL};
58
59 FSSetStateChangeNotification(&client, &changeParams);
60 SAVEInit();
61 _17A4[0] = 0;
62 _1824 = 0;
63#elif defined(NNSDK)
64 // For release builds, only content is mounted using the regular nn::fs::MountRom.
65 // For debug builds, content is mounted using nn::fs::MountRom or by mounting
66 // SEAD_NIN_CONTENT_DIR on the host computer and the host root and SD are also mounted.
67#ifdef SEAD_DEBUG
68 const auto mount_host_result = nn::fs::MountHostRoot();
69 if (mount_host_result.IsFailure())
70 {
71 SEAD_WARN("nn::fs::MountHostRoot() failed. module = %d desc = %d innervalue = 0x%08x",
72 mount_host_result.GetModule(), mount_host_result.GetDescription(),
73 mount_host_result.GetInnerValueForDebug());
74 mMountedHost = false;
75 }
76 else
77 {
78 mMountedHost = true;
79 }
80#endif // SEAD_DEBUG
81
82#ifdef SEAD_DEBUG
83 if (nn::fs::CanMountRomForDebug())
84#endif
85 {
86 u64 cache_size = 0;
87 const auto query_result = nn::fs::QueryMountRomCacheSize(&cache_size);
88 SEAD_ASSERT_MSG(query_result.IsSuccess(),
89 "nn::fs::QueryMountRomCacheSize() failed. module = %d desc = %d "
90 "innervalue = 0x%08x",
91 query_result.GetModule(), query_result.GetDescription(),
92 query_result.GetInnerValueForDebug());
93
94 SEAD_DEBUG_PRINT("FileDeviceMgr: MountRom cache size => %zd\n", cache_size);
95 mRomCache = new (heap) u8[cache_size];
96
97 const auto result = nn::fs::MountRom("content", mRomCache, cache_size);
98 SEAD_ASSERT_MSG(result.IsSuccess(),
99 "nn::fs::MountRom() failed. module = %d desc = %d innervalue = 0x%08x",
100 result.GetModule(), result.GetDescription(),
101 result.GetInnerValueForDebug());
102 }
103#ifdef SEAD_DEBUG
104 else
105 {
106 FixedSafeString<256> content_dir;
107 if (EnvUtil::getEnvironmentVariable(&content_dir, "SEAD_NIN_CONTENT_DIR") == -1)
108 {
109 SEAD_WARN("SEAD_NIN_CONTENT_DIR is not set.");
110 }
111 else
112 {
113 const auto result = nn::fs::MountHost("content", content_dir.cstr());
114 SEAD_ASSERT_MSG(result.IsSuccess(),
115 "nn::fs::MountHost() failed. module = %d desc = %d innervalue = 0x%08x",
116 result.GetModule(), result.GetDescription(),
117 result.GetInnerValueForDebug());
118 system::Print("FileDeviceMgr: MountHost => %s\n", content_dir.cstr());
119 }
120 }
121
122 const auto sd_result = nn::fs::MountSdCardForDebug("sd");
123 mMountedSd = sd_result.IsSuccess();
124 if (sd_result.IsSuccess())
125 system::Print("FileDeviceMgr: mount SD card\n");
126 else if (nn::fs::ResultMountNameAlreadyExists().Includes(sd_result))
127 system::Print("FileDeviceMgr: SD card already mounted\n");
128 else if (nn::fs::ResultSdCardAccessFailed().Includes(sd_result))
129 system::Print("FileDeviceMgr: SD card is not ready\n");
130#endif // SEAD_DEBUG
131#else
132#error "Unknown platform"
133#endif
134}
135
136void FileDeviceMgr::unmount_()
137{
138#ifdef cafe
139 FSDelClient(&client, FS_RET_NO_ERROR);
140 SAVEShutdown();
141 FSShutdown();
142#elif defined(NNSDK)
143#ifdef SEAD_DEBUG
144 if (mMountedHost)
145 nn::fs::UnmountHostRoot();
146#endif
147
148 nn::fs::Unmount("content");
149 if (mRomCache)
150 delete[] mRomCache;
151
152#ifdef SEAD_DEBUG
153 if (mMountedSd)
154 nn::fs::Unmount("sd");
155#endif
156#else
157#error "Unknown platform"
158#endif
159}
160
161void FileDeviceMgr::traceFilePath(const SafeString& path) const
162{
163 SEAD_DEBUG_PRINT("[FileDeviceMgr] %s\n", path.cstr());
164 FixedSafeString<256> pathNoDrive;
165 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
166
167 if (device != NULL)
168 device->traceFilePath(pathNoDrive);
169 else
170 SEAD_WARN("FileDevice not found: %s", path.cstr());
171}
172
173void FileDeviceMgr::traceDirectoryPath(const SafeString& path) const
174{
175 SEAD_DEBUG_PRINT("[FileDeviceMgr] %s\n", path.cstr());
176 FixedSafeString<256> pathNoDrive;
177 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
178
179 if (device != NULL)
180 device->traceDirectoryPath(pathNoDrive);
181 else
182 SEAD_WARN("FileDevice not found: %s", path.cstr());
183}
184
185void FileDeviceMgr::resolveFilePath(BufferedSafeString* out, const SafeString& path) const
186{
187 FixedSafeString<256> pathNoDrive;
188 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
189
190 if (device != NULL)
191 device->resolveFilePath(out, pathNoDrive);
192 else
193 SEAD_WARN("FileDevice not found: %s", path.cstr());
194}
195
196void FileDeviceMgr::resolveDirectoryPath(BufferedSafeString* out, const SafeString& path) const
197{
198 FixedSafeString<256> pathNoDrive;
199 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
200
201 if (device != NULL)
202 device->resolveDirectoryPath(out, pathNoDrive);
203 else
204 SEAD_WARN("FileDevice not found: %s", path.cstr());
205}
206
207void FileDeviceMgr::mount(FileDevice* device, const SafeString& name)
208{
209 if (!name.isEqual(SafeString::cEmptyString))
210 device->setDriveName(name);
211
212 mDeviceList.pushBack(device);
213}
214
215void FileDeviceMgr::unmount(FileDevice* device)
216{
217 mDeviceList.erase(device);
218
219 if (device == mDefaultFileDevice)
220 mDefaultFileDevice = NULL;
221}
222
223void FileDeviceMgr::unmount(const SafeString& name)
224{
225 auto* device = findDevice(name);
226 if (!device)
227 {
228 SEAD_ASSERT_MSG(false, "drive not found: %s\n", name.cstr());
229 return;
230 }
231 unmount(device);
232}
233
234FileDevice* FileDeviceMgr::findDeviceFromPath(const SafeString& path,
235 BufferedSafeString* pathNoDrive) const
236{
237 FixedSafeString<32> driveName;
238 FileDevice* device;
239
240 if (!Path::getDriveName(&driveName, path))
241 {
242 device = mDefaultFileDevice;
243 if (!device)
244 {
245 SEAD_ASSERT_MSG(false, "drive name not found and default file device is null");
246 return nullptr;
247 }
248 }
249 else
250 device = findDevice(driveName);
251
252 if (!device)
253 return nullptr;
254
255 if (pathNoDrive != NULL)
256 Path::getPathExceptDrive(pathNoDrive, path);
257
258 return device;
259}
260
261FileDevice* FileDeviceMgr::findDevice(const SafeString& name) const
262{
263 for (auto it = mDeviceList.begin(); it != mDeviceList.end(); ++it)
264 if ((*it)->getDriveName() == name)
265 return *it;
266
267 return nullptr;
268}
269
270FileDevice* FileDeviceMgr::tryOpen(FileHandle* handle, const SafeString& path,
271 FileDevice::FileOpenFlag flag, u32 divSize)
272{
273 FixedSafeString<256> pathNoDrive;
274 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
275
276 if (device == NULL)
277 return NULL;
278
279 return device->tryOpen(handle, pathNoDrive, flag, divSize);
280}
281
282FileDevice* FileDeviceMgr::tryOpenDirectory(DirectoryHandle* handle, const SafeString& path)
283{
284 FixedSafeString<256> pathNoDrive;
285 FileDevice* device = findDeviceFromPath(path, &pathNoDrive);
286 if (!device)
287 return nullptr;
288
289 if (!device->isExistDirectory(pathNoDrive))
290 return nullptr;
291
292 return device->tryOpenDirectory(handle, pathNoDrive);
293}
294
295u8* FileDeviceMgr::tryLoad(FileDevice::LoadArg& arg)
296{
297 SEAD_ASSERT_MSG(arg.path != SafeString::cEmptyString, "path is null");
298
299 FixedSafeString<256> pathNoDrive;
300 FileDevice* device = findDeviceFromPath(arg.path, &pathNoDrive);
301
302 if (device == NULL)
303 return NULL;
304
305 FileDevice::LoadArg arg2(arg);
306 arg2.path = pathNoDrive.cstr();
307
308 u8* data = device->tryLoad(arg2);
309
310 arg.read_size = arg2.read_size;
311 arg.roundup_size = arg2.roundup_size;
312 arg.need_unload = arg2.need_unload;
313
314 return data;
315}
316
317void FileDeviceMgr::unload(u8* data)
318{
319 SEAD_ASSERT(data);
320 if (data)
321 delete data;
322}
323
324bool FileDeviceMgr::trySave(FileDevice::SaveArg& arg)
325{
326 SEAD_ASSERT_MSG(arg.path != SafeString::cEmptyString, "path is null");
327
328 FixedSafeString<256> pathNoDrive;
329 FileDevice* device = findDeviceFromPath(arg.path, &pathNoDrive);
330 if (!device)
331 return false;
332
333 FileDevice::SaveArg arg2(arg);
334 arg2.path = pathNoDrive.cstr();
335
336 const bool ret = device->trySave(arg2);
337 arg.write_size = arg2.write_size;
338 return ret;
339}
340
341#ifdef NNSDK
342void FileDeviceMgr::mountSaveDataForDebug(Heap*)
343{
344 const auto result = nn::fs::MountSaveDataForDebug("save");
345 SEAD_ASSERT_MSG(
346 result.IsSuccess(),
347 "nn::fs::MountSaveDataForDebug() failed. module = %d desc = %d innervalue = 0x%08x",
348 result.GetModule(), result.GetDescription(), result.GetInnerValueForDebug());
349}
350
351void FileDeviceMgr::unmountSaveDataForDebug()
352{
353 nn::fs::Unmount("save");
354}
355#endif
356
357#ifdef cafe
358void FileDeviceMgr::stateChangeCallback_(FSClient* client, FSVolumeState state, void* context)
359{
360 FSGetLastError(client);
361}
362#endif // cafe
363
364} // namespace sead
365