mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-07-25 11:34:55 +00:00
* sceKernelOpen: Clean up flag handling
* sceKernelOpen: fix params
Based on decompilation, the second parameter of _open should be flags.
Additionally swaps the return and parameter types to align with our current standards.
* sceKernelOpen: Fix errors
Based on POSIX spec, if part of the path is missing, ENOENT is the correct return.
Additionally, decompilation suggests that open sets errno too.
* Fix exports
Fixes function exports to align with what they should be, based on what I've seen from decompilation and our module generator.
* Proper errno behavior on open
Since sceKernelOpen calls posix_open, errno should be set during this process.
Simplest way to handle that is to move the actual open code to posix_open and adjust error cases accordingly.
* Reorganize open calls, add error log
* Improve close
Removes the EPERM return, as it makes no sense, and swaps sceKernelClose with posix_close to properly emulate errno behavior.
* Fix log on close
* posix_open fixups
* Readd hack in posix_close
It's either this, or removing LLE DiscMap.
Or shadow implements posix sockets.
* Missing exports
Commented out while I gradually work through them all
* Remaining placeholder exports
* Swap some stuff around
I see nothing that suggests "open" only takes two parameters, so this should be completely safe.
It's also more accurate to how these are handled in libkernel, and means I won't need to reorganize anything for readv and writev.
* Update file_system.cpp
* Implement write and posix_write
* Oops
* Implement posix_readv and sceKernelReadv
Also fixes error behavior on readv, as that function shouldn't be returning any kernel error codes.
* Move sceKernelUnlink
Will deal with this one later, was just annoyed by how it's location doesn't align with the export order.
* Cleanup readv
* Implement posix_writev and sceKernelWritev
Also fixes error behavior on writev, since it shouldn't ever return kernel errors (since our device files return those)
* More cleanup on older functions
* Swap around sceKernelLseek and posix_lseek
This ensures that these have the correct error behavior, and makes their behavior align with the updated implementations for earlier functions.
* Update file_system.cpp
* Implement read
Also fixes error behavior
* Swap sceKernelMkdir and posix_mkdir
Fixes errno behavior on kernel calls, also fixed some incorrect error returns.
* Fix errno behavior on sceKernelRmdir
Also reduces function logging to bring it closer to the level of logging seen in other filesystem functions.
* Slight clean up of sceKernelStat
Fixes error behavior and changes some of the old data types.
* Refactor fstat
Fixes errno behavior, implements fstat, and shifts exports around based on file position.
Might reorganize function locations later though.
* Implement posix_ftruncate
Implements posix_ftruncate and fixes errno behavior for sceKernelFtruncate
* Add missing error conversions for more device functions
* Implement posix_rename, fix sceKernelRename errno behavior
* Add posix_preadv and posix_pread
Also fixes some incorrect error returns, fixes errno behavior, and removes an unnecessary hack.
* Fix compile
* Implement posix_getdents, getdirentries, and posix_getdirentries
Also fixes errno behavior for the kernel variants of these functions.
* Fix errno behavior of sceKernelFsync
* Implement posix_pwrite and posix_unlink
Also fixes errno behavior in related functions.
* Update file_system.cpp
* Remove SetPosixErrno
Ideally, we've handled all possible error conditions before calling these functions, so treat errors in platform-specific code as IO errors and return POSIX_EIO instead.
* Update header exports
Not sure where these get used, but might as well keep them consistent with the rest of this.
* Check if file exists before calling platform-specific code
Bloodborne checks if a file doesn't exist using open, checking if it specifically failed with error code ENOENT. To avoid working with platform-specific errnos, add a proper error return for if the file doesn't exist.
Fixes a regression in Bloodborne.
* Clang
Out of all the changes, this is apparently the only thing Clang-Format doesn't like.
I'm honestly surprised.
* Improve error checks on posix_unlink
Just because a file isn't opened doesn't mean the file doesn't exist.
Fixes the error returned if host_path.empty(), and removes the error return for when GetFile fails.
* Fix the Bloodborne fix
* Limit exports to tested functions
* More confirmed working exports
* Remaining stuff my games can test
* FS exports from firmware tests
* Bring back missing exports from main
I don't have any bootable things that call these, but since they were working well enough on main, they should be fine to readd.
* Add export for posix_pread
Spotted in Dreams a while back, might as well add it.
* Revert "Remove SetPosixErrno"
This reverts commit bdfc0c246c
.
* Revert SetPosixErrno changes
shadow's using it for posix sockets, so significant modifications would introduce unnecessary merge conflicts.
* Update comment
* Add EACCES errno to SetPosixErrno
Seen in Gravity Rush.
Also reorganizes the switch case based on the posix errno value, since ordering by errno makes no sense on some OSes.
* More export fixups
Missed these during my initial pass through FS stuff because they were in kernel.cpp for some reason.
* Symbols from FS tests
Tested by messing around with firmware elfs, these atleast don't cause any crashes.
* Remove inaccurate error behavior
Seek can have offsets past the end of a file.
Also add logging for two valid whence values that are unsupported on Windows.
I'll need to verify that SEEK_HOLE and SEEK_DATA correspond to 3 and 4 respectively, I've yet to check source to verify.
* Fix error log
Oops
* Clang
Clang
* Remove close hack
Since LLE libSceDiscMap is no longer a concern, this hack shouldn't be needed.
Since sockets are still stubbed, and close can be used on sockets, I've added a warning log just in case this still occurs in some titles.
* Change SetPosixErrno unreachable to warning
I changed it to an unreachable in an earlier commit to make testing easier.
At this point, having an unreachable for this seems unnecessary, so change it to a warning instead.
* Remove Bloodborne hack
Games should be able to unlink files that aren't opened file descriptors. As far as I've tested, this doesn't break Bloodborne.
229 lines
6.9 KiB
C++
229 lines
6.9 KiB
C++
// SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <cstdio>
|
|
#include <filesystem>
|
|
#include <span>
|
|
#include <type_traits>
|
|
|
|
#include "common/concepts.h"
|
|
#include "common/types.h"
|
|
#include "enum.h"
|
|
|
|
namespace Common::FS {
|
|
|
|
enum class FileAccessMode {
|
|
/**
|
|
* If the file at path exists, it opens the file for reading.
|
|
* If the file at path does not exist, it fails to open the file.
|
|
*/
|
|
Read = 1 << 0,
|
|
/**
|
|
* If the file at path exists, the existing contents of the file are erased.
|
|
* The empty file is then opened for writing.
|
|
* If the file at path does not exist, it creates and opens a new empty file for writing.
|
|
*/
|
|
Write = 1 << 1,
|
|
/**
|
|
* If the file at path exists, it opens the file for reading and writing.
|
|
* If the file at path does not exist, it fails to open the file.
|
|
*/
|
|
ReadWrite = Read | Write,
|
|
/**
|
|
* If the file at path exists, it opens the file for appending.
|
|
* If the file at path does not exist, it creates and opens a new empty file for appending.
|
|
*/
|
|
Append = 1 << 2,
|
|
/**
|
|
* If the file at path exists, it opens the file for both reading and appending.
|
|
* If the file at path does not exist, it creates and opens a new empty file for both
|
|
* reading and appending.
|
|
*/
|
|
ReadAppend = Read | Append,
|
|
};
|
|
DECLARE_ENUM_FLAG_OPERATORS(FileAccessMode);
|
|
|
|
enum class FileType {
|
|
BinaryFile,
|
|
TextFile,
|
|
};
|
|
|
|
enum class FileShareFlag {
|
|
ShareNone, // Provides exclusive access to the file.
|
|
ShareReadOnly, // Provides read only shared access to the file.
|
|
ShareWriteOnly, // Provides write only shared access to the file.
|
|
ShareReadWrite, // Provides read and write shared access to the file.
|
|
};
|
|
|
|
enum class SeekOrigin : u32 {
|
|
SetOrigin, // Seeks from the start of the file.
|
|
CurrentPosition, // Seeks from the current file pointer position.
|
|
End, // Seeks from the end of the file.
|
|
SeekHole, // Seeks from the start of the next hole in the file.
|
|
SeekData, // Seeks from the start of the next non-hole region in the file.
|
|
};
|
|
|
|
class IOFile final {
|
|
public:
|
|
IOFile();
|
|
|
|
explicit IOFile(const std::string& path, FileAccessMode mode,
|
|
FileType type = FileType::BinaryFile,
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
explicit IOFile(std::string_view path, FileAccessMode mode,
|
|
FileType type = FileType::BinaryFile,
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
explicit IOFile(const std::filesystem::path& path, FileAccessMode mode,
|
|
FileType type = FileType::BinaryFile,
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
|
|
~IOFile();
|
|
|
|
IOFile(const IOFile&) = delete;
|
|
IOFile& operator=(const IOFile&) = delete;
|
|
|
|
IOFile(IOFile&& other) noexcept;
|
|
IOFile& operator=(IOFile&& other) noexcept;
|
|
|
|
std::filesystem::path GetPath() const {
|
|
return file_path;
|
|
}
|
|
|
|
FileAccessMode GetAccessMode() const {
|
|
return file_access_mode;
|
|
}
|
|
|
|
FileType GetType() const {
|
|
return file_type;
|
|
}
|
|
|
|
bool IsOpen() const {
|
|
return file != nullptr;
|
|
}
|
|
|
|
uintptr_t GetFileMapping();
|
|
|
|
int Open(const std::filesystem::path& path, FileAccessMode mode,
|
|
FileType type = FileType::BinaryFile,
|
|
FileShareFlag flag = FileShareFlag::ShareReadOnly);
|
|
void Close();
|
|
|
|
void Unlink();
|
|
|
|
bool Flush() const;
|
|
bool Commit() const;
|
|
|
|
bool SetSize(u64 size) const;
|
|
u64 GetSize() const;
|
|
|
|
bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const;
|
|
s64 Tell() const;
|
|
|
|
template <typename T>
|
|
size_t Read(T& data) const {
|
|
if constexpr (IsContiguousContainer<T>) {
|
|
using ContiguousType = typename T::value_type;
|
|
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
|
"Data type must be trivially copyable.");
|
|
return ReadSpan<ContiguousType>(data);
|
|
} else {
|
|
return ReadObject(data) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
size_t Write(const T& data) const {
|
|
if constexpr (IsContiguousContainer<T>) {
|
|
using ContiguousType = typename T::value_type;
|
|
static_assert(std::is_trivially_copyable_v<ContiguousType>,
|
|
"Data type must be trivially copyable.");
|
|
return WriteSpan<ContiguousType>(data);
|
|
} else {
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
return WriteObject(data) ? 1 : 0;
|
|
}
|
|
}
|
|
|
|
template <typename T>
|
|
size_t ReadSpan(std::span<T> data) const {
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
if (!IsOpen()) {
|
|
return 0;
|
|
}
|
|
|
|
return ReadRaw<T>(data.data(), data.size());
|
|
}
|
|
|
|
template <typename T>
|
|
size_t ReadRaw(void* data, size_t size) const {
|
|
return std::fread(data, sizeof(T), size, file);
|
|
}
|
|
|
|
template <typename T>
|
|
size_t WriteSpan(std::span<const T> data) const {
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
|
|
if (!IsOpen()) {
|
|
return 0;
|
|
}
|
|
|
|
return std::fwrite(data.data(), sizeof(T), data.size(), file);
|
|
}
|
|
|
|
template <typename T>
|
|
bool ReadObject(T& object) const {
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
|
|
|
if (!IsOpen()) {
|
|
return false;
|
|
}
|
|
|
|
return std::fread(&object, sizeof(T), 1, file) == 1;
|
|
}
|
|
|
|
template <typename T>
|
|
size_t WriteRaw(const void* data, size_t size) const {
|
|
return std::fwrite(data, sizeof(T), size, file);
|
|
}
|
|
|
|
template <typename T>
|
|
bool WriteObject(const T& object) const {
|
|
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
|
|
static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object.");
|
|
|
|
if (!IsOpen()) {
|
|
return false;
|
|
}
|
|
|
|
return std::fwrite(&object, sizeof(T), 1, file) == 1;
|
|
}
|
|
|
|
std::string ReadString(size_t length) const;
|
|
|
|
size_t WriteString(std::span<const char> string) const {
|
|
return WriteSpan(string);
|
|
}
|
|
|
|
static size_t WriteBytes(const std::filesystem::path path, const auto& data) {
|
|
IOFile out(path, FileAccessMode::Write);
|
|
return out.Write(data);
|
|
}
|
|
|
|
private:
|
|
std::filesystem::path file_path;
|
|
FileAccessMode file_access_mode{};
|
|
FileType file_type{};
|
|
|
|
std::FILE* file = nullptr;
|
|
uintptr_t file_mapping = 0;
|
|
};
|
|
|
|
u64 GetDirectorySize(const std::filesystem::path& path);
|
|
|
|
} // namespace Common::FS
|