libgui 2.01
libgui is a GUI library for the GameCube, Wii, and Wii U
 
Loading...
Searching...
No Matches
OgcFileSystemDriver.cpp
1/****************************************************************************
2 * Platform Abstraction Layer (OGC driver)
3 * Daryl Borth 2026
4 * OgcFileSystemDriver.cpp
5 ***************************************************************************/
6#include <stdio.h>
7#include <string.h>
8#include <fat.h>
9#include <iso9660.h>
10
11#include "OgcFileSystemDriver.h"
12
14{
15 for(int i = 0; i < fatSlotCount; i++)
16 if(fatSlots[i].deviceId == deviceId)
17 return &fatSlots[i];
18 return nullptr;
19}
20
22{
23 for(int i = 0; i < fatSlotCount; i++)
24 {
25 if(!fatSlots[i].pollable)
26 continue;
27
28 DISC_INTERFACE * disc = fatSlots[i].getDisc();
29 isPresentCache[fatSlots[i].deviceId] = disc->isInserted(disc);
30 }
31
32 // DVD is deliberately not probed here
33
34 StorageDevice devices[MAX_STORAGE_DEVICES];
35 int count = enumerateStorageDevices(devices);
36
37 for(int i = 0; i < count; i++)
38 if(devices[i].autoMountAtStartup)
39 mountStorageDevice(devices[i].id);
40}
41
43{
44 smbDriver.shutdown();
45
46 for(int i = 0; i < fatSlotCount; i++)
47 {
48 char mountPoint[10];
49 snprintf(mountPoint, sizeof(mountPoint), "%s:", fatSlots[i].mountName);
50 fatUnmount(mountPoint);
51 }
52
54}
55
57{
58 int count = 0;
59
60 for(int i = 0; i < fatSlotCount && count < MAX_STORAGE_DEVICES; i++)
61 {
62 const OgcFatSlotDescriptor & slot = fatSlots[i];
63 StorageDevice & out = outDevices[count];
64
65 out.id = slot.deviceId;
66 snprintf(out.prefix, sizeof(out.prefix), "%s", slot.prefix);
67 snprintf(out.name, sizeof(out.name), "%s", slot.displayName);
68 snprintf(out.volumeLabel, sizeof(out.volumeLabel), "%s", volumeLabel[slot.deviceId]);
69 out.removable = slot.removable;
71 out.alwaysListed = false;
72 count++;
73 }
74
75 // DVD and SMB are the same on both platforms, and both alwaysListed
76 if(count < MAX_STORAGE_DEVICES)
77 {
78 outDevices[count] = StorageDevice{ DEVICE_DVD, "dvd:/", "Data DVD", "", false, false, true };
79 count++;
80 }
81
82 if(count < MAX_STORAGE_DEVICES)
83 {
84 outDevices[count] = StorageDevice{ DEVICE_SMB, "smb:/", "Network Share", "", false, false, true };
85 count++;
86 }
87
88 return count;
89}
90
92{
93 const OgcFatSlotDescriptor * slot = findFatSlot(deviceId);
94 if(!slot)
96
97 char mountPoint[10];
98 snprintf(mountPoint, sizeof(mountPoint), "%s:", slot->mountName);
99
100 DISC_INTERFACE * disc = slot->getDisc();
101
102 if(unmountRequired[deviceId])
103 {
104 unmountRequired[deviceId] = false;
105 fatUnmount(mountPoint);
106 disc->shutdown(disc);
107 isMounted[deviceId] = false;
108 labelFetched[deviceId] = false;
109 }
110
111 // Distinguish "nothing there" from "something's there but we can't read it"
112 if(!disc->startup(disc) || !disc->isInserted(disc))
113 {
114 isMounted[deviceId] = false;
115 volumeLabel[deviceId][0] = '\0';
116 labelFetched[deviceId] = false;
118 }
119
120 bool mounted = fatMountSimple(slot->mountName, disc);
121 isMounted[deviceId] = mounted;
122
123 if(!mounted)
124 {
125 volumeLabel[deviceId][0] = '\0';
126 }
127 else if(!labelFetched[deviceId])
128 {
129 // Only ever looked up once per mount
130 fatGetVolumeLabel(mountPoint, volumeLabel[deviceId]);
131 labelFetched[deviceId] = true;
132 }
133
134 return mounted ? MountResult::Success : MountResult::MountFailed;
135}
136
138{
139 if(!dvdDisc)
141
142 if(unmountRequired[DEVICE_DVD])
143 {
144 unmountRequired[DEVICE_DVD] = false;
145 ISO9660_Unmount("dvd:");
146 }
147
148 if(!dvdDisc->isInserted(dvdDisc))
149 {
150 isMounted[DEVICE_DVD] = false;
151 isPresentCache[DEVICE_DVD] = false;
153 }
154
155 isPresentCache[DEVICE_DVD] = true;
156
157 if(!ISO9660_Mount("dvd", dvdDisc))
158 {
159 isMounted[DEVICE_DVD] = false;
161 }
162
163 isMounted[DEVICE_DVD] = true;
164 return MountResult::Success;
165}
166
168{
169 MountResult result = mountFAT(deviceId);
170 mountFailed[deviceId] = (result == MountResult::MountFailed);
171 return result;
172}
173
175{
176 if(deviceId < 0 || deviceId >= MAX_STORAGE_DEVICES)
178
179 if(deviceId == DEVICE_SMB)
180 return smbDriver.isConnected() ? MountResult::Success : MountResult::DeviceNotFound;
181
182 if(isMounted[deviceId])
183 return MountResult::Success;
184
185 // Re-resolve hardware topology first, so a device inserted since the
186 // last poll cycle mounts on this attempt rather than the next one.
187 prepareMount(deviceId);
188
189 if(deviceId == DEVICE_DVD)
190 return mountDVD();
191
192 if(findFatSlot(deviceId))
193 return attemptFatMount(deviceId);
194
196}
197
198const char * OgcFileSystemDriver::mountResultMessage(int deviceId, MountResult result)
199{
200 if(deviceId == DEVICE_DVD)
201 return result == MountResult::MountFailed ? "Unrecognized DVD format." : "No disc inserted!";
202
203 if(deviceId == DEVICE_SMB)
204 return "Network share not connected!";
205
206 const OgcFatSlotDescriptor * slot = findFatSlot(deviceId);
207 if(!slot)
208 return "Device not found!";
209
211}
212
214{
215 if(deviceId < 0 || deviceId >= MAX_STORAGE_DEVICES)
216 return;
217
218 if(deviceId == DEVICE_SMB)
219 {
220 smbDriver.disconnect();
221 return;
222 }
223
224 isMounted[deviceId] = false;
225 unmountRequired[deviceId] = true;
226 labelFetched[deviceId] = false;
227 volumeLabel[deviceId][0] = '\0';
228
229 // Whatever shows up in this slot next (even the same disk, replugged)
230 // gets exactly one fresh attempt - see attemptFatMount().
231 mountFailed[deviceId] = false;
232}
233
234void OgcFileSystemDriver::pollStorageDevices(int removedIds[MAX_STORAGE_DEVICES], int & outRemovedCount, bool & deviceListChanged)
235{
236 outRemovedCount = 0;
237 deviceListChanged = pollPlatformExtra();
238
239 // DVD is deliberately excluded from this loop - see mountDVD()/class
240 // comment. Its isPresentCache/isMounted state only ever changes as a
241 // side effect of an actual mount attempt.
242 for(int i = 0; i < fatSlotCount; i++)
243 {
244 const OgcFatSlotDescriptor & slot = fatSlots[i];
245 if(!slot.pollable)
246 continue;
247
248 DISC_INTERFACE * disc = slot.getDisc();
249 bool present = disc->isInserted(disc);
250
251 if(present != isPresentCache[slot.deviceId])
252 {
253 isPresentCache[slot.deviceId] = present;
254
255 // Hardware topology changed either way
256 mountFailed[slot.deviceId] = false;
257 }
258
259 if(isMounted[slot.deviceId] && !present)
260 {
262 if(outRemovedCount < MAX_STORAGE_DEVICES)
263 removedIds[outRemovedCount++] = slot.deviceId;
264 deviceListChanged = true;
265 }
266 else if(present && !isMounted[slot.deviceId] && !mountFailed[slot.deviceId])
267 {
268 // Exactly one attempt per insertion
269 if(attemptFatMount(slot.deviceId) == MountResult::Success)
270 deviceListChanged = true;
271 }
272 }
273}
274
276{
277 if(deviceId == DEVICE_SMB)
278 return smbDriver.isConnected(); // informational only - SMB is alwaysListed
279
280 if(deviceId == DEVICE_DVD)
281 return isPresentCache[DEVICE_DVD]; // informational only - DVD is alwaysListed
282
283 const OgcFatSlotDescriptor * slot = findFatSlot(deviceId);
284 if(!slot || !slot->pollable)
285 return false; // non-pollable slots (GC Loader) can't be known without an explicit mount attempt
286
287 return isMounted[deviceId];
288}
289
290const char * OgcFileSystemDriver::getDevicePrefix(int device) const
291{
292 if(device < 0 || device >= MAX_STORAGE_DEVICES)
293 return "";
294
295 if(device == DEVICE_SMB)
296 return "smb:/";
297
298 if(device == DEVICE_DVD)
299 return "dvd:/";
300
301 const OgcFatSlotDescriptor * slot = findFatSlot(device);
302 return slot ? slot->prefix : "";
303}
304
305const char * OgcFileSystemDriver::getMountPath(int device) const
306{
307 if(device < 0 || device >= MAX_STORAGE_DEVICES)
308 return "";
309
310 if(device == DEVICE_SMB)
311 return smbDriver.getMountPath();
312
313 if(!isMounted[device])
314 return "";
315
316 return getDevicePrefix(device);
317}
bool isMounted[MAX_STORAGE_DEVICES]
A filesystem is currently mounted for this device.
MountResult attemptFatMount(int deviceId)
Single mountFAT() attempt that also records the outcome in mountFailed.
bool isDevicePresent(int deviceId) const override
Lightweight, cached hardware-presence check for a single device - does NOT mount and does no invasive...
MountResult mountFAT(int deviceId)
Mounts the FAT filesystem on a slot with fatMountSimple(), unmounting first if unmountRequired is set...
void pollStorageDevices(int removedIds[MAX_STORAGE_DEVICES], int &outRemovedCount, bool &deviceListChanged) override
Called once per device-checking thread cycle.
bool unmountRequired[MAX_STORAGE_DEVICES]
A stale mount must be torn down before the next mount attempt.
const OgcFatSlotDescriptor * fatSlots
Table of FAT slots this platform supports; set by the concrete driver.
char volumeLabel[MAX_STORAGE_DEVICES][16]
Volume label cached for each device.
void initFatSlotsAndAutoMount()
Runs the shared boot-time pass: probe every pollable fatSlots[] entry once for isPresentCache,...
const char * unsupportedFormatMessage
Text returned by mountResultMessage() when a present device fails to mount.
bool labelFetched[MAX_STORAGE_DEVICES]
fetched since the last mount/removal - see mountFAT()/invalidateStorageDevice()
virtual void prepareMount(int deviceId)
Platform-specific work that must run immediately before a user-initiated mount attempt on deviceId,...
const char * getMountPath(int device) const override
devoptab-style mount path for device (eg.
const OgcFatSlotDescriptor * findFatSlot(int deviceId) const
int fatSlotCount
Number of entries in fatSlots.
DISC_INTERFACE * dvdDisc
Disc interface used to mount the DVD drive, or nullptr if the platform has none.
const char * getDevicePrefix(int device) const override
The devoptab prefix this device would use (eg.
void shutdown() override
Unmounts every device and releases the driver's resources.
bool mountFailed[MAX_STORAGE_DEVICES]
set by a real MountFailed; cleared on removal/reinsertion - see attemptFatMount()
OgcSmbDriver smbDriver
The network-share driver returned by getSmb()
const char * mountResultMessage(int deviceId, MountResult result) override
A short, user-displayable reason for a non-Success MountResult (eg.
virtual bool pollPlatformExtra()
Platform-specific extra polling that must run before the shared per-slot presence check (eg.
virtual void shutdownPlatform()
Platform-specific extra teardown beyond the shared per-slot fatUnmount() pass (eg.
MountResult mountDVD()
Mounts the DVD as ISO9660 if a disc is inserted.
MountResult mountStorageDevice(int deviceId) override
Attempts to mount deviceId exactly once.
int enumerateStorageDevices(StorageDevice outDevices[MAX_STORAGE_DEVICES]) override
Fills outDevices (size MAX_STORAGE_DEVICES) and returns the device count.
bool isPresentCache[MAX_STORAGE_DEVICES]
raw hardware insertion only - see isDevicePresent()'s comment for why this is no longer what it retur...
void invalidateStorageDevice(int deviceId) override
Marks deviceId as needing a fresh mount next time mountStorageDevice() is called, eg.
MountResult
Result of a single mount attempt.
@ MountFailed
present, but couldn't be mounted (eg.
@ DeviceNotFound
not physically present / not inserted
One hot-pluggable FAT-formatted slot, shared shape between GameCube (SD Gecko A/B/port2,...
bool autoMountAtStartup
Mounted silently at boot.
const char * notFoundMessage
eg.
const char * displayName
Short name shown in device lists.
bool pollable
true: probed every pollStorageDevices() cycle
bool removable
advisory only - not currently read by app code
DISC_INTERFACE *(* getDisc)()
resolved lazily - Wii's USB slots move between physical interfaces at runtime (see WiiUsbMulti)
const char * prefix
precomputed "name:/" literal - returned by getMountPath()/enumerateStorageDevices() as-is
int deviceId
Device enum value this slot mounts (DEVICE_SD, DEVICE_USB, ...)
const char * mountName
devoptab basename, no colon/slash - eg.
Description of one storage device: id, display name, mount prefix, volume label and flags such as rem...
bool autoMountAtStartup
silently attempted at boot
bool removable
can this device disappear at runtime?
int id
Device enum value (DEVICE_SD, DEVICE_USB, ...)
bool alwaysListed
show in a device listing unconditionally, regardless of isDevicePresent()
char volumeLabel[16]
Volume label of the mounted filesystem, or empty if unknown.
char prefix[32]
devoptab mount prefix, eg.
char name[20]
Short display name for device lists.