#pragma once #include "Emu/Memory/vm_ptr.h" #include "Emu/Cell/ErrorCodes.h" #include "Utilities/File.h" #include #include // Open Flags enum : s32 { CELL_FS_O_RDONLY = 000000, CELL_FS_O_WRONLY = 000001, CELL_FS_O_RDWR = 000002, CELL_FS_O_ACCMODE = 000003, CELL_FS_O_CREAT = 000100, CELL_FS_O_EXCL = 000200, CELL_FS_O_TRUNC = 001000, CELL_FS_O_APPEND = 002000, CELL_FS_O_MSELF = 010000, CELL_FS_O_UNK = 01000000, // Tests have shown this is independent of other flags. Only known to be called in Rockband games. }; // Seek Mode enum : s32 { CELL_FS_SEEK_SET, CELL_FS_SEEK_CUR, CELL_FS_SEEK_END, }; enum : s32 { CELL_FS_MAX_FS_PATH_LENGTH = 1024, CELL_FS_MAX_FS_FILE_NAME_LENGTH = 255, CELL_FS_MAX_MP_LENGTH = 31, }; enum : s32 { CELL_FS_S_IFMT = 0170000, CELL_FS_S_IFDIR = 0040000, // directory CELL_FS_S_IFREG = 0100000, // regular CELL_FS_S_IFLNK = 0120000, // symbolic link CELL_FS_S_IFWHT = 0160000, // unknown CELL_FS_S_IRUSR = 0000400, // R for owner CELL_FS_S_IWUSR = 0000200, // W for owner CELL_FS_S_IXUSR = 0000100, // X for owner CELL_FS_S_IRGRP = 0000040, // R for group CELL_FS_S_IWGRP = 0000020, // W for group CELL_FS_S_IXGRP = 0000010, // X for group CELL_FS_S_IROTH = 0000004, // R for other CELL_FS_S_IWOTH = 0000002, // W for other CELL_FS_S_IXOTH = 0000001, // X for other }; // CellFsDirent.d_type enum : u8 { CELL_FS_TYPE_UNKNOWN = 0, CELL_FS_TYPE_DIRECTORY = 1, CELL_FS_TYPE_REGULAR = 2, CELL_FS_TYPE_SYMLINK = 3, }; struct CellFsDirent { u8 d_type; u8 d_namlen; char d_name[256]; }; struct CellFsStat { be_t mode; be_t uid; be_t gid; be_t atime; be_t mtime; be_t ctime; be_t size; be_t blksize; }; CHECK_SIZE_ALIGN(CellFsStat, 52, 4); struct CellFsDirectoryEntry { CellFsStat attribute; CellFsDirent entry_name; }; struct CellFsUtimbuf { be_t actime; be_t modtime; }; CHECK_SIZE_ALIGN(CellFsUtimbuf, 16, 4); // MSelf file structs struct FsMselfHeader { be_t m_magic; be_t m_format_version; be_t m_file_size; be_t m_entry_num; be_t m_entry_size; u8 m_reserve[40]; }; struct FsMselfEntry { char m_name[32]; be_t m_offset; be_t m_size; u8 m_reserve[16]; }; enum class lv2_mp_flag { read_only, no_uid_gid, strict_get_block_size, cache, __bitset_enum_max }; enum class lv2_file_type { regular = 0, sdata, edata, }; struct lv2_fs_mount_point { const std::string_view root; const u32 sector_size = 512; const u32 block_size = 4096; const bs_t flags{}; mutable std::recursive_mutex mutex; }; extern lv2_fs_mount_point g_mp_sys_dev_hdd0; extern lv2_fs_mount_point g_mp_sys_dev_hdd1; struct lv2_fs_object { using id_type = lv2_fs_object; static const u32 id_base = 3; static const u32 id_step = 1; static const u32 id_count = 255 - id_base; // Mount Point const std::add_pointer_t mp; // File Name (max 1055) const std::array name; lv2_fs_object(lv2_fs_mount_point* mp, std::string_view filename) : mp(mp) , name(get_name(filename)) { } virtual ~lv2_fs_object() = default; static lv2_fs_mount_point* get_mp(std::string_view filename); static std::array get_name(std::string_view filename) { std::array name; if (filename.size() >= 0x420) { filename = filename.substr(0, 0x420 - 1); } filename.copy(name.data(), filename.size()); name[filename.size()] = 0; return name; } virtual std::string to_string() const { return {}; } }; struct lv2_file final : lv2_fs_object { fs::file file; const s32 mode; const s32 flags; std::string real_path; const lv2_file_type type; // Stream lock atomic_t lock{0}; // Some variables for convinience of data restoration struct save_restore_t { u64 seek_pos; u64 atime; u64 mtime; } restore_data{}; lv2_file(std::string_view filename, fs::file&& file, s32 mode, s32 flags, const std::string& real_path, lv2_file_type type = {}) : lv2_fs_object(lv2_fs_object::get_mp(filename), filename) , file(std::move(file)) , mode(mode) , flags(flags) , real_path(real_path) , type(type) { } lv2_file(const lv2_file& host, fs::file&& file, s32 mode, s32 flags, const std::string& real_path, lv2_file_type type = {}) : lv2_fs_object(host.mp, host.name.data()) , file(std::move(file)) , mode(mode) , flags(flags) , real_path(real_path) , type(type) { } struct open_raw_result_t { CellError error; fs::file file; }; struct open_result_t { CellError error; std::string ppath; std::string real_path; fs::file file; lv2_file_type type; }; // Open a file with wrapped logic of sys_fs_open static open_raw_result_t open_raw(const std::string& path, s32 flags, s32 mode, lv2_file_type type = lv2_file_type::regular, const lv2_fs_mount_point* mp = nullptr); static open_result_t open(std::string_view vpath, s32 flags, s32 mode, const void* arg = {}, u64 size = 0); // File reading with intermediate buffer static u64 op_read(const fs::file& file, vm::ptr buf, u64 size); u64 op_read(vm::ptr buf, u64 size) { return op_read(file, buf, size); } // File writing with intermediate buffer static u64 op_write(const fs::file& file, vm::cptr buf, u64 size); u64 op_write(vm::cptr buf, u64 size) { return op_write(file, buf, size); } // For MSELF support struct file_view; // Make file view from lv2_file object (for MSELF support) static fs::file make_view(const std::shared_ptr& _file, u64 offset); virtual std::string to_string() const override { std::string_view type_s; switch (type) { case lv2_file_type::regular: type_s = "Regular file"; break; case lv2_file_type::sdata: type_s = "SDATA"; break; case lv2_file_type::edata: type_s = "EDATA"; break; } return fmt::format(u8"%s, ā€œ%sā€, Mode: 0x%x, Flags: 0x%x", type_s, name.data(), mode, flags); } }; struct lv2_dir final : lv2_fs_object { const std::vector entries; // Current reading position atomic_t pos{0}; lv2_dir(std::string_view filename, std::vector&& entries) : lv2_fs_object(lv2_fs_object::get_mp(filename), filename) , entries(std::move(entries)) { } // Read next const fs::dir_entry* dir_read() { if (const u64 cur = pos++; cur < entries.size()) { return &entries[cur]; } return nullptr; } virtual std::string to_string() const override { return fmt::format(u8"Directory, ā€œ%sā€, Entries: %u/%u", name.data(), std::min(pos, entries.size()), entries.size()); } }; // sys_fs_fcntl arg base class (left empty for PODness) struct lv2_file_op { }; namespace vtable { struct lv2_file_op { // Speculation vm::bptrb(vm::ptrb)> get_data; vm::bptrb)> get_size; vm::bptrb)> _dtor1; vm::bptrb)> _dtor2; }; } // sys_fs_fcntl: read with offset, write with offset struct lv2_file_op_rw : lv2_file_op { vm::bptrb _vtable; be_t op; be_t _x8; // ??? be_t _xc; // ??? be_t fd; // File descriptor (3..255) vm::bptrb buf; // Buffer for data be_t offset; // File offset be_t size; // Access size be_t out_code; // Op result be_t out_size; // Size processed }; CHECK_SIZE(lv2_file_op_rw, 0x38); // sys_fs_fcntl: cellFsSdataOpenByFd struct lv2_file_op_09 : lv2_file_op { vm::bptrb _vtable; be_t op; be_t _x8; be_t _xc; be_t fd; be_t offset; be_t _vtabl2; be_t arg1; // 0x180 be_t arg2; // 0x10 be_t arg_size; // 6th arg be_t arg_ptr; // 5th arg be_t _x34; be_t out_code; be_t out_fd; }; CHECK_SIZE(lv2_file_op_09, 0x40); // sys_fs_fnctl: cellFsGetDirectoryEntries struct lv2_file_op_dir : lv2_file_op { struct dir_info : lv2_file_op { be_t _code; // Op result be_t _size; // Number of entries written vm::bptrb ptr; be_t max; }; CHECK_SIZE(dir_info, 0x10); vm::bptrb _vtable; be_t op; be_t _x8; dir_info arg; }; CHECK_SIZE(lv2_file_op_dir, 0x1c); // sys_fs_fcntl: cellFsGetFreeSize (for dev_hdd0) struct lv2_file_c0000002 : lv2_file_op { vm::bptrb _vtable; be_t op; be_t _x8; vm::bcptr path; be_t _x10; // 0 be_t _x14; be_t out_code; // CELL_ENOSYS be_t out_block_size; be_t out_block_count; }; CHECK_SIZE(lv2_file_c0000002, 0x28); // sys_fs_fcntl: unknown (called before cellFsOpen, for example) struct lv2_file_c0000006 : lv2_file_op { be_t size; // 0x20 be_t _x4; // 0x10 be_t _x8; // 0x18 - offset of out_code be_t name_size; vm::bcptr name; be_t _x14; // 0 be_t out_code; // 0x80010003 be_t out_id; // set to 0, may return 0x1b5 }; CHECK_SIZE(lv2_file_c0000006, 0x20); // sys_fs_fcntl: cellFsAllocateFileAreaWithoutZeroFill struct lv2_file_e0000017 : lv2_file_op { be_t size; // 0x28 be_t _x4; // 0x10, offset be_t _x8; // 0x20, offset be_t _xc; // - vm::bcptr file_path; be_t file_size; be_t out_code; }; CHECK_SIZE(lv2_file_e0000017, 0x28); struct CellFsMountInfo { char mount_path[0x20]; // 0x0 char filesystem[0x20]; // 0x20 char dev_name[0x40]; // 0x40 be_t unk1; // 0x80 be_t unk2; // 0x84 be_t unk3; // 0x88 be_t unk4; // 0x8C be_t unk5; // 0x90 }; CHECK_SIZE(CellFsMountInfo, 0x94); // Syscalls error_code sys_fs_test(ppu_thread& ppu, u32 arg1, u32 arg2, vm::ptr arg3, u32 arg4, vm::ptr buf, u32 buf_size); error_code sys_fs_open(ppu_thread& ppu, vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::cptr arg, u64 size); error_code sys_fs_read(ppu_thread& ppu, u32 fd, vm::ptr buf, u64 nbytes, vm::ptr nread); error_code sys_fs_write(ppu_thread& ppu, u32 fd, vm::cptr buf, u64 nbytes, vm::ptr nwrite); error_code sys_fs_close(ppu_thread& ppu, u32 fd); error_code sys_fs_opendir(ppu_thread& ppu, vm::cptr path, vm::ptr fd); error_code sys_fs_readdir(ppu_thread& ppu, u32 fd, vm::ptr dir, vm::ptr nread); error_code sys_fs_closedir(ppu_thread& ppu, u32 fd); error_code sys_fs_stat(ppu_thread& ppu, vm::cptr path, vm::ptr sb); error_code sys_fs_fstat(ppu_thread& ppu, u32 fd, vm::ptr sb); error_code sys_fs_link(ppu_thread& ppu, vm::cptr from, vm::cptr to); error_code sys_fs_mkdir(ppu_thread& ppu, vm::cptr path, s32 mode); error_code sys_fs_rename(ppu_thread& ppu, vm::cptr from, vm::cptr to); error_code sys_fs_rmdir(ppu_thread& ppu, vm::cptr path); error_code sys_fs_unlink(ppu_thread& ppu, vm::cptr path); error_code sys_fs_access(ppu_thread& ppu, vm::cptr path, s32 mode); error_code sys_fs_fcntl(ppu_thread& ppu, u32 fd, u32 op, vm::ptr arg, u32 size); error_code sys_fs_lseek(ppu_thread& ppu, u32 fd, s64 offset, s32 whence, vm::ptr pos); error_code sys_fs_fdatasync(ppu_thread& ppu, u32 fd); error_code sys_fs_fsync(ppu_thread& ppu, u32 fd); error_code sys_fs_fget_block_size(ppu_thread& ppu, u32 fd, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4, vm::ptr out_flags); error_code sys_fs_get_block_size(ppu_thread& ppu, vm::cptr path, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4); error_code sys_fs_truncate(ppu_thread& ppu, vm::cptr path, u64 size); error_code sys_fs_ftruncate(ppu_thread& ppu, u32 fd, u64 size); error_code sys_fs_symbolic_link(ppu_thread& ppu, vm::cptr target, vm::cptr linkpath); error_code sys_fs_chmod(ppu_thread& ppu, vm::cptr path, s32 mode); error_code sys_fs_chown(ppu_thread& ppu, vm::cptr path, s32 uid, s32 gid); error_code sys_fs_disk_free(ppu_thread& ppu, vm::cptr path, vm::ptr total_free, vm::ptr avail_free); error_code sys_fs_utime(ppu_thread& ppu, vm::cptr path, vm::cptr timep); error_code sys_fs_acl_read(ppu_thread& ppu, vm::cptr path, vm::ptr); error_code sys_fs_acl_write(ppu_thread& ppu, vm::cptr path, vm::ptr); error_code sys_fs_lsn_get_cda_size(ppu_thread& ppu, u32 fd, vm::ptr ptr); error_code sys_fs_lsn_get_cda(ppu_thread& ppu, u32 fd, vm::ptr, u64, vm::ptr); error_code sys_fs_lsn_lock(ppu_thread& ppu, u32 fd); error_code sys_fs_lsn_unlock(ppu_thread& ppu, u32 fd); error_code sys_fs_lsn_read(ppu_thread& ppu, u32 fd, vm::cptr, u64); error_code sys_fs_lsn_write(ppu_thread& ppu, u32 fd, vm::cptr, u64); error_code sys_fs_mapped_allocate(ppu_thread& ppu, u32 fd, u64, vm::pptr out_ptr); error_code sys_fs_mapped_free(ppu_thread& ppu, u32 fd, vm::ptr ptr); error_code sys_fs_truncate2(ppu_thread& ppu, u32 fd, u64 size); error_code sys_fs_mount(ppu_thread& ppu, vm::cptr dev_name, vm::cptr file_system, vm::cptr path, s32 unk1, s32 prot, s32 unk3, vm::cptr str1, u32 str_len); error_code sys_fs_get_mount_info_size(ppu_thread& ppu, vm::ptr len); error_code sys_fs_get_mount_info(ppu_thread& ppu, vm::ptr info, u32 len, vm::ptr out_len);