diff --git a/rpcs3/Emu/Cell/Modules/cellFs.cpp b/rpcs3/Emu/Cell/Modules/cellFs.cpp index f8e7525597..ee56ab390b 100644 --- a/rpcs3/Emu/Cell/Modules/cellFs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellFs.cpp @@ -551,113 +551,6 @@ s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr return CELL_OK; } -bool sdata_check(u32 version, u32 flags, u64 filesizeInput, u64 filesizeTmp) -{ - if (version > 4 || flags & 0x7EFFFFC0){ - printf("ERROR: unknown version"); - return false; - } - - if ((version == 1 && (flags & 0x7FFFFFFE)) || - (version == 2 && (flags & 0x7EFFFFC0))){ - printf("ERROR: unknown or unsupported type"); - return false; - } - - if (filesizeTmp > filesizeInput){ - printf("ERROR: input file size is too short."); - return false; - } - - if (!(flags & 0x80000000)){ - printf("ERROR: cannot extract finalized edata."); - return false; - } - - return true; -} - -s32 sdata_unpack(const std::string& packed_file, const std::string& unpacked_file) -{ - fs::file packed_stream(vfs::get(packed_file)); - fs::file unpacked_stream(vfs::get(unpacked_file), fs::rewrite); - - if (!packed_stream) - { - cellFs.error("File '%s' not found!", packed_file); - return CELL_ENOENT; - } - - if (!unpacked_stream) - { - cellFs.error("File '%s' couldn't be created!", unpacked_file); - return CELL_ENOENT; - } - - char buffer[10200]; - packed_stream.read(buffer, 256); - u32 format = *(be_t*)&buffer[0]; - if (format != 0x4E504400) // "NPD\x00" - { - cellFs.error("Illegal format. Expected 0x4E504400, but got 0x%08x", format); - return CELL_EFSSPECIFIC; - } - - u32 version = *(be_t*)&buffer[0x04]; - u32 flags = *(be_t*)&buffer[0x80]; - u32 blockSize = *(be_t*)&buffer[0x84]; - u64 filesizeOutput = *(be_t*)&buffer[0x88]; - u64 filesizeInput = packed_stream.size(); - u32 blockCount = (u32)((filesizeOutput + blockSize - 1) / blockSize); - - // SDATA file is compressed - if (flags & 0x1) - { - cellFs.warning("cellFsSdataOpen: Compressed SDATA files are not supported yet."); - return CELL_EFSSPECIFIC; - } - // SDATA file is NOT compressed - else - { - u32 t1 = (flags & 0x20) ? 0x20 : 0x10; - u32 startOffset = (blockCount * t1) + 0x100; - u64 filesizeTmp = (filesizeOutput + 0xF) & 0xFFFFFFF0 + startOffset; - - if (!sdata_check(version, flags, filesizeInput, filesizeTmp)) - { - cellFs.error("cellFsSdataOpen: Wrong header information."); - return CELL_EFSSPECIFIC; - } - - if (flags & 0x20) - { - packed_stream.seek(0x100); - } - else - { - packed_stream.seek(startOffset); - } - - for (u32 i = 0; i < blockCount; i++) - { - if (flags & 0x20) - { - packed_stream.seek(t1, fs::seek_cur); - } - - if (!(blockCount - i - 1)) - { - blockSize = (u32)(filesizeOutput - i * blockSize); - } - - packed_stream.read(buffer + 256, blockSize); - unpacked_stream.write(buffer + 256, blockSize); - } - } - - return CELL_OK; -} - s32 cellFsSdataOpen(vm::cptr path, s32 flags, vm::ptr fd, vm::cptr arg, u64 size) { cellFs.notice("cellFsSdataOpen(path=%s, flags=%#o, fd=*0x%x, arg=*0x%x, size=0x%llx)", path, flags, fd, arg, size); @@ -667,33 +560,52 @@ s32 cellFsSdataOpen(vm::cptr path, s32 flags, vm::ptr fd, vm::cptr[2]>({ 0x180, 0x10 }), 8); - - // Don't implement sdata decryption in this function, it should be done in sys_fs_open() syscall or somewhere else - - /* - std::string suffix = path.substr(path.length() - 5, 5); - if (suffix != ".sdat" && suffix != ".SDAT") - return CELL_ENOTSDATA; - - std::string::size_type last_slash = path.rfind('/'); //TODO: use a filesystem library to solve this more robustly - last_slash = last_slash == std::string::npos ? 0 : last_slash+1; - std::string unpacked_path = "/dev_hdd1/"+path.substr(last_slash,path.length()-last_slash)+".unpacked"; - s32 ret = sdata_unpack(path, unpacked_path); - if (ret) return ret; - - fd = idm::GetNewID(Emu.GetVFS().OpenFile(unpacked_path, vfsRead), TYPE_FS_FILE); - - return CELL_OK; - */ + return cellFsOpen(path, CELL_FS_O_RDONLY, fd, vm::make_var[2]>({0x180, 0x10}), 8); } s32 cellFsSdataOpenByFd(u32 mself_fd, s32 flags, vm::ptr sdata_fd, u64 offset, vm::cptr arg, u64 size) { - cellFs.todo("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size); + cellFs.notice("cellFsSdataOpenByFd(mself_fd=0x%x, flags=%#o, sdata_fd=*0x%x, offset=0x%llx, arg=*0x%x, size=0x%llx)", mself_fd, flags, sdata_fd, offset, arg, size); - // TODO: + if (!sdata_fd) + { + return CELL_EFAULT; + } + + *sdata_fd = -1; + if (mself_fd < 3 || mself_fd > 255) + { + return CELL_EBADF; + } + + if (flags) + { + return CELL_EINVAL; + } + + vm::var ctrl; + ctrl->_vtable = vm::cast(0xfa880000); // Intentionally wrong (provide correct vtable if necessary) + ctrl->op = 0x80000009; + ctrl->fd = mself_fd; + ctrl->offset = offset; + ctrl->_vtabl2 = vm::cast(0xfa880020); + ctrl->arg1 = 0x180; + ctrl->arg2 = 0x10; + ctrl->arg_ptr = arg.addr(); + ctrl->arg_size = u32(size); + + if (const s32 rc = sys_fs_fcntl(mself_fd, 0x80000009, ctrl, 0x40)) + { + return rc; + } + + if (const s32 rc = ctrl->out_code) + { + return rc; + } + + *sdata_fd = ctrl->out_fd; return CELL_OK; } diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index e9a1bb79b4..10fd45e767 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -46,6 +46,73 @@ u64 lv2_file::op_write(vm::ps3::cptr buf, u64 size) return file.write(local_buf.get(), size); } +struct lv2_file::file_view : fs::file_base +{ + const std::shared_ptr m_file; + const u64 m_off; + u64 m_pos; + + explicit file_view(const std::shared_ptr& _file, u64 offset) + : m_file(_file) + , m_off(offset) + , m_pos(0) + { + } + + ~file_view() override + { + } + + fs::stat_t stat() override + { + return m_file->file.stat(); + } + + bool trunc(u64 length) override + { + return false; + } + + u64 read(void* buffer, u64 size) override + { + std::lock_guard lock(m_file->mp->mutex); + + const u64 old_pos = m_file->file.pos(); + const u64 new_pos = m_file->file.seek(m_off + m_pos); + const u64 result = m_file->file.read(buffer, size); + verify(HERE), old_pos == m_file->file.seek(old_pos); + + m_pos += result; + return result; + } + + u64 write(const void* buffer, u64 size) override + { + return 0; + } + + u64 seek(s64 offset, fs::seek_mode whence) override + { + return + whence == fs::seek_set ? m_pos = offset : + whence == fs::seek_cur ? m_pos = offset + m_pos : + whence == fs::seek_end ? m_pos = offset + size() : + (fmt::raw_error("lv2_file::file_view::seek(): invalid whence"), 0); + } + + u64 size() override + { + return m_off + m_file->file.size(); + } +}; + +fs::file lv2_file::make_view(const std::shared_ptr& _file, u64 offset) +{ + fs::file result; + result.reset(std::make_unique(_file, offset)); + return result; +} + error_code sys_fs_test(u32 arg1, u32 arg2, vm::ptr arg3, u32 arg4, vm::ptr arg5, u32 arg6) { sys_fs.todo("sys_fs_test(arg1=0x%x, arg2=0x%x, arg3=*0x%x, arg4=0x%x, arg5=*0x%x, arg6=0x%x) -> CELL_OK", arg1, arg2, arg3, arg4, arg5, arg6); @@ -475,6 +542,35 @@ error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr _arg, u32 _size) break; } + + case 0x80000009: // cellFsSdataOpenByFd + { + const auto arg = vm::static_ptr_cast(_arg); + + if (_size < arg.size()) + { + return CELL_EINVAL; + } + + const auto file = idm::get(fd); + + if (!file) + { + return CELL_EBADF; + } + + // TODO + if (const u32 id = idm::make(file->mp, lv2_file::make_view(file, arg->offset), file->mode, file->flags)) + { + arg->out_code = CELL_OK; + arg->out_fd = id; + return CELL_OK; + } + + // Out of file descriptors + return CELL_EMFILE; + } + default: { sys_fs.todo("sys_fs_fcntl(): Unknown operation 0x%08x (fd=%d, arg=*0x%x, size=0x%x)", op, fd, _arg, _size); diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.h b/rpcs3/Emu/Cell/lv2/sys_fs.h index 71a08b913c..6c1c92a887 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.h +++ b/rpcs3/Emu/Cell/lv2/sys_fs.h @@ -126,11 +126,25 @@ struct lv2_file final : lv2_fs_object { } + lv2_file(lv2_fs_mount_point* mp, fs::file&& file, s32 mode, s32 flags) + : lv2_fs_object(mp) + , file(std::move(file)) + , mode(mode) + , flags(flags) + { + } + // File reading with intermediate buffer u64 op_read(vm::ps3::ptr buf, u64 size); // File writing with intermediate buffer u64 op_write(vm::ps3::cptr buf, u64 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); }; struct lv2_dir final : lv2_fs_object @@ -181,6 +195,29 @@ struct lv2_file_op_rw : lv2_file_op 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 out_code; + be_t out_fd; +}; + +CHECK_SIZE(lv2_file_op_09, 0x40); + // Syscalls error_code sys_fs_test(u32 arg1, u32 arg2, vm::ps3::ptr arg3, u32 arg4, vm::ps3::ptr arg5, u32 arg6);