diff --git a/CMakeLists.txt b/CMakeLists.txt index 4115d218a..78e3c7997 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -441,7 +441,7 @@ set(NP_LIBS src/core/libraries/np_common/np_common.cpp ) set(ZLIB_LIB src/core/libraries/zlib/zlib.cpp - src/core/libraries/zlib/zlib.h + src/core/libraries/zlib/zlib_sce.h src/core/libraries/zlib/zlib_error.h ) diff --git a/src/core/libraries/libs.cpp b/src/core/libraries/libs.cpp index 3e77a6cac..8cf286d13 100644 --- a/src/core/libraries/libs.cpp +++ b/src/core/libraries/libs.cpp @@ -54,7 +54,7 @@ #include "core/libraries/videodec/videodec2.h" #include "core/libraries/videoout/video_out.h" #include "core/libraries/web_browser_dialog/webbrowserdialog.h" -#include "core/libraries/zlib/zlib.h" +#include "core/libraries/zlib/zlib_sce.h" #include "fiber/fiber.h" #include "jpeg/jpegenc.h" diff --git a/src/core/libraries/zlib/zlib.cpp b/src/core/libraries/zlib/zlib.cpp index 0574d7452..db42b126b 100644 --- a/src/core/libraries/zlib/zlib.cpp +++ b/src/core/libraries/zlib/zlib.cpp @@ -1,60 +1,174 @@ // SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later +#include +#include +#include +#include +#include +#include + #include "common/logging/log.h" -#include "common/singleton.h" -#include "core/libraries/error_codes.h" -#include "core/libraries/kernel/equeue.h" +#include "common/thread.h" +#include "core/libraries/kernel/threads.h" #include "core/libraries/libs.h" #include "core/libraries/zlib/zlib_error.h" +#include "core/libraries/zlib/zlib_sce.h" namespace Libraries::Zlib { -int PS4_SYSV_ABI sceZlibInitialize(const void* buffer, std::size_t length) { - LOG_ERROR(Lib_Zlib, "(STUBBED)called"); +struct InflateTask { + u64 request_id; + const void* src; + u32 src_length; + void* dst; + u32 dst_length; +}; - // buffer and length passed as 0 is expected behavior, though module may use them in - // specific ways. +struct InflateResult { + u32 length; + s32 status; +}; +Kernel::Thread task_thread; + +std::mutex mutex; +std::queue task_queue; +std::condition_variable_any task_queue_cv; +std::queue done_queue; +std::condition_variable_any done_queue_cv; +std::unordered_map results; +u64 next_request_id; + +void ZlibTaskThread(const std::stop_token& stop) { + Common::SetCurrentThreadName("shadPS4:ZlibTaskThread"); + + while (!stop.stop_requested()) { + InflateTask task; + { + // Lock and pop from the task queue, unless stop has been requested. + std::unique_lock lock(mutex); + if (!task_queue_cv.wait(lock, stop, [&] { return !task_queue.empty(); })) { + break; + } + task = task_queue.back(); + task_queue.pop(); + } + + uLongf decompressed_length = task.dst_length; + const auto ret = uncompress(static_cast(task.dst), &decompressed_length, + static_cast(task.src), task.src_length); + + { + // Lock, insert the new result, and push the finished request ID to the done queue. + std::unique_lock lock(mutex); + results[task.request_id] = InflateResult{ + .length = static_cast(decompressed_length), + .status = ret == Z_BUF_ERROR ? ORBIS_ZLIB_ERROR_NOSPACE + : ret == Z_OK ? ORBIS_OK + : ORBIS_ZLIB_ERROR_FATAL, + }; + done_queue.push(task.request_id); + } + done_queue_cv.notify_one(); + } +} + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length) { + LOG_INFO(Lib_Zlib, "called"); + if (task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_ALREADY_INITIALIZED; + } + + // Initialize with empty task data + task_queue = std::queue(); + done_queue = std::queue(); + results.clear(); + next_request_id = 1; + + task_thread.Run([](const std::stop_token& stop) { ZlibTaskThread(stop); }); return ORBIS_OK; } -int PS4_SYSV_ABI sceZlibInflate(const void* source, u32 sourceLength, void* destination, - u32 destinationLength, u64* requestId) { - LOG_ERROR(Lib_Zlib, "(STUBBED)called"); - - if (!source || !sourceLength || !destination || !destinationLength || - destinationLength > 64_KB || destinationLength % 2_KB != 0) +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!src || !src_len || !dst || !dst_len || !request_id || dst_len > 64_KB || + dst_len % 2_KB != 0) { return ORBIS_ZLIB_ERROR_INVALID; + } - *requestId = 0; + { + std::unique_lock lock(mutex); + *request_id = next_request_id++; + task_queue.emplace(InflateTask{ + .request_id = *request_id, + .src = src, + .src_length = src_len, + .dst = dst, + .dst_length = dst_len, + }); + task_queue_cv.notify_one(); + } return ORBIS_OK; } -int PS4_SYSV_ABI sceZlibWaitForDone(u64* requestId, u32* timeout) { - LOG_ERROR(Lib_Zlib, "(STUBBED)called"); - if (!requestId) +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!request_id) { return ORBIS_ZLIB_ERROR_INVALID; + } - // timeout is checked by sceKernelWaitEqueue - - *requestId = 0; + { + // Pop from the done queue, unless the timeout is reached. + std::unique_lock lock(mutex); + const auto pred = [] { return !done_queue.empty(); }; + if (timeout) { + if (!done_queue_cv.wait_for(lock, std::chrono::milliseconds(*timeout), pred)) { + return ORBIS_ZLIB_ERROR_TIMEDOUT; + } + } else { + done_queue_cv.wait(lock, pred); + } + *request_id = done_queue.back(); + done_queue.pop(); + } return ORBIS_OK; } -int PS4_SYSV_ABI sceZlibGetResult(u64 requestId, u32* destinationLength, int* status) { - LOG_ERROR(Lib_Zlib, "(STUBBED)called"); - - if (!destinationLength || !status) +s32 PS4_SYSV_ABI sceZlibGetResult(const u64 request_id, u32* dst_length, s32* status) { + LOG_DEBUG(Lib_Zlib, "(STUBBED) called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + if (!dst_length || !status) { return ORBIS_ZLIB_ERROR_INVALID; + } - *destinationLength = 0; - *status = 0; + { + std::unique_lock lock(mutex); + if (!results.contains(request_id)) { + return ORBIS_ZLIB_ERROR_NOT_FOUND; + } + const auto result = results[request_id]; + *dst_length = result.length; + *status = result.status; + } return ORBIS_OK; } -int PS4_SYSV_ABI sceZlibFinalize() { - LOG_ERROR(Lib_Zlib, "(STUBBED)called"); +s32 PS4_SYSV_ABI sceZlibFinalize() { + LOG_INFO(Lib_Zlib, "called"); + if (!task_thread.Joinable()) { + return ORBIS_ZLIB_ERROR_NOT_INITIALIZED; + } + task_thread.Stop(); return ORBIS_OK; } diff --git a/src/core/libraries/zlib/zlib.h b/src/core/libraries/zlib/zlib.h deleted file mode 100644 index 4c46a5fa7..000000000 --- a/src/core/libraries/zlib/zlib.h +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project -// SPDX-License-Identifier: GPL-2.0-or-later - -#pragma once - -#include "common/types.h" - -namespace Core::Loader { -class SymbolsResolver; -} -namespace Libraries::Zlib { - -int PS4_SYSV_ABI sceZlibInitialize(const void* buffer, std::size_t length); -int PS4_SYSV_ABI sceZlibInflate(const void* source, u32 sourceLength, void* destination, - u32 destinationLength, u64* requestId); -int PS4_SYSV_ABI sceZlibWaitForDone(u64* requestId, u32* timeout); -int PS4_SYSV_ABI sceZlibGetResult(u64 requestId, u32* destinationLength, int* status); -int PS4_SYSV_ABI sceZlibFinalize(); - -void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym); -} // namespace Libraries::Zlib \ No newline at end of file diff --git a/src/core/libraries/zlib/zlib_error.h b/src/core/libraries/zlib/zlib_error.h index 03047b332..59574f0b2 100644 --- a/src/core/libraries/zlib/zlib_error.h +++ b/src/core/libraries/zlib/zlib_error.h @@ -7,7 +7,7 @@ // Zlib library constexpr int ORBIS_ZLIB_ERROR_NOT_FOUND = 0x81120002; -constexpr int ORBIS_ZLIB_ERROR_AGAIN = 0x80ED0002; +constexpr int ORBIS_ZLIB_ERROR_BUSY = 0x8112000B; constexpr int ORBIS_ZLIB_ERROR_FAULT = 0x8112000E; constexpr int ORBIS_ZLIB_ERROR_INVALID = 0x81120016; constexpr int ORBIS_ZLIB_ERROR_NOSPACE = 0x8112001C; diff --git a/src/core/libraries/zlib/zlib_sce.h b/src/core/libraries/zlib/zlib_sce.h new file mode 100644 index 000000000..6f8cf9468 --- /dev/null +++ b/src/core/libraries/zlib/zlib_sce.h @@ -0,0 +1,21 @@ +// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include "common/types.h" + +namespace Core::Loader { +class SymbolsResolver; +} +namespace Libraries::Zlib { + +s32 PS4_SYSV_ABI sceZlibInitialize(const void* buffer, u32 length); +s32 PS4_SYSV_ABI sceZlibInflate(const void* src, u32 src_len, void* dst, u32 dst_len, + u64* request_id); +s32 PS4_SYSV_ABI sceZlibWaitForDone(u64* request_id, const u32* timeout); +s32 PS4_SYSV_ABI sceZlibGetResult(u64 request_id, u32* dst_length, s32* status); +s32 PS4_SYSV_ABI sceZlibFinalize(); + +void RegisterlibSceZlib(Core::Loader::SymbolsResolver* sym); +} // namespace Libraries::Zlib \ No newline at end of file