mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-19 02:47:55 +02:00
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>
163 lines
6.2 KiB
C++
163 lines
6.2 KiB
C++
// Copyright (c) 2012- PPSSPP Project.
|
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation, version 2.0 or later versions.
|
|
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License 2.0 for more details.
|
|
|
|
// A copy of the GPL 2.0 should have been included with the program.
|
|
// If not, see http://www.gnu.org/licenses/
|
|
|
|
// Official git repository and contact information can be found at
|
|
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
|
|
|
|
#pragma once
|
|
|
|
// TODO: Remove the Windows-specific code, FILE is fine there too.
|
|
|
|
#include <map>
|
|
|
|
#include "Common/File/Path.h"
|
|
#include "Core/FileSystems/FileSystem.h"
|
|
|
|
#if defined(_WIN32) && !defined(HAVE_LIBRETRO_VFS)
|
|
typedef void * HANDLE;
|
|
#endif
|
|
|
|
struct DirectoryFileHandle {
|
|
enum Flags {
|
|
NORMAL,
|
|
SKIP_REPLAY,
|
|
};
|
|
|
|
#ifdef HAVE_LIBRETRO_VFS
|
|
FILE *hFile = nullptr;
|
|
#elif defined(_WIN32)
|
|
HANDLE hFile = (HANDLE)-1;
|
|
#else
|
|
int hFile = -1;
|
|
#endif
|
|
s64 needsTrunc_ = -1;
|
|
bool replay_ = true;
|
|
bool inGameDir_ = false;
|
|
FileSystemFlags fileSystemFlags_ = (FileSystemFlags)0;
|
|
|
|
DirectoryFileHandle() {}
|
|
|
|
DirectoryFileHandle(Flags flags, FileSystemFlags fileSystemFlags)
|
|
: replay_(flags != SKIP_REPLAY), fileSystemFlags_(fileSystemFlags) {}
|
|
|
|
Path GetLocalPath(const Path &basePath, std::string_view localPath) const;
|
|
bool Open(const Path &basePath, std::string &fileName, FileAccess access, u32 &err);
|
|
size_t Read(u8* pointer, s64 size);
|
|
size_t Write(const u8* pointer, s64 size);
|
|
size_t Seek(s32 position, FileMove type);
|
|
void Close();
|
|
};
|
|
|
|
class DirectoryFileSystem : public IFileSystem {
|
|
public:
|
|
DirectoryFileSystem(IHandleAllocator *_hAlloc, const Path &_basePath, FileSystemFlags _flags = FileSystemFlags::NONE);
|
|
~DirectoryFileSystem();
|
|
|
|
void CloseAll();
|
|
|
|
void DoState(PointerWrap &p) override;
|
|
std::vector<PSPFileInfo> GetDirListing(std::string_view path, bool *exists = nullptr) override;
|
|
int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override;
|
|
void CloseFile(u32 handle) override;
|
|
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
|
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
|
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
|
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
|
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
|
PSPFileInfo GetFileInfo(std::string filename) override;
|
|
PSPFileInfo GetFileInfoByHandle(u32 handle) override;
|
|
bool OwnsHandle(u32 handle) override;
|
|
int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) override;
|
|
PSPDevType DevType(u32 handle) override;
|
|
|
|
bool MkDir(const std::string &dirname) override;
|
|
bool RmDir(const std::string &dirname) override;
|
|
int RenameFile(const std::string &from, const std::string &to) override;
|
|
bool RemoveFile(const std::string &filename) override;
|
|
FileSystemFlags Flags() const override { return flags; }
|
|
u64 FreeDiskSpace(const std::string &path) override;
|
|
|
|
bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override;
|
|
void Describe(char *buf, size_t size) const override { snprintf(buf, size, "Dir: %s", basePath.c_str()); }
|
|
|
|
private:
|
|
struct OpenFileEntry {
|
|
DirectoryFileHandle hFile;
|
|
std::string guestFilename;
|
|
FileAccess access = FILEACCESS_NONE;
|
|
};
|
|
|
|
typedef std::map<u32, OpenFileEntry> EntryMap;
|
|
EntryMap entries;
|
|
Path basePath;
|
|
IHandleAllocator *hAlloc;
|
|
FileSystemFlags flags;
|
|
|
|
Path GetLocalPath(std::string_view internalPath) const;
|
|
|
|
// Rewrites any FAT 8.3 short-name components of a guest path to the long names they were
|
|
// generated from, so a game that read a short name out of d_private can open the file by it.
|
|
void ResolveShortNames(std::string &path);
|
|
|
|
// Guards ResolveShortNames against re-entering itself through GetDirListing.
|
|
bool resolvingShortNames_ = false;
|
|
};
|
|
|
|
// VFSFileSystem: Ability to map in Android APK paths as well! Does not support all features, only meant for fonts.
|
|
// Very inefficient - always load the whole file on open.
|
|
class VFSFileSystem : public IFileSystem {
|
|
public:
|
|
VFSFileSystem(IHandleAllocator *_hAlloc, std::string _basePath);
|
|
~VFSFileSystem();
|
|
|
|
void DoState(PointerWrap &p) override;
|
|
std::vector<PSPFileInfo> GetDirListing(std::string_view path, bool *exists = nullptr) override;
|
|
int OpenFile(std::string filename, FileAccess access, const char *devicename = nullptr) override;
|
|
void CloseFile(u32 handle) override;
|
|
size_t ReadFile(u32 handle, u8 *pointer, s64 size) override;
|
|
size_t ReadFile(u32 handle, u8 *pointer, s64 size, int &usec) override;
|
|
size_t WriteFile(u32 handle, const u8 *pointer, s64 size) override;
|
|
size_t WriteFile(u32 handle, const u8 *pointer, s64 size, int &usec) override;
|
|
size_t SeekFile(u32 handle, s32 position, FileMove type) override;
|
|
PSPFileInfo GetFileInfo(std::string filename) override;
|
|
PSPFileInfo GetFileInfoByHandle(u32 handle) override;
|
|
bool OwnsHandle(u32 handle) override;
|
|
int Ioctl(u32 handle, u32 cmd, u32 indataPtr, u32 inlen, u32 outdataPtr, u32 outlen, int &usec) override;
|
|
PSPDevType DevType(u32 handle) override;
|
|
|
|
bool MkDir(const std::string &dirname) override;
|
|
bool RmDir(const std::string &dirname) override;
|
|
int RenameFile(const std::string &from, const std::string &to) override;
|
|
bool RemoveFile(const std::string &filename) override;
|
|
FileSystemFlags Flags() const override { return FileSystemFlags::FLASH; }
|
|
u64 FreeDiskSpace(const std::string &path) override { return 0; }
|
|
|
|
bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override { return false; }
|
|
void Describe(char *buf, size_t size) const override { snprintf(buf, size, "VFS: %s", basePath.c_str()); }
|
|
|
|
private:
|
|
struct OpenFileEntry {
|
|
u8 *fileData;
|
|
size_t size;
|
|
size_t seekPos;
|
|
};
|
|
|
|
typedef std::map<u32, OpenFileEntry> EntryMap;
|
|
EntryMap entries;
|
|
std::string basePath;
|
|
IHandleAllocator *hAlloc;
|
|
|
|
std::string GetLocalPath(std::string_view localpath) const;
|
|
};
|