diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 05a3f584f8..9a7d9370c6 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -80,6 +80,8 @@ static fs::error to_error(DWORD e) case ERROR_ALREADY_EXISTS: return fs::error::exist; case ERROR_FILE_EXISTS: return fs::error::exist; case ERROR_NEGATIVE_SEEK: return fs::error::inval; + case ERROR_DIRECTORY: return fs::error::inval; + case ERROR_INVALID_NAME: return fs::error::inval; default: throw fmt::exception("Unknown Win32 error: %u.", e); } } diff --git a/Utilities/lockless.h b/Utilities/lockless.h index 863fe1d647..c6890722b2 100644 --- a/Utilities/lockless.h +++ b/Utilities/lockless.h @@ -103,3 +103,46 @@ public: }); } }; + +//! Simple lock-free map. Based on lf_array<>. All elements are accessible, implicitly initialized. +template, std::size_t Size = 256> +class lf_hashmap +{ + struct pair_t + { + // Default-constructed key means "no key" + atomic_t key{}; + T value{}; + }; + + // + lf_array m_data{}; + + // Value for default-constructed key + T m_default_key_data{}; + +public: + constexpr lf_hashmap() = default; + + // Access element (added implicitly) + T& operator [](const K& key) + { + if (UNLIKELY(key == K{})) + { + return m_default_key_data; + } + + // Calculate hash and array position + for (std::size_t pos = Hash{}(key) % Size;; pos += Size) + { + // Access the array + auto& pair = m_data[pos]; + + // Check the key value (optimistic) + if (LIKELY(pair.key == key) || pair.key.compare_and_swap_test(K{}, key)) + { + return pair.value; + } + } + } +}; diff --git a/Utilities/types.h b/Utilities/types.h index ad61bce0e2..8483e1b698 100644 --- a/Utilities/types.h +++ b/Utilities/types.h @@ -423,6 +423,15 @@ struct pointer_hash } }; +template +struct value_hash +{ + std::size_t operator()(T value) const + { + return static_cast(value) >> Shift; + } +}; + // Contains value of any POD type with fixed size and alignment. TT<> is the type converter applied. // For example, `simple_t` may be used to remove endianness. template class TT, std::size_t S, std::size_t A = S> diff --git a/rpcs3/Emu/Cell/Modules/cellFs.cpp b/rpcs3/Emu/Cell/Modules/cellFs.cpp index c0ca6a1ed8..56ca4a3524 100644 --- a/rpcs3/Emu/Cell/Modules/cellFs.cpp +++ b/rpcs3/Emu/Cell/Modules/cellFs.cpp @@ -58,8 +58,13 @@ s32 cellFsReaddir(u32 fd, vm::ptr dir, vm::ptr nread) { cellFs.trace("cellFsReaddir(fd=0x%x, dir=*0x%x, nread=*0x%x)", fd, dir, nread); + if (!dir || !nread) + { + return CELL_EFAULT; + } + // call the syscall - return dir && nread ? sys_fs_readdir(fd, dir, nread) : CELL_FS_EFAULT; + return sys_fs_readdir(fd, dir, nread); } s32 cellFsClosedir(u32 fd) @@ -132,8 +137,13 @@ s32 cellFsLseek(u32 fd, s64 offset, u32 whence, vm::ptr pos) { cellFs.trace("cellFsLseek(fd=0x%x, offset=0x%llx, whence=0x%x, pos=*0x%x)", fd, offset, whence, pos); + if (!pos) + { + return CELL_EFAULT; + } + // call the syscall - return pos ? sys_fs_lseek(fd, offset, whence, pos) : CELL_FS_EFAULT; + return sys_fs_lseek(fd, offset, whence, pos); } s32 cellFsFsync(u32 fd) @@ -147,8 +157,13 @@ s32 cellFsFGetBlockSize(u32 fd, vm::ptr sector_size, vm::ptr block_siz { cellFs.trace("cellFsFGetBlockSize(fd=0x%x, sector_size=*0x%x, block_size=*0x%x)", fd, sector_size, block_size); + if (!sector_size || !block_size) + { + return CELL_EFAULT; + } + // call the syscall - return sector_size && block_size ? sys_fs_fget_block_size(fd, sector_size, block_size, vm::var{}, vm::var{}) : CELL_FS_EFAULT; + return sys_fs_fget_block_size(fd, sector_size, block_size, vm::var{}, vm::var{}); } s32 cellFsGetBlockSize(vm::cptr path, vm::ptr sector_size, vm::ptr block_size) @@ -205,11 +220,11 @@ s32 cellFsGetDirectoryEntries(u32 fd, vm::ptr entries, u32 { cellFs.warning("cellFsGetDirectoryEntries(fd=%d, entries=*0x%x, entries_size=0x%x, data_count=*0x%x)", fd, entries, entries_size, data_count); - const auto directory = idm::get(fd); + const auto directory = idm::get(fd); if (!directory) { - return CELL_FS_EBADF; + return CELL_EBADF; } u32 count = 0; @@ -246,474 +261,278 @@ s32 cellFsGetDirectoryEntries(u32 fd, vm::ptr entries, u32 return CELL_OK; } -s32 cellFsReadWithOffset(u32 fd, u64 offset, vm::ptr buf, u64 buffer_size, vm::ptr nread) +ppu_error_code cellFsReadWithOffset(u32 fd, u64 offset, vm::ptr buf, u64 buffer_size, vm::ptr nread) { cellFs.trace("cellFsReadWithOffset(fd=%d, offset=0x%llx, buf=*0x%x, buffer_size=0x%llx, nread=*0x%x)", fd, offset, buf, buffer_size, nread); - // TODO: use single sys_fs_fcntl syscall - - const auto file = idm::get(fd); - - if (!file || file->flags & CELL_FS_O_WRONLY) + if (fd - 3 > 252) { - return CELL_FS_EBADF; + if (nread) *nread = 0; + return CELL_EBADF; } - std::lock_guard lock(file->mutex); + vm::var arg; - const auto old_pos = file->file.pos(); file->file.seek(offset); + arg->_vtable = vm::cast(0xfa8a0000); // Intentionally wrong (provide correct vtable if necessary) + + arg->op = 0x8000000a; + arg->fd = fd; + arg->buf = buf; + arg->offset = offset; + arg->size = buffer_size; - const auto read = file->file.read(buf.get_ptr(), buffer_size); + // Call the syscall + const s32 rc = sys_fs_fcntl(fd, 0x8000000a, arg, arg.size()); - file->file.seek(old_pos); + // Write size read + if (nread) *nread = rc && rc != CELL_EFSSPECIFIC ? 0 : arg->out_size.value(); - if (nread) - { - *nread = read; - } - - return CELL_OK; + return NOT_AN_ERROR(rc ? rc : arg->out_code.value()); } -s32 cellFsWriteWithOffset(u32 fd, u64 offset, vm::cptr buf, u64 data_size, vm::ptr nwrite) +ppu_error_code cellFsWriteWithOffset(u32 fd, u64 offset, vm::cptr buf, u64 data_size, vm::ptr nwrite) { cellFs.trace("cellFsWriteWithOffset(fd=%d, offset=0x%llx, buf=*0x%x, data_size=0x%llx, nwrite=*0x%x)", fd, offset, buf, data_size, nwrite); - // TODO: use single sys_fs_fcntl syscall - - const auto file = idm::get(fd); - - if (!file || !(file->flags & CELL_FS_O_ACCMODE)) + if (!buf) { - return CELL_FS_EBADF; + if (nwrite) *nwrite = 0; + return CELL_EFAULT; } - std::lock_guard lock(file->mutex); - - const auto old_pos = file->file.pos(); file->file.seek(offset); - - const auto written = file->file.write(buf.get_ptr(), data_size); - - file->file.seek(old_pos); - - if (nwrite) + if (fd - 3 > 252) { - *nwrite = written; + if (nwrite) *nwrite = 0; + return CELL_EBADF; } - return CELL_OK; + vm::var arg; + + arg->_vtable = vm::cast(0xfa8b0000); // Intentionally wrong (provide correct vtable if necessary) + + arg->op = 0x8000000b; + arg->fd = fd; + arg->buf = vm::const_ptr_cast(buf); + arg->offset = offset; + arg->size = data_size; + + // Call the syscall + const s32 rc = sys_fs_fcntl(fd, 0x8000000b, arg, arg.size()); + + // Write size written + if (nwrite) *nwrite = rc && rc != CELL_EFSSPECIFIC ? 0 : arg->out_size.value(); + + return NOT_AN_ERROR(rc ? rc : arg->out_code.value()); } s32 cellFsStReadInit(u32 fd, vm::cptr ringbuf) { - cellFs.warning("cellFsStReadInit(fd=%d, ringbuf=*0x%x)", fd, ringbuf); + cellFs.todo("cellFsStReadInit(fd=%d, ringbuf=*0x%x)", fd, ringbuf); if (ringbuf->copy & ~CELL_FS_ST_COPYLESS) { - return CELL_FS_EINVAL; + return CELL_EINVAL; } if (ringbuf->block_size & 0xfff) // check if a multiple of sector size { - return CELL_FS_EINVAL; + return CELL_EINVAL; } if (ringbuf->ringbuf_size % ringbuf->block_size) // check if a multiple of block_size { - return CELL_FS_EINVAL; + return CELL_EINVAL; } - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } if (file->flags & CELL_FS_O_WRONLY) { - return CELL_FS_EPERM; + return CELL_EPERM; } - std::lock_guard lock(file->mutex); - - if (!file->st_status.compare_and_swap_test(SSS_NOT_INITIALIZED, SSS_INITIALIZED)) - { - return CELL_FS_EBUSY; - } - - file->st_ringbuf_size = ringbuf->ringbuf_size; - file->st_block_size = ringbuf->ringbuf_size; - file->st_trans_rate = ringbuf->transfer_rate; - file->st_copyless = ringbuf->copy == CELL_FS_ST_COPYLESS; - - const u64 alloc_size = align(file->st_ringbuf_size, file->st_ringbuf_size < 1024 * 1024 ? 64 * 1024 : 1024 * 1024); - - file->st_buffer = vm::alloc(static_cast(alloc_size), vm::main); - file->st_read_size = 0; - file->st_total_read = 0; - file->st_copied = 0; + // TODO return CELL_OK; } s32 cellFsStReadFinish(u32 fd) { - cellFs.warning("cellFsStReadFinish(fd=%d)", fd); + cellFs.todo("cellFsStReadFinish(fd=%d)", fd); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; // ??? + return CELL_EBADF; // ??? } - std::lock_guard lock(file->mutex); - - if (!file->st_status.compare_and_swap_test(SSS_INITIALIZED, SSS_NOT_INITIALIZED)) - { - return CELL_FS_ENXIO; - } - - vm::dealloc(file->st_buffer, vm::main); + // TODO return CELL_OK; } s32 cellFsStReadGetRingBuf(u32 fd, vm::ptr ringbuf) { - cellFs.warning("cellFsStReadGetRingBuf(fd=%d, ringbuf=*0x%x)", fd, ringbuf); + cellFs.todo("cellFsStReadGetRingBuf(fd=%d, ringbuf=*0x%x)", fd, ringbuf); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED) - { - return CELL_FS_ENXIO; - } - - ringbuf->ringbuf_size = file->st_ringbuf_size; - ringbuf->block_size = file->st_block_size; - ringbuf->transfer_rate = file->st_trans_rate; - ringbuf->copy = file->st_copyless ? CELL_FS_ST_COPYLESS : CELL_FS_ST_COPY; + // TODO return CELL_OK; } s32 cellFsStReadGetStatus(u32 fd, vm::ptr status) { - cellFs.warning("cellFsStReadGetRingBuf(fd=%d, status=*0x%x)", fd, status); + cellFs.todo("cellFsStReadGetRingBuf(fd=%d, status=*0x%x)", fd, status); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - switch (file->st_status.load()) - { - case SSS_INITIALIZED: - case SSS_STOPPED: - { - *status = CELL_FS_ST_INITIALIZED | CELL_FS_ST_STOP; - break; - } - case SSS_STARTED: - { - *status = CELL_FS_ST_INITIALIZED | CELL_FS_ST_PROGRESS; - break; - } - default: - { - *status = CELL_FS_ST_NOT_INITIALIZED | CELL_FS_ST_STOP; - break; - } - } + // TODO return CELL_OK; } s32 cellFsStReadGetRegid(u32 fd, vm::ptr regid) { - cellFs.warning("cellFsStReadGetRingBuf(fd=%d, regid=*0x%x)", fd, regid); + cellFs.todo("cellFsStReadGetRingBuf(fd=%d, regid=*0x%x)", fd, regid); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED) - { - return CELL_FS_ENXIO; - } - - *regid = file->st_total_read - file->st_copied; + // TODO return CELL_OK; } s32 cellFsStReadStart(u32 fd, u64 offset, u64 size) { - cellFs.warning("cellFsStReadStart(fd=%d, offset=0x%llx, size=0x%llx)", fd, offset, size); + cellFs.todo("cellFsStReadStart(fd=%d, offset=0x%llx, size=0x%llx)", fd, offset, size); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - switch (auto status = file->st_status.compare_and_swap(SSS_INITIALIZED, SSS_STARTED)) - { - case SSS_NOT_INITIALIZED: - { - return CELL_FS_ENXIO; - } - - case SSS_STARTED: - { - return CELL_FS_EBUSY; - } - } - - offset = std::min(file->file.size(), offset); - size = std::min(file->file.size() - offset, size); - - file->st_read_size = size; - - file->st_thread = thread_ctrl::spawn("FS ST Thread", [=]() - { - std::unique_lock lock(file->mutex); - - while (file->st_status == SSS_STARTED && !Emu.IsStopped()) - { - // check free space in buffer and available data in stream - if (file->st_total_read - file->st_copied <= file->st_ringbuf_size - file->st_block_size && file->st_total_read < file->st_read_size) - { - // get buffer position - const u32 position = vm::cast(file->st_buffer + file->st_total_read % file->st_ringbuf_size, HERE); - - // read data - auto old = file->file.pos(); - file->file.seek(offset + file->st_total_read); - auto res = file->file.read(vm::base(position), file->st_block_size); - file->file.seek(old); - - // notify - file->st_total_read += res; - file->cv.notify_one(); - } - - // check callback condition if set - if (file->st_callback.load().func) - { - const u64 available = file->st_total_read - file->st_copied; - - if (available >= file->st_callback.load().size) - { - const auto func = file->st_callback.exchange({}).func; - - Emu.GetCallbackManager().Async([=](PPUThread& ppu) - { - func(ppu, fd, available); - }); - } - } - - file->cv.wait_for(lock, 1ms); - } - - file->st_status.compare_and_swap(SSS_STOPPED, SSS_INITIALIZED); - file->st_read_size = 0; - file->st_total_read = 0; - file->st_copied = 0; - file->st_callback.store({}); - }); + // TODO return CELL_OK; } s32 cellFsStReadStop(u32 fd) { - cellFs.warning("cellFsStReadStop(fd=%d)", fd); + cellFs.todo("cellFsStReadStop(fd=%d)", fd); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - switch (auto status = file->st_status.compare_and_swap(SSS_STARTED, SSS_STOPPED)) - { - case SSS_NOT_INITIALIZED: - { - return CELL_FS_ENXIO; - } - - case SSS_INITIALIZED: - case SSS_STOPPED: - { - return CELL_OK; - } - } - - file->cv.notify_all(); - file->st_thread->join(); + // TODO return CELL_OK; } s32 cellFsStRead(u32 fd, vm::ptr buf, u64 size, vm::ptr rsize) { - cellFs.warning("cellFsStRead(fd=%d, buf=*0x%x, size=0x%llx, rsize=*0x%x)", fd, buf, size, rsize); + cellFs.todo("cellFsStRead(fd=%d, buf=*0x%x, size=0x%llx, rsize=*0x%x)", fd, buf, size, rsize); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED || file->st_copyless) - { - return CELL_FS_ENXIO; - } + // TODO - const u64 copied = file->st_copied; - const u32 position = vm::cast(file->st_buffer + copied % file->st_ringbuf_size, HERE); - const u64 total_read = file->st_total_read; - const u64 copy_size = (*rsize = std::min(size, total_read - copied)); // write rsize - - // copy data - const u64 first_size = std::min(copy_size, file->st_ringbuf_size - (position - file->st_buffer)); - std::memcpy(buf.get_ptr(), vm::base(position), first_size); - std::memcpy((buf + first_size).get_ptr(), vm::base(file->st_buffer), copy_size - first_size); - - // notify - file->st_copied += copy_size; - file->cv.notify_one(); - - // check end of stream - return total_read < file->st_read_size ? CELL_OK : CELL_FS_ERANGE; + return CELL_OK; } s32 cellFsStReadGetCurrentAddr(u32 fd, vm::ptr addr, vm::ptr size) { - cellFs.warning("cellFsStReadGetCurrentAddr(fd=%d, addr=*0x%x, size=*0x%x)", fd, addr, size); + cellFs.todo("cellFsStReadGetCurrentAddr(fd=%d, addr=*0x%x, size=*0x%x)", fd, addr, size); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED || !file->st_copyless) - { - return CELL_FS_ENXIO; - } + // TODO - const u64 copied = file->st_copied; - const u32 position = vm::cast(file->st_buffer + copied % file->st_ringbuf_size, HERE); - const u64 total_read = file->st_total_read; - - if ((*size = std::min(file->st_ringbuf_size - (position - file->st_buffer), total_read - copied))) - { - *addr = position; - } - else - { - *addr = 0; - } - - // check end of stream - return total_read < file->st_read_size ? CELL_OK : CELL_FS_ERANGE; + return CELL_OK; } s32 cellFsStReadPutCurrentAddr(u32 fd, vm::ptr addr, u64 size) { - cellFs.warning("cellFsStReadPutCurrentAddr(fd=%d, addr=*0x%x, size=0x%llx)", fd, addr, size); + cellFs.todo("cellFsStReadPutCurrentAddr(fd=%d, addr=*0x%x, size=0x%llx)", fd, addr, size); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED || !file->st_copyless) - { - return CELL_FS_ENXIO; - } + // TODO - const u64 copied = file->st_copied; - const u64 total_read = file->st_total_read; - - // notify - file->st_copied += size; - file->cv.notify_one(); - - // check end of stream - return total_read < file->st_read_size ? CELL_OK : CELL_FS_ERANGE; + return CELL_OK; } s32 cellFsStReadWait(u32 fd, u64 size) { - cellFs.warning("cellFsStReadWait(fd=%d, size=0x%llx)", fd, size); + cellFs.todo("cellFsStReadWait(fd=%d, size=0x%llx)", fd, size); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED) - { - return CELL_FS_ENXIO; - } - - std::unique_lock lock(file->mutex); - - // wait for size availability or stream end - while (file->st_total_read - file->st_copied < size && file->st_total_read < file->st_read_size) - { - CHECK_EMU_STATUS; - - file->cv.wait_for(lock, 1ms); - } + // TODO return CELL_OK; } -s32 cellFsStReadWaitCallback(u32 fd, u64 size, fs_st_cb_t func) +s32 cellFsStReadWaitCallback(u32 fd, u64 size, vm::ptr func) { - cellFs.warning("cellFsStReadWaitCallback(fd=%d, size=0x%llx, func=*0x%x)", fd, size, func); + cellFs.todo("cellFsStReadWaitCallback(fd=%d, size=0x%llx, func=*0x%x)", fd, size, func); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - if (file->st_status == SSS_NOT_INITIALIZED) - { - return CELL_FS_ENXIO; - } - - if (!file->st_callback.compare_and_swap_test({}, { size, func })) - { - return CELL_FS_EIO; - } + // TODO return CELL_OK; } @@ -831,7 +650,7 @@ s32 cellFsSdataOpen(vm::cptr path, s32 flags, vm::ptr fd, vm::cptr[2]>({ 0x180, 0x10 }), 8); @@ -866,6 +685,12 @@ s32 cellFsSdataOpenByFd(u32 mself_fd, s32 flags, vm::ptr sdata_fd, u64 offs using fs_aio_cb_t = vm::ptr xaio, s32 error, s32 xid, u64 size)>; +// temporarily +struct lv2_fs_mount_point +{ + std::mutex mutex; +}; + void fsAio(vm::ptr aio, bool write, s32 xid, fs_aio_cb_t func) { cellFs.notice("FS AIO Request(%d): fd=%d, offset=0x%llx, buf=*0x%x, size=0x%llx, user_data=0x%llx", xid, aio->fd, aio->offset, aio->buf, aio->size, aio->user_data); @@ -873,21 +698,21 @@ void fsAio(vm::ptr aio, bool write, s32 xid, fs_aio_cb_t func) s32 error = CELL_OK; u64 result = 0; - const auto file = idm::get(aio->fd); + const auto file = idm::get(aio->fd); if (!file || (!write && file->flags & CELL_FS_O_WRONLY) || (write && !(file->flags & CELL_FS_O_ACCMODE))) { - error = CELL_FS_EBADF; + error = CELL_EBADF; } else { - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); const auto old_pos = file->file.pos(); file->file.seek(aio->offset); result = write - ? file->file.write(aio->buf.get_ptr(), aio->size) - : file->file.read(aio->buf.get_ptr(), aio->size); + ? file->op_write(aio->buf, aio->size) + : file->op_read(aio->buf, aio->size); file->file.seek(old_pos); } @@ -949,11 +774,11 @@ s32 cellFsAioWrite(vm::ptr aio, vm::ptr id, fs_aio_cb_t func) s32 cellFsAioCancel(s32 id) { - cellFs.warning("cellFsAioCancel(id=%d) -> CELL_FS_EINVAL", id); + cellFs.warning("cellFsAioCancel(id=%d) -> CELL_EINVAL", id); - // TODO: cancelled requests return CELL_FS_ECANCELED through their own callbacks + // TODO: cancelled requests return CELL_ECANCELED through their own callbacks - return CELL_FS_EINVAL; + return CELL_EINVAL; } s32 cellFsSetDefaultContainer(u32 id, u32 total_limit) @@ -967,11 +792,11 @@ s32 cellFsSetIoBufferFromDefaultContainer(u32 fd, u32 buffer_size, u32 page_type { cellFs.todo("cellFsSetIoBufferFromDefaultContainer(fd=%d, buffer_size=%d, page_type=%d)", fd, buffer_size, page_type); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } return CELL_OK; @@ -1020,6 +845,7 @@ s32 cellFsChangeFileSizeWithoutAllocation() s32 cellFsAllocateFileAreaWithoutZeroFill(vm::cptr path, u64 size) { cellFs.warning("cellFsAllocateFileAreaWithoutZeroFill(path=*0x%x, size=0x%llx)", path, size); + return sys_fs_truncate(path, size); } @@ -1099,7 +925,7 @@ DECLARE(ppu_module_manager::cellFs)("sys_fs", []() REG_FUNC(sys_fs, cellFsAllocateFileAreaByFdWithInitialData); REG_FUNC(sys_fs, cellFsTruncate2); REG_FUNC(sys_fs, cellFsChangeFileSizeWithoutAllocation); - REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill); + REG_FUNC(sys_fs, cellFsAllocateFileAreaWithoutZeroFill, MFF_FORCED_HLE); REG_FUNC(sys_fs, cellFsChangeFileSizeByFdWithoutAllocation); REG_FUNC(sys_fs, cellFsSetDiscReadRetrySetting); REG_FUNC(sys_fs, cellFsRegisterConversionCallback); diff --git a/rpcs3/Emu/Cell/Modules/cellFs.h b/rpcs3/Emu/Cell/Modules/cellFs.h index b74d0df31b..5d22a8417b 100644 --- a/rpcs3/Emu/Cell/Modules/cellFs.h +++ b/rpcs3/Emu/Cell/Modules/cellFs.h @@ -1,6 +1,6 @@ #pragma once -namespace vm { using namespace ps3; } +#include "Emu/Cell/lv2/sys_fs.h" struct CellFsDirectoryEntry { @@ -42,7 +42,7 @@ struct CellFsAio { be_t fd; be_t offset; - vm::bptr buf; + vm::bptrb buf; be_t size; be_t user_data; }; diff --git a/rpcs3/Emu/Cell/Modules/cellGifDec.cpp b/rpcs3/Emu/Cell/Modules/cellGifDec.cpp index f5420ed299..c52c816762 100644 --- a/rpcs3/Emu/Cell/Modules/cellGifDec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGifDec.cpp @@ -61,7 +61,7 @@ s32 cellGifDecOpen(PMainHandle mainHandle, PPSubHandle subHandle, PSrc src, POpe if (!file_s) return CELL_GIFDEC_ERROR_OPEN_FILE; current_subHandle.fileSize = file_s.size(); - current_subHandle.fd = idm::make(std::move(file_s), 0, 0); + current_subHandle.fd = idm::make(src->fileName.get_ptr(), std::move(file_s), 0, 0); break; } } @@ -97,7 +97,7 @@ s32 cellGifDecReadHeader(PMainHandle mainHandle, PSubHandle subHandle, PInfo inf case CELL_GIFDEC_FILE: { - auto file = idm::get(fd); + auto file = idm::get(fd); file->file.seek(0); file->file.read(buffer, sizeof(buffer)); break; @@ -181,7 +181,7 @@ s32 cellGifDecDecodeData(PMainHandle mainHandle, PSubHandle subHandle, vm::ptr(fd); + auto file = idm::get(fd); file->file.seek(0); file->file.read(gif.get(), fileSize); break; @@ -283,7 +283,7 @@ s32 cellGifDecClose(PMainHandle mainHandle, PSubHandle subHandle) { cellGifDec.warning("cellGifDecClose(mainHandle=*0x%x, subHandle=*0x%x)", mainHandle, subHandle); - idm::remove(subHandle->fd); + idm::remove(subHandle->fd); vm::dealloc(subHandle.addr()); diff --git a/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp b/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp index 2cfc9f5efc..6a436ae247 100644 --- a/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellJpgDec.cpp @@ -51,7 +51,7 @@ s32 cellJpgDecOpen(u32 mainHandle, vm::ptr subHandle, vm::ptr(std::move(file_s), 0, 0); + current_subHandle.fd = idm::make(src->fileName.get_ptr(), std::move(file_s), 0, 0); break; } } @@ -78,7 +78,7 @@ s32 cellJpgDecClose(u32 mainHandle, u32 subHandle) return CELL_JPGDEC_ERROR_FATAL; } - idm::remove(subHandle_data->fd); + idm::remove(subHandle_data->fd); idm::remove(subHandle); return CELL_OK; @@ -110,7 +110,7 @@ s32 cellJpgDecReadHeader(u32 mainHandle, u32 subHandle, vm::ptr case CELL_JPGDEC_FILE: { - auto file = idm::get(fd); + auto file = idm::get(fd); file->file.seek(0); file->file.read(buffer.get(), fileSize); break; @@ -189,7 +189,7 @@ s32 cellJpgDecDecodeData(u32 mainHandle, u32 subHandle, vm::ptr data, vm::cp case CELL_JPGDEC_FILE: { - auto file = idm::get(fd); + auto file = idm::get(fd); file->file.seek(0); file->file.read(jpg.get(), fileSize); break; diff --git a/rpcs3/Emu/Cell/Modules/cellPngDec.cpp b/rpcs3/Emu/Cell/Modules/cellPngDec.cpp index 2a40efa27d..79991b56f9 100644 --- a/rpcs3/Emu/Cell/Modules/cellPngDec.cpp +++ b/rpcs3/Emu/Cell/Modules/cellPngDec.cpp @@ -65,7 +65,7 @@ void pngDecReadBuffer(png_structp png_ptr, png_bytep out, png_size_t length) if (buffer.file) { // Get the file - auto file = idm::get(buffer.fd); + auto file = idm::get(buffer.fd); // Read the data file->file.read(out, length); @@ -364,7 +364,7 @@ s32 pngDecOpen(PPUThread& ppu, PHandle handle, PPStream png_stream, PSrc source, } // Get the file descriptor - buffer->fd = idm::make(std::move(file_stream), 0, 0); + buffer->fd = idm::make(stream->source.fileName.get_ptr(), std::move(file_stream), 0, 0); // Indicate that we need to read from a file stream buffer->file = true; @@ -447,7 +447,7 @@ s32 pngDecClose(PPUThread& ppu, PHandle handle, PStream stream) // Remove the file descriptor, if a file descriptor was used for decoding if (stream->buffer->file) { - idm::remove(stream->buffer->fd); + idm::remove(stream->buffer->fd); } // Deallocate the PNG buffer structure used to decode from memory, if we decoded from memory diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index 3dbab511eb..22969c1da0 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -1,24 +1,58 @@ #include "stdafx.h" -#include "Emu/Memory/Memory.h" -#include "Emu/System.h" -#include "Emu/IdManager.h" - -#include "Emu/Cell/ErrorCodes.h" #include "sys_fs.h" +#include + +#include "Emu/VFS.h" #include "Utilities/StrUtil.h" -#include + +namespace vm { using namespace ps3; } logs::channel sys_fs("sys_fs", logs::level::notice); -s32 sys_fs_test(u32 arg1, u32 arg2, vm::ptr arg3, u32 arg4, vm::ptr arg5, u32 arg6) +struct lv2_fs_mount_point +{ + std::mutex mutex; +}; + +lv2_fs_mount_point g_mp_sys_dev_hdd0; +lv2_fs_mount_point g_mp_sys_dev_hdd1; +lv2_fs_mount_point g_mp_sys_dev_usb; +lv2_fs_mount_point g_mp_sys_dev_bdvd; +lv2_fs_mount_point g_mp_sys_app_home; +lv2_fs_mount_point g_mp_sys_host_root; + +lv2_fs_mount_point* lv2_fs_object::get_mp(const char* filename) +{ + // TODO + return &g_mp_sys_dev_hdd0; +} + +u64 lv2_file::op_read(vm::ps3::ptr buf, u64 size) +{ + // Copy data from intermediate buffer (avoid passing vm pointer to a native API) + std::unique_ptr local_buf(new u8[size]); + const u64 result = file.read(local_buf.get(), size); + std::memcpy(buf.get_ptr(), local_buf.get(), result); + return result; +} + +u64 lv2_file::op_write(vm::ps3::cptr buf, u64 size) +{ + // Copy data to intermediate buffer (avoid passing vm pointer to a native API) + std::unique_ptr local_buf(new u8[size]); + std::memcpy(local_buf.get(), buf.get_ptr(), size); + return file.write(local_buf.get(), size); +} + +ppu_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); return CELL_OK; } -s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::cptr arg, u64 size) +ppu_error_code sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::cptr arg, u64 size) { sys_fs.warning("sys_fs_open(path=*0x%x, flags=%#o, fd=*0x%x, mode=%#o, arg=*0x%x, size=0x%llx)", path, flags, fd, mode, arg, size); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -26,14 +60,15 @@ s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::c if (!path[0]) { sys_fs.error("sys_fs_open('%s') failed: path is invalid", path.get_ptr()); - return CELL_FS_EINVAL; + return CELL_EINVAL; } const std::string& local_path = vfs::get(path.get_ptr()); + if (local_path.empty()) { sys_fs.error("sys_fs_open('%s') failed: device not mounted", path.get_ptr()); - return CELL_FS_ENOTMOUNTED; + return CELL_ENOTMOUNTED; } // TODO: other checks for path @@ -41,7 +76,7 @@ s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::c if (fs::is_dir(local_path)) { sys_fs.error("sys_fs_open('%s') failed: path is a directory", path.get_ptr()); - return CELL_FS_EISDIR; + return CELL_EISDIR; } bitset_t open_mode{}; @@ -103,18 +138,18 @@ s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::c if (open_mode & fs::excl) { - return CELL_FS_EEXIST; // approximation + return CELL_EEXIST; // approximation } - return CELL_FS_ENOENT; + return CELL_ENOENT; } - const auto _file = idm::make_ptr(std::move(file), mode, flags); + const auto _file = idm::make_ptr(path.get_ptr(), std::move(file), mode, flags); if (!_file) { // out of file descriptors - return CELL_FS_EMFILE; + return CELL_EMFILE; } *fd = _file->id; @@ -122,7 +157,7 @@ s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::c return CELL_OK; } -s32 sys_fs_read(u32 fd, vm::ptr buf, u64 nbytes, vm::ptr nread) +ppu_error_code sys_fs_read(u32 fd, vm::ptr buf, u64 nbytes, vm::ptr nread) { sys_fs.trace("sys_fs_read(fd=%d, buf=*0x%x, nbytes=0x%llx, nread=*0x%x)", fd, buf, nbytes, nread); @@ -131,80 +166,93 @@ s32 sys_fs_read(u32 fd, vm::ptr buf, u64 nbytes, vm::ptr nread) return CELL_EFAULT; } - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file || file->flags & CELL_FS_O_WRONLY) { - return CELL_FS_EBADF; + return CELL_EBADF; } - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); - std::unique_ptr local_buf(new u8[nbytes]); - std::memcpy(buf.get_ptr(), local_buf.get(), *nread = file->file.read(local_buf.get(), nbytes)); + *nread = file->op_read(buf, nbytes); return CELL_OK; } -s32 sys_fs_write(u32 fd, vm::cptr buf, u64 nbytes, vm::ptr nwrite) +ppu_error_code sys_fs_write(u32 fd, vm::cptr buf, u64 nbytes, vm::ptr nwrite) { sys_fs.trace("sys_fs_write(fd=%d, buf=*0x%x, nbytes=0x%llx, nwrite=*0x%x)", fd, buf, nbytes, nwrite); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file || !(file->flags & CELL_FS_O_ACCMODE)) { - return CELL_FS_EBADF; + return CELL_EBADF; } - // TODO: return CELL_FS_EBUSY if locked by stream + // TODO: return CELL_EBUSY if locked by stream - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); - std::unique_ptr local_buf(new u8[nbytes]); - std::memcpy(local_buf.get(), buf.get_ptr(), nbytes); - *nwrite = file->file.write(local_buf.get(), nbytes); + *nwrite = file->op_write(buf, nbytes); return CELL_OK; } -s32 sys_fs_close(u32 fd) +ppu_error_code sys_fs_close(u32 fd) { sys_fs.trace("sys_fs_close(fd=%d)", fd); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - // TODO: return CELL_FS_EBUSY if locked + // TODO: return CELL_EBUSY if locked - idm::remove(fd); + idm::remove(fd); return CELL_OK; } -s32 sys_fs_opendir(vm::cptr path, vm::ptr fd) +ppu_error_code sys_fs_opendir(vm::cptr path, vm::ptr fd) { sys_fs.warning("sys_fs_opendir(path=*0x%x, fd=*0x%x)", path, fd); sys_fs.warning("*** path = '%s'", path.get_ptr()); - fs::dir dir(vfs::get(path.get_ptr())); + const std::string& local_path = vfs::get(path.get_ptr()); + + if (local_path.empty()) + { + sys_fs.error("sys_fs_opendir('%s') failed: device not mounted", path.get_ptr()); + return CELL_ENOTMOUNTED; + } + + // TODO: other checks for path + + if (fs::is_file(local_path)) + { + sys_fs.error("sys_fs_opendir('%s') failed: path is a file", path.get_ptr()); + return CELL_ENOTDIR; + } + + fs::dir dir(local_path); if (!dir) { sys_fs.error("sys_fs_opendir('%s'): failed to open directory", path.get_ptr()); - return CELL_FS_ENOENT; + return CELL_ENOENT; } - const auto _dir = idm::make_ptr(std::move(dir)); + const auto _dir = idm::make_ptr(path.get_ptr(), std::move(dir)); if (!_dir) { // out of file descriptors - return CELL_FS_EMFILE; + return CELL_EMFILE; } *fd = _dir->id; @@ -212,15 +260,15 @@ s32 sys_fs_opendir(vm::cptr path, vm::ptr fd) return CELL_OK; } -s32 sys_fs_readdir(u32 fd, vm::ptr dir, vm::ptr nread) +ppu_error_code sys_fs_readdir(u32 fd, vm::ptr dir, vm::ptr nread) { sys_fs.warning("sys_fs_readdir(fd=%d, dir=*0x%x, nread=*0x%x)", fd, dir, nread); - const auto directory = idm::get(fd); + const auto directory = idm::get(fd); if (!directory) { - return CELL_FS_EBADF; + return CELL_EBADF; } fs::dir_entry info; @@ -240,23 +288,23 @@ s32 sys_fs_readdir(u32 fd, vm::ptr dir, vm::ptr nread) return CELL_OK; } -s32 sys_fs_closedir(u32 fd) +ppu_error_code sys_fs_closedir(u32 fd) { sys_fs.trace("sys_fs_closedir(fd=%d)", fd); - const auto directory = idm::get(fd); + const auto directory = idm::get(fd); if (!directory) { - return CELL_FS_EBADF; + return CELL_EBADF; } - idm::remove(fd); + idm::remove(fd); return CELL_OK; } -s32 sys_fs_stat(vm::cptr path, vm::ptr sb) +ppu_error_code sys_fs_stat(vm::cptr path, vm::ptr sb) { sys_fs.warning("sys_fs_stat(path=*0x%x, sb=*0x%x)", path, sb); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -266,7 +314,7 @@ s32 sys_fs_stat(vm::cptr path, vm::ptr sb) if (local_path.empty()) { sys_fs.warning("sys_fs_stat('%s') failed: not mounted", path.get_ptr()); - return CELL_FS_ENOTMOUNTED; + return CELL_ENOTMOUNTED; } fs::stat_t info; @@ -274,7 +322,7 @@ s32 sys_fs_stat(vm::cptr path, vm::ptr sb) if (!fs::stat(local_path, info)) { sys_fs.error("sys_fs_stat('%s') failed: not found", path.get_ptr()); - return CELL_FS_ENOENT; + return CELL_ENOENT; } sb->mode = info.is_directory ? CELL_FS_S_IFDIR | 0777 : CELL_FS_S_IFREG | 0666; @@ -289,18 +337,18 @@ s32 sys_fs_stat(vm::cptr path, vm::ptr sb) return CELL_OK; } -s32 sys_fs_fstat(u32 fd, vm::ptr sb) +ppu_error_code sys_fs_fstat(u32 fd, vm::ptr sb) { sys_fs.warning("sys_fs_fstat(fd=%d, sb=*0x%x)", fd, sb); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); const fs::stat_t& info = file->file.stat(); @@ -316,7 +364,7 @@ s32 sys_fs_fstat(u32 fd, vm::ptr sb) return CELL_OK; } -s32 sys_fs_mkdir(vm::cptr path, s32 mode) +ppu_error_code sys_fs_mkdir(vm::cptr path, s32 mode) { sys_fs.warning("sys_fs_mkdir(path=*0x%x, mode=%#o)", path, mode); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -325,19 +373,19 @@ s32 sys_fs_mkdir(vm::cptr path, s32 mode) if (fs::is_dir(local_path)) { - return CELL_FS_EEXIST; + return CELL_EEXIST; } if (!fs::create_path(local_path)) { - return CELL_FS_EIO; // ??? + return CELL_EIO; // ??? } sys_fs.notice("sys_fs_mkdir(): directory '%s' created", path.get_ptr()); return CELL_OK; } -s32 sys_fs_rename(vm::cptr from, vm::cptr to) +ppu_error_code sys_fs_rename(vm::cptr from, vm::cptr to) { sys_fs.warning("sys_fs_rename(from=*0x%x, to=*0x%x)", from, to); sys_fs.warning("*** from = '%s'", from.get_ptr()); @@ -345,14 +393,14 @@ s32 sys_fs_rename(vm::cptr from, vm::cptr to) if (!fs::rename(vfs::get(from.get_ptr()), vfs::get(to.get_ptr()))) { - return CELL_FS_ENOENT; // ??? + return CELL_ENOENT; // ??? } sys_fs.notice("sys_fs_rename(): '%s' renamed to '%s'", from.get_ptr(), to.get_ptr()); return CELL_OK; } -s32 sys_fs_rmdir(vm::cptr path) +ppu_error_code sys_fs_rmdir(vm::cptr path) { sys_fs.warning("sys_fs_rmdir(path=*0x%x)", path); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -361,18 +409,18 @@ s32 sys_fs_rmdir(vm::cptr path) { switch (auto error = fs::g_tls_error) { - case fs::error::noent: return CELL_FS_ENOENT; + case fs::error::noent: return CELL_ENOENT; default: sys_fs.error("sys_fs_rmdir(): unknown error %d", error); } - return CELL_FS_EIO; // ??? + return CELL_EIO; // ??? } sys_fs.notice("sys_fs_rmdir(): directory '%s' removed", path.get_ptr()); return CELL_OK; } -s32 sys_fs_unlink(vm::cptr path) +ppu_error_code sys_fs_unlink(vm::cptr path) { sys_fs.warning("sys_fs_unlink(path=*0x%x)", path); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -381,57 +429,107 @@ s32 sys_fs_unlink(vm::cptr path) { switch (auto error = fs::g_tls_error) { - case fs::error::noent: return CELL_FS_ENOENT; + case fs::error::noent: return CELL_ENOENT; default: sys_fs.error("sys_fs_unlink(): unknown error %d", error); } - return CELL_FS_EIO; // ??? + return CELL_EIO; // ??? } sys_fs.notice("sys_fs_unlink(): file '%s' deleted", path.get_ptr()); return CELL_OK; } -s32 sys_fs_fcntl(u32 fd, s32 flags, u32 addr, u32 arg4, u32 arg5, u32 arg6) +ppu_error_code sys_fs_fcntl(u32 fd, u32 op, vm::ptr _arg, u32 _size) { - sys_fs.todo("sys_fs_fcntl(fd=0x%x, flags=0x%x, addr=*0x%x, arg4=0x%x, arg5=0x%x, arg6=0x%x) -> CELL_OK", fd, flags, addr, arg4, arg5, arg6); + sys_fs.trace("sys_fs_fcntl(fd=%d, op=0x%x, arg=*0x%x, size=0x%x)", fd, op, _arg, _size); + + switch (op) + { + case 0x8000000A: // Read with offset + case 0x8000000B: // Write with offset + { + 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; + } + + if (op == 0x8000000A && file->flags & CELL_FS_O_WRONLY) + { + return CELL_EBADF; + } + + if (op == 0x8000000B && !(file->flags & CELL_FS_O_ACCMODE)) + { + return CELL_EBADF; + } + + std::lock_guard lock(file->mp->mutex); + + const u64 old_pos = file->file.pos(); + const u64 new_pos = file->file.seek(arg->offset); + + arg->out_size = op == 0x8000000A + ? file->op_read(arg->buf, arg->size) + : file->op_write(arg->buf, arg->size); + + VERIFY(old_pos == file->file.seek(old_pos)); + + arg->out_code = CELL_OK; + + break; + } + default: + { + sys_fs.todo("sys_fs_fcntl(): Unknown operation 0x%08x (fd=%d, arg=*0x%x, size=0x%x)", op, fd, _arg, _size); + } + } return CELL_OK; } -s32 sys_fs_lseek(u32 fd, s64 offset, s32 whence, vm::ptr pos) +ppu_error_code sys_fs_lseek(u32 fd, s64 offset, s32 whence, vm::ptr pos) { sys_fs.trace("sys_fs_lseek(fd=%d, offset=0x%llx, whence=0x%x, pos=*0x%x)", fd, offset, whence, pos); if (whence >= 3) { sys_fs.error("sys_fs_lseek(): invalid seek whence (%d)", whence); - return CELL_FS_EINVAL; + return CELL_EINVAL; } - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); *pos = file->file.seek(offset, static_cast(whence)); return CELL_OK; } -s32 sys_fs_fget_block_size(u32 fd, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4, vm::ptr arg5) +ppu_error_code sys_fs_fget_block_size(u32 fd, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4, vm::ptr arg5) { sys_fs.todo("sys_fs_fget_block_size(fd=%d, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x, arg5=*0x%x)", fd, sector_size, block_size, arg4, arg5); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file) { - return CELL_FS_EBADF; + return CELL_EBADF; } *sector_size = 4096; // ? @@ -440,7 +538,7 @@ s32 sys_fs_fget_block_size(u32 fd, vm::ptr sector_size, vm::ptr block_ return CELL_OK; } -s32 sys_fs_get_block_size(vm::cptr path, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4) +ppu_error_code sys_fs_get_block_size(vm::cptr path, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4) { sys_fs.todo("sys_fs_get_block_size(path=*0x%x, sector_size=*0x%x, block_size=*0x%x, arg4=*0x%x, arg5=*0x%x)", path, sector_size, block_size, arg4); sys_fs.todo("*** path = '%s'", path.get_ptr()); @@ -451,7 +549,7 @@ s32 sys_fs_get_block_size(vm::cptr path, vm::ptr sector_size, vm::ptr return CELL_OK; } -s32 sys_fs_truncate(vm::cptr path, u64 size) +ppu_error_code sys_fs_truncate(vm::cptr path, u64 size) { sys_fs.warning("sys_fs_truncate(path=*0x%x, size=0x%llx)", path, size); sys_fs.warning("*** path = '%s'", path.get_ptr()); @@ -460,28 +558,28 @@ s32 sys_fs_truncate(vm::cptr path, u64 size) { switch (auto error = fs::g_tls_error) { - case fs::error::noent: return CELL_FS_ENOENT; + case fs::error::noent: return CELL_ENOENT; default: sys_fs.error("sys_fs_truncate(): unknown error %d", error); } - return CELL_FS_EIO; // ??? + return CELL_EIO; // ??? } return CELL_OK; } -s32 sys_fs_ftruncate(u32 fd, u64 size) +ppu_error_code sys_fs_ftruncate(u32 fd, u64 size) { sys_fs.warning("sys_fs_ftruncate(fd=%d, size=0x%llx)", fd, size); - const auto file = idm::get(fd); + const auto file = idm::get(fd); if (!file || !(file->flags & CELL_FS_O_ACCMODE)) { - return CELL_FS_EBADF; + return CELL_EBADF; } - std::lock_guard lock(file->mutex); + std::lock_guard lock(file->mp->mutex); if (!file->file.trunc(size)) { @@ -491,13 +589,13 @@ s32 sys_fs_ftruncate(u32 fd, u64 size) default: sys_fs.error("sys_fs_ftruncate(): unknown error %d", error); } - return CELL_FS_EIO; // ??? + return CELL_EIO; // ??? } return CELL_OK; } -s32 sys_fs_chmod(vm::cptr path, s32 mode) +ppu_error_code sys_fs_chmod(vm::cptr path, s32 mode) { sys_fs.todo("sys_fs_chmod(path=*0x%x, mode=%#o) -> CELL_OK", path, mode); sys_fs.todo("*** path = '%s'", path.get_ptr()); diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.h b/rpcs3/Emu/Cell/lv2/sys_fs.h index d1f56d59a3..e3fea5aa6b 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.h +++ b/rpcs3/Emu/Cell/lv2/sys_fs.h @@ -1,67 +1,8 @@ #pragma once -#include "Utilities/Thread.h" - -#include -#include - -namespace vm { using namespace ps3; } - -// Error Codes -enum : s32 -{ - CELL_FS_EDOM = CELL_EDOM, - CELL_FS_EFAULT = CELL_EFAULT, - CELL_FS_EFBIG = CELL_EFBIG, - CELL_FS_EFPOS = CELL_EFPOS, - CELL_FS_EMLINK = CELL_EMLINK, - CELL_FS_ENFILE = CELL_ENFILE, - CELL_FS_ENOENT = CELL_ENOENT, - CELL_FS_ENOSPC = CELL_ENOSPC, - CELL_FS_ENOTTY = CELL_ENOTTY, - CELL_FS_EPIPE = CELL_EPIPE, - CELL_FS_ERANGE = CELL_ERANGE, - CELL_FS_EROFS = CELL_EROFS, - CELL_FS_ESPIPE = CELL_ESPIPE, - CELL_FS_E2BIG = CELL_E2BIG, - CELL_FS_EACCES = CELL_EACCES, - CELL_FS_EAGAIN = CELL_EAGAIN, - CELL_FS_EBADF = CELL_EBADF, - CELL_FS_EBUSY = CELL_EBUSY, - //CELL_FS_ECHILD = CELL_ECHILD, - CELL_FS_EEXIST = CELL_EEXIST, - CELL_FS_EINTR = CELL_EINTR, - CELL_FS_EINVAL = CELL_EINVAL, - CELL_FS_EIO = CELL_EIO, - CELL_FS_EISDIR = CELL_EISDIR, - CELL_FS_EMFILE = CELL_EMFILE, - CELL_FS_ENODEV = CELL_ENODEV, - CELL_FS_ENOEXEC = CELL_ENOEXEC, - CELL_FS_ENOMEM = CELL_ENOMEM, - CELL_FS_ENOTDIR = CELL_ENOTDIR, - CELL_FS_ENXIO = CELL_ENXIO, - CELL_FS_EPERM = CELL_EPERM, - CELL_FS_ESRCH = CELL_ESRCH, - CELL_FS_EXDEV = CELL_EXDEV, - CELL_FS_EBADMSG = CELL_EBADMSG, - CELL_FS_ECANCELED = CELL_ECANCELED, - CELL_FS_EDEADLK = CELL_EDEADLK, - CELL_FS_EILSEQ = CELL_EILSEQ, - CELL_FS_EINPROGRESS = CELL_EINPROGRESS, - CELL_FS_EMSGSIZE = CELL_EMSGSIZE, - CELL_FS_ENAMETOOLONG = CELL_ENAMETOOLONG, - CELL_FS_ENOLCK = CELL_ENOLCK, - CELL_FS_ENOSYS = CELL_ENOSYS, - CELL_FS_ENOTEMPTY = CELL_ENOTEMPTY, - CELL_FS_ENOTSUP = CELL_ENOTSUP, - CELL_FS_ETIMEDOUT = CELL_ETIMEDOUT, - CELL_FS_EFSSPECIFIC = CELL_EFSSPECIFIC, - CELL_FS_EOVERFLOW = CELL_EOVERFLOW, - CELL_FS_ENOTMOUNTED = CELL_ENOTMOUNTED, - CELL_FS_ENOTMSELF = CELL_ENOTMSELF, - CELL_FS_ENOTSDATA = CELL_ENOTSDATA, - CELL_FS_EAUTHFATAL = CELL_EAUTHFATAL, -}; +#include "Emu/Memory/Memory.h" +#include "Emu/Cell/ErrorCodes.h" +#include "Emu/IdManager.h" // Open Flags enum : s32 @@ -92,7 +33,7 @@ enum : s32 CELL_FS_MAX_MP_LENGTH = 31, }; -enum CellFsMode : s32 +enum : s32 { CELL_FS_S_IFMT = 0170000, CELL_FS_S_IFDIR = 0040000, // directory @@ -151,98 +92,117 @@ struct CellFsUtimbuf CHECK_SIZE_ALIGN(CellFsUtimbuf, 16, 4); -// Stream Support Status (st_status) -enum : u32 -{ - SSS_NOT_INITIALIZED = 0, - SSS_INITIALIZED, - SSS_STARTED, - SSS_STOPPED, -}; +struct lv2_fs_mount_point; -using fs_st_cb_t = vm::ptr; - -struct alignas(16) fs_st_cb_rec_t +struct lv2_fs_object { - u64 size; - fs_st_cb_t func; - u32 pad; -}; - -struct lv2_fs_object_t -{ - using id_base = lv2_fs_object_t; + // ID Manager setups + using id_base = lv2_fs_object; static constexpr u32 id_min = 3; static constexpr u32 id_max = 255; const id_value<> id{}; + + // Mount Point + const std::add_pointer_t mp; + + lv2_fs_object(lv2_fs_mount_point* mp) + : mp(mp) + { + } + + static lv2_fs_mount_point* get_mp(const char* filename); }; -struct lv2_file_t : lv2_fs_object_t +struct lv2_file : lv2_fs_object { const fs::file file; const s32 mode; const s32 flags; - std::mutex mutex; - std::condition_variable cv; - - atomic_t st_status; - - u64 st_ringbuf_size; - u64 st_block_size; - u64 st_trans_rate; - bool st_copyless; - - std::shared_ptr st_thread; - - u32 st_buffer; - u64 st_read_size; - atomic_t st_total_read; - atomic_t st_copied; - - atomic_t st_callback; - - lv2_file_t(fs::file file, s32 mode, s32 flags) - : file(std::move(file)) + lv2_file(const char* filename, fs::file&& file, s32 mode, s32 flags) + : lv2_fs_object(lv2_fs_object::get_mp(filename)) + , file(std::move(file)) , mode(mode) , flags(flags) - , st_status(SSS_NOT_INITIALIZED) - , st_callback(fs_st_cb_rec_t{}) { } + + // 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); }; -struct lv2_dir_t : lv2_fs_object_t +struct lv2_dir : lv2_fs_object { const fs::dir dir; - lv2_dir_t(fs::dir dir) - : dir(std::move(dir)) + lv2_dir(const char* filename, fs::dir&& dir) + : lv2_fs_object(lv2_fs_object::get_mp(filename)) + , dir(std::move(dir)) { } }; +// 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); + // SysCalls -s32 sys_fs_test(u32 arg1, u32 arg2, vm::ptr arg3, u32 arg4, vm::ptr arg5, u32 arg6); -s32 sys_fs_open(vm::cptr path, s32 flags, vm::ptr fd, s32 mode, vm::cptr arg, u64 size); -s32 sys_fs_read(u32 fd, vm::ptr buf, u64 nbytes, vm::ptr nread); -s32 sys_fs_write(u32 fd, vm::cptr buf, u64 nbytes, vm::ptr nwrite); -s32 sys_fs_close(u32 fd); -s32 sys_fs_opendir(vm::cptr path, vm::ptr fd); -s32 sys_fs_readdir(u32 fd, vm::ptr dir, vm::ptr nread); -s32 sys_fs_closedir(u32 fd); -s32 sys_fs_stat(vm::cptr path, vm::ptr sb); -s32 sys_fs_fstat(u32 fd, vm::ptr sb); -s32 sys_fs_mkdir(vm::cptr path, s32 mode); -s32 sys_fs_rename(vm::cptr from, vm::cptr to); -s32 sys_fs_rmdir(vm::cptr path); -s32 sys_fs_unlink(vm::cptr path); -s32 sys_fs_fcntl(u32 fd, s32 flags, u32 addr, u32 arg4, u32 arg5, u32 arg6); -s32 sys_fs_lseek(u32 fd, s64 offset, s32 whence, vm::ptr pos); -s32 sys_fs_fget_block_size(u32 fd, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4, vm::ptr arg5); -s32 sys_fs_get_block_size(vm::cptr path, vm::ptr sector_size, vm::ptr block_size, vm::ptr arg4); -s32 sys_fs_truncate(vm::cptr path, u64 size); -s32 sys_fs_ftruncate(u32 fd, u64 size); -s32 sys_fs_chmod(vm::cptr path, s32 mode); +ppu_error_code sys_fs_test(u32 arg1, u32 arg2, vm::ps3::ptr arg3, u32 arg4, vm::ps3::ptr arg5, u32 arg6); +ppu_error_code sys_fs_open(vm::ps3::cptr path, s32 flags, vm::ps3::ptr fd, s32 mode, vm::ps3::cptr arg, u64 size); +ppu_error_code sys_fs_read(u32 fd, vm::ps3::ptr buf, u64 nbytes, vm::ps3::ptr nread); +ppu_error_code sys_fs_write(u32 fd, vm::ps3::cptr buf, u64 nbytes, vm::ps3::ptr nwrite); +ppu_error_code sys_fs_close(u32 fd); +ppu_error_code sys_fs_opendir(vm::ps3::cptr path, vm::ps3::ptr fd); +ppu_error_code sys_fs_readdir(u32 fd, vm::ps3::ptr dir, vm::ps3::ptr nread); +ppu_error_code sys_fs_closedir(u32 fd); +ppu_error_code sys_fs_stat(vm::ps3::cptr path, vm::ps3::ptr sb); +ppu_error_code sys_fs_fstat(u32 fd, vm::ps3::ptr sb); +ppu_error_code sys_fs_mkdir(vm::ps3::cptr path, s32 mode); +ppu_error_code sys_fs_rename(vm::ps3::cptr from, vm::ps3::cptr to); +ppu_error_code sys_fs_rmdir(vm::ps3::cptr path); +ppu_error_code sys_fs_unlink(vm::ps3::cptr path); +ppu_error_code sys_fs_fcntl(u32 fd, u32 op, vm::ps3::ptr arg, u32 size); +ppu_error_code sys_fs_lseek(u32 fd, s64 offset, s32 whence, vm::ps3::ptr pos); +ppu_error_code sys_fs_fget_block_size(u32 fd, vm::ps3::ptr sector_size, vm::ps3::ptr block_size, vm::ps3::ptr arg4, vm::ps3::ptr arg5); +ppu_error_code sys_fs_get_block_size(vm::ps3::cptr path, vm::ps3::ptr sector_size, vm::ps3::ptr block_size, vm::ps3::ptr arg4); +ppu_error_code sys_fs_truncate(vm::ps3::cptr path, u64 size); +ppu_error_code sys_fs_ftruncate(u32 fd, u64 size); +ppu_error_code sys_fs_chmod(vm::ps3::cptr path, s32 mode); diff --git a/rpcs3/Emu/Cell/lv2/sys_process.cpp b/rpcs3/Emu/Cell/lv2/sys_process.cpp index 09ac2af5f9..284286b299 100644 --- a/rpcs3/Emu/Cell/lv2/sys_process.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_process.cpp @@ -89,7 +89,7 @@ s32 sys_process_get_number_of_object(u32 object, vm::ptr nump) case SYS_LWMUTEX_OBJECT: *nump = idm::get_count(); break; case SYS_TIMER_OBJECT: *nump = idm::get_count(); break; case SYS_SEMAPHORE_OBJECT: *nump = idm::get_count(); break; - case SYS_FS_FD_OBJECT: *nump = idm::get_count(); break; + case SYS_FS_FD_OBJECT: *nump = idm::get_count(); break; case SYS_LWCOND_OBJECT: *nump = idm::get_count(); break; case SYS_EVENT_FLAG_OBJECT: *nump = idm::get_count(); break; @@ -136,7 +136,7 @@ s32 sys_process_get_id(u32 object, vm::ptr buffer, u32 size, vm::ptr s case SYS_LWMUTEX_OBJECT: idm_get_set(objects); break; case SYS_TIMER_OBJECT: idm_get_set(objects); break; case SYS_SEMAPHORE_OBJECT: idm_get_set(objects); break; - case SYS_FS_FD_OBJECT: idm_get_set(objects); break; + case SYS_FS_FD_OBJECT: idm_get_set(objects); break; case SYS_LWCOND_OBJECT: idm_get_set(objects); break; case SYS_EVENT_FLAG_OBJECT: idm_get_set(objects); break; diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 8627aad7d9..1bb8b69fc5 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -28,10 +28,17 @@ cfg::bool_entry g_cfg_autostart(cfg::root.misc, "Always start after boot", true); cfg::bool_entry g_cfg_autoexit(cfg::root.misc, "Exit RPCS3 when process finishes"); -std::string g_cfg_defaults; +cfg::string_entry g_cfg_vfs_emulator_dir(cfg::root.vfs, "$(EmulatorDir)"); // Default (empty): taken from fs::get_executable_dir() +cfg::string_entry g_cfg_vfs_dev_hdd0(cfg::root.vfs, "/dev_hdd0/", "$(EmulatorDir)dev_hdd0/"); +cfg::string_entry g_cfg_vfs_dev_hdd1(cfg::root.vfs, "/dev_hdd1/", "$(EmulatorDir)dev_hdd1/"); +cfg::string_entry g_cfg_vfs_dev_flash(cfg::root.vfs, "/dev_flash/", "$(EmulatorDir)dev_flash/"); +cfg::string_entry g_cfg_vfs_dev_usb000(cfg::root.vfs, "/dev_usb000/", "$(EmulatorDir)dev_usb000/"); +cfg::string_entry g_cfg_vfs_dev_bdvd(cfg::root.vfs, "/dev_bdvd/"); // Not mounted +cfg::string_entry g_cfg_vfs_app_home(cfg::root.vfs, "/app_home/"); // Not mounted -extern cfg::string_entry g_cfg_vfs_dev_bdvd; -extern cfg::string_entry g_cfg_vfs_app_home; +cfg::bool_entry g_cfg_vfs_allow_host_root(cfg::root.vfs, "Enable /host_root/", true); + +std::string g_cfg_defaults; extern atomic_t g_thread_count; @@ -124,6 +131,22 @@ bool Emulator::BootGame(const std::string& path, bool direct) return false; } +std::string Emulator::GetGameDir() +{ + const std::string& emu_dir_ = g_cfg_vfs_emulator_dir; + const std::string& emu_dir = emu_dir_.empty() ? fs::get_executable_dir() : emu_dir_; + + return fmt::replace_all(g_cfg_vfs_dev_hdd0, "$(EmulatorDir)", emu_dir) + "game/"; +} + +std::string Emulator::GetLibDir() +{ + const std::string& emu_dir_ = g_cfg_vfs_emulator_dir; + const std::string& emu_dir = emu_dir_.empty() ? fs::get_executable_dir() : emu_dir_; + + return fmt::replace_all(g_cfg_vfs_dev_flash, "$(EmulatorDir)", emu_dir) + "sys/external/"; +} + void Emulator::Load() { Stop(); @@ -208,33 +231,49 @@ void Emulator::Load() LOG_NOTICE(LOADER, "Title: %s", GetTitle()); LOG_NOTICE(LOADER, "Serial: %s", GetTitleID()); - LOG_NOTICE(LOADER, ""); - LOG_NOTICE(LOADER, "Used configuration:\n%s\n", cfg::root.to_string()); + // Mount all devices + const std::string& emu_dir_ = g_cfg_vfs_emulator_dir; + const std::string& emu_dir = emu_dir_.empty() ? fs::get_executable_dir() : emu_dir_; + const std::string& bdvd_dir = g_cfg_vfs_dev_bdvd; + const std::string& home_dir = g_cfg_vfs_app_home; - // Mount /dev_bdvd/ - if (g_cfg_vfs_dev_bdvd.size() == 0 && fs::is_file(elf_dir + "/../../PS3_DISC.SFB")) + vfs::mount("dev_hdd0", fmt::replace_all(g_cfg_vfs_dev_hdd0, "$(EmulatorDir)", emu_dir)); + vfs::mount("dev_hdd1", fmt::replace_all(g_cfg_vfs_dev_hdd1, "$(EmulatorDir)", emu_dir)); + vfs::mount("dev_flash", fmt::replace_all(g_cfg_vfs_dev_flash, "$(EmulatorDir)", emu_dir)); + vfs::mount("dev_usb", fmt::replace_all(g_cfg_vfs_dev_usb000, "$(EmulatorDir)", emu_dir)); + vfs::mount("dev_usb000", fmt::replace_all(g_cfg_vfs_dev_usb000, "$(EmulatorDir)", emu_dir)); + vfs::mount("app_home", home_dir.empty() ? elf_dir + '/' : fmt::replace_all(home_dir, "$(EmulatorDir)", emu_dir)); + + // Mount /dev_bdvd/ if necessary + if (bdvd_dir.empty() && fs::is_file(elf_dir + "/../../PS3_DISC.SFB")) { const auto dir_list = fmt::split(elf_dir, { "/", "\\" }); // Check latest two directories if (dir_list.size() >= 2 && dir_list.back() == "USRDIR" && *(dir_list.end() - 2) == "PS3_GAME") { - g_cfg_vfs_dev_bdvd = elf_dir.substr(0, elf_dir.length() - 15); + vfs::mount("dev_bdvd", elf_dir.substr(0, elf_dir.length() - 15)); } else { - g_cfg_vfs_dev_bdvd = elf_dir + "/../../"; + vfs::mount("dev_bdvd", elf_dir + "/../../"); } - } - // Mount /app_home/ - if (g_cfg_vfs_app_home.size() == 0) + LOG_NOTICE(LOADER, "Disc: %s", vfs::get("/dev_bdvd")); + } + else if (bdvd_dir.size()) { - g_cfg_vfs_app_home = elf_dir + '/'; + vfs::mount("dev_bdvd", fmt::replace_all(bdvd_dir, "$(EmulatorDir)", emu_dir)); } - vfs::dump(); + // Mount /host_root/ if necessary + if (g_cfg_vfs_allow_host_root) + { + vfs::mount("host_root", {}); + } + + LOG_NOTICE(LOADER, "Used configuration:\n%s\n", cfg::root.to_string()); ppu_load_exec(ppu_exec); diff --git a/rpcs3/Emu/System.h b/rpcs3/Emu/System.h index 0d7d720b23..3923035d0b 100644 --- a/rpcs3/Emu/System.h +++ b/rpcs3/Emu/System.h @@ -172,6 +172,9 @@ public: bool BootGame(const std::string& path, bool direct = false); + static std::string GetGameDir(); + static std::string GetLibDir(); + void Load(); void Run(); bool Pause(); diff --git a/rpcs3/Emu/VFS.cpp b/rpcs3/Emu/VFS.cpp index 32e54511ed..4ec9670e70 100644 --- a/rpcs3/Emu/VFS.cpp +++ b/rpcs3/Emu/VFS.cpp @@ -1,79 +1,51 @@ #include "stdafx.h" -#include "Utilities/Config.h" -#include "Emu/System.h" - +#include "IdManager.h" #include "VFS.h" -#include "Utilities/StrUtil.h" +#include -cfg::string_entry g_cfg_vfs_emulator_dir(cfg::root.vfs, "$(EmulatorDir)"); // Default (empty): taken from fs::get_executable_dir() -cfg::string_entry g_cfg_vfs_dev_hdd0(cfg::root.vfs, "/dev_hdd0/", "$(EmulatorDir)dev_hdd0/"); -cfg::string_entry g_cfg_vfs_dev_hdd1(cfg::root.vfs, "/dev_hdd1/", "$(EmulatorDir)dev_hdd1/"); -cfg::string_entry g_cfg_vfs_dev_flash(cfg::root.vfs, "/dev_flash/", "$(EmulatorDir)dev_flash/"); -cfg::string_entry g_cfg_vfs_dev_usb000(cfg::root.vfs, "/dev_usb000/", "$(EmulatorDir)dev_usb000/"); -cfg::string_entry g_cfg_vfs_dev_bdvd(cfg::root.vfs, "/dev_bdvd/"); // Not mounted -cfg::string_entry g_cfg_vfs_app_home(cfg::root.vfs, "/app_home/"); // Not mounted - -cfg::bool_entry g_cfg_vfs_allow_host_root(cfg::root.vfs, "Enable /host_root/", true); - -void vfs::dump() +struct vfs_manager { - LOG_NOTICE(LOADER, "Mount info:"); - LOG_NOTICE(LOADER, "/dev_hdd0/ -> %s", g_cfg_vfs_dev_hdd0.get()); - LOG_NOTICE(LOADER, "/dev_hdd1/ -> %s", g_cfg_vfs_dev_hdd1.get()); - LOG_NOTICE(LOADER, "/dev_flash/ -> %s", g_cfg_vfs_dev_flash.get()); - LOG_NOTICE(LOADER, "/dev_usb/ -> %s", g_cfg_vfs_dev_usb000.get()); - LOG_NOTICE(LOADER, "/dev_usb000/ -> %s", g_cfg_vfs_dev_usb000.get()); - if (g_cfg_vfs_dev_bdvd.size()) LOG_NOTICE(LOADER, "/dev_bdvd/ -> %s", g_cfg_vfs_dev_bdvd.get()); - if (g_cfg_vfs_app_home.size()) LOG_NOTICE(LOADER, "/app_home/ -> %s", g_cfg_vfs_app_home.get()); - if (g_cfg_vfs_allow_host_root) LOG_NOTICE(LOADER, "/host_root/ -> ."); - LOG_NOTICE(LOADER, ""); + shared_mutex mutex; + + // Device name -> Real path + std::unordered_map mounted; +}; + +const std::regex s_regex_ps3("^/+(.*?)(?:$|/)(.*)", std::regex::optimize); +const std::regex s_regex_psv("^(.*?):(.*)", std::regex::optimize); + +bool vfs::mount(const std::string& dev_name, const std::string& path) +{ + const auto table = fxm::get_always(); + + writer_lock lock(table->mutex); + + return table->mounted.emplace(dev_name, path).second; } -std::string vfs::get(const std::string& vpath) +std::string vfs::get(const std::string& vpath, vfs::type _type) { - const cfg::string_entry* vdir = nullptr; - std::size_t f_pos = vpath.find_first_not_of('/'); - std::size_t start = 0; + std::smatch match; - // Compare vpath with device name - auto detect = [&](const auto& vdev) -> bool + if (!std::regex_match(vpath, match, _type == type::ps3 ? s_regex_ps3 : s_regex_psv)) { - const std::size_t size = ::size32(vdev) - 1; // Char array size - - if (f_pos && f_pos != -1 && vpath.compare(f_pos - 1, size, vdev, size) == 0) - { - start = size; - return true; - } - - return false; - }; - - if (g_cfg_vfs_allow_host_root && detect("/host_root/")) - return vpath.substr(start); // Accessing host FS directly - else if (detect("/dev_hdd0/")) - vdir = &g_cfg_vfs_dev_hdd0; - else if (detect("/dev_hdd1/")) - vdir = &g_cfg_vfs_dev_hdd1; - else if (detect("/dev_flash/")) - vdir = &g_cfg_vfs_dev_flash; - else if (detect("/dev_usb000/")) - vdir = &g_cfg_vfs_dev_usb000; - else if (detect("/dev_usb/")) - vdir = &g_cfg_vfs_dev_usb000; - else if (detect("/dev_bdvd/")) - vdir = &g_cfg_vfs_dev_bdvd; - else if (detect("/app_home/")) - vdir = &g_cfg_vfs_app_home; - - // Return empty path if not mounted - if (!vdir || !start) - { - LOG_WARNING(GENERAL, "vfs::get() failed for %s", vpath); + LOG_WARNING(GENERAL, "vfs::get(): invalid input: %s", vpath); return{}; } - // Replace $(EmulatorDir), concatenate - return fmt::replace_all(*vdir, "$(EmulatorDir)", g_cfg_vfs_emulator_dir.size() == 0 ? fs::get_executable_dir() : g_cfg_vfs_emulator_dir) + vpath.substr(start); + const auto table = fxm::get_always(); + + reader_lock lock(table->mutex); + + const auto found = table->mounted.find(match.str(1)); + + if (found == table->mounted.end()) + { + LOG_WARNING(GENERAL, "vfs::get(): device not found: %s", vpath); + return{}; + } + + // Concatenate + return found->second + match.str(2); } diff --git a/rpcs3/Emu/VFS.h b/rpcs3/Emu/VFS.h index 55c6ceeb57..9397b01395 100644 --- a/rpcs3/Emu/VFS.h +++ b/rpcs3/Emu/VFS.h @@ -2,9 +2,16 @@ namespace vfs { - // Print mounted directories - void dump(); + // VFS type + enum class type + { + ps3, + psv, + }; - // Convert PS3/PSV path to fs-compatible path - std::string get(const std::string& vpath); + // Mount VFS device + bool mount(const std::string& dev_name, const std::string& path); + + // Convert VFS path to fs path + std::string get(const std::string& vpath, type _type = type::ps3); } diff --git a/rpcs3/Gui/GameViewer.cpp b/rpcs3/Gui/GameViewer.cpp index 46d2e6a2fa..495f403aa1 100644 --- a/rpcs3/Gui/GameViewer.cpp +++ b/rpcs3/Gui/GameViewer.cpp @@ -42,7 +42,6 @@ GameViewer::GameViewer(wxWindow* parent) : wxListView(parent) m_sortColumn = 1; m_sortAscending = true; - m_path = "/dev_hdd0/game/"; m_popup = new wxMenu(); Bind(wxEVT_LIST_ITEM_ACTIVATED, &GameViewer::DClick, this); @@ -80,7 +79,7 @@ void GameViewer::LoadGames() { m_games.clear(); - for (const auto& entry : fs::dir(vfs::get(m_path))) + for (const auto& entry : fs::dir(Emu.GetGameDir())) { if (entry.is_directory) { @@ -93,10 +92,13 @@ void GameViewer::LoadPSF() { m_game_data.clear(); + const std::string& game_path = Emu.GetGameDir(); + for (u32 i = 0; i < m_games.size(); ++i) { - const std::string sfb = vfs::get(m_path) + m_games[i] + "/PS3_DISC.SFB"; - const std::string sfo = vfs::get(m_path) + m_games[i] + (fs::is_file(sfb) ? "/PS3_GAME/PARAM.SFO" : "/PARAM.SFO"); + const std::string& dir = game_path + m_games[i]; + const std::string& sfb = dir + "/PS3_DISC.SFB"; + const std::string& sfo = dir + (fs::is_file(sfb) ? "/PS3_GAME/PARAM.SFO" : "/PARAM.SFO"); const fs::file sfo_file(sfo); if (!sfo_file) @@ -125,27 +127,27 @@ void GameViewer::LoadPSF() if (game.category == "HG") { game.category = "HDD Game"; - game.icon_path = vfs::get(m_path) + m_games[i] + "/ICON0.PNG"; + game.icon_path = dir + "/ICON0.PNG"; } else if (game.category == "DG") { game.category = "Disc Game"; - game.icon_path = vfs::get(m_path) + m_games[i] + "/PS3_GAME/ICON0.PNG"; + game.icon_path = dir + "/PS3_GAME/ICON0.PNG"; } else if (game.category == "HM") { game.category = "Home"; - game.icon_path = vfs::get(m_path) + m_games[i] + "/ICON0.PNG"; + game.icon_path = dir + "/ICON0.PNG"; } else if (game.category == "AV") { game.category = "Audio/Video"; - game.icon_path = vfs::get(m_path) + m_games[i] + "/ICON0.PNG"; + game.icon_path = dir + "/ICON0.PNG"; } else if (game.category == "GD") { game.category = "Game Data"; - game.icon_path = vfs::get(m_path) + m_games[i] + "/ICON0.PNG"; + game.icon_path = dir + "/ICON0.PNG"; } m_game_data.push_back(game); @@ -183,13 +185,13 @@ void GameViewer::DClick(wxListEvent& event) long i = GetFirstSelected(); if (i < 0) return; - const std::string& path = m_path + m_game_data[i].root; + const std::string& path = Emu.GetGameDir() + m_game_data[i].root; Emu.Stop(); - if (!Emu.BootGame(vfs::get(path))) + if (!Emu.BootGame(path)) { - LOG_ERROR(LOADER, "Failed to boot %s", path); + LOG_ERROR(LOADER, "Failed to boot /dev_hdd0/game/%s", m_game_data[i].root); } } @@ -238,7 +240,7 @@ void GameViewer::RemoveGame(wxCommandEvent& event) if (wxMessageBox("Permanently delete game files?", "Confirm Delete", wxYES_NO | wxNO_DEFAULT) == wxYES) { - fs::remove_all(vfs::get(m_path) + this->GetItemText(i, 6).ToStdString()); + fs::remove_all(Emu.GetGameDir() + this->GetItemText(i, 6).ToStdString()); } Refresh(); diff --git a/rpcs3/Gui/GameViewer.h b/rpcs3/Gui/GameViewer.h index 9b286244bd..b6b9b4df4c 100644 --- a/rpcs3/Gui/GameViewer.h +++ b/rpcs3/Gui/GameViewer.h @@ -63,7 +63,6 @@ class GameViewer : public wxListView { int m_sortColumn; bool m_sortAscending; - std::string m_path; std::vector m_games; std::vector m_game_data; ColumnsArr m_columns; diff --git a/rpcs3/Gui/MainFrame.cpp b/rpcs3/Gui/MainFrame.cpp index 3b826a7c9d..6dc1406cf7 100644 --- a/rpcs3/Gui/MainFrame.cpp +++ b/rpcs3/Gui/MainFrame.cpp @@ -253,7 +253,7 @@ void MainFrame::InstallPkg(wxCommandEvent& WXUNUSED(event)) pkg_f.seek(0); // Get full path - const auto& local_path = vfs::get("/dev_hdd0/game/") + std::string(std::begin(title_id), std::end(title_id)); + const auto& local_path = Emu.GetGameDir() + std::string(std::begin(title_id), std::end(title_id)); if (!fs::create_dir(local_path)) { diff --git a/rpcs3/Gui/SettingsDialog.cpp b/rpcs3/Gui/SettingsDialog.cpp index 2feb06bcc6..c2e74f3843 100644 --- a/rpcs3/Gui/SettingsDialog.cpp +++ b/rpcs3/Gui/SettingsDialog.cpp @@ -332,7 +332,7 @@ SettingsDialog::SettingsDialog(wxWindow* parent) chbox_list_core_lle->Check(chbox_list_core_lle->Append(unk)); } - const std::string& lle_dir = vfs::get("/dev_flash/sys/external/"); // TODO + const std::string& lle_dir = Emu.GetLibDir(); // TODO std::unordered_set set(data.begin(), data.end()); std::vector lle_module_list_unselected;