libraries: Implement libSceZlib.

This commit is contained in:
squidbus 2025-01-27 21:27:56 -08:00
parent 976a5713f5
commit 3a10014fa4
6 changed files with 166 additions and 52 deletions

View File

@ -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
)

View File

@ -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"

View File

@ -1,60 +1,174 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <condition_variable>
#include <mutex>
#include <stop_token>
#include <unordered_map>
#include <queue>
#include <zlib.h>
#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<InflateTask> task_queue;
std::condition_variable_any task_queue_cv;
std::queue<u64> done_queue;
std::condition_variable_any done_queue_cv;
std::unordered_map<u64, InflateResult> 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<Bytef*>(task.dst), &decompressed_length,
static_cast<const Bytef*>(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<u32>(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<InflateTask>();
done_queue = std::queue<u64>();
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;
}

View File

@ -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

View File

@ -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;

View File

@ -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