sceIoDread hands back a dirent whose d_private holds the 8.3 short name
ahead of the long name, and we never wrote the short name at all - the game
got whatever was on the stack there. Crazy Taxi: Fare Wars reads it rather
than d_name, so it rejected every file in ms0:/MUSIC, ended up with an empty
playlist and never even reserved an mp3 handle: custom soundtracks were
silently dead, with the game spinning on InitResource/SetLoopNum forever.
Generate the names from the directory listing, and resolve them back in
DirectoryFileSystem so a game can open a file by the short name it was given.
Both sides come from the same function, so they agree.
We can't lean on the host for any of this. Linux, macOS and Android have no
8.3 names at all, and while Windows does keep aliases it generates them by a
different rule - it counts to ~4 and then switches to a hash - so resolution
runs before the literal path is tried rather than as a fallback, or on
Windows we'd quietly open a different file than the one we handed the game.
The exact names a real PSP produces are still unverified - no pspautotest
covers d_private - so this implements the ordinary FAT rule and the new
FatShortNames unit test pins that down until hardware can settle it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
DirectoryFileSystem passed the host's permission bits and directory
size straight through to the game. The PSP has neither - its FAT driver
makes a mode up from the entry type and whether it's writable, and
reports no size for a directory. So a game saw 0644/0755 and a 4096
byte directory on Linux, but 0664/0777 and 0 on Windows, where the file
layer already synthesizes those bits.
Now both platforms report what the PSP does: 0777 for directories, 0664
for writable files, 0444 for read-only ones, and no size on a
directory. The parent ".." entry keeps its 4096, which is what the
hardware reports for that one.
Also fixes the synthetic PSP directory entry using 0x777 where 0777 was
meant.
Fixes io/directory/directory, moved to tests_good.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
SeekFile stored a signed s32 position into the unsigned size_t seekPos,
so a negative position (e.g. from a truncating s32 cast of a large
lseek offset) wrapped seekPos to near 2^64. ReadFile's clamp arithmetic
then also wrapped, driving a memcpy from a wild pointer.
- Clamp the computed seek position to 0 in SeekFile.
- Clamp the read size against the remaining data in ReadFile, returning
0 when seekPos is at or past the end.
* Rename LogType to Log
* Explicitly use the Log:: enum when logging. Allows for autocomplete when editing.
* Mac/ARM64 buildfix
* Do the same with the hle result log macros
* Rename the log names to mixed case while at it.
* iOS buildfix
* Qt buildfix attempt, ARM32 buildfix
If there were files open before that don't exist, things could go badly.
But we could at least load the state file and hope the game doesn't break.
That's better that refusing to load at all.