Switch remaining CRLF terminated files to LF

This commit is contained in:
Daniel R. 2024-12-24 13:56:31 +01:00
parent 2c0f986c52
commit c284cf72e1
No known key found for this signature in database
GPG key ID: B8ADC8F57BA18DBA
28 changed files with 4856 additions and 4856 deletions

View file

@ -1,484 +1,484 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "fiber.h"
#include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/libs.h"
#include "core/tls.h"
namespace Libraries::Fiber {
static constexpr u32 kFiberSignature0 = 0xdef1649c;
static constexpr u32 kFiberSignature1 = 0xb37592a0;
static constexpr u32 kFiberOptSignature = 0xbb40e64d;
static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca;
static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef;
static std::atomic<u32> context_size_check = false;
OrbisFiberContext* GetFiberContext() {
return Core::GetTcbBase()->tcb_fiber;
}
extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp");
extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp");
extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data,
bool set_fpu) asm("_sceFiberSwitchEntry");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) {
OrbisFiberContext* g_ctx = GetFiberContext();
g_ctx->return_val = ret;
_sceFiberLongJmp(g_ctx);
}
void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
u64* stack_base = reinterpret_cast<u64*>(ctx->current_fiber->addr_context);
if (stack_base && *stack_base != kFiberStackSignature) {
UNREACHABLE_MSG("Stack overflow detected in fiber.");
}
}
void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
OrbisFiberContext* fiber_ctx = fiber->context;
if (fiber_ctx) {
ctx->arg_on_run_to = arg_on_run_to;
_sceFiberLongJmp(fiber_ctx);
__builtin_trap();
}
OrbisFiberData data{};
if (ctx->prev_fiber) {
OrbisFiber* prev_fiber = ctx->prev_fiber;
ctx->prev_fiber = nullptr;
data.state = reinterpret_cast<u32*>(&prev_fiber->state);
} else {
data.state = nullptr;
}
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr =
reinterpret_cast<void*>(reinterpret_cast<u64>(fiber->addr_context) + fiber->size_context);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
ctx->prev_fiber = cur_fiber;
ctx->current_fiber = fiber;
if (fiber->addr_context == nullptr) {
ctx->prev_fiber = nullptr;
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx->rsp & ~15);
data.state = reinterpret_cast<u32*>(&cur_fiber->state);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
_sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx);
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) {
ctx->arg_on_return = arg_on_return;
_sceFiberLongJmp(ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_ver) {
if (!fiber || !name || !entry) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)addr_context & 15) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (opt_param && (u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) {
return ORBIS_FIBER_ERROR_RANGE;
}
if (size_context & 15) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (!addr_context && size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (addr_context && !size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (opt_param && opt_param->magic != kFiberOptSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 flags = FiberFlags::None;
if (build_ver >= 0x3500000) {
flags |= FiberFlags::SetFpuRegs;
}
if (context_size_check) {
flags |= FiberFlags::ContextSizeCheck;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber->entry = entry;
fiber->arg_on_initialize = arg_on_initialize;
fiber->addr_context = addr_context;
fiber->size_context = size_context;
fiber->context = nullptr;
fiber->flags = flags;
/*
A low stack area is problematic, as we can easily
cause a stack overflow with our HLE.
*/
if (size_context && size_context <= 4096) {
LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area.");
}
fiber->magic_start = kFiberSignature0;
fiber->magic_end = kFiberSignature1;
if (addr_context != nullptr) {
fiber->context_start = addr_context;
fiber->context_end =
reinterpret_cast<void*>(reinterpret_cast<u64>(addr_context) + size_context);
/* Apply signature to start of stack */
*(u64*)addr_context = kFiberStackSignature;
if (flags & FiberFlags::ContextSizeCheck) {
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
*stack_ptr++ = kFiberStackSizeCheck;
}
}
}
fiber->state = FiberState::Idle;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) {
if (!opt_param) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
opt_param->magic = kFiberOptSignature;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
Core::Tcb* tcb = Core::GetTcbBase();
if (tcb->tcb_fiber) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiberContext ctx{};
ctx.current_fiber = fiber;
ctx.prev_fiber = nullptr;
ctx.return_val = 0;
tcb->tcb_fiber = &ctx;
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
if (fiber->addr_context) {
_sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx);
__builtin_trap();
}
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx.rsp & ~15);
data.state = nullptr;
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
}
OrbisFiber* cur_fiber = ctx.current_fiber;
ctx.current_fiber = nullptr;
cur_fiber->state = FiberState::Idle;
if (ctx.return_val != 0) {
/* Fiber entry returned! This should never happen. */
UNREACHABLE_MSG("Fiber entry function returned.");
}
if (arg_on_return) {
*arg_on_return = ctx.arg_on_return;
}
tcb->tcb_fiber = nullptr;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context == nullptr) {
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
*fiber = g_ctx->current_fiber;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) {
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context) {
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (jmp) {
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
}
_sceFiberTerminate(cur_fiber, arg_on_return, g_ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) {
if (!fiber || !fiber_info) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)fiber_info & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber_info->size != sizeof(OrbisFiberInfo)) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
fiber_info->entry = fiber->entry;
fiber_info->arg_on_initialize = fiber->arg_on_initialize;
fiber_info->addr_context = fiber->addr_context;
fiber_info->size_context = fiber->size_context;
strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber_info->size_context_margin = -1;
if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) {
u64 stack_margin = 0;
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
if (*stack_start == kFiberStackSignature) {
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
if (*stack_ptr == kFiberStackSizeCheck) {
stack_ptr++;
}
}
stack_margin =
reinterpret_cast<u64>(stack_ptr) - reinterpret_cast<u64>(stack_start + 1);
}
fiber_info->size_context_margin = stack_margin;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
if (flags != 0) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 expected = 0;
if (!context_size_check.compare_exchange_strong(expected, 1u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
u32 expected = 1;
if (!context_size_check.compare_exchange_strong(expected, 0u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) {
if (!fiber || !name) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun);
LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch);
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStartContextSizeCheck);
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStopContextSizeCheck);
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
}
} // namespace Libraries::Fiber
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "fiber.h"
#include "common/logging/log.h"
#include "core/libraries/fiber/fiber_error.h"
#include "core/libraries/libs.h"
#include "core/tls.h"
namespace Libraries::Fiber {
static constexpr u32 kFiberSignature0 = 0xdef1649c;
static constexpr u32 kFiberSignature1 = 0xb37592a0;
static constexpr u32 kFiberOptSignature = 0xbb40e64d;
static constexpr u64 kFiberStackSignature = 0x7149f2ca7149f2ca;
static constexpr u64 kFiberStackSizeCheck = 0xdeadbeefdeadbeef;
static std::atomic<u32> context_size_check = false;
OrbisFiberContext* GetFiberContext() {
return Core::GetTcbBase()->tcb_fiber;
}
extern "C" s32 PS4_SYSV_ABI _sceFiberSetJmp(OrbisFiberContext* ctx) asm("_sceFiberSetJmp");
extern "C" s32 PS4_SYSV_ABI _sceFiberLongJmp(OrbisFiberContext* ctx) asm("_sceFiberLongJmp");
extern "C" void PS4_SYSV_ABI _sceFiberSwitchEntry(OrbisFiberData* data,
bool set_fpu) asm("_sceFiberSwitchEntry");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) asm("_sceFiberForceQuit");
extern "C" void PS4_SYSV_ABI _sceFiberForceQuit(u64 ret) {
OrbisFiberContext* g_ctx = GetFiberContext();
g_ctx->return_val = ret;
_sceFiberLongJmp(g_ctx);
}
void PS4_SYSV_ABI _sceFiberCheckStackOverflow(OrbisFiberContext* ctx) {
u64* stack_base = reinterpret_cast<u64*>(ctx->current_fiber->addr_context);
if (stack_base && *stack_base != kFiberStackSignature) {
UNREACHABLE_MSG("Stack overflow detected in fiber.");
}
}
void PS4_SYSV_ABI _sceFiberSwitchToFiber(OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
OrbisFiberContext* fiber_ctx = fiber->context;
if (fiber_ctx) {
ctx->arg_on_run_to = arg_on_run_to;
_sceFiberLongJmp(fiber_ctx);
__builtin_trap();
}
OrbisFiberData data{};
if (ctx->prev_fiber) {
OrbisFiber* prev_fiber = ctx->prev_fiber;
ctx->prev_fiber = nullptr;
data.state = reinterpret_cast<u32*>(&prev_fiber->state);
} else {
data.state = nullptr;
}
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr =
reinterpret_cast<void*>(reinterpret_cast<u64>(fiber->addr_context) + fiber->size_context);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberSwitch(OrbisFiber* cur_fiber, OrbisFiber* fiber, u64 arg_on_run_to,
OrbisFiberContext* ctx) {
ctx->prev_fiber = cur_fiber;
ctx->current_fiber = fiber;
if (fiber->addr_context == nullptr) {
ctx->prev_fiber = nullptr;
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx->rsp & ~15);
data.state = reinterpret_cast<u32*>(&cur_fiber->state);
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
__builtin_trap();
}
_sceFiberSwitchToFiber(fiber, arg_on_run_to, ctx);
__builtin_trap();
}
void PS4_SYSV_ABI _sceFiberTerminate(OrbisFiber* fiber, u64 arg_on_return, OrbisFiberContext* ctx) {
ctx->arg_on_return = arg_on_return;
_sceFiberLongJmp(ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_ver) {
if (!fiber || !name || !entry) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)addr_context & 15) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (opt_param && (u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (size_context && size_context < ORBIS_FIBER_CONTEXT_MINIMUM_SIZE) {
return ORBIS_FIBER_ERROR_RANGE;
}
if (size_context & 15) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (!addr_context && size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (addr_context && !size_context) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (opt_param && opt_param->magic != kFiberOptSignature) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 flags = FiberFlags::None;
if (build_ver >= 0x3500000) {
flags |= FiberFlags::SetFpuRegs;
}
if (context_size_check) {
flags |= FiberFlags::ContextSizeCheck;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber->entry = entry;
fiber->arg_on_initialize = arg_on_initialize;
fiber->addr_context = addr_context;
fiber->size_context = size_context;
fiber->context = nullptr;
fiber->flags = flags;
/*
A low stack area is problematic, as we can easily
cause a stack overflow with our HLE.
*/
if (size_context && size_context <= 4096) {
LOG_WARNING(Lib_Fiber, "Fiber initialized with small stack area.");
}
fiber->magic_start = kFiberSignature0;
fiber->magic_end = kFiberSignature1;
if (addr_context != nullptr) {
fiber->context_start = addr_context;
fiber->context_end =
reinterpret_cast<void*>(reinterpret_cast<u64>(addr_context) + size_context);
/* Apply signature to start of stack */
*(u64*)addr_context = kFiberStackSignature;
if (flags & FiberFlags::ContextSizeCheck) {
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
*stack_ptr++ = kFiberStackSizeCheck;
}
}
}
fiber->state = FiberState::Idle;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param) {
if (!opt_param) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)opt_param & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
opt_param->magic = kFiberOptSignature;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Terminated)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
Core::Tcb* tcb = Core::GetTcbBase();
if (tcb->tcb_fiber) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiberContext ctx{};
ctx.current_fiber = fiber;
ctx.prev_fiber = nullptr;
ctx.return_val = 0;
tcb->tcb_fiber = &ctx;
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
if (fiber->addr_context) {
_sceFiberSwitchToFiber(fiber, arg_on_run_to, &ctx);
__builtin_trap();
}
OrbisFiberData data{};
data.entry = fiber->entry;
data.arg_on_initialize = fiber->arg_on_initialize;
data.arg_on_run_to = arg_on_run_to;
data.stack_addr = reinterpret_cast<void*>(ctx.rsp & ~15);
data.state = nullptr;
if (fiber->flags & FiberFlags::SetFpuRegs) {
data.fpucw = 0x037f;
data.mxcsr = 0x9fc0;
_sceFiberSwitchEntry(&data, true);
} else {
_sceFiberSwitchEntry(&data, false);
}
}
OrbisFiber* cur_fiber = ctx.current_fiber;
ctx.current_fiber = nullptr;
cur_fiber->state = FiberState::Idle;
if (ctx.return_val != 0) {
/* Fiber entry returned! This should never happen. */
UNREACHABLE_MSG("Fiber entry function returned.");
}
if (arg_on_return) {
*arg_on_return = ctx.arg_on_return;
}
tcb->tcb_fiber = nullptr;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
FiberState expected = FiberState::Idle;
if (!fiber->state.compare_exchange_strong(expected, FiberState::Run)) {
return ORBIS_FIBER_ERROR_STATE;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context == nullptr) {
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (!jmp) {
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
_sceFiberSwitch(cur_fiber, fiber, arg_on_run_to, g_ctx);
__builtin_trap();
}
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber) {
if (!fiber) {
return ORBIS_FIBER_ERROR_NULL;
}
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
*fiber = g_ctx->current_fiber;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run) {
OrbisFiberContext* g_ctx = GetFiberContext();
if (!g_ctx) {
return ORBIS_FIBER_ERROR_PERMISSION;
}
OrbisFiber* cur_fiber = g_ctx->current_fiber;
if (cur_fiber->addr_context) {
OrbisFiberContext ctx{};
s32 jmp = _sceFiberSetJmp(&ctx);
if (jmp) {
g_ctx = GetFiberContext();
if (g_ctx->prev_fiber) {
g_ctx->prev_fiber->state = FiberState::Idle;
g_ctx->prev_fiber = nullptr;
}
if (arg_on_run) {
*arg_on_run = g_ctx->arg_on_run_to;
}
return ORBIS_OK;
}
cur_fiber->context = &ctx;
_sceFiberCheckStackOverflow(g_ctx);
}
_sceFiberTerminate(cur_fiber, arg_on_return, g_ctx);
__builtin_trap();
}
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info) {
if (!fiber || !fiber_info) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7 || (u64)fiber_info & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber_info->size != sizeof(OrbisFiberInfo)) {
return ORBIS_FIBER_ERROR_INVALID;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
fiber_info->entry = fiber->entry;
fiber_info->arg_on_initialize = fiber->arg_on_initialize;
fiber_info->addr_context = fiber->addr_context;
fiber_info->size_context = fiber->size_context;
strncpy(fiber_info->name, fiber->name, ORBIS_FIBER_MAX_NAME_LENGTH);
fiber_info->size_context_margin = -1;
if (fiber->flags & FiberFlags::ContextSizeCheck && fiber->addr_context != nullptr) {
u64 stack_margin = 0;
u64* stack_start = reinterpret_cast<u64*>(fiber->context_start);
u64* stack_end = reinterpret_cast<u64*>(fiber->context_end);
if (*stack_start == kFiberStackSignature) {
u64* stack_ptr = stack_start + 1;
while (stack_ptr < stack_end) {
if (*stack_ptr == kFiberStackSizeCheck) {
stack_ptr++;
}
}
stack_margin =
reinterpret_cast<u64>(stack_ptr) - reinterpret_cast<u64>(stack_start + 1);
}
fiber_info->size_context_margin = stack_margin;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags) {
if (flags != 0) {
return ORBIS_FIBER_ERROR_INVALID;
}
u32 expected = 0;
if (!context_size_check.compare_exchange_strong(expected, 1u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck() {
u32 expected = 1;
if (!context_size_check.compare_exchange_strong(expected, 0u)) {
return ORBIS_FIBER_ERROR_STATE;
}
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name) {
if (!fiber || !name) {
return ORBIS_FIBER_ERROR_NULL;
}
if ((u64)fiber & 7) {
return ORBIS_FIBER_ERROR_ALIGNMENT;
}
if (fiber->magic_start != kFiberSignature0 || fiber->magic_end != kFiberSignature1) {
return ORBIS_FIBER_ERROR_INVALID;
}
strncpy(fiber->name, name, ORBIS_FIBER_MAX_NAME_LENGTH);
return ORBIS_OK;
}
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("hVYD7Ou2pCQ", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("7+OJIpko9RY", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberInitialize);
LIB_FUNCTION("asjUJJ+aa8s", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberOptParamInitialize);
LIB_FUNCTION("JeNX5F-NzQU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberFinalize);
LIB_FUNCTION("a0LLrZWac0M", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRun);
LIB_FUNCTION("PFT2S-tJ7Uk", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberSwitch);
LIB_FUNCTION("p+zLIOg27zU", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetSelf);
LIB_FUNCTION("B0ZX2hx9DMw", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberReturnToThread);
LIB_FUNCTION("uq2Y5BFz0PE", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberGetInfo);
LIB_FUNCTION("Lcqty+QNWFc", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStartContextSizeCheck);
LIB_FUNCTION("Kj4nXMpnM8Y", "libSceFiber", 1, "libSceFiber", 1, 1,
sceFiberStopContextSizeCheck);
LIB_FUNCTION("JzyT91ucGDc", "libSceFiber", 1, "libSceFiber", 1, 1, sceFiberRename);
}
} // namespace Libraries::Fiber

View file

@ -1,118 +1,118 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/types.h"
#include <atomic>
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Fiber {
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512)
typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run);
enum FiberState : u32 {
Run = 1u,
Idle = 2u,
Terminated = 3u,
};
enum FiberFlags : u32 {
None = 0x0,
NoUlobjmgr = 0x1,
ContextSizeCheck = 0x10,
SetFpuRegs = 0x100,
};
struct OrbisFiber;
struct OrbisFiberContext {
struct {
u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15;
u16 fpucw;
u32 mxcsr;
};
OrbisFiber* current_fiber;
OrbisFiber* prev_fiber;
u64 arg_on_run_to;
u64 arg_on_return;
u64 return_val;
};
struct OrbisFiberData {
OrbisFiberEntry entry;
u64 arg_on_initialize;
u64 arg_on_run_to;
void* stack_addr;
u32* state;
u16 fpucw;
s8 pad[2];
u32 mxcsr;
};
struct OrbisFiber {
u32 magic_start;
std::atomic<FiberState> state;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
OrbisFiberContext* context;
u32 flags;
void* context_start;
void* context_end;
u32 magic_end;
};
static_assert(sizeof(OrbisFiber) <= 256);
struct OrbisFiberInfo {
u64 size;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
u64 size_context_margin;
u8 pad[48];
};
static_assert(sizeof(OrbisFiberInfo) == 128);
struct OrbisFiberOptParam {
u32 magic;
};
static_assert(sizeof(OrbisFiberOptParam) <= 128);
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_version);
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param);
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber);
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return);
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber);
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info);
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name);
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/assert.h"
#include "common/types.h"
#include <atomic>
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Fiber {
#define ORBIS_FIBER_MAX_NAME_LENGTH (31)
#define ORBIS_FIBER_CONTEXT_MINIMUM_SIZE (512)
typedef void PS4_SYSV_ABI (*OrbisFiberEntry)(u64 arg_on_initialize, u64 arg_on_run);
enum FiberState : u32 {
Run = 1u,
Idle = 2u,
Terminated = 3u,
};
enum FiberFlags : u32 {
None = 0x0,
NoUlobjmgr = 0x1,
ContextSizeCheck = 0x10,
SetFpuRegs = 0x100,
};
struct OrbisFiber;
struct OrbisFiberContext {
struct {
u64 rax, rcx, rdx, rbx, rsp, rbp, r8, r9, r10, r11, r12, r13, r14, r15;
u16 fpucw;
u32 mxcsr;
};
OrbisFiber* current_fiber;
OrbisFiber* prev_fiber;
u64 arg_on_run_to;
u64 arg_on_return;
u64 return_val;
};
struct OrbisFiberData {
OrbisFiberEntry entry;
u64 arg_on_initialize;
u64 arg_on_run_to;
void* stack_addr;
u32* state;
u16 fpucw;
s8 pad[2];
u32 mxcsr;
};
struct OrbisFiber {
u32 magic_start;
std::atomic<FiberState> state;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
OrbisFiberContext* context;
u32 flags;
void* context_start;
void* context_end;
u32 magic_end;
};
static_assert(sizeof(OrbisFiber) <= 256);
struct OrbisFiberInfo {
u64 size;
OrbisFiberEntry entry;
u64 arg_on_initialize;
void* addr_context;
u64 size_context;
char name[ORBIS_FIBER_MAX_NAME_LENGTH + 1];
u64 size_context_margin;
u8 pad[48];
};
static_assert(sizeof(OrbisFiberInfo) == 128);
struct OrbisFiberOptParam {
u32 magic;
};
static_assert(sizeof(OrbisFiberOptParam) <= 128);
s32 PS4_SYSV_ABI sceFiberInitialize(OrbisFiber* fiber, const char* name, OrbisFiberEntry entry,
u64 arg_on_initialize, void* addr_context, u64 size_context,
const OrbisFiberOptParam* opt_param, u32 build_version);
s32 PS4_SYSV_ABI sceFiberOptParamInitialize(OrbisFiberOptParam* opt_param);
s32 PS4_SYSV_ABI sceFiberFinalize(OrbisFiber* fiber);
s32 PS4_SYSV_ABI sceFiberRun(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_return);
s32 PS4_SYSV_ABI sceFiberSwitch(OrbisFiber* fiber, u64 arg_on_run_to, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetSelf(OrbisFiber** fiber);
s32 PS4_SYSV_ABI sceFiberReturnToThread(u64 arg_on_return, u64* arg_on_run);
s32 PS4_SYSV_ABI sceFiberGetInfo(OrbisFiber* fiber, OrbisFiberInfo* fiber_info);
s32 PS4_SYSV_ABI sceFiberStartContextSizeCheck(u32 flags);
s32 PS4_SYSV_ABI sceFiberStopContextSizeCheck(void);
s32 PS4_SYSV_ABI sceFiberRename(OrbisFiber* fiber, const char* name);
void RegisterlibSceFiber(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Fiber

View file

@ -1,121 +1,121 @@
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
.global _sceFiberSetJmp
_sceFiberSetJmp:
movq %rax, 0x0(%rdi)
movq (%rsp), %rdx
movq %rdx, 0x10(%rdi)
movq %rcx, 0x08(%rdi)
movq %rbx, 0x18(%rdi)
movq %rsp, 0x20(%rdi)
movq %rbp, 0x28(%rdi)
movq %r8, 0x30(%rdi)
movq %r9, 0x38(%rdi)
movq %r10, 0x40(%rdi)
movq %r11, 0x48(%rdi)
movq %r12, 0x50(%rdi)
movq %r13, 0x58(%rdi)
movq %r14, 0x60(%rdi)
movq %r15, 0x68(%rdi)
fnstcw 0x70(%rdi)
stmxcsr 0x72(%rdi)
xor %eax, %eax
ret
.global _sceFiberLongJmp
_sceFiberLongJmp:
# MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f)
stmxcsr -0x4(%rsp)
movl 0x72(%rdi), %eax
andl $0xffffffc0, %eax
movl -0x4(%rsp), %ecx
andl $0x3f, %ecx
xorl %eax, %ecx
movl %ecx, -0x4(%rsp)
ldmxcsr -0x4(%rsp)
movq 0x00(%rdi), %rax
movq 0x08(%rdi), %rcx
movq 0x10(%rdi), %rdx
movq 0x18(%rdi), %rbx
movq 0x20(%rdi), %rsp
movq 0x28(%rdi), %rbp
movq 0x30(%rdi), %r8
movq 0x38(%rdi), %r9
movq 0x40(%rdi), %r10
movq 0x48(%rdi), %r11
movq 0x50(%rdi), %r12
movq 0x58(%rdi), %r13
movq 0x60(%rdi), %r14
movq 0x68(%rdi), %r15
fldcw 0x70(%rdi)
# Make the jump and return 1
movq %rdx, 0x00(%rsp)
movl $0x1, %eax
ret
.global _sceFiberSwitchEntry
_sceFiberSwitchEntry:
mov %rdi, %r11
# Set stack address to provided stack
movq 0x18(%r11), %rsp
xorl %ebp, %ebp
movq 0x20(%r11), %r10 # data->state
# Set previous fiber state to Idle
test %r10, %r10
jz .clear_regs
movl $2, (%r10)
.clear_regs:
test %esi, %esi
jz .skip_fpu_regs
ldmxcsr 0x2c(%r11)
fldcw 0x28(%r11)
.skip_fpu_regs:
movq 0x08(%r11), %rdi # data->arg_on_initialize
movq 0x10(%r11), %rsi # data->arg_on_run_to
movq 0x00(%r11), %r11 # data->entry
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
xorq %r8, %r8
xorq %r9, %r9
xorq %r10, %r10
xorq %r12, %r12
xorq %r13, %r13
xorq %r14, %r14
xorq %r15, %r15
pxor %mm0, %mm0
pxor %mm1, %mm1
pxor %mm2, %mm2
pxor %mm3, %mm3
pxor %mm4, %mm4
pxor %mm5, %mm5
pxor %mm6, %mm6
pxor %mm7, %mm7
emms
vzeroall
# Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to)
call *%r11
# Fiber returned, not good
movl $1, %edi
call _sceFiberForceQuit
# SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
# SPDX-License-Identifier: GPL-2.0-or-later
.global _sceFiberSetJmp
_sceFiberSetJmp:
movq %rax, 0x0(%rdi)
movq (%rsp), %rdx
movq %rdx, 0x10(%rdi)
movq %rcx, 0x08(%rdi)
movq %rbx, 0x18(%rdi)
movq %rsp, 0x20(%rdi)
movq %rbp, 0x28(%rdi)
movq %r8, 0x30(%rdi)
movq %r9, 0x38(%rdi)
movq %r10, 0x40(%rdi)
movq %r11, 0x48(%rdi)
movq %r12, 0x50(%rdi)
movq %r13, 0x58(%rdi)
movq %r14, 0x60(%rdi)
movq %r15, 0x68(%rdi)
fnstcw 0x70(%rdi)
stmxcsr 0x72(%rdi)
xor %eax, %eax
ret
.global _sceFiberLongJmp
_sceFiberLongJmp:
# MXCSR = (MXCSR & 0x3f) ^ (ctx->mxcsr & ~0x3f)
stmxcsr -0x4(%rsp)
movl 0x72(%rdi), %eax
andl $0xffffffc0, %eax
movl -0x4(%rsp), %ecx
andl $0x3f, %ecx
xorl %eax, %ecx
movl %ecx, -0x4(%rsp)
ldmxcsr -0x4(%rsp)
movq 0x00(%rdi), %rax
movq 0x08(%rdi), %rcx
movq 0x10(%rdi), %rdx
movq 0x18(%rdi), %rbx
movq 0x20(%rdi), %rsp
movq 0x28(%rdi), %rbp
movq 0x30(%rdi), %r8
movq 0x38(%rdi), %r9
movq 0x40(%rdi), %r10
movq 0x48(%rdi), %r11
movq 0x50(%rdi), %r12
movq 0x58(%rdi), %r13
movq 0x60(%rdi), %r14
movq 0x68(%rdi), %r15
fldcw 0x70(%rdi)
# Make the jump and return 1
movq %rdx, 0x00(%rsp)
movl $0x1, %eax
ret
.global _sceFiberSwitchEntry
_sceFiberSwitchEntry:
mov %rdi, %r11
# Set stack address to provided stack
movq 0x18(%r11), %rsp
xorl %ebp, %ebp
movq 0x20(%r11), %r10 # data->state
# Set previous fiber state to Idle
test %r10, %r10
jz .clear_regs
movl $2, (%r10)
.clear_regs:
test %esi, %esi
jz .skip_fpu_regs
ldmxcsr 0x2c(%r11)
fldcw 0x28(%r11)
.skip_fpu_regs:
movq 0x08(%r11), %rdi # data->arg_on_initialize
movq 0x10(%r11), %rsi # data->arg_on_run_to
movq 0x00(%r11), %r11 # data->entry
xorl %eax, %eax
xorl %ebx, %ebx
xorl %ecx, %ecx
xorl %edx, %edx
xorq %r8, %r8
xorq %r9, %r9
xorq %r10, %r10
xorq %r12, %r12
xorq %r13, %r13
xorq %r14, %r14
xorq %r15, %r15
pxor %mm0, %mm0
pxor %mm1, %mm1
pxor %mm2, %mm2
pxor %mm3, %mm3
pxor %mm4, %mm4
pxor %mm5, %mm5
pxor %mm6, %mm6
pxor %mm7, %mm7
emms
vzeroall
# Call the fiber's entry function: entry(arg_on_initialize, arg_on_run_to)
call *%r11
# Fiber returned, not good
movl $1, %edi
call _sceFiberForceQuit
ret

View file

@ -1,183 +1,183 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
Url = 2,
Mail = 3,
Number = 4,
};
enum class OrbisImeHorizontalAlignment : u32 {
Left = 0,
Center = 1,
Right = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
Top = 0,
Center = 1,
Bottom = 2,
};
enum class OrbisImeEnterLabel : u32 {
Default = 0,
Send = 1,
Search = 2,
Go = 3,
};
enum class OrbisImeInputMethod : u32 {
Default = 0,
};
enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
PressClose = 4,
PressEnter = 5,
Abort = 6,
CandidateListStart = 7,
CandidateListEnd = 8,
CandidateWord = 9,
CandidateIndex = 10,
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
ChangeInputMethodState = 18,
KeyboardOpen = 256,
KeyboardKeycodeDoen = 257,
KeyboardKeycodeUp = 258,
KeyboardKeycodeRepeat = 259,
KeyboardConnection = 260,
KeyboardDisconnection = 261,
KeyboardAbort = 262,
};
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
enum class OrbisImeDeviceType : u32 {
None = 0,
Controller = 1,
ExtKeyboard = 2,
RemoteOsk = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
u32 width;
u32 height;
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
u32 index;
s32 length;
};
struct OrbisImeEditText {
char16_t* str;
u32 caret_index;
u32 area_num;
OrbisImeTextAreaProperty text_area[4];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
Still = 0,
Left = 1,
Right = 2,
Up = 3,
Down = 4,
Home = 5,
End = 6,
PageUp = 7,
PageDown = 8,
Top = 9,
Bottom = 10,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
OrbisImeCaretMovementDirection caret_move;
OrbisImeKeycode keycode;
OrbisImeKeyboardResourceIdArray resource_id_array;
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
u32 input_method_state;
s8 reserved[64];
};
struct OrbisImeEvent {
OrbisImeEventId id;
OrbisImeEventParam param;
};
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "core/libraries/rtc/rtc.h"
enum class OrbisImeType : u32 {
Default = 0,
BasicLatin = 1,
Url = 2,
Mail = 3,
Number = 4,
};
enum class OrbisImeHorizontalAlignment : u32 {
Left = 0,
Center = 1,
Right = 2,
};
enum class OrbisImeVerticalAlignment : u32 {
Top = 0,
Center = 1,
Bottom = 2,
};
enum class OrbisImeEnterLabel : u32 {
Default = 0,
Send = 1,
Search = 2,
Go = 3,
};
enum class OrbisImeInputMethod : u32 {
Default = 0,
};
enum class OrbisImeEventId : u32 {
Open = 0,
UpdateText = 1,
UpdateCaret = 2,
PressClose = 4,
PressEnter = 5,
Abort = 6,
CandidateListStart = 7,
CandidateListEnd = 8,
CandidateWord = 9,
CandidateIndex = 10,
CandidateDone = 11,
CandidateCancel = 12,
ChangeDevice = 14,
ChangeInputMethodState = 18,
KeyboardOpen = 256,
KeyboardKeycodeDoen = 257,
KeyboardKeycodeUp = 258,
KeyboardKeycodeRepeat = 259,
KeyboardConnection = 260,
KeyboardDisconnection = 261,
KeyboardAbort = 262,
};
enum class OrbisImeKeyboardType : u32 {
NONE = 0,
DANISH = 1,
GERMAN = 2,
GERMAN_SW = 3,
ENGLISH_US = 4,
ENGLISH_GB = 5,
SPANISH = 6,
SPANISH_LA = 7,
FINNISH = 8,
FRENCH = 9,
FRENCH_BR = 10,
FRENCH_CA = 11,
FRENCH_SW = 12,
ITALIAN = 13,
DUTCH = 14,
NORWEGIAN = 15,
POLISH = 16,
PORTUGUESE_BR = 17,
PORTUGUESE_PT = 18,
RUSSIAN = 19,
SWEDISH = 20,
TURKISH = 21,
JAPANESE_ROMAN = 22,
JAPANESE_KANA = 23,
KOREAN = 24,
SM_CHINESE = 25,
TR_CHINESE_ZY = 26,
TR_CHINESE_PY_HK = 27,
TR_CHINESE_PY_TW = 28,
TR_CHINESE_CG = 29,
ARABIC_AR = 30,
THAI = 31,
CZECH = 32,
GREEK = 33,
INDONESIAN = 34,
VIETNAMESE = 35,
ROMANIAN = 36,
HUNGARIAN = 37,
};
enum class OrbisImeDeviceType : u32 {
None = 0,
Controller = 1,
ExtKeyboard = 2,
RemoteOsk = 3,
};
struct OrbisImeRect {
f32 x;
f32 y;
u32 width;
u32 height;
};
struct OrbisImeTextAreaProperty {
u32 mode; // OrbisImeTextAreaMode
u32 index;
s32 length;
};
struct OrbisImeEditText {
char16_t* str;
u32 caret_index;
u32 area_num;
OrbisImeTextAreaProperty text_area[4];
};
struct OrbisImeKeycode {
u16 keycode;
char16_t character;
u32 status;
OrbisImeKeyboardType type;
s32 user_id;
u32 resource_id;
Libraries::Rtc::OrbisRtcTick timestamp;
};
struct OrbisImeKeyboardResourceIdArray {
s32 userId;
u32 resourceId[5];
};
enum class OrbisImeCaretMovementDirection : u32 {
Still = 0,
Left = 1,
Right = 2,
Up = 3,
Down = 4,
Home = 5,
End = 6,
PageUp = 7,
PageDown = 8,
Top = 9,
Bottom = 10,
};
union OrbisImeEventParam {
OrbisImeRect rect;
OrbisImeEditText text;
OrbisImeCaretMovementDirection caret_move;
OrbisImeKeycode keycode;
OrbisImeKeyboardResourceIdArray resource_id_array;
char16_t* candidate_word;
s32 candidate_index;
OrbisImeDeviceType device_type;
u32 input_method_state;
s8 reserved[64];
};
struct OrbisImeEvent {
OrbisImeEventId id;
OrbisImeEventParam param;
};
using OrbisImeTextFilter = PS4_SYSV_ABI int (*)(char16_t* outText, u32* outTextLength,
const char16_t* srcText, u32 srcTextLength);
using OrbisImeEventHandler = PS4_SYSV_ABI void (*)(void* arg, const OrbisImeEvent* e);

View file

@ -1,253 +1,253 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ime_ui.h"
#include "imgui/imgui_std.h"
namespace Libraries::Ime {
using namespace ImGui;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImeState::ImeState(const OrbisImeParam* param) {
if (!param) {
return;
}
work_buffer = param->work;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
ORBIS_IME_MAX_TEXT_LENGTH * 4)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
}
}
ImeState::ImeState(ImeState&& other) noexcept
: work_buffer(other.work_buffer), text_buffer(other.text_buffer),
current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
event_queue = std::move(other.event_queue);
other.text_buffer = nullptr;
}
return *this;
}
void ImeState::SendEvent(OrbisImeEvent* event) {
std::unique_lock lock{queue_mutex};
event_queue.push(*event);
}
void ImeState::SendEnterEvent() {
OrbisImeEvent enterEvent{};
enterEvent.id = OrbisImeEventId::PressEnter;
SendEvent(&enterEvent);
}
void ImeState::SendCloseEvent() {
OrbisImeEvent closeEvent{};
closeEvent.id = OrbisImeEventId::PressClose;
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
SendEvent(&closeEvent);
}
void ImeState::SetText(const char16_t* text, u32 length) {}
void ImeState::SetCaret(u32 position) {}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
return true;
}
bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
return true;
}
ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) {
if (param) {
AddLayer(this);
}
}
ImeUi::~ImeUi() {
std::scoped_lock lock(draw_mutex);
Free();
}
ImeUi& ImeUi::operator=(ImeUi&& other) {
std::scoped_lock lock(draw_mutex, other.draw_mutex);
Free();
state = other.state;
ime_param = other.ime_param;
first_render = other.first_render;
other.state = nullptr;
other.ime_param = nullptr;
AddLayer(this);
return *this;
}
void ImeUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) {
return;
}
const auto& ctx = *GetCurrentContext();
const auto& io = ctx.IO;
// TODO: Figure out how to properly translate the positions -
// for example, if a game wants to center the IME panel,
// we have to translate the panel position in a way that it
// still becomes centered, as the game normally calculates
// the position assuming a it's running on a 1920x1080 screen,
// whereas we are running on a 1280x720 window size (by default).
//
// e.g. Panel position calculation from a game:
// param.posx = (1920 / 2) - (panelWidth / 2);
// param.posy = (1080 / 2) - (panelHeight / 2);
const auto size = GetIO().DisplaySize;
f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x);
f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y);
ImVec2 window_pos = {pos_x, pos_y};
ImVec2 window_size = {500.0f, 100.0f};
// SetNextWindowPos(window_pos);
SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {
SetNextWindowFocus();
}
if (Begin("IME##Ime", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground();
DrawInputText();
SetCursorPosY(GetCursorPosY() + 10.0f);
const char* button_text;
button_text = "Done##ImeDone";
float button_spacing = 10.0f;
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
SetCursorPosX(button_start_pos);
if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) {
state->SendEnterEvent();
}
SameLine(0.0f, button_spacing);
if (Button("Close##ImeClose", BUTTON_SIZE)) {
state->SendCloseEvent();
}
}
End();
first_render = false;
}
void ImeUi::DrawInputText() {
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
SetCursorPosX(20.0f);
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
}
}
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
} else if (data->CursorPos != lastCaretPos) {
OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still;
if (data->CursorPos < lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Left;
} else if (data->CursorPos > lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Right;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateCaret;
event.param.caret_move = caretDirection;
lastCaretPos = data->CursorPos;
ui->state->SendEvent(&event);
}
return 0;
}
void ImeUi::Free() {
RemoveLayer(this);
}
}; // namespace Libraries::Ime
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "ime_ui.h"
#include "imgui/imgui_std.h"
namespace Libraries::Ime {
using namespace ImGui;
static constexpr ImVec2 BUTTON_SIZE{100.0f, 30.0f};
ImeState::ImeState(const OrbisImeParam* param) {
if (!param) {
return;
}
work_buffer = param->work;
text_buffer = param->inputTextBuffer;
std::size_t text_len = std::char_traits<char16_t>::length(text_buffer);
if (!ConvertOrbisToUTF8(text_buffer, text_len, current_text.begin(),
ORBIS_IME_MAX_TEXT_LENGTH * 4)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert text to utf8 encoding");
}
}
ImeState::ImeState(ImeState&& other) noexcept
: work_buffer(other.work_buffer), text_buffer(other.text_buffer),
current_text(std::move(other.current_text)), event_queue(std::move(other.event_queue)) {
other.text_buffer = nullptr;
}
ImeState& ImeState::operator=(ImeState&& other) noexcept {
if (this != &other) {
work_buffer = other.work_buffer;
text_buffer = other.text_buffer;
current_text = std::move(other.current_text);
event_queue = std::move(other.event_queue);
other.text_buffer = nullptr;
}
return *this;
}
void ImeState::SendEvent(OrbisImeEvent* event) {
std::unique_lock lock{queue_mutex};
event_queue.push(*event);
}
void ImeState::SendEnterEvent() {
OrbisImeEvent enterEvent{};
enterEvent.id = OrbisImeEventId::PressEnter;
SendEvent(&enterEvent);
}
void ImeState::SendCloseEvent() {
OrbisImeEvent closeEvent{};
closeEvent.id = OrbisImeEventId::PressClose;
closeEvent.param.text.str = reinterpret_cast<char16_t*>(work_buffer);
SendEvent(&closeEvent);
}
void ImeState::SetText(const char16_t* text, u32 length) {}
void ImeState::SetCaret(u32 position) {}
bool ImeState::ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len,
char* utf8_text, std::size_t utf8_text_len) {
std::fill(utf8_text, utf8_text + utf8_text_len, '\0');
const ImWchar* orbis_text_ptr = reinterpret_cast<const ImWchar*>(orbis_text);
ImTextStrToUtf8(utf8_text, utf8_text_len, orbis_text_ptr, orbis_text_ptr + orbis_text_len);
return true;
}
bool ImeState::ConvertUTF8ToOrbis(const char* utf8_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len) {
std::fill(orbis_text, orbis_text + orbis_text_len, u'\0');
ImTextStrFromUtf8(reinterpret_cast<ImWchar*>(orbis_text), orbis_text_len, utf8_text, nullptr);
return true;
}
ImeUi::ImeUi(ImeState* state, const OrbisImeParam* param) : state(state), ime_param(param) {
if (param) {
AddLayer(this);
}
}
ImeUi::~ImeUi() {
std::scoped_lock lock(draw_mutex);
Free();
}
ImeUi& ImeUi::operator=(ImeUi&& other) {
std::scoped_lock lock(draw_mutex, other.draw_mutex);
Free();
state = other.state;
ime_param = other.ime_param;
first_render = other.first_render;
other.state = nullptr;
other.ime_param = nullptr;
AddLayer(this);
return *this;
}
void ImeUi::Draw() {
std::unique_lock lock{draw_mutex};
if (!state) {
return;
}
const auto& ctx = *GetCurrentContext();
const auto& io = ctx.IO;
// TODO: Figure out how to properly translate the positions -
// for example, if a game wants to center the IME panel,
// we have to translate the panel position in a way that it
// still becomes centered, as the game normally calculates
// the position assuming a it's running on a 1920x1080 screen,
// whereas we are running on a 1280x720 window size (by default).
//
// e.g. Panel position calculation from a game:
// param.posx = (1920 / 2) - (panelWidth / 2);
// param.posy = (1080 / 2) - (panelHeight / 2);
const auto size = GetIO().DisplaySize;
f32 pos_x = (ime_param->posx / 1920.0f * (float)size.x);
f32 pos_y = (ime_param->posy / 1080.0f * (float)size.y);
ImVec2 window_pos = {pos_x, pos_y};
ImVec2 window_size = {500.0f, 100.0f};
// SetNextWindowPos(window_pos);
SetNextWindowPos(ImVec2(io.DisplaySize.x * 0.5f, io.DisplaySize.y * 0.5f),
ImGuiCond_FirstUseEver, ImVec2(0.5f, 0.5f));
SetNextWindowSize(window_size);
SetNextWindowCollapsed(false);
if (first_render || !io.NavActive) {
SetNextWindowFocus();
}
if (Begin("IME##Ime", nullptr,
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize |
ImGuiWindowFlags_NoSavedSettings)) {
DrawPrettyBackground();
DrawInputText();
SetCursorPosY(GetCursorPosY() + 10.0f);
const char* button_text;
button_text = "Done##ImeDone";
float button_spacing = 10.0f;
float total_button_width = BUTTON_SIZE.x * 2 + button_spacing;
float button_start_pos = (window_size.x - total_button_width) / 2.0f;
SetCursorPosX(button_start_pos);
if (Button(button_text, BUTTON_SIZE) || (IsKeyPressed(ImGuiKey_Enter))) {
state->SendEnterEvent();
}
SameLine(0.0f, button_spacing);
if (Button("Close##ImeClose", BUTTON_SIZE)) {
state->SendCloseEvent();
}
}
End();
first_render = false;
}
void ImeUi::DrawInputText() {
ImVec2 input_size = {GetWindowWidth() - 40.0f, 0.0f};
SetCursorPosX(20.0f);
if (first_render) {
SetKeyboardFocusHere();
}
if (InputTextEx("##ImeInput", nullptr, state->current_text.begin(), ime_param->maxTextLength,
input_size, ImGuiInputTextFlags_CallbackAlways, InputTextCallback, this)) {
}
}
int ImeUi::InputTextCallback(ImGuiInputTextCallbackData* data) {
ImeUi* ui = static_cast<ImeUi*>(data->UserData);
ASSERT(ui);
static std::string lastText;
std::string currentText(data->Buf, data->BufTextLen);
if (currentText != lastText) {
OrbisImeEditText eventParam{};
eventParam.str = reinterpret_cast<char16_t*>(ui->ime_param->work);
eventParam.caret_index = data->CursorPos;
eventParam.area_num = 1;
eventParam.text_area[0].mode = 1; // Edit mode
eventParam.text_area[0].index = data->CursorPos;
eventParam.text_area[0].length = data->BufTextLen;
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen, eventParam.str,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
if (!ui->state->ConvertUTF8ToOrbis(data->Buf, data->BufTextLen,
ui->ime_param->inputTextBuffer,
ui->ime_param->maxTextLength)) {
LOG_ERROR(Lib_ImeDialog, "Failed to convert Orbis char to UTF-8");
return 0;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateText;
event.param.text = eventParam;
lastText = currentText;
ui->state->SendEvent(&event);
}
static int lastCaretPos = -1;
if (lastCaretPos == -1) {
lastCaretPos = data->CursorPos;
} else if (data->CursorPos != lastCaretPos) {
OrbisImeCaretMovementDirection caretDirection = OrbisImeCaretMovementDirection::Still;
if (data->CursorPos < lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Left;
} else if (data->CursorPos > lastCaretPos) {
caretDirection = OrbisImeCaretMovementDirection::Right;
}
OrbisImeEvent event{};
event.id = OrbisImeEventId::UpdateCaret;
event.param.caret_move = caretDirection;
lastCaretPos = data->CursorPos;
ui->state->SendEvent(&event);
}
return 0;
}
void ImeUi::Free() {
RemoveLayer(this);
}
}; // namespace Libraries::Ime

View file

@ -1,76 +1,76 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <imgui.h>
#include <queue>
#include "imgui/imgui_layer.h"
#include "common/cstring.h"
#include "common/types.h"
#include "ime.h"
namespace Libraries::Ime {
class ImeHandler;
class ImeUi;
class ImeState {
friend class ImeHandler;
friend class ImeUi;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
Common::CString<ORBIS_IME_MAX_TEXT_LENGTH * 4> current_text;
std::queue<OrbisImeEvent> event_queue;
std::mutex queue_mutex;
public:
ImeState(const OrbisImeParam* param = nullptr);
ImeState(ImeState&& other) noexcept;
ImeState& operator=(ImeState&& other) noexcept;
void SendEvent(OrbisImeEvent* event);
void SendEnterEvent();
void SendCloseEvent();
void SetText(const char16_t* text, u32 length);
void SetCaret(u32 position);
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len);
};
class ImeUi : public ImGui::Layer {
ImeState* state{};
const OrbisImeParam* ime_param{};
bool first_render = true;
std::mutex draw_mutex;
public:
explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr);
~ImeUi() override;
ImeUi(const ImeUi& other) = delete;
ImeUi& operator=(ImeUi&& other);
void Draw() override;
private:
void Free();
void DrawInputText();
static int InputTextCallback(ImGuiInputTextCallbackData* data);
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <mutex>
#include <imgui.h>
#include <queue>
#include "imgui/imgui_layer.h"
#include "common/cstring.h"
#include "common/types.h"
#include "ime.h"
namespace Libraries::Ime {
class ImeHandler;
class ImeUi;
class ImeState {
friend class ImeHandler;
friend class ImeUi;
void* work_buffer{};
char16_t* text_buffer{};
// A character can hold up to 4 bytes in UTF-8
Common::CString<ORBIS_IME_MAX_TEXT_LENGTH * 4> current_text;
std::queue<OrbisImeEvent> event_queue;
std::mutex queue_mutex;
public:
ImeState(const OrbisImeParam* param = nullptr);
ImeState(ImeState&& other) noexcept;
ImeState& operator=(ImeState&& other) noexcept;
void SendEvent(OrbisImeEvent* event);
void SendEnterEvent();
void SendCloseEvent();
void SetText(const char16_t* text, u32 length);
void SetCaret(u32 position);
private:
bool ConvertOrbisToUTF8(const char16_t* orbis_text, std::size_t orbis_text_len, char* utf8_text,
std::size_t native_text_len);
bool ConvertUTF8ToOrbis(const char* native_text, std::size_t utf8_text_len,
char16_t* orbis_text, std::size_t orbis_text_len);
};
class ImeUi : public ImGui::Layer {
ImeState* state{};
const OrbisImeParam* ime_param{};
bool first_render = true;
std::mutex draw_mutex;
public:
explicit ImeUi(ImeState* state = nullptr, const OrbisImeParam* param = nullptr);
~ImeUi() override;
ImeUi(const ImeUi& other) = delete;
ImeUi& operator=(ImeUi&& other);
void Draw() override;
private:
void Free();
void DrawInputText();
static int InputTextCallback(ImGuiInputTextCallbackData* data);
};
}; // namespace Libraries::Ime

View file

@ -1,52 +1,52 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "mutex.h"
#include "common/assert.h"
namespace Libraries::Kernel {
TimedMutex::TimedMutex() {
#ifdef _WIN64
mtx = CreateMutex(nullptr, false, nullptr);
ASSERT(mtx);
#endif
}
TimedMutex::~TimedMutex() {
#ifdef _WIN64
CloseHandle(mtx);
#endif
}
void TimedMutex::lock() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(mtx, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#else
mtx.lock();
#endif
}
bool TimedMutex::try_lock() {
#ifdef _WIN64
return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0;
#else
return mtx.try_lock();
#endif
}
void TimedMutex::unlock() {
#ifdef _WIN64
ReleaseMutex(mtx);
#else
mtx.unlock();
#endif
}
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "mutex.h"
#include "common/assert.h"
namespace Libraries::Kernel {
TimedMutex::TimedMutex() {
#ifdef _WIN64
mtx = CreateMutex(nullptr, false, nullptr);
ASSERT(mtx);
#endif
}
TimedMutex::~TimedMutex() {
#ifdef _WIN64
CloseHandle(mtx);
#endif
}
void TimedMutex::lock() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(mtx, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#else
mtx.lock();
#endif
}
bool TimedMutex::try_lock() {
#ifdef _WIN64
return WaitForSingleObjectEx(mtx, 0, true) == WAIT_OBJECT_0;
#else
return mtx.try_lock();
#endif
}
void TimedMutex::unlock() {
#ifdef _WIN64
ReleaseMutex(mtx);
#else
mtx.unlock();
#endif
}
} // namespace Libraries::Kernel

View file

@ -1,80 +1,80 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <mutex>
#endif
namespace Libraries::Kernel {
class TimedMutex {
public:
TimedMutex();
~TimedMutex();
void lock();
bool try_lock();
void unlock();
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
constexpr auto zero = std::chrono::duration<Rep, Period>::zero();
const auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point abs_time = now;
if (rel_time > zero) {
constexpr auto max = (std::chrono::steady_clock::time_point::max)();
if (abs_time < max - rel_time) {
abs_time += rel_time;
} else {
abs_time = max;
}
}
return try_lock_until(abs_time);
#else
return mtx.try_lock_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
for (;;) {
const auto now = Clock::now();
if (abs_time <= now) {
return false;
}
const auto rel_ms = std::chrono::ceil<std::chrono::milliseconds>(abs_time - now);
u64 res = WaitForSingleObjectEx(mtx, static_cast<u64>(rel_ms.count()), true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_TIMEOUT) {
return false;
}
}
#else
return mtx.try_lock_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE mtx;
#else
std::timed_mutex mtx;
#endif
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <chrono>
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#else
#include <mutex>
#endif
namespace Libraries::Kernel {
class TimedMutex {
public:
TimedMutex();
~TimedMutex();
void lock();
bool try_lock();
void unlock();
template <class Rep, class Period>
bool try_lock_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
constexpr auto zero = std::chrono::duration<Rep, Period>::zero();
const auto now = std::chrono::steady_clock::now();
std::chrono::steady_clock::time_point abs_time = now;
if (rel_time > zero) {
constexpr auto max = (std::chrono::steady_clock::time_point::max)();
if (abs_time < max - rel_time) {
abs_time += rel_time;
} else {
abs_time = max;
}
}
return try_lock_until(abs_time);
#else
return mtx.try_lock_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_lock_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
for (;;) {
const auto now = Clock::now();
if (abs_time <= now) {
return false;
}
const auto rel_ms = std::chrono::ceil<std::chrono::milliseconds>(abs_time - now);
u64 res = WaitForSingleObjectEx(mtx, static_cast<u64>(rel_ms.count()), true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_TIMEOUT) {
return false;
}
}
#else
return mtx.try_lock_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE mtx;
#else
std::timed_mutex mtx;
#endif
};
} // namespace Libraries::Kernel

View file

@ -1,167 +1,167 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include "common/assert.h"
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#include <semaphore>
#endif
namespace Libraries::Kernel {
template <s64 max>
class Semaphore {
public:
Semaphore(s32 initialCount)
#if !defined(_WIN64) && !defined(__APPLE__)
: sem{initialCount}
#endif
{
#ifdef _WIN64
sem = CreateSemaphore(nullptr, initialCount, max, nullptr);
ASSERT(sem);
#elif defined(__APPLE__)
sem = dispatch_semaphore_create(initialCount);
ASSERT(sem);
#endif
}
~Semaphore() {
#ifdef _WIN64
CloseHandle(sem);
#elif defined(__APPLE__)
dispatch_release(sem);
#endif
}
void release() {
#ifdef _WIN64
ReleaseSemaphore(sem, 1, nullptr);
#elif defined(__APPLE__)
dispatch_semaphore_signal(sem);
#else
sem.release();
#endif
}
void acquire() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(sem, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#elif defined(__APPLE__)
for (;;) {
const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (res == 0) {
return;
}
}
#else
sem.acquire();
#endif
}
bool try_acquire() {
#ifdef _WIN64
return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
return sem.try_acquire();
#endif
}
template <class Rep, class Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto rel_time_ms = std::chrono::ceil<std::chrono::milliseconds>(rel_time);
while (rel_time_ms.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time_ms.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time;
rel_time_ms -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
const auto rel_time_ns = std::chrono::ceil<std::chrono::nanoseconds>(rel_time).count();
const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
const auto start_time = Clock::now();
if (start_time >= abs_time) {
return false;
}
auto rel_time = std::chrono::ceil<std::chrono::milliseconds>(abs_time - start_time);
while (rel_time.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = Clock::now() - start_time;
rel_time -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
auto abs_s = std::chrono::time_point_cast<std::chrono::seconds>(abs_time);
auto abs_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_time) -
std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_s);
const timespec abs_timespec = {
.tv_sec = abs_s.time_since_epoch().count(),
.tv_nsec = abs_ns.count(),
};
const auto timeout = dispatch_walltime(&abs_timespec, 0);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE sem;
#elif defined(__APPLE__)
dispatch_semaphore_t sem;
#else
std::counting_semaphore<max> sem;
#endif
};
using BinarySemaphore = Semaphore<1>;
using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>;
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <atomic>
#include <chrono>
#include "common/assert.h"
#include "common/types.h"
#ifdef _WIN64
#include <windows.h>
#elif defined(__APPLE__)
#include <dispatch/dispatch.h>
#else
#include <semaphore>
#endif
namespace Libraries::Kernel {
template <s64 max>
class Semaphore {
public:
Semaphore(s32 initialCount)
#if !defined(_WIN64) && !defined(__APPLE__)
: sem{initialCount}
#endif
{
#ifdef _WIN64
sem = CreateSemaphore(nullptr, initialCount, max, nullptr);
ASSERT(sem);
#elif defined(__APPLE__)
sem = dispatch_semaphore_create(initialCount);
ASSERT(sem);
#endif
}
~Semaphore() {
#ifdef _WIN64
CloseHandle(sem);
#elif defined(__APPLE__)
dispatch_release(sem);
#endif
}
void release() {
#ifdef _WIN64
ReleaseSemaphore(sem, 1, nullptr);
#elif defined(__APPLE__)
dispatch_semaphore_signal(sem);
#else
sem.release();
#endif
}
void acquire() {
#ifdef _WIN64
for (;;) {
u64 res = WaitForSingleObjectEx(sem, INFINITE, true);
if (res == WAIT_OBJECT_0) {
return;
}
}
#elif defined(__APPLE__)
for (;;) {
const auto res = dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);
if (res == 0) {
return;
}
}
#else
sem.acquire();
#endif
}
bool try_acquire() {
#ifdef _WIN64
return WaitForSingleObjectEx(sem, 0, true) == WAIT_OBJECT_0;
#elif defined(__APPLE__)
return dispatch_semaphore_wait(sem, DISPATCH_TIME_NOW) == 0;
#else
return sem.try_acquire();
#endif
}
template <class Rep, class Period>
bool try_acquire_for(const std::chrono::duration<Rep, Period>& rel_time) {
#ifdef _WIN64
const auto start_time = std::chrono::high_resolution_clock::now();
auto rel_time_ms = std::chrono::ceil<std::chrono::milliseconds>(rel_time);
while (rel_time_ms.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time_ms.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = std::chrono::high_resolution_clock::now() - start_time;
rel_time_ms -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
const auto rel_time_ns = std::chrono::ceil<std::chrono::nanoseconds>(rel_time).count();
const auto timeout = dispatch_time(DISPATCH_TIME_NOW, rel_time_ns);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_for(rel_time);
#endif
}
template <class Clock, class Duration>
bool try_acquire_until(const std::chrono::time_point<Clock, Duration>& abs_time) {
#ifdef _WIN64
const auto start_time = Clock::now();
if (start_time >= abs_time) {
return false;
}
auto rel_time = std::chrono::ceil<std::chrono::milliseconds>(abs_time - start_time);
while (rel_time.count() > 0) {
u64 timeout_ms = static_cast<u64>(rel_time.count());
u64 res = WaitForSingleObjectEx(sem, timeout_ms, true);
if (res == WAIT_OBJECT_0) {
return true;
} else if (res == WAIT_IO_COMPLETION) {
auto elapsed_time = Clock::now() - start_time;
rel_time -= std::chrono::duration_cast<std::chrono::milliseconds>(elapsed_time);
} else {
return false;
}
}
return false;
#elif defined(__APPLE__)
auto abs_s = std::chrono::time_point_cast<std::chrono::seconds>(abs_time);
auto abs_ns = std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_time) -
std::chrono::time_point_cast<std::chrono::nanoseconds>(abs_s);
const timespec abs_timespec = {
.tv_sec = abs_s.time_since_epoch().count(),
.tv_nsec = abs_ns.count(),
};
const auto timeout = dispatch_walltime(&abs_timespec, 0);
return dispatch_semaphore_wait(sem, timeout) == 0;
#else
return sem.try_acquire_until(abs_time);
#endif
}
private:
#ifdef _WIN64
HANDLE sem;
#elif defined(__APPLE__)
dispatch_semaphore_t sem;
#else
std::counting_semaphore<max> sem;
#endif
};
using BinarySemaphore = Semaphore<1>;
using CountingSemaphore = Semaphore<0x7FFFFFFF /*ORBIS_KERNEL_SEM_VALUE_MAX*/>;
} // namespace Libraries::Kernel

View file

@ -1,199 +1,199 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/libs.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videodec/videodec2_impl.h"
#include "core/libraries/videodec/videodec_error.h"
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
computeMemInfo->cpuGpuMemory = nullptr;
computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
decoderMemInfo->cpuMemory = nullptr;
decoderMemInfo->gpuMemory = nullptr;
decoderMemInfo->cpuGpuMemory = nullptr;
decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
decoderMemInfo->cpuMemorySize = kMinimumMemorySize;
decoderMemInfo->gpuMemorySize = kMinimumMemorySize;
decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize;
decoderMemInfo->frameBufferAlignment = 0x100;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
delete decoder;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Decode(*inputData, *frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Flush(*frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
return decoder->Reset();
}
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfoOut, void* p2ndPictureInfoOut) {
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
UNREACHABLE();
}
return ORBIS_OK;
}
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryComputeMemoryInfo);
LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2AllocateComputeQueue);
LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2ReleaseComputeQueue);
LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryDecoderMemoryInfo);
LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2CreateDecoder);
LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2DeleteDecoder);
LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode);
LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush);
LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset);
LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2GetPictureInfo);
}
} // namespace Libraries::Vdec2
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/libs.h"
#include "core/libraries/videodec/videodec2.h"
#include "core/libraries/videodec/videodec2_impl.h"
#include "core/libraries/videodec/videodec_error.h"
namespace Libraries::Vdec2 {
static constexpr u64 kMinimumMemorySize = 32_MB; ///> Fake minimum memory size for querying
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!computeMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (computeMemInfo->thisSize != sizeof(OrbisVideodec2ComputeMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
computeMemInfo->cpuGpuMemory = nullptr;
computeMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue) {
LOG_INFO(Lib_Vdec2, "called");
return ORBIS_OK;
}
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
decoderMemInfo->cpuMemory = nullptr;
decoderMemInfo->gpuMemory = nullptr;
decoderMemInfo->cpuGpuMemory = nullptr;
decoderMemInfo->cpuGpuMemorySize = kMinimumMemorySize;
decoderMemInfo->cpuMemorySize = kMinimumMemorySize;
decoderMemInfo->gpuMemorySize = kMinimumMemorySize;
decoderMemInfo->maxFrameBufferSize = kMinimumMemorySize;
decoderMemInfo->frameBufferAlignment = 0x100;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoderCfgInfo || !decoderMemInfo || !decoder) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (decoderCfgInfo->thisSize != sizeof(OrbisVideodec2DecoderConfigInfo) ||
decoderMemInfo->thisSize != sizeof(OrbisVideodec2DecoderMemoryInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*decoder = new VdecDecoder(*decoderCfgInfo, *decoderMemInfo);
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
delete decoder;
return ORBIS_OK;
}
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_TRACE(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!inputData || !frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (inputData->thisSize != sizeof(OrbisVideodec2InputData) ||
frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Decode(*inputData, *frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
if (!frameBuffer || !outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (frameBuffer->thisSize != sizeof(OrbisVideodec2FrameBuffer) ||
outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
return decoder->Flush(*frameBuffer, *outputInfo);
}
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder) {
LOG_INFO(Lib_Vdec2, "called");
if (!decoder) {
return ORBIS_VIDEODEC2_ERROR_DECODER_INSTANCE;
}
return decoder->Reset();
}
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfoOut, void* p2ndPictureInfoOut) {
LOG_TRACE(Lib_Vdec2, "called");
if (!outputInfo) {
return ORBIS_VIDEODEC2_ERROR_ARGUMENT_POINTER;
}
if (outputInfo->thisSize != sizeof(OrbisVideodec2OutputInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
if (outputInfo->pictureCount == 0 || gPictureInfos.empty()) {
return ORBIS_OK;
}
if (p1stPictureInfoOut) {
OrbisVideodec2AvcPictureInfo* picInfo =
static_cast<OrbisVideodec2AvcPictureInfo*>(p1stPictureInfoOut);
if (picInfo->thisSize != sizeof(OrbisVideodec2AvcPictureInfo)) {
return ORBIS_VIDEODEC2_ERROR_STRUCT_SIZE;
}
*picInfo = gPictureInfos.back();
}
if (outputInfo->pictureCount > 1) {
UNREACHABLE();
}
return ORBIS_OK;
}
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym) {
LIB_FUNCTION("RnDibcGCPKw", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryComputeMemoryInfo);
LIB_FUNCTION("eD+X2SmxUt4", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2AllocateComputeQueue);
LIB_FUNCTION("UvtA3FAiF4Y", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2ReleaseComputeQueue);
LIB_FUNCTION("qqMCwlULR+E", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2QueryDecoderMemoryInfo);
LIB_FUNCTION("CNNRoRYd8XI", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2CreateDecoder);
LIB_FUNCTION("jwImxXRGSKA", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2DeleteDecoder);
LIB_FUNCTION("852F5+q6+iM", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Decode);
LIB_FUNCTION("l1hXwscLuCY", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Flush);
LIB_FUNCTION("wJXikG6QFN8", "libSceVideodec2", 1, "libSceVideodec2", 1, 1, sceVideodec2Reset);
LIB_FUNCTION("NtXRa3dRzU0", "libSceVideodec2", 1, "libSceVideodec2", 1, 1,
sceVideodec2GetPictureInfo);
}
} // namespace Libraries::Vdec2

View file

@ -1,139 +1,139 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "videodec2_avc.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Vdec2 {
class VdecDecoder;
using OrbisVideodec2Decoder = VdecDecoder*;
using OrbisVideodec2ComputeQueue = void*;
struct OrbisVideodec2DecoderConfigInfo {
u64 thisSize;
u32 resourceType;
u32 codecType;
u32 profile;
u32 maxLevel;
s32 maxFrameWidth;
s32 maxFrameHeight;
s32 maxDpbFrameCount;
u32 decodePipelineDepth;
OrbisVideodec2ComputeQueue computeQueue;
u64 cpuAffinityMask;
s32 cpuThreadPriority;
bool optimizeProgressiveVideo;
bool checkMemoryType;
u8 reserved0;
u8 reserved1;
void* extraConfigInfo;
};
static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48);
struct OrbisVideodec2DecoderMemoryInfo {
u64 thisSize;
u64 cpuMemorySize;
void* cpuMemory;
u64 gpuMemorySize;
void* gpuMemory;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
u64 maxFrameBufferSize;
u32 frameBufferAlignment;
u32 reserved0;
};
static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48);
struct OrbisVideodec2InputData {
u64 thisSize;
void* auData;
u64 auSize;
u64 ptsData;
u64 dtsData;
u64 attachedData;
};
static_assert(sizeof(OrbisVideodec2InputData) == 0x30);
struct OrbisVideodec2OutputInfo {
u64 thisSize;
bool isValid;
bool isErrorFrame;
u8 pictureCount;
u32 codecType;
u32 frameWidth;
u32 framePitch;
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
};
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
void* frameBuffer;
u64 frameBufferSize;
bool isAccepted;
};
static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20);
struct OrbisVideodec2ComputeMemoryInfo {
u64 thisSize;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
};
static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18);
struct OrbisVideodec2ComputeConfigInfo {
u64 thisSize;
u16 computePipeId;
u16 computeQueueId;
bool checkMemoryType;
u8 reserved0;
u16 reserved1;
};
static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10);
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo);
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue);
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue);
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo);
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder);
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfo, void* p2ndPictureInfo);
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym);
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
#include "videodec2_avc.h"
namespace Core::Loader {
class SymbolsResolver;
}
namespace Libraries::Vdec2 {
class VdecDecoder;
using OrbisVideodec2Decoder = VdecDecoder*;
using OrbisVideodec2ComputeQueue = void*;
struct OrbisVideodec2DecoderConfigInfo {
u64 thisSize;
u32 resourceType;
u32 codecType;
u32 profile;
u32 maxLevel;
s32 maxFrameWidth;
s32 maxFrameHeight;
s32 maxDpbFrameCount;
u32 decodePipelineDepth;
OrbisVideodec2ComputeQueue computeQueue;
u64 cpuAffinityMask;
s32 cpuThreadPriority;
bool optimizeProgressiveVideo;
bool checkMemoryType;
u8 reserved0;
u8 reserved1;
void* extraConfigInfo;
};
static_assert(sizeof(OrbisVideodec2DecoderConfigInfo) == 0x48);
struct OrbisVideodec2DecoderMemoryInfo {
u64 thisSize;
u64 cpuMemorySize;
void* cpuMemory;
u64 gpuMemorySize;
void* gpuMemory;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
u64 maxFrameBufferSize;
u32 frameBufferAlignment;
u32 reserved0;
};
static_assert(sizeof(OrbisVideodec2DecoderMemoryInfo) == 0x48);
struct OrbisVideodec2InputData {
u64 thisSize;
void* auData;
u64 auSize;
u64 ptsData;
u64 dtsData;
u64 attachedData;
};
static_assert(sizeof(OrbisVideodec2InputData) == 0x30);
struct OrbisVideodec2OutputInfo {
u64 thisSize;
bool isValid;
bool isErrorFrame;
u8 pictureCount;
u32 codecType;
u32 frameWidth;
u32 framePitch;
u32 frameHeight;
void* frameBuffer;
u64 frameBufferSize;
};
static_assert(sizeof(OrbisVideodec2OutputInfo) == 0x30);
struct OrbisVideodec2FrameBuffer {
u64 thisSize;
void* frameBuffer;
u64 frameBufferSize;
bool isAccepted;
};
static_assert(sizeof(OrbisVideodec2FrameBuffer) == 0x20);
struct OrbisVideodec2ComputeMemoryInfo {
u64 thisSize;
u64 cpuGpuMemorySize;
void* cpuGpuMemory;
};
static_assert(sizeof(OrbisVideodec2ComputeMemoryInfo) == 0x18);
struct OrbisVideodec2ComputeConfigInfo {
u64 thisSize;
u16 computePipeId;
u16 computeQueueId;
bool checkMemoryType;
u8 reserved0;
u16 reserved1;
};
static_assert(sizeof(OrbisVideodec2ComputeConfigInfo) == 0x10);
s32 PS4_SYSV_ABI
sceVideodec2QueryComputeMemoryInfo(OrbisVideodec2ComputeMemoryInfo* computeMemInfo);
s32 PS4_SYSV_ABI
sceVideodec2AllocateComputeQueue(const OrbisVideodec2ComputeConfigInfo* computeCfgInfo,
const OrbisVideodec2ComputeMemoryInfo* computeMemInfo,
OrbisVideodec2ComputeQueue* computeQueue);
s32 PS4_SYSV_ABI sceVideodec2ReleaseComputeQueue(OrbisVideodec2ComputeQueue computeQueue);
s32 PS4_SYSV_ABI
sceVideodec2QueryDecoderMemoryInfo(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
OrbisVideodec2DecoderMemoryInfo* decoderMemInfo);
s32 PS4_SYSV_ABI sceVideodec2CreateDecoder(const OrbisVideodec2DecoderConfigInfo* decoderCfgInfo,
const OrbisVideodec2DecoderMemoryInfo* decoderMemInfo,
OrbisVideodec2Decoder* decoder);
s32 PS4_SYSV_ABI sceVideodec2DeleteDecoder(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2Decode(OrbisVideodec2Decoder decoder,
const OrbisVideodec2InputData* inputData,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Flush(OrbisVideodec2Decoder decoder,
OrbisVideodec2FrameBuffer* frameBuffer,
OrbisVideodec2OutputInfo* outputInfo);
s32 PS4_SYSV_ABI sceVideodec2Reset(OrbisVideodec2Decoder decoder);
s32 PS4_SYSV_ABI sceVideodec2GetPictureInfo(const OrbisVideodec2OutputInfo* outputInfo,
void* p1stPictureInfo, void* p2ndPictureInfo);
void RegisterlibSceVdec2(Core::Loader::SymbolsResolver* sym);
} // namespace Libraries::Vdec2

View file

@ -1,60 +1,60 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Vdec2 {
struct OrbisVideodec2AvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include "common/types.h"
namespace Libraries::Vdec2 {
struct OrbisVideodec2AvcPictureInfo {
u64 thisSize;
bool isValid;
u64 ptsData;
u64 dtsData;
u64 attachedData;
u8 idrPictureflag;
u8 profile_idc;
u8 level_idc;
u32 pic_width_in_mbs_minus1;
u32 pic_height_in_map_units_minus1;
u8 frame_mbs_only_flag;
u8 frame_cropping_flag;
u32 frameCropLeftOffset;
u32 frameCropRightOffset;
u32 frameCropTopOffset;
u32 frameCropBottomOffset;
u8 aspect_ratio_info_present_flag;
u8 aspect_ratio_idc;
u16 sar_width;
u16 sar_height;
u8 video_signal_type_present_flag;
u8 video_format;
u8 video_full_range_flag;
u8 colour_description_present_flag;
u8 colour_primaries;
u8 transfer_characteristics;
u8 matrix_coefficients;
u8 timing_info_present_flag;
u32 num_units_in_tick;
u32 time_scale;
u8 fixed_frame_rate_flag;
u8 bitstream_restriction_flag;
u8 max_dec_frame_buffering;
u8 pic_struct_present_flag;
u8 pic_struct;
u8 field_pic_flag;
u8 bottom_field_flag;
};
} // namespace Libraries::Vdec2

View file

@ -1,229 +1,229 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2_impl.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h"
#include "common/support/avdec.h"
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
}
VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo) {
ASSERT(configInfo.codecType == 1); /* AVC */
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
ASSERT(codec);
mCodecContext = avcodec_alloc_context3(codec);
ASSERT(mCodecContext);
mCodecContext->width = configInfo.maxFrameWidth;
mCodecContext->height = configInfo.maxFrameHeight;
avcodec_open2(mCodecContext, codec, nullptr);
}
VdecDecoder::~VdecDecoder() {
avcodec_free_context(&mCodecContext);
sws_freeContext(mSwsContext);
gPictureInfos.clear();
}
s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
if (inputData.auSize == 0) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE;
}
AVPacket* packet = av_packet_alloc();
if (!packet) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate packet");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
packet->data = (u8*)inputData.auData;
packet->size = inputData.auSize;
packet->pts = inputData.ptsData;
packet->dts = inputData.dtsData;
int ret = avcodec_send_packet(mCodecContext, packet);
if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret);
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
}
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// FIXME: Should we add picture info here too?
}
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Reset() {
avcodec_flush_buffers(mCodecContext);
gPictureInfos.clear();
return ORBIS_OK;
}
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
AVFrame* nv12_frame = av_frame_alloc();
nv12_frame->pts = frame.pts;
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
nv12_frame->format = AV_PIX_FMT_NV12;
nv12_frame->width = frame.width;
nv12_frame->height = frame.height;
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
nv12_frame->crop_top = frame.crop_top;
nv12_frame->crop_bottom = frame.crop_bottom;
nv12_frame->crop_left = frame.crop_left;
nv12_frame->crop_right = frame.crop_right;
av_frame_get_buffer(nv12_frame, 0);
if (mSwsContext == nullptr) {
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
}
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
nv12_frame->data, nv12_frame->linesize);
if (res < 0) {
LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res));
return nullptr;
}
return nv12_frame;
}
} // namespace Libraries::Vdec2
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include "videodec2_impl.h"
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/libraries/videodec/videodec_error.h"
#include "common/support/avdec.h"
namespace Libraries::Vdec2 {
std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
static inline void CopyNV12Data(u8* dst, const AVFrame& src) {
std::memcpy(dst, src.data[0], src.width * src.height);
std::memcpy(dst + (src.width * src.height), src.data[1], (src.width * src.height) / 2);
}
VdecDecoder::VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo) {
ASSERT(configInfo.codecType == 1); /* AVC */
const AVCodec* codec = avcodec_find_decoder(AV_CODEC_ID_H264);
ASSERT(codec);
mCodecContext = avcodec_alloc_context3(codec);
ASSERT(mCodecContext);
mCodecContext->width = configInfo.maxFrameWidth;
mCodecContext->height = configInfo.maxFrameHeight;
avcodec_open2(mCodecContext, codec, nullptr);
}
VdecDecoder::~VdecDecoder() {
avcodec_free_context(&mCodecContext);
sws_freeContext(mSwsContext);
gPictureInfos.clear();
}
s32 VdecDecoder::Decode(const OrbisVideodec2InputData& inputData,
OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
if (!inputData.auData) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_POINTER;
}
if (inputData.auSize == 0) {
return ORBIS_VIDEODEC2_ERROR_ACCESS_UNIT_SIZE;
}
AVPacket* packet = av_packet_alloc();
if (!packet) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate packet");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
packet->data = (u8*)inputData.auData;
packet->size = inputData.auSize;
packet->pts = inputData.ptsData;
packet->dts = inputData.dtsData;
int ret = avcodec_send_packet(mCodecContext, packet);
if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error sending packet to decoder: {}", ret);
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
AVFrame* frame = av_frame_alloc();
if (frame == nullptr) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
av_packet_free(&packet);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
if (outputInfo.isValid) {
OrbisVideodec2AvcPictureInfo pictureInfo = {};
pictureInfo.thisSize = sizeof(OrbisVideodec2AvcPictureInfo);
pictureInfo.isValid = true;
pictureInfo.ptsData = inputData.ptsData;
pictureInfo.dtsData = inputData.dtsData;
pictureInfo.attachedData = inputData.attachedData;
pictureInfo.frameCropLeftOffset = frame->crop_left;
pictureInfo.frameCropRightOffset = frame->crop_right;
pictureInfo.frameCropTopOffset = frame->crop_top;
pictureInfo.frameCropBottomOffset = frame->crop_bottom;
gPictureInfos.push_back(pictureInfo);
}
}
av_packet_free(&packet);
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Flush(OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo) {
frameBuffer.isAccepted = false;
outputInfo.thisSize = sizeof(OrbisVideodec2OutputInfo);
outputInfo.isValid = false;
outputInfo.isErrorFrame = true;
outputInfo.pictureCount = 0;
AVFrame* frame = av_frame_alloc();
if (!frame) {
LOG_ERROR(Lib_Vdec2, "Failed to allocate frame");
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
while (true) {
int ret = avcodec_receive_frame(mCodecContext, frame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
break;
} else if (ret < 0) {
LOG_ERROR(Lib_Vdec2, "Error receiving frame from decoder: {}", ret);
av_frame_free(&frame);
return ORBIS_VIDEODEC2_ERROR_API_FAIL;
}
if (frame->format != AV_PIX_FMT_NV12) {
AVFrame* nv12_frame = ConvertNV12Frame(*frame);
ASSERT(nv12_frame);
av_frame_free(&frame);
frame = nv12_frame;
}
CopyNV12Data((u8*)frameBuffer.frameBuffer, *frame);
frameBuffer.isAccepted = true;
outputInfo.codecType = 1; // FIXME: Hardcoded to AVC
outputInfo.frameWidth = frame->width;
outputInfo.frameHeight = frame->height;
outputInfo.framePitch = frame->linesize[0];
outputInfo.frameBufferSize = frameBuffer.frameBufferSize;
outputInfo.frameBuffer = frameBuffer.frameBuffer;
outputInfo.isValid = true;
outputInfo.isErrorFrame = false;
outputInfo.pictureCount = 1; // TODO: 2 pictures for interlaced video
// FIXME: Should we add picture info here too?
}
av_frame_free(&frame);
return ORBIS_OK;
}
s32 VdecDecoder::Reset() {
avcodec_flush_buffers(mCodecContext);
gPictureInfos.clear();
return ORBIS_OK;
}
AVFrame* VdecDecoder::ConvertNV12Frame(AVFrame& frame) {
AVFrame* nv12_frame = av_frame_alloc();
nv12_frame->pts = frame.pts;
nv12_frame->pkt_dts = frame.pkt_dts < 0 ? 0 : frame.pkt_dts;
nv12_frame->format = AV_PIX_FMT_NV12;
nv12_frame->width = frame.width;
nv12_frame->height = frame.height;
nv12_frame->sample_aspect_ratio = frame.sample_aspect_ratio;
nv12_frame->crop_top = frame.crop_top;
nv12_frame->crop_bottom = frame.crop_bottom;
nv12_frame->crop_left = frame.crop_left;
nv12_frame->crop_right = frame.crop_right;
av_frame_get_buffer(nv12_frame, 0);
if (mSwsContext == nullptr) {
mSwsContext = sws_getContext(frame.width, frame.height, AVPixelFormat(frame.format),
nv12_frame->width, nv12_frame->height, AV_PIX_FMT_NV12,
SWS_FAST_BILINEAR, nullptr, nullptr, nullptr);
}
const auto res = sws_scale(mSwsContext, frame.data, frame.linesize, 0, frame.height,
nv12_frame->data, nv12_frame->linesize);
if (res < 0) {
LOG_ERROR(Lib_Vdec2, "Could not convert to NV12: {}", av_err2str(res));
return nullptr;
}
return nv12_frame;
}
} // namespace Libraries::Vdec2

View file

@ -1,39 +1,39 @@
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec2.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
class VdecDecoder {
public:
VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo);
~VdecDecoder();
s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo);
s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo);
s32 Reset();
private:
AVFrame* ConvertNV12Frame(AVFrame& frame);
private:
AVCodecContext* mCodecContext = nullptr;
SwsContext* mSwsContext = nullptr;
};
// SPDX-FileCopyrightText: Copyright 2024 shadPS4 Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#pragma once
#include <vector>
#include "videodec2.h"
extern "C" {
#include <libavcodec/avcodec.h>
#include <libavutil/imgutils.h>
#include <libswscale/swscale.h>
}
namespace Libraries::Vdec2 {
extern std::vector<OrbisVideodec2AvcPictureInfo> gPictureInfos;
class VdecDecoder {
public:
VdecDecoder(const OrbisVideodec2DecoderConfigInfo& configInfo,
const OrbisVideodec2DecoderMemoryInfo& memoryInfo);
~VdecDecoder();
s32 Decode(const OrbisVideodec2InputData& inputData, OrbisVideodec2FrameBuffer& frameBuffer,
OrbisVideodec2OutputInfo& outputInfo);
s32 Flush(OrbisVideodec2FrameBuffer& frameBuffer, OrbisVideodec2OutputInfo& outputInfo);
s32 Reset();
private:
AVFrame* ConvertNV12Frame(AVFrame& frame);
private:
AVCodecContext* mCodecContext = nullptr;
SwsContext* mSwsContext = nullptr;
};
} // namespace Libraries::Vdec2