mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-09-21 20:06:25 +02:00
Cashing in the io/stat and io/shortname recordings. __IoGetStat began with memset(stat, 0xfe, sizeof(SceIoStat)), which destroyed 24 bytes of the caller's buffer that a real PSP never touches - it writes only as far as the timestamps and leaves all six st_private words exactly as it found them. It also wrote a made-up sector number into st_private[0] on the memory stick. That word carries the LBN on a UMD, which games read to build disc0:/sce_lbn paths, so it stays for non-FAT and is left alone otherwise. FAT has no permissions of its own and everything reads back as 0777. We were passing the host's idea of the file through instead. The existing "all files look executable on FAT" hack for Beats (issue #14812) was right in substance but lived only in sceIoDread, so sceIoGetstat and sceIoDread disagreed about the same file where hardware has them agree. Both now go through one path, which also gets the read-only case right: no write bits means mode 0555 and attr 0x21. sceIoGetstat on the root of a volume is refused, as on hardware. sceIoChstat was a logging stub. It now applies the read-only flag, which is what st_mode's write bits and st_attr's 0x01 both mean on FAT - setting either produces both, and it's reversible. That needs a new IFileSystem::SetFileWritable, defaulting to "can't" so read-only filesystems and hosts that can't express it (Android content URIs) are unaffected; the call still succeeds there, since hardware would have. GenerateFatShortNames now accounts for capitalisation. FAT keeps a lowercase flag for the base and another for the extension, but the PSP only honours the base one, so "shrt" becomes SHRT while "readme.txt" becomes README~1.TXT. We were only adding a counter on collision. The unit test carries the whole recorded set, including the corrected README~1.MD. io/shortname stays in tests_next: its d_name column can't match while SimulateVFATBug is uppercasing lowercase 8.3 names, which is deliberate and load-bearing for homebrew.
265 lines
8.2 KiB
C++
265 lines
8.2 KiB
C++
// Copyright (C) 2003 Dolphin 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 SVN repository and contact information can be found at
|
|
// http://code.google.com/p/dolphin-emu/
|
|
|
|
#pragma once
|
|
|
|
#include <cstdio>
|
|
#include <string>
|
|
#include <string_view>
|
|
#include <time.h>
|
|
#include <cstdint>
|
|
|
|
#include "Common/File/Path.h"
|
|
|
|
// Some functions here support Android content URIs. These are marked as such.
|
|
|
|
#ifdef _WIN32
|
|
inline struct tm* localtime_r(const time_t *clock, struct tm *result) {
|
|
if (localtime_s(result, clock) == 0)
|
|
return result;
|
|
return NULL;
|
|
}
|
|
#endif
|
|
|
|
namespace File {
|
|
|
|
// Mostly to handle UTF-8 filenames better on Windows.
|
|
FILE *OpenCFile(const Path &filename, const char *mode);
|
|
|
|
// Reminiscent of PSP's FileAccess enum, due to its use in emulating it.
|
|
enum OpenFlag {
|
|
OPEN_NONE = 0,
|
|
OPEN_READ = 1,
|
|
OPEN_WRITE = 2,
|
|
OPEN_APPEND = 4,
|
|
OPEN_CREATE = 8,
|
|
OPEN_TRUNCATE = 16,
|
|
// EXCL?
|
|
};
|
|
|
|
// TODO: This currently only handles Android Content URIs, but might port the rest
|
|
// of DirectoryFileSystem::Open here eventually for symmetry.
|
|
int OpenFD(const Path &filename, OpenFlag flags);
|
|
|
|
// Cross-platform way to close FDs, corresponsing in platform support with OpenFD above.
|
|
void CloseFD(int fd);
|
|
|
|
// Resolves symlinks and similar.
|
|
std::string ResolvePath(std::string_view path);
|
|
|
|
// Returns true if file filename exists
|
|
bool Exists(const Path &path);
|
|
|
|
// Returns true if file filename exists in directory path.
|
|
bool ExistsInDir(const Path &path, std::string_view filename);
|
|
|
|
// Returns true if filename exists, and is a directory
|
|
// Supports Android content URIs.
|
|
bool IsDirectory(const Path &path);
|
|
|
|
// Returns struct with modification date of file
|
|
bool GetModifTime(const Path &filename, tm &return_time);
|
|
// Same but with unix timestamp
|
|
bool GetModifTimeT(const Path &filename, time_t *return_time); // the time_t, of course, matches time_now_unix_utc().
|
|
|
|
// Returns the size of filename (64bit)
|
|
uint64_t GetFileSize(const Path &filename);
|
|
|
|
// Overloaded GetSize, accepts FILE*
|
|
uint64_t GetFileSize(FILE *f);
|
|
|
|
// Computes the recursive size of a directory. Warning: Might be slow!
|
|
uint64_t ComputeRecursiveDirectorySize(const Path &path);
|
|
|
|
// Returns true if successful, or path already exists.
|
|
bool CreateDir(const Path &filename, bool quiet = false);
|
|
|
|
void ChangeMTime(const Path &path, time_t mtime);
|
|
|
|
// Creates the full path of fullPath returns true on success
|
|
bool CreateFullPath(const Path &fullPath);
|
|
|
|
// Deletes a given file by name, return true on success
|
|
// Doesn't support deleting a directory (although it will work on some platforms - ideally shouldn't)
|
|
bool Delete(const Path &filename, bool quiet = false);
|
|
|
|
// Deletes a directory by name, returns true on success
|
|
// Directory must be empty.
|
|
bool DeleteDir(const Path &filename);
|
|
|
|
// Deletes the given directory and anything under it. Returns true on success.
|
|
bool DeleteDirRecursively(const Path &directory);
|
|
|
|
// Renames/moves file srcFilename to destFilename, returns true on success
|
|
// Will usually only work with in the same partition or other unit of storage,
|
|
// so you might have to fall back to copy/delete.
|
|
bool Rename(const Path &srcFilename, const Path &destFilename);
|
|
|
|
// copies file srcFilename to destFilename, returns true on success
|
|
bool Copy(const Path &srcFilename, const Path &destFilename);
|
|
|
|
// Tries to rename srcFilename to destFilename, if that fails,
|
|
// it tries to copy and delete the src if succeeded. If that fails too,
|
|
// returns false, otherwise returns true.
|
|
bool Move(const Path &srcFilename, const Path &destFilename);
|
|
|
|
// Move file, but only if it can be done quickly (rename or similar).
|
|
bool MoveIfFast(const Path &srcFilename, const Path &destFilename);
|
|
|
|
// creates an empty file filename, returns true on success
|
|
bool CreateEmptyFile(const Path &filename);
|
|
|
|
// Can we actually create files in this directory? Checks by trying, since permission bits
|
|
// don't tell the whole story (Windows ACLs, read-only mounts, ...).
|
|
bool IsDirectoryWritable(const Path &path);
|
|
|
|
// Set or clear a file's read-only-ness, which is what a FAT read-only attribute maps onto.
|
|
// Returns false where the platform can't express it - notably Android content URIs.
|
|
bool SetFileWritable(const Path &filename, bool writable);
|
|
|
|
// Opens ini file (cheats, texture replacements etc.)
|
|
// TODO: Belongs in System or something.
|
|
bool OpenFileInEditor(const Path &fileName);
|
|
|
|
// Uses some heuristics to determine if this is a folder that we would want to
|
|
// write to.
|
|
bool IsProbablyInDownloadsFolder(const Path &folder);
|
|
|
|
// TODO: Belongs in System or something.
|
|
const Path &GetExeDirectory();
|
|
|
|
const Path GetCurDirectory();
|
|
|
|
// Portable version of fseek() that works with 64-bit offsets if supported by
|
|
// the operating system.
|
|
int Fseek(FILE *file, int64_t offset, int whence);
|
|
|
|
// Portable version of fseek() that works with 64-bit offsets if supported by
|
|
// the operating system, and returns the new offset from the start of the file
|
|
// or -1 if it failed.
|
|
int64_t Fseektell(FILE *file, int64_t offset, int whence);
|
|
|
|
// Portable version of ftell() that works with 64-bit offsets if supported by
|
|
// the operating system.
|
|
int64_t Ftell(FILE *file);
|
|
|
|
// simple wrapper for cstdlib file functions to
|
|
// hopefully will make error checking easier
|
|
// and make forgetting an fclose() harder
|
|
class IOFile {
|
|
public:
|
|
IOFile() {}
|
|
IOFile(const Path &filename, const char openmode[]);
|
|
~IOFile();
|
|
|
|
// Prevent copies.
|
|
IOFile(const IOFile &) = delete;
|
|
void operator=(const IOFile &) = delete;
|
|
|
|
bool Open(const Path &filename, const char openmode[]);
|
|
bool Close();
|
|
|
|
template <typename T>
|
|
bool ReadArray(T* data, size_t length)
|
|
{
|
|
if (!IsOpen() || length != fread(data, sizeof(T), length, m_file))
|
|
m_good = false;
|
|
|
|
return m_good;
|
|
}
|
|
|
|
template <typename T>
|
|
bool WriteArray(const T* data, size_t length)
|
|
{
|
|
if (!IsOpen() || length != fwrite(data, sizeof(T), length, m_file))
|
|
m_good = false;
|
|
|
|
return m_good;
|
|
}
|
|
|
|
bool ReadBytes(void* data, size_t length)
|
|
{
|
|
return ReadArray(reinterpret_cast<char*>(data), length);
|
|
}
|
|
|
|
bool WriteBytes(const void* data, size_t length)
|
|
{
|
|
return WriteArray(reinterpret_cast<const char*>(data), length);
|
|
}
|
|
|
|
bool IsOpen() const { return nullptr != m_file; }
|
|
|
|
// m_good is set to false when a read, write or other function fails
|
|
bool IsGood() const { return m_good; }
|
|
operator bool() const { return IsGood() && IsOpen(); }
|
|
|
|
FILE* ReleaseHandle();
|
|
|
|
FILE* GetHandle() { return m_file; }
|
|
|
|
void SetHandle(FILE* file);
|
|
|
|
bool Seek(int64_t off, int origin);
|
|
uint64_t Tell();
|
|
uint64_t GetSize();
|
|
bool Resize(uint64_t size);
|
|
bool Flush();
|
|
|
|
// clear error state
|
|
void Clear() {
|
|
m_good = true;
|
|
#ifndef HAVE_LIBRETRO_VFS
|
|
#undef clearerr
|
|
std::clearerr(m_file);
|
|
#endif
|
|
}
|
|
|
|
private:
|
|
FILE *m_file = nullptr;
|
|
bool m_good = true;
|
|
};
|
|
|
|
#ifdef HAVE_LIBRETRO_VFS
|
|
// Call this on libretro core initialization with the libretro virtual file
|
|
// system interface.
|
|
void InitLibretroVFS(const struct retro_vfs_interface_info *vfs) noexcept;
|
|
#endif
|
|
|
|
// TODO: Refactor, this was moved from the old file_util.cpp.
|
|
|
|
// Whole-file reading/writing
|
|
bool WriteStringToFile(bool textFile, std::string_view str, const Path &filename);
|
|
bool WriteDataToFile(bool textFile, const void* data, size_t size, const Path &filename);
|
|
|
|
bool ReadFileToStringOptions(bool textFile, bool allowShort, const Path &path, std::string *str);
|
|
|
|
// Wrappers that clarify the intentions.
|
|
inline bool ReadBinaryFileToString(const Path &path, std::string *str) {
|
|
return ReadFileToStringOptions(false, false, path, str);
|
|
}
|
|
inline bool ReadSysTextFileToString(const Path &path, std::string *str) {
|
|
return ReadFileToStringOptions(true, true, path, str);
|
|
}
|
|
inline bool ReadTextFileToString(const Path &path, std::string *str) {
|
|
return ReadFileToStringOptions(true, false, path, str);
|
|
}
|
|
|
|
// Return value must be delete[]-d.
|
|
uint8_t *ReadLocalFile(const Path &path, size_t *size);
|
|
|
|
} // namespace
|