Merge branch 'main' into hello_world_OpenOrbis

This commit is contained in:
georgemoralis 2023-10-23 20:01:59 +03:00 committed by GitHub
commit c3ce005e62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 25877 additions and 12145 deletions

View File

@ -19,11 +19,51 @@ include_directories(third-party/zydis/include/Zydis)
include_directories(third-party/winpthread/include)
include_directories(third-party/vulkan/include)
include_directories(third-party/xxhash/include)
include_directories(third-party/result/include)
add_subdirectory("third-party")
#=================== EXAMPLE ===================
include_directories(src)
set(LIBC_SOURCES src/Emulator/HLE/Libraries/LibC/Libc.cpp
src/Emulator/HLE/Libraries/LibC/Libc.h
src/Emulator/HLE/Libraries/LibC/printf.h
src/Emulator/HLE/Libraries/LibC/va_ctx.h
src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp
src/Emulator/HLE/Libraries/LibC/libc_cxa.h
)
set(USERSERVICE_SOURCES src/Emulator/HLE/Libraries/LibUserService/user_service.cpp
src/Emulator/HLE/Libraries/LibUserService/user_service.h
)
set(PAD_SOURCES src/Emulator/HLE/Libraries/LibPad/pad.cpp
src/Emulator/HLE/Libraries/LibPad/pad.h
)
set(SYSTEMSERVICE_SOURCES src/Emulator/HLE/Libraries/LibSystemService/system_service.cpp
src/Emulator/HLE/Libraries/LibSystemService/system_service.h
)
set(FILESYSTEM_SOURCES src/Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.cpp
src/Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.h
src/Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.cpp
src/Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.h
)
set(HOST_SOURCES src/Emulator/Host/controller.cpp
src/Emulator/Host/controller.h
)
set(UTIL_SOURCES src/Emulator/Util/singleton.h
)
add_executable(shadps4
${LIBC_SOURCES}
${USERSERVICE_SOURCES}
${PAD_SOURCES}
${SYSTEMSERVICE_SOURCES}
${FILESYSTEM_SOURCES}
${HOST_SOURCES}
${UTIL_SOURCES}
src/main.cpp
src/types.h
src/Core/FsFile.cpp
@ -40,8 +80,9 @@ add_executable(shadps4
src/Core/virtual_memory.h
src/Core/PS4/Linker.cpp
src/Core/PS4/Linker.h
src/Lib/Threads.cpp
src/Lib/Threads.h
src/Core/PS4/Stubs.cpp
src/Core/PS4/Stubs.h
src/Core/PS4/Util/aerolib.cpp
src/Core/PS4/HLE/Kernel/Objects/physical_memory.h
src/Core/PS4/HLE/Kernel/Objects/physical_memory.cpp
src/Util/string_util.cpp
@ -55,7 +96,7 @@ add_executable(shadps4
src/Core/PS4/HLE/Kernel/cpu_management.cpp
src/Core/PS4/HLE/Kernel/cpu_management.h
"src/Util/Singleton.h" "src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/Lib/Timer.cpp" "src/Lib/Timer.h" "src/Core/PS4/HLE/LibKernel.cpp" "src/Core/PS4/HLE/LibKernel.h" "src/Core/PS4/HLE/LibSceGnmDriver.cpp" "src/Core/PS4/HLE/LibSceGnmDriver.h" "src/Core/PS4/HLE/Kernel/ThreadManagement.cpp" "src/Core/PS4/HLE/Kernel/ThreadManagement.h" "src/Core/PS4/HLE/ErrorCodes.h" "src/debug.h" "src/Core/PS4/HLE/Kernel/memory_management.cpp" "src/Core/PS4/HLE/Kernel/memory_management.h" "src/Core/PS4/GPU/gpu_memory.cpp" "src/Core/PS4/GPU/gpu_memory.h" "src/emulator.cpp" "src/emulator.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.h" "src/Core/PS4/HLE/Graphics/graphics_ctx.h" "src/vulkan_util.cpp" "src/vulkan_util.h" "src/Core/PS4/GPU/video_out_buffer.cpp" "src/Core/PS4/GPU/video_out_buffer.h" "src/Core/PS4/HLE/Graphics/graphics_render.cpp" "src/Core/PS4/HLE/Graphics/graphics_render.h" "src/Core/PS4/GPU/tile_manager.cpp" "src/Core/PS4/GPU/tile_manager.h" "src/version.h")
"src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/Core/PS4/HLE/LibKernel.cpp" "src/Core/PS4/HLE/LibKernel.h" "src/Core/PS4/HLE/LibSceGnmDriver.cpp" "src/Core/PS4/HLE/LibSceGnmDriver.h" "src/Core/PS4/HLE/Kernel/ThreadManagement.cpp" "src/Core/PS4/HLE/Kernel/ThreadManagement.h" "src/Core/PS4/HLE/ErrorCodes.h" "src/debug.h" "src/Core/PS4/HLE/Kernel/memory_management.cpp" "src/Core/PS4/HLE/Kernel/memory_management.h" "src/Core/PS4/GPU/gpu_memory.cpp" "src/Core/PS4/GPU/gpu_memory.h" "src/emulator.cpp" "src/emulator.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.h" "src/Core/PS4/HLE/Graphics/graphics_ctx.h" "src/vulkan_util.cpp" "src/vulkan_util.h" "src/Core/PS4/GPU/video_out_buffer.cpp" "src/Core/PS4/GPU/video_out_buffer.h" "src/Core/PS4/HLE/Graphics/graphics_render.cpp" "src/Core/PS4/HLE/Graphics/graphics_render.h" "src/Core/PS4/GPU/tile_manager.cpp" "src/Core/PS4/GPU/tile_manager.h" "src/version.h" "src/Emulator/HLE/Libraries/LibSystemService/system_service.cpp" "src/Emulator/HLE/Libraries/LibSystemService/system_service.h" "src/Emulator/HLE/Libraries/LibKernel/FileSystem/meta_file_system.h" "src/Emulator/HLE/Libraries/LibKernel/FileSystem/meta_file_system.cpp")
find_package(OpenGL REQUIRED)
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY} vulkan-1 spirv-tools-opt spirv-tools)

View File

@ -1,15 +1,13 @@
# shadPS4
An early PS4 emulator for Windows and Linux written in C++
by shadow , skmp , wheremyfoodat
[Check us on twitter](https://twitter.com/shadps4 "Check us on twitter")
# Status
Currently, it can only load PS4 ELF files.
Progress is focused on videoout_basic.elf from SDK demos, currently, it can load with fully working graphics. Others probably won't run since they might not be able to relocate all necessary functions
Early progress , a small amount of ps4 sdk demos and homebrew games working
![](https://geps.dev/progress/60) Elf Loader

View File

@ -1,3 +1,16 @@
v0.0.2 21/10/2023
=================
-using cstdint header in variable types
-run_main_entry: Rewrite in asm for stack setup
-printf libc implementation for work with sysv_abi
-initial pad emulation (only digital pad atm)
-Implemented sceVideoOutIsFlipPending
-Added auto stubs , now unsupported hle function will resolve as empty stubs
-Rewrote libc_cxa functions
-Libc implementations ( _ZdlPv,_Znwm,rand,_Fsin,qsort,free,strncpy,memmove,atan2f,pow,_Sin)
-ET_SCE_DYNAMIC behaves as valid for execution now.
-Initial FileSystem work (not yet usable).
v0.0.1 29/09/2023
=================
First public release . Everything is new

11223
scripts/ps4_names.txt Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,45 @@
# Helper script that generates stub implementation of all known nids + lookup tables
# for shadps4
import sys, os
import struct
#from hashlib import sha1
import hashlib
from base64 import b64encode as base64enc
from binascii import unhexlify as uhx
#ref https://github.com/SocraticBliss/ps4_name2nid_plugin
#ref derived from https://github.com/zecoxao/sce_symbols.git
# needs ps4_names.txt (see: https://github.com/zecoxao/sce_symbols.git for full list)
# generates "stubs.inl" to include in Core\PS4\Util
NEW_NIDS = {}
NAMES = 'ps4_names.txt'
def name2nid(name):
symbol = hashlib.sha1(name.encode() + uhx('518D64A635DED8C1E6B039B1C3E55230')).digest()
id = struct.unpack('<Q', symbol[:8])[0]
nid = base64enc(uhx('%016x' % id), b'+-').rstrip(b'=')
NEW_NIDS[nid]=name
def save_stubs(NIDS):
nidsSorted=sorted(NIDS.items(), key=lambda x: x[0])
nidsFile=open("aerolib.inl", "w")
nidsFile.writelines('// generated using ps4_names2stubs.py\n\n')
for nid, name in nidsSorted:
nidsFile.writelines('STUB("%s", %s)\n' % (str(nid,'utf-8'), name))
nidsFile.close()
f = open(NAMES,"r")
for line in f.readlines():
line = line.strip()
name2nid(line)
f.close()
save_stubs(NEW_NIDS)

View File

@ -2,19 +2,19 @@
#include <xxhash/xxh3.h>
#include "Util/Singleton.h"
#include "Emulator/Util/singleton.h"
void* GPU::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo /*CommandBuffer?*/, u64 virtual_addr, u64 size,
const GPUObject& info) {
auto* gpumemory = Singleton<GPUMemory>::Instance();
auto* gpumemory = singleton<GPUMemory>::instance();
return gpumemory->memoryCreateObj(submit_id, ctx, nullptr, &virtual_addr, &size, 1, info);
}
void GPU::memorySetAllocArea(u64 virtual_addr, u64 size) {
auto* gpumemory = Singleton<GPUMemory>::Instance();
auto* gpumemory = singleton<GPUMemory>::instance();
Lib::LockMutexGuard lock(gpumemory->m_mutex);
std::scoped_lock lock{gpumemory->m_mutex};
MemoryHeap h;
h.allocated_virtual_addr = virtual_addr;
@ -59,7 +59,7 @@ bool GPU::vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::
}
void GPU::flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx) {
auto* gpumemory = Singleton<GPUMemory>::Instance();
auto* gpumemory = singleton<GPUMemory>::instance();
gpumemory->flushAllHeaps(ctx);
}
@ -78,9 +78,9 @@ int GPU::GPUMemory::getHeapId(u64 virtual_addr, u64 size) {
void* GPU::GPUMemory::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo, const u64* virtual_addr, const u64* size,
int virtual_addr_num, const GPUObject& info) {
auto* gpumemory = Singleton<GPUMemory>::Instance();
auto* gpumemory = singleton<GPUMemory>::instance();
Lib::LockMutexGuard lock(gpumemory->m_mutex);
std::scoped_lock lock{gpumemory->m_mutex};
int heap_id = gpumemory->getHeapId(virtual_addr[0], size[0]);
@ -172,7 +172,7 @@ void GPU::GPUMemory::update(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx,
}
void GPU::GPUMemory::flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
int heap_id = 0;
for (auto& heap : m_heaps) {

View File

@ -2,6 +2,7 @@
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
#include <types.h>
#include <mutex>
#include <vector>
namespace GPU {
@ -66,7 +67,7 @@ class GPUMemory {
GPUMemory() {}
virtual ~GPUMemory() {}
int getHeapId(u64 vaddr, u64 size);
Lib::Mutex m_mutex;
std::mutex m_mutex;
std::vector<MemoryHeap> m_heaps;
void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, const u64* virtual_addr,
const u64* size, int virtual_addr_num, const GPUObject& info);

View File

@ -1,6 +1,6 @@
#include "tile_manager.h"
#include "Lib/Threads.h"
#include "Util/Singleton.h"
#include "Emulator/Util/singleton.h"
#include <mutex>
namespace GPU {
@ -10,7 +10,7 @@ class TileManager {
public:
TileManager(){}
virtual ~TileManager() { }
Lib::Mutex m_mutex;
std::mutex m_mutex;
};
class TileManager32 {
@ -143,9 +143,9 @@ void convertTileToLinear(void* dst, const void* src,u32 width, u32 height, bool
TileManager32 t;
t.Init(width, height, is_neo);
auto* g_TileManager = Singleton<TileManager>::Instance();
auto* g_TileManager = singleton<TileManager>::instance();
Lib::LockMutexGuard lock(g_TileManager->m_mutex);
std::scoped_lock lock{g_TileManager->m_mutex};
for (u32 y = 0; y < height; y++) {
u32 x = 0;

View File

@ -1,6 +1,7 @@
#include "video_out_ctx.h"
#include <Core/PS4/HLE/LibKernel.h>
#include <debug.h>
namespace HLE::Graphics::Objects {
@ -11,7 +12,7 @@ void VideoOutCtx::Init(u32 width, u32 height) {
m_video_out_ctx.m_resolution.paneHeight = height;
}
int VideoOutCtx::Open() {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
int handle = -1;
@ -28,20 +29,45 @@ int VideoOutCtx::Open() {
return handle;
}
void VideoOutCtx::Close(s32 handle) {
std::scoped_lock lock{m_mutex};
m_video_out_ctx.isOpened = false;
if (m_video_out_ctx.m_flip_evtEq.size() > 0)
{
BREAKPOINT(); //we need to clear all events if they have been created
}
m_video_out_ctx.m_flip_rate = 0;
// clear buffers
for (auto& buffer : m_video_out_ctx.buffers) {
buffer.buffer = nullptr;
buffer.buffer_render = nullptr;
buffer.buffer_size = 0;
buffer.set_id = 0;
}
m_video_out_ctx.buffers_sets.clear();
m_video_out_ctx.buffers_registration_index = 0;
}
VideoConfigInternal* VideoOutCtx::getCtx(int handle) {
if (handle != 1) return nullptr;
if (handle != 1) [[unlikely]] {
return nullptr;
}
return &m_video_out_ctx; // assuming that it's the only ctx TODO check if we need more
}
void FlipQueue::getFlipStatus(VideoConfigInternal* cfg, SceVideoOutFlipStatus* out) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock(m_mutex);
*out = cfg->m_flip_status;
}
bool FlipQueue::submitFlip(VideoConfigInternal* cfg, s32 index, s64 flip_arg) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
if (m_requests.size() >= 2) {
return false;
@ -58,52 +84,53 @@ bool FlipQueue::submitFlip(VideoConfigInternal* cfg, s32 index, s64 flip_arg) {
cfg->m_flip_status.flipPendingNum = static_cast<int>(m_requests.size());
cfg->m_flip_status.gcQueueNum = 0;
m_submit_cond.SignalCondVar();
m_submit_cond.notify_one();
return true;
}
bool FlipQueue::flip(u32 micros) {
m_mutex.LockMutex();
if (m_requests.size() == 0) {
m_submit_cond.WaitCondVarFor(&m_mutex, micros);
const auto request = [&]() -> Request* {
std::unique_lock lock{m_mutex};
m_submit_cond.wait_for(lock, std::chrono::microseconds(micros),
[&] { return !m_requests.empty(); });
if (m_requests.empty()) {
return nullptr;
}
return &m_requests.at(0); // Process first request
}();
if (m_requests.size() == 0) {
m_mutex.UnlockMutex();
return false;
if (!request) {
return false;
}
const auto buffer = request->cfg->buffers[request->index].buffer_render;
Emu::DrawBuffer(buffer);
std::scoped_lock lock{m_mutex};
{
std::scoped_lock cfg_lock{request->cfg->m_mutex};
for (auto& flip_eq : request->cfg->m_flip_evtEq) {
if (flip_eq != nullptr) {
flip_eq->triggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, HLE::Kernel::Objects::EVFILT_VIDEO_OUT,
reinterpret_cast<void*>(request->flip_arg));
}
}
}
auto request = m_requests.at(0); // proceed first request
m_mutex.UnlockMutex();
auto* buffer = request.cfg->buffers[request.index].buffer_render;
Emulator::DrawBuffer(buffer);
m_mutex.LockMutex();
request.cfg->m_mutex.LockMutex();
for (auto& flip_eq : request.cfg->m_flip_evtEq) {
if (flip_eq != nullptr) {
flip_eq->triggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, HLE::Kernel::Objects::EVFILT_VIDEO_OUT, reinterpret_cast<void*>(request.flip_arg));
}
}
request.cfg->m_mutex.UnlockMutex();
m_requests.erase(m_requests.begin());
m_done_cond.SignalCondVar();
m_done_cond.notify_one();
request.cfg->m_flip_status.count++;
request->cfg->m_flip_status.count++;
//TODO request.cfg->m_flip_status.processTime = LibKernel::KernelGetProcessTime();
request.cfg->m_flip_status.tsc = HLE::Libs::LibKernel::sceKernelReadTsc();
request.cfg->m_flip_status.submitTsc = request.submit_tsc;
request.cfg->m_flip_status.flipArg = request.flip_arg;
request.cfg->m_flip_status.currentBuffer = request.index;
request.cfg->m_flip_status.flipPendingNum = static_cast<int>(m_requests.size());
m_mutex.UnlockMutex();
request->cfg->m_flip_status.tsc = HLE::Libs::LibKernel::sceKernelReadTsc();
request->cfg->m_flip_status.submitTsc = request->submit_tsc;
request->cfg->m_flip_status.flipArg = request->flip_arg;
request->cfg->m_flip_status.currentBuffer = request->index;
request->cfg->m_flip_status.flipPendingNum = static_cast<int>(m_requests.size());
return false;
}
}; // namespace HLE::Graphics::Objects
}; // namespace HLE::Graphics::Objects

View File

@ -1,6 +1,8 @@
#pragma once
#include <condition_variable>
#include <mutex>
#include <Core/PS4/HLE/Graphics/video_out.h>
#include <Lib/Threads.h>
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
#include <emulator.h>
@ -17,7 +19,7 @@ struct VideoOutBufferInfo {
};
struct VideoConfigInternal {
Lib::Mutex m_mutex;
std::mutex m_mutex;
SceVideoOutResolutionStatus m_resolution;
bool isOpened = false;
SceVideoOutFlipStatus m_flip_status;
@ -45,9 +47,9 @@ class FlipQueue {
uint64_t submit_tsc;
};
Lib::Mutex m_mutex;
Lib::ConditionVariable m_submit_cond;
Lib::ConditionVariable m_done_cond;
std::mutex m_mutex;
std::condition_variable m_submit_cond;
std::condition_variable m_done_cond;
std::vector<Request> m_requests;
};
@ -58,19 +60,20 @@ class VideoOutCtx {
virtual ~VideoOutCtx() {}
void Init(u32 width, u32 height);
int Open();
void Close(s32 handle);
VideoConfigInternal* getCtx(int handle);
FlipQueue& getFlipQueue() { return m_flip_queue; }
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
if (m_graphic_ctx == nullptr) {
m_graphic_ctx = Emulator::getGraphicCtx();
if (!m_graphic_ctx) {
m_graphic_ctx = Emu::getGraphicCtx();
}
return m_graphic_ctx;
}
private:
Lib::Mutex m_mutex;
std::mutex m_mutex;
VideoConfigInternal m_video_out_ctx;
FlipQueue m_flip_queue;
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;

View File

@ -2,13 +2,12 @@
#include <types.h>
#include <vulkan/vulkan_core.h>
#include "Lib/Threads.h"
#include <mutex>
namespace HLE::Libs::Graphics {
struct VulkanCommandPool {
Lib::Mutex mutex;
std::mutex mutex;
VkCommandPool pool = nullptr;
VkCommandBuffer* buffers = nullptr;
VkFence* fences = nullptr;
@ -18,7 +17,7 @@ struct VulkanCommandPool {
};
struct VulkanQueueInfo {
Lib::Mutex* mutex = nullptr;
std::mutex* mutex = nullptr;
u32 family = static_cast<u32>(-1);
u32 index = static_cast<u32>(-1);
VkQueue vk_queue = nullptr;

View File

@ -1,20 +1,20 @@
#include "graphics_render.h"
#include "Util/Singleton.h"
#include "Emulator/Util/singleton.h"
#include "emulator.h"
static thread_local GPU::CommandPool g_command_pool;
void GPU::renderCreateCtx() {
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
render_ctx->setGraphicCtx(Emulator::getGraphicCtx());
render_ctx->setGraphicCtx(Emu::getGraphicCtx());
}
void GPU::CommandBuffer::allocateBuffer() {
m_pool = g_command_pool.getPool(m_queue);
Lib::LockMutexGuard lock(m_pool->mutex);
std::scoped_lock lock{m_pool->mutex};
for (uint32_t i = 0; i < m_pool->buffers_count; i++) {
if (!m_pool->busy[i]) {
@ -27,7 +27,7 @@ void GPU::CommandBuffer::allocateBuffer() {
}
void GPU::CommandBuffer::freeBuffer() {
Lib::LockMutexGuard lock(m_pool->mutex);
std::scoped_lock lock{m_pool->mutex};
waitForFence();
@ -37,7 +37,7 @@ void GPU::CommandBuffer::freeBuffer() {
}
void GPU::CommandBuffer::waitForFence() {
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
if (m_execute) {
auto* device = render_ctx->getGraphicCtx()->m_device;
@ -89,19 +89,10 @@ void GPU::CommandBuffer::executeWithSemaphore() {
submit_info.signalSemaphoreCount = 1;
submit_info.pSignalSemaphores = &m_pool->semaphores[m_index];
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
if (queue.mutex != nullptr) {
queue.mutex->LockMutex();
}
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
if (queue.mutex != nullptr) {
queue.mutex->LockMutex();
}
m_execute = true;
if (result != VK_SUCCESS) {
@ -124,19 +115,10 @@ void GPU::CommandBuffer::execute() {
submit_info.signalSemaphoreCount = 0;
submit_info.pSignalSemaphores = nullptr;
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
if (queue.mutex != nullptr) {
queue.mutex->LockMutex();
}
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
if (queue.mutex != nullptr) {
queue.mutex->UnlockMutex();
}
m_execute = true;
if (result != VK_SUCCESS) {
@ -145,7 +127,7 @@ void GPU::CommandBuffer::execute() {
}
}
void GPU::CommandPool::createPool(int id) {
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
auto* ctx = render_ctx->getGraphicCtx();
m_pool[id] = new HLE::Libs::Graphics::VulkanCommandPool;
@ -206,7 +188,7 @@ void GPU::CommandPool::createPool(int id) {
}
void GPU::CommandPool::deleteAllPool() {
auto* render_ctx = Singleton<RenderCtx>::Instance();
auto* render_ctx = singleton<RenderCtx>::instance();
auto* ctx = render_ctx->getGraphicCtx();
for (auto& pool : m_pool) {

View File

@ -1,7 +1,9 @@
#include "video_out.h"
#include <Core/PS4/GPU/gpu_memory.h>
#include <Core/PS4/GPU/video_out_buffer.h>
#include <Core/PS4/HLE/ErrorCodes.h>
#include <Core/PS4/HLE/LibSceGnmDriver.h>
#include <Core/PS4/HLE/Libs.h>
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
#include <Util/config.h>
@ -13,9 +15,8 @@
#include <string>
#include "Objects/video_out_ctx.h"
#include "Util/Singleton.h"
#include "Emulator/Util/singleton.h"
#include "emulator.h"
#include <Core/PS4/GPU/gpu_memory.h>
#include "graphics_render.h"
#include <Core/PS4/HLE/LibSceGnmDriver.h>
@ -24,12 +25,12 @@ namespace HLE::Libs::Graphics::VideoOut {
constexpr bool log_file_videoout = true; // disable it to disable logging
void videoOutInit(u32 width, u32 height) {
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
videoOut->Init(width, height);
}
bool videoOutFlip(u32 micros) {
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
return videoOut->getFlipQueue().flip(micros);
}
@ -87,14 +88,14 @@ static void flip_delete_event_func(LibKernel::EventQueues::SceKernelEqueue eq, H
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue eq, s32 handle, void* udata) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
auto* ctx = videoOut->getCtx(handle);
if (ctx == nullptr) {
return SCE_VIDEO_OUT_ERROR_INVALID_HANDLE;
}
Lib::LockMutexGuard lock(ctx->m_mutex);
std::scoped_lock lock(ctx->m_mutex);
if (eq == nullptr) {
return SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
@ -122,7 +123,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum,
const SceVideoOutBufferAttribute* attribute) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
auto* ctx = videoOut->getCtx(handle);
if (handle == 1) { // main port
@ -164,7 +165,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
int registration_index = ctx->buffers_registration_index++;
Emulator::checkAndWaitForGraphicsInit();
Emu::checkAndWaitForGraphicsInit();
GPU::renderCreateCtx();
// try to calculate buffer size
@ -206,8 +207,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
ctx->buffers[i + startIndex].buffer_size = buffer_size;
ctx->buffers[i + startIndex].buffer_pitch = buffer_pitch;
ctx->buffers[i + startIndex].buffer_render = static_cast<Graphics::VideoOutVulkanImage*>(
GPU::memoryCreateObj(
0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
GPU::memoryCreateObj(0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
LOG_INFO_IF(log_file_videoout, "buffers[{}] = {}\n", i + startIndex, log_hex_full(reinterpret_cast<uint64_t>(addresses[i])));
}
@ -216,22 +216,24 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
}
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
videoOut->getCtx(handle)->m_flip_rate = rate;
return SCE_OK;
}
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
// BREAKPOINT();
PRINT_DUMMY_FUNCTION_NAME();
return 0;
PRINT_FUNCTION_NAME();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
s32 pending = videoOut->getCtx(handle)->m_flip_status.flipPendingNum;
return pending;
}
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
auto* ctx = videoOut->getCtx(handle);
if (flipMode != 1) {
BREAKPOINT(); // only flipmode==1 is supported
// BREAKPOINT(); // only flipmode==1 is supported
LOG_TRACE_IF(log_file_videoout, "sceVideoOutSubmitFlip flipmode {}\n", bufferIndex);//openBOR needs 2 but seems to work
}
if (bufferIndex == -1) {
BREAKPOINT(); // blank output not supported
@ -248,12 +250,14 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
LOG_TRACE_IF(log_file_videoout, "sceVideoOutSubmitFlip flip queue is full\n");
return SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
}
HLE::Libs::LibSceGnmDriver::sceGnmFlushGarlic();
HLE::Libs::LibSceGnmDriver::sceGnmFlushGarlic(); // hackish should be done that neccesary for niko's homebrew
return SCE_OK;
}
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
auto* ctx = videoOut->getCtx(handle);
videoOut->getFlipQueue().getFlipStatus(ctx, status);
@ -269,13 +273,13 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* sta
}
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
PRINT_FUNCTION_NAME();
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
*status = videoOut->getCtx(handle)->m_resolution;
return SCE_OK;
}
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param) {
PRINT_FUNCTION_NAME();
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM) {
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM && userId != 0) {
BREAKPOINT();
}
if (busType != SCE_VIDEO_OUT_BUS_TYPE_MAIN) {
@ -288,7 +292,7 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
if (param != nullptr) {
BREAKPOINT();
}
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
int handle = videoOut->Open();
if (handle < 0) {
@ -298,6 +302,12 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
return handle;
}
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle) {
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
videoOut->Close(handle);
return SCE_OK;
}
s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) { BREAKPOINT(); }
void videoOutRegisterLib(SymbolsResolver* sym) {
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetFlipStatus);
@ -309,6 +319,8 @@ void videoOutRegisterLib(SymbolsResolver* sym) {
LIB_FUNCTION("6kPnj51T62Y", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetResolutionStatus);
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutOpen);
LIB_FUNCTION("zgXifHT9ErY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutIsFlipPending);
LIB_FUNCTION("N5KDtkIjjJ4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutUnregisterBuffers);
LIB_FUNCTION("uquVH4-Du78", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutClose);
//openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
@ -318,7 +330,5 @@ void videoOutRegisterLib(SymbolsResolver* sym) {
LIB_FUNCTION("w3BY+tAEiQY", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutRegisterBuffers);
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip);
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutGetFlipStatus);
}
} // namespace HLE::Libs::Graphics::VideoOut

View File

@ -110,5 +110,5 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status);
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param);
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
} // namespace HLE::Libs::Graphics::VideoOut

View File

@ -1,14 +1,13 @@
#include "event_queue.h"
#include <Lib/Timer.h>
#include "debug.h"
#include <chrono>
namespace HLE::Kernel::Objects {
EqueueInternal::~EqueueInternal() {}
int EqueueInternal::addEvent(const EqueueEvent& event) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
if (m_events.size() > 0) {
BREAKPOINT();
@ -24,11 +23,10 @@ int EqueueInternal::addEvent(const EqueueEvent& event) {
}
int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
Lib::LockMutexGuard lock(m_mutex);
std::unique_lock lock{m_mutex};
u32 timeElapsed = 0;
Lib::Timer t;
t.Start();
u64 timeElapsed = 0;
const auto start = std::chrono::high_resolution_clock::now();
for (;;) {
int ret = getTriggeredEvents(ev, num);
@ -38,19 +36,20 @@ int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
}
if (micros == 0) {
m_cond.WaitCondVar(&m_mutex);
m_cond.wait(lock);
} else {
m_cond.WaitCondVarFor(&m_mutex, micros - timeElapsed);
m_cond.wait_for(lock, std::chrono::microseconds(micros - timeElapsed));
}
timeElapsed = static_cast<uint32_t>(t.GetTimeSec() * 1000000.0);
const auto end = std::chrono::high_resolution_clock::now();
timeElapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
return 0;
}
bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
if (m_events.size() > 1) {
BREAKPOINT(); // we currently support one event
@ -63,14 +62,12 @@ bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
event.isTriggered = true;
}
m_cond.SignalCondVar();
m_cond.notify_one();
return true;
}
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
Lib::LockMutexGuard lock(m_mutex);
int ret = 0;
if (m_events.size() > 1) {

View File

@ -1,6 +1,7 @@
#pragma once
#include <types.h>
#include <Lib/Threads.h>
#include <mutex>
#include <string>
#include <vector>
@ -71,8 +72,8 @@ class EqueueInternal {
int getTriggeredEvents(SceKernelEvent* ev, int num);
private:
std::string m_name;
Lib::Mutex m_mutex;
std::mutex m_mutex;
std::vector<EqueueEvent> m_events;
Lib::ConditionVariable m_cond;
std::condition_variable m_cond;
};
}; // namespace HLE::Kernel::Objects

View File

@ -5,7 +5,7 @@ namespace HLE::Kernel::Objects {
static u64 AlignUp(u64 pos, u64 align) { return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos); }
bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignment, u64* physAddrOut, int memoryType) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
u64 find_free_pos = 0;
// iterate through allocated blocked and find the next free position
@ -40,7 +40,7 @@ bool PhysicalMemory::Alloc(u64 searchStart, u64 searchEnd, u64 len, u64 alignmen
return false;
}
bool PhysicalMemory::Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot, VirtualMemory::MemoryMode cpu_mode, GPU::MemoryMode gpu_mode) {
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
for (auto& b : m_allocatedBlocks) {
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size) {
if (b.map_virtual_addr != 0 || b.map_size != 0) {

View File

@ -2,8 +2,8 @@
#include <types.h>
#include <Core/virtual_memory.h>
#include <Core/PS4/GPU/gpu_memory.h>
#include <mutex>
#include <vector>
#include "Lib/Threads.h"
namespace HLE::Kernel::Objects {
@ -28,7 +28,7 @@ class PhysicalMemory {
private:
std::vector<AllocatedBlock> m_allocatedBlocks;
Lib::Mutex m_mutex;
std::mutex m_mutex;
};
} // namespace HLE::Kernel::Objects

View File

@ -2,13 +2,13 @@
#include <Core/PS4/GPU/gpu_memory.h>
#include <Core/virtual_memory.h>
#include <Util/log.h>
#include <debug.h>
#include <bit>
#include <magic_enum.hpp>
#include <Util/log.h>
#include "../../../../Util/Singleton.h"
#include "Emulator/Util/singleton.h"
#include "../ErrorCodes.h"
#include "../Libs.h"
#include "Objects/physical_memory.h"
@ -55,7 +55,7 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
LOG_INFO_IF(log_file_memory, "memory_type = {}\n", magic_enum::enum_name(memtype.value()));
u64 physical_addr = 0;
auto* physical_memory = Singleton<HLE::Kernel::Objects::PhysicalMemory>::Instance();
auto* physical_memory = singleton<HLE::Kernel::Objects::PhysicalMemory>::instance();
if (!physical_memory->Alloc(searchStart, searchEnd, len, alignment, &physical_addr, memoryType)) {
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EAGAIN can't allocate physical memory\n");
return SCE_KERNEL_ERROR_EAGAIN;
@ -76,10 +76,10 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
return SCE_KERNEL_ERROR_EINVAL;
}
if (alignment != 0) {
if ((!isPowerOfTwo(alignment) && !is16KBAligned(alignment))){
if ((!isPowerOfTwo(alignment) && !is16KBAligned(alignment))) {
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL alignment invalid\n");
return SCE_KERNEL_ERROR_EINVAL;
}
}
}
LOG_INFO_IF(log_file_memory, "len = {}\n", log_hex_full(len));
@ -92,6 +92,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
switch (prot) {
case 0x32:
case 0x33: // SCE_KERNEL_PROT_CPU_READ|SCE_KERNEL_PROT_CPU_WRITE|SCE_KERNEL_PROT_GPU_READ|SCE_KERNEL_PROT_GPU_ALL
cpu_mode = VirtualMemory::MemoryMode::ReadWrite;
gpu_mode = GPU::MemoryMode::ReadWrite;
@ -114,7 +115,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
return SCE_KERNEL_ERROR_ENOMEM;
}
auto* physical_memory = Singleton<HLE::Kernel::Objects::PhysicalMemory>::Instance();
auto* physical_memory = singleton<HLE::Kernel::Objects::PhysicalMemory>::instance();
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode, gpu_mode)) {
BREAKPOINT();
}

View File

@ -1,110 +1,98 @@
#include "LibC.h"
#include "Libs.h"
#include "../Loader/Elf.h"
#include <debug.h>
#include <pthread.h>
#include "../Loader/Elf.h"
#include "Emulator/HLE/Libraries/LibC/libc.h"
#include "Emulator/HLE/Libraries/LibC/libc_cxa.h"
#include "ErrorCodes.h"
#include "Libs.h"
namespace HLE::Libs::LibC {
static u32 g_need_sceLibc = 1;
static u32 g_need_sceLibc = 1;
static PS4_SYSV_ABI void init_env() // every game/demo should probably
{
//dummy no need atm
}
static PS4_SYSV_ABI void init_env() // every game/demo should probably
{
// dummy no need atm
}
static pthread_mutex_t __guard_mutex;
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
static PS4_SYSV_ABI void catchReturnFromMain(int status) {
// dummy
}
static void recursiveMutex()
{
pthread_mutexattr_t recursiveMutexAttr;
pthread_mutexattr_init(&recursiveMutexAttr);
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
static PS4_SYSV_ABI void _Assert() { BREAKPOINT(); }
PS4_SYSV_ABI int puts(const char* s) {
std::puts(s);
return SCE_OK;
}
PS4_SYSV_ABI int rand() { return std::rand(); }
PS4_SYSV_ABI void _ZdlPv(void* ptr) { std::free(ptr); }
PS4_SYSV_ABI void _ZSt11_Xbad_allocv() { BREAKPOINT(); }
PS4_SYSV_ABI void _ZSt14_Xlength_errorPKc() { BREAKPOINT(); }
PS4_SYSV_ABI void* _Znwm(u64 count) {
if (count == 0) {
BREAKPOINT();
}
void* ptr = std::malloc(count);
return ptr;
}
static pthread_mutex_t* mutex_quard() {
pthread_once(&__once_control, &recursiveMutex);
return &__guard_mutex;
}
float PS4_SYSV_ABI _Fsin(float arg) { return std::sinf(arg); }
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) {
if ((*((uint8_t*)guard_object) != 0)) // low 8 bits checks if its already init
{
return 0;
}
int result = ::pthread_mutex_lock(mutex_quard());
if (result != 0) {
BREAKPOINT();
}
typedef int(PS4_SYSV_ABI* pfunc_QsortCmp)(const void*, const void*);
thread_local static pfunc_QsortCmp compair_ps4;
// Check if another thread has completed initializer run
if ((*((uint8_t*)guard_object) != 0)) { // check again if other thread init it
int result = ::pthread_mutex_unlock(mutex_quard());
if (result != 0) {
BREAKPOINT();
}
return 0;
}
int qsort_compair(const void* arg1, const void* arg2) { return compair_ps4(arg1, arg2); }
if (((uint8_t*)guard_object)[1] != 0) { // the second lowest byte marks if it's being used by a thread
BREAKPOINT();
}
void PS4_SYSV_ABI qsort(void* ptr, size_t count,size_t size, int(PS4_SYSV_ABI* comp)(const void*, const void*)) {
compair_ps4 = comp;
std::qsort(ptr, count, size, qsort_compair);
}
// mark this guard object as being in use
((uint8_t*)guard_object)[1] = 1;
void LibC_Register(SymbolsResolver* sym) {
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_acquire);
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_release);
LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_abort);
LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcmp);
LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcpy);
LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memset);
LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, catchReturnFromMain);
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::exit);
LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::atexit);
LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, _Assert);
LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::printf);
LIB_FUNCTION("Q2V+iqvjgC0", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::vsnprintf);
LIB_FUNCTION("YQ0navp+YIc", "libc", 1, "libc", 1, 1, puts);
LIB_FUNCTION("cpCOXWMgha0", "libc", 1, "libc", 1, 1, rand);
LIB_FUNCTION("ZtjspkJQ+vw", "libc", 1, "libc", 1, 1, _Fsin);
LIB_FUNCTION("AEJdIVZTEmo", "libc", 1, "libc", 1, 1, qsort);
LIB_FUNCTION("Ovb2dSJOAuE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcmp);
LIB_FUNCTION("gQX+4GDQjpM", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::malloc);
LIB_FUNCTION("tIhsqj0qsFE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::free);
LIB_FUNCTION("j4ViWNHEgww", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strlen);
LIB_FUNCTION("6sJWiWSRuqk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strncpy);
LIB_FUNCTION("+P6FRGH4LfA", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memmove);
LIB_FUNCTION("kiZSXIWd9vg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcpy);
LIB_FUNCTION("Ls4tzzhimqQ", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcat);
LIB_FUNCTION("EH-x713A99c", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::atan2f);
LIB_FUNCTION("QI-x0SL8jhw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::acosf);
LIB_FUNCTION("ZE6RNL+eLbk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::tanf);
LIB_FUNCTION("GZWjF-YIFFk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::asinf);
LIB_FUNCTION("9LCjpWyQ5Zc", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::pow);
LIB_FUNCTION("cCXjU72Z0Ow", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::_Sin);
return 1;
}
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object)
{
*((uint8_t*)guard_object) = 1;//mark it as done
LIB_FUNCTION("z+P+xCnWLBk", "libc", 1, "libc", 1, 1, _ZdlPv);
LIB_FUNCTION("eT2UsmTewbU", "libc", 1, "libc", 1, 1, _ZSt11_Xbad_allocv);
LIB_FUNCTION("tQIo+GIPklo", "libc", 1, "libc", 1, 1, _ZSt14_Xlength_errorPKc);
LIB_FUNCTION("fJnpuVVBbKk", "libc", 1, "libc", 1, 1, _Znwm);
}
// release global mutex
int result = ::pthread_mutex_unlock(mutex_quard());
if (result != 0) {
BREAKPOINT();
}
}
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n) {
return ::memcmp(s1, s2, n);
}
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n) {
return ::memcpy(dest, src, n);
}
static PS4_SYSV_ABI void catchReturnFromMain(int status) { BREAKPOINT();
}
static PS4_SYSV_ABI void exit(int code) { BREAKPOINT();
}
static PS4_SYSV_ABI int atexit(void (*func)())
{
int rt = ::atexit(func);
if (rt != 0)
{
BREAKPOINT();
}
return rt;
}
static PS4_SYSV_ABI void _Assert() { BREAKPOINT(); }
void LibC_Register(SymbolsResolver* sym)
{
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, __cxa_guard_acquire);
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, __cxa_guard_release);
LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, memcmp);
LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, memcpy);
LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, catchReturnFromMain);
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, exit);
LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, atexit);
LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, _Assert);
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
}
};
}; // namespace HLE::Libs::LibC

View File

@ -7,12 +7,7 @@ namespace HLE::Libs::LibC {
//functions
static PS4_SYSV_ABI void init_env();
static PS4_SYSV_ABI void exit(int code);
static PS4_SYSV_ABI void _Assert();
static PS4_SYSV_ABI void catchReturnFromMain(int status);
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object);
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object);
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n);
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n);
};

View File

@ -5,8 +5,7 @@
#include <io.h>
#include <sys/types.h>
#include <windows.h>
#include "../../../Util/Singleton.h"
#include "Emulator/Util/singleton.h"
#include "../Loader/Elf.h"
#include "Kernel/Objects/physical_memory.h"
#include "Kernel/ThreadManagement.h"
@ -14,6 +13,8 @@
#include "Kernel/event_queues.h"
#include "Kernel/memory_management.h"
#include "Libs.h"
#include "Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.h"
#include "Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.h"
namespace HLE::Libs::LibKernel {
@ -207,6 +208,7 @@ void LibKernel_Register(SymbolsResolver* sym) {
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelGetDirectMemorySize);
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelMapDirectMemory);
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
// equeue
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelCreateEqueue);
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelWaitEqueue);
@ -215,6 +217,7 @@ void LibKernel_Register(SymbolsResolver* sym) {
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
// time
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
// Pthreads
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadMutexattrInit);
LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadMutexattrSettype);
@ -225,7 +228,6 @@ void LibKernel_Register(SymbolsResolver* sym) {
LIB_FUNCTION("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadCondInit);
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadCondBroadcast);
// TODO
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
LIB_FUNCTION("mkawd0NA9ts", "libkernel", 1, "libkernel", 1, 1, sysconf);
@ -276,6 +278,11 @@ void LibKernel_Register(SymbolsResolver* sym) {
LIB_FUNCTION("t0fXUzq61Z4", "libkernel", 1, "libkernel", 1, 1, POSIX::_fcntl);
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, POSIX::_readv);
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, POSIX::_read);
// fs
LIB_FUNCTION("1G3lF1Gg1k8", "libkernel", 1, "libkernel", 1, 1, Emulator::HLE::Libraries::LibKernel::FileSystem::sceKernelOpen);
LIB_FUNCTION("wuCroIGjt2g", "libScePosix", 1, "libkernel", 1, 1, Emulator::HLE::Libraries::LibKernel::FileSystem::POSIX::open);
}
}; // namespace HLE::Libs::LibKernel

View File

@ -8,161 +8,20 @@
namespace HLE::Libs::LibSceGnmDriver {
int sceGnmAddEqEvent(/* SceKernelEqueue eq, EqEventType id,*/ void* udata)
{ return 0;
}
bool sceGnmAreSubmitsAllowed()
{
return true;
}
int /* WorkloadStatus*/ sceGnmBeginWorkload(uint64_t* workload /*, WorkloadStream stream*/)
{
return 0;
}
int /* WorkloadStatus*/ sceGnmCreateWorkloadStream(/* WorkloadStream* workloadStream,*/ const char* name)
{
return 0;
}
void sceGnmDebugHardwareStatus(/* HardwareStatus flag*/) {
}
void sceGnmSetGsRingSizes(/* GsRingSizeSetup esgsRingSize, GsRingSizeSetup gsvsRingSize*/)
{
}
int32_t sceGnmSetWaveLimitMultipliers(uint16_t targetPipeMask, uint8_t gfxRatio, const uint8_t (*pipeRatios)[7])
{ return 0;
}
int /*MipStatsError*/ sceGnmSetupMipStatsReport(void* outputBuffer, uint32_t sizeInBytes, uint8_t intervalsBetweenReports,
uint8_t numReportsBeforeReset /*, MipStatsResetForce mipStatsResetForce*/)
{
return 0;
}
int sceGnmSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
const uint32_t* ccb_sizes_in_bytes)
{
return 0;
}
int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes, int handle, int index,
int flip_mode, int64_t flip_arg)
{
return 0;
}
void sceGnmDingDong(u32 ring_id, u32 offset_dw)
{
}
bool sceRazorIsLoaded()
{ return true;// hmm???
}
int sceGnmDeleteEqEvent(/* SceKernelEqueue eq, EqEventType id*/)
{ return 0;
}
int32_t sceGnmSubmitDone()
{
PRINT_DUMMY_FUNCTION_NAME();
return 0;
}
int /* MipStatsError*/ sceGnmDisableMipStatsReport()
{ return 0;
}
int sceGnmSubmitAndFlipCommandBuffersForWorkload()
{ return 0;
}
int sceGnmSubmitCommandBuffersForWorkload()
{ return 0;
}
int /* WorkloadStatus*/ sceGnmDestroyWorkloadStream(/*WorkloadStream workloadStream*/)
{ return 0;
}
void sceGnmDingDongForWorkload()
{
}
void sceGnmDriverCaptureInProgress() {}
void sceGnmUnmapComputeQueue(){}
void sceGnmDriverTraceInProgress(){}
void sceGnmDriverTriggerCapture(){}
void sceGnmEndWorkload(){}
void sceGnmFlushGarlic() { PRINT_FUNCTION_NAME();
GPU::flushGarlic(Emulator::getGraphicCtx());
GPU::flushGarlic(Emu::getGraphicCtx());
}
void sceGnmGetEqEventType(){}
void sceGnmGetEqTimeStamp(){}
void sceGnmGetGpuBlockStatus(){}
void sceGnmGetGpuInfoStatus(){}
void sceGnmGetLastWaitedAddress(){}
void sceGnmGetNumTcaUnits(){}
void sceGnmGetOffChipTessellationBufferSize(){}
void sceGnmGetPhysicalCounterFromVirtualized(){}
void sceGnmGetProtectionFaultTimeStamp(){}
void sceGnmGetShaderProgramBaseAddress(){}
void sceGnmGetShaderStatus(){}
void sceGnmGetTheTessellationFactorRingBufferBaseAddress(){}
void sceGnmIsUserPaEnabled(){}
void sceGnmLogicalCuIndexToPhysicalCuIndex(){}
void sceGnmLogicalCuMaskToPhysicalCuMask(){}
void sceGnmMapComputeQueue(){}
void sceGnmMapComputeQueueWithPriority(){}
void sceRazorCaptureImmediate(){}
void sceGnmRequestFlipAndSubmitDone(){}
void sceGnmRequestFlipAndSubmitDoneForWorkload(){}
void sceGnmRequestMipStatsReportAndReset(){}
void LibSceGnmDriver_Register(SymbolsResolver* sym)
{
LIB_FUNCTION("b0xyllnVY-I", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmAddEqEvent);
LIB_FUNCTION("b08AgtPlHPg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmAreSubmitsAllowed);
LIB_FUNCTION("ihxrbsoSKWc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmBeginWorkload);
LIB_FUNCTION("5udAm+6boVg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmCreateWorkloadStream);
LIB_FUNCTION("qpGITzPE+Zc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDebugHardwareStatus);
LIB_FUNCTION("jtkqXpAOY6w", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetGsRingSizes);
LIB_FUNCTION("XiyzNZ9J4nQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetWaveLimitMultipliers);
LIB_FUNCTION("+xuDhxlWRPg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetupMipStatsReport);
LIB_FUNCTION("zwY0YV91TTI", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitCommandBuffers);
LIB_FUNCTION("xbxNatawohc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitAndFlipCommandBuffers);
LIB_FUNCTION("Ga6r7H6Y0RI", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitAndFlipCommandBuffersForWorkload);
LIB_FUNCTION("f33OrruQYbM", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceRazorIsLoaded);
LIB_FUNCTION("jRcI8VcgTz4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitCommandBuffersForWorkload);
LIB_FUNCTION("PVT+fuoS9gU", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDeleteEqEvent);
LIB_FUNCTION("yvZ73uQUqrk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitDone);
LIB_FUNCTION("UtObDRQiGbs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDestroyWorkloadStream);
LIB_FUNCTION("bX5IbRvECXk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDingDong);
LIB_FUNCTION("byXlqupd8cE", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDingDongForWorkload);
LIB_FUNCTION("HHo1BAljZO8", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDisableMipStatsReport);
LIB_FUNCTION("TLV4mswiZ4A", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverCaptureInProgress);
LIB_FUNCTION("ArSg-TGinhk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmUnmapComputeQueue);
LIB_FUNCTION("R6z1xM3pW-w", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverTraceInProgress);
LIB_FUNCTION("d88anrgNoKY", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverTriggerCapture);
LIB_FUNCTION("Fa3x75OOLRA", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmEndWorkload);
LIB_FUNCTION("iBt3Oe00Kvc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmFlushGarlic);
LIB_FUNCTION("UoYY0DWMC0U", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetEqEventType);
LIB_FUNCTION("H7-fgvEutM0", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetEqTimeStamp);
LIB_FUNCTION("oL4hGI1PMpw", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetGpuBlockStatus);
LIB_FUNCTION("tZCSL5ulnB4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetGpuInfoStatus);
LIB_FUNCTION("iFirFzgYsvw", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetLastWaitedAddress);
LIB_FUNCTION("KnldROUkWJY", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetNumTcaUnits);
LIB_FUNCTION("FFVZcCu3zWU", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetOffChipTessellationBufferSize);
LIB_FUNCTION("dewXw5roLs0", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetPhysicalCounterFromVirtualized);
LIB_FUNCTION("fzJdEihTFV4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetProtectionFaultTimeStamp);
LIB_FUNCTION("nEyFbYUloIM", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetShaderProgramBaseAddress);
LIB_FUNCTION("k7iGTvDQPLQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetShaderStatus);
LIB_FUNCTION("ln33zjBrfjk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetTheTessellationFactorRingBufferBaseAddress);
LIB_FUNCTION("jg33rEKLfVs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmIsUserPaEnabled);
LIB_FUNCTION("26PM5Mzl8zc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmLogicalCuIndexToPhysicalCuIndex);
LIB_FUNCTION("RU74kek-N0c", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmLogicalCuMaskToPhysicalCuMask);
LIB_FUNCTION("29oKvKXzEZo", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmMapComputeQueue);
LIB_FUNCTION("A+uGq+3KFtQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmMapComputeQueueWithPriority);
LIB_FUNCTION("u9YKpRRHe-M", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceRazorCaptureImmediate);
LIB_FUNCTION("gObODli-OH8", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestFlipAndSubmitDone);
LIB_FUNCTION("6YRHhh5mHCs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestFlipAndSubmitDoneForWorkload);
LIB_FUNCTION("f85orjx7qts", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestMipStatsReportAndReset);
}
};

View File

@ -4,55 +4,6 @@
namespace HLE::Libs::LibSceGnmDriver {
void LibSceGnmDriver_Register(SymbolsResolver* sym);
// functions
int sceGnmAddEqEvent(/* SceKernelEqueue eq, EqEventType id,*/ void* udata);
bool sceGnmAreSubmitsAllowed();
int /* WorkloadStatus*/ sceGnmBeginWorkload(uint64_t* workload /*, WorkloadStream stream*/);
int /* WorkloadStatus*/ sceGnmCreateWorkloadStream(/* WorkloadStream* workloadStream,*/ const char* name);
void sceGnmDebugHardwareStatus(/* HardwareStatus flag*/);
void sceGnmSetGsRingSizes(/* GsRingSizeSetup esgsRingSize, GsRingSizeSetup gsvsRingSize*/);
int32_t sceGnmSetWaveLimitMultipliers(uint16_t targetPipeMask, uint8_t gfxRatio, const uint8_t (*pipeRatios)[7]);
int /*MipStatsError*/ sceGnmSetupMipStatsReport(void* outputBuffer, uint32_t sizeInBytes, uint8_t intervalsBetweenReports,
uint8_t numReportsBeforeReset /*, MipStatsResetForce mipStatsResetForce*/);
int sceGnmSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
const uint32_t* ccb_sizes_in_bytes);
int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
const uint32_t* ccb_sizes_in_bytes, int handle, int index, int flip_mode, int64_t flip_arg);
void sceGnmDingDong(u32 ring_id, u32 offset_dw);
bool sceRazorIsLoaded();
int sceGnmDeleteEqEvent(/* SceKernelEqueue eq, EqEventType id*/);
int32_t sceGnmSubmitDone();
int /* MipStatsError*/ sceGnmDisableMipStatsReport();
int sceGnmSubmitAndFlipCommandBuffersForWorkload();
int sceGnmSubmitCommandBuffersForWorkload();
int /* WorkloadStatus*/ sceGnmDestroyWorkloadStream(/*WorkloadStream workloadStream*/);
void sceGnmDingDongForWorkload();
void sceGnmDriverCaptureInProgress();
void sceGnmUnmapComputeQueue();
void sceGnmDriverTraceInProgress();
void sceGnmDriverTriggerCapture();
void sceGnmEndWorkload();
void sceGnmFlushGarlic();
void sceGnmGetEqEventType();
void sceGnmGetEqTimeStamp();
void sceGnmGetGpuBlockStatus();
void sceGnmGetGpuInfoStatus();
void sceGnmGetLastWaitedAddress();
void sceGnmGetNumTcaUnits();
void sceGnmGetOffChipTessellationBufferSize();
void sceGnmGetPhysicalCounterFromVirtualized();
void sceGnmGetProtectionFaultTimeStamp();
void sceGnmGetShaderProgramBaseAddress();
void sceGnmGetShaderStatus();
void sceGnmGetTheTessellationFactorRingBufferBaseAddress();
void sceGnmIsUserPaEnabled();
void sceGnmLogicalCuIndexToPhysicalCuIndex();
void sceGnmLogicalCuMaskToPhysicalCuMask();
void sceGnmMapComputeQueue();
void sceGnmMapComputeQueueWithPriority();
void sceRazorCaptureImmediate();
void sceGnmRequestFlipAndSubmitDone();
void sceGnmRequestFlipAndSubmitDoneForWorkload();
void sceGnmRequestMipStatsReportAndReset();
}; // namespace HLE::Libs::LibSceGnmDriver

View File

@ -4,6 +4,9 @@
#include "LibKernel.h"
#include "LibSceGnmDriver.h"
#include <Core/PS4/HLE/Graphics/video_out.h>
#include "Emulator/HLE/Libraries/LibUserService/user_service.h"
#include "Emulator/HLE/Libraries/LibPad/pad.h"
#include <Emulator/HLE/Libraries/LibSystemService/system_service.h>
namespace HLE::Libs {
@ -12,5 +15,8 @@ void Init_HLE_Libs(SymbolsResolver *sym) {
LibKernel::LibKernel_Register(sym);
Graphics::VideoOut::videoOutRegisterLib(sym);
LibSceGnmDriver::LibSceGnmDriver_Register(sym);
Emulator::HLE::Libraries::LibUserService::libUserService_Register(sym);
Emulator::HLE::Libraries::LibPad::libPad_Register(sym);
Emulator::HLE::Libraries::LibSystemService::libSystemService_Register(sym);
}
} // namespace HLE::Libs

View File

@ -6,7 +6,7 @@
#include "Util/aerolib.h"
#include "Loader/SymbolsResolver.h"
#include "HLE/Kernel/ThreadManagement.h"
#include "Stubs.h"
constexpr bool debug_loader = true;
@ -70,7 +70,7 @@ static std::string encodeId(u64 nVal)
}
Module* Linker::LoadModule(const std::string& elf_name)
{
Lib::LockMutexGuard lock(m_mutex);
std::scoped_lock lock{m_mutex};
auto* m = new Module;
m->linker = this;
m->elf = new Elf;
@ -469,14 +469,18 @@ void Linker::LoadSymbols(Module* m)
//if st_value!=0 then it's export symbol
bool is_sym_export = sym->st_value != 0;
std::string nidName = "";
if (aerolib::symbolsMap.find(ids.at(0)) != aerolib::symbolsMap.end())
auto aeronid = aerolib::find_by_nid(ids.at(0).c_str());
if (aeronid != nullptr)
{
nidName = aerolib::symbolsMap.at(ids.at(0));
nidName = aeronid->name;
}
else
{
nidName = "UNK";
}
SymbolRes sym_r{};
sym_r.name = ids.at(0);
sym_r.nidName = nidName;
@ -591,7 +595,6 @@ void Linker::Relocate(Module* m)
}
}
void Linker::Resolve(const std::string& name, int Symtype, Module* m, SymbolRecord* return_info) {
auto ids = StringUtil::split_string(name, '#');
@ -618,8 +621,15 @@ void Linker::Resolve(const std::string& name, int Symtype, Module* m, SymbolReco
if (rec != nullptr) {
*return_info = *rec;
} else {
return_info->virtual_address = 0;
return_info->name = "Unresolved!!!";
auto aeronid = aerolib::find_by_nid(sr.name.c_str());
if (aeronid) {
return_info->name = aeronid->name;
return_info->virtual_address = GetStub(aeronid->nid);
} else {
return_info->virtual_address = GetStub(sr.name.c_str());
return_info->name = "Unknown !!!";
}
LOG_ERROR_IF(debug_loader, "Linker: Stub resolved {} as {} (lib: {}, mod: {}) \n", sr.name, return_info->name, library->name, module->name);
}
}
else

View File

@ -1,9 +1,9 @@
#pragma once
#include <vector>
#include <mutex>
#include "Loader/Elf.h"
#include "Loader/SymbolsResolver.h"
#include "Lib/Threads.h"
struct DynamicModuleInfo;
class Linker;
@ -130,5 +130,5 @@ public:
std::vector<Module*> m_modules;
SymbolsResolver* m_HLEsymbols = nullptr;
Lib::Mutex m_mutex;
std::mutex m_mutex;
};

View File

@ -206,8 +206,8 @@ bool Elf::isElfFile() const {
return false;
}
if (m_elf_header->e_type != ET_SCE_DYNEXEC && m_elf_header->e_type != ET_SCE_DYNAMIC) {
printf("ERROR:e_type expected 0xFE10 OR 0xFE18 is (%04x)\n", m_elf_header->e_type);
if (m_elf_header->e_type != ET_SCE_DYNEXEC&& m_elf_header->e_type != ET_SCE_DYNAMIC&& m_elf_header->e_type != ET_SCE_EXEC) {
printf("ERROR:e_type expected 0xFE10 OR 0xFE18 OR 0xfe00 is (%04x)\n", m_elf_header->e_type);
return false;
}

80
src/Core/PS4/Stubs.cpp Normal file
View File

@ -0,0 +1,80 @@
#include "Stubs.h"
#include "Util/aerolib.h"
#include "Util/log.h"
// Helper to provide stub implementations for missing functions
//
// This works by pre-compiling generic stub functions ("slots"), and then
// on lookup, setting up the nid_entry they are matched with
//
// If it runs out of stubs with name information, it will return
// a default implemetnation without function name details
// Up to 512, larger values lead to more resolve stub slots
// and to longer compile / CI times
//
// Must match STUBS_LIST define
#define MAX_STUBS 128
u64 UnresolvedStub() {
LOG_ERROR("Unresolved Stub: called, returning zero to {}\n", __builtin_return_address(0));
return 0;
}
static u64 UnknownStub() {
LOG_ERROR("Stub: Unknown (nid: Unknown) called, returning zero to {}\n", __builtin_return_address(0));
return 0;
}
static aerolib::nid_entry* stub_nids[MAX_STUBS];
static std::string stub_nids_unknown[MAX_STUBS];
template <int stub_index>
static u64 CommonStub() {
auto entry = stub_nids[stub_index];
if (entry) {
LOG_ERROR("Stub: {} (nid: {}) called, returning zero to {}\n", entry->name, entry->nid, __builtin_return_address(0));
} else {
LOG_ERROR("Stub: Unknown (nid: {}) called, returning zero to {}\n", stub_nids_unknown[stub_index], __builtin_return_address(0));
}
return 0;
}
static u32 UsedStubEntries;
#define XREP_1(x) \
&CommonStub<x>,
#define XREP_2(x) XREP_1(x) XREP_1(x + 1)
#define XREP_4(x) XREP_2(x) XREP_2(x + 2)
#define XREP_8(x) XREP_4(x) XREP_4(x + 4)
#define XREP_16(x) XREP_8(x) XREP_8(x + 8)
#define XREP_32(x) XREP_16(x) XREP_16(x + 16)
#define XREP_64(x) XREP_32(x) XREP_32(x + 32)
#define XREP_128(x) XREP_64(x) XREP_64(x + 64)
#define XREP_256(x) XREP_128(x) XREP_128(x + 128)
#define XREP_512(x) XREP_256(x) XREP_256(x + 256)
#define STUBS_LIST XREP_128(0)
static u64 (*stub_handlers[MAX_STUBS])() = {
STUBS_LIST
};
u64 GetStub(const char* nid) {
if (UsedStubEntries >= MAX_STUBS) {
return (u64)&UnknownStub;
}
auto entry = aerolib::find_by_nid(nid);
if (!entry) {
stub_nids_unknown[UsedStubEntries] = nid;
} else {
stub_nids[UsedStubEntries] = entry;
}
return (u64)stub_handlers[UsedStubEntries++];
}

4
src/Core/PS4/Stubs.h Normal file
View File

@ -0,0 +1,4 @@
#include "types.h"
u64 UnresolvedStub();
u64 GetStub(const char *nid);

View File

@ -0,0 +1,38 @@
#include "aerolib.h"
#include "types.h"
#include <string.h>
#include "Util/log.h"
namespace aerolib {
// Use a direct table here + binary search as contents are static
nid_entry nids[] = {
#define STUB(nid, name) \
{ nid, #name },
#include "aerolib.inl"
#undef STUB
};
nid_entry* find_by_nid(const char* nid) {
s64 l = 0;
s64 r = sizeof(nids) / sizeof(nids[0]) - 1;
while (l <= r) {
size_t m = l + (r - l) / 2;
int cmp = strcmp(nids[m].nid, nid);
if (cmp == 0)
return &nids[m];
else if (cmp < 0)
l = m + 1;
else
r = m - 1;
}
return nullptr;
}
} // namespace aerolib

File diff suppressed because it is too large Load Diff

11225
src/Core/PS4/Util/aerolib.inl Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,63 @@
#include "libc.h"
#include <debug.h>
#include <cmath>
#include <cstdlib>
#include <cstring>
namespace Emulator::HLE::Libraries::LibC {
PS4_SYSV_ABI int printf(VA_ARGS) {
VA_CTX(ctx);
return printf_ctx(&ctx);
}
int PS4_SYSV_ABI vsnprintf(char* s, size_t n, const char* format, VaList* arg) { return vsnprintf_ctx(s, n, format, arg); }
PS4_SYSV_ABI void exit(int code) { std::exit(code); }
PS4_SYSV_ABI int atexit(void (*func)()) {
int rt = std::atexit(func);
if (rt != 0) {
BREAKPOINT();
}
return rt;
}
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n) { return std::memcmp(s1, s2, n); }
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n) { return std::memcpy(dest, src, n); }
void* PS4_SYSV_ABI memset(void* s, int c, size_t n) { return std::memset(s, c, n); }
void* PS4_SYSV_ABI malloc(size_t size) { return std::malloc(size); }
void PS4_SYSV_ABI free(void* ptr) { std::free(ptr); }
int PS4_SYSV_ABI strcmp(const char* str1, const char* str2) { return std::strcmp(str1, str2); }
size_t PS4_SYSV_ABI strlen(const char* str) { return std::strlen(str); }
char* PS4_SYSV_ABI strncpy(char* dest, const char* src, size_t count) { return std::strncpy(dest, src, count); }
void* PS4_SYSV_ABI memmove(void* dest, const void* src, std::size_t count) { return std::memmove(dest, src, count); }
char* PS4_SYSV_ABI strcpy(char* dest, const char* src) { return std::strcpy(dest, src); }
char* PS4_SYSV_ABI strcat(char* dest, const char* src) { return std::strcat(dest, src); }
// math
float PS4_SYSV_ABI atan2f(float y, float x) { return std::atan2f(y, x); }
float PS4_SYSV_ABI acosf(float num) { return std::acosf(num); }
float PS4_SYSV_ABI tanf(float num) { return std::tanf(num); }
float PS4_SYSV_ABI asinf(float num) { return std::asinf(num); }
double PS4_SYSV_ABI pow(double base, double exponent) { return std::pow(base, exponent); }
double PS4_SYSV_ABI _Sin(double x) { return std::sin(x); }
}; // namespace Emulator::HLE::Libraries::LibC

View File

@ -0,0 +1,32 @@
#pragma once
#include <types.h>
#include "printf.h"
namespace Emulator::HLE::Libraries::LibC {
// HLE functions
PS4_SYSV_ABI int printf(VA_ARGS);
int PS4_SYSV_ABI vsnprintf(char* s, size_t n, const char* format, VaList* arg);
PS4_SYSV_ABI void exit(int code);
PS4_SYSV_ABI int atexit(void (*func)());
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n);
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n);
void* PS4_SYSV_ABI memset(void* s, int c, size_t n);
void* PS4_SYSV_ABI malloc(size_t size);
void PS4_SYSV_ABI free(void* ptr);
int PS4_SYSV_ABI strcmp(const char* str1, const char* str2);
size_t PS4_SYSV_ABI strlen(const char* str);
char* PS4_SYSV_ABI strncpy(char* dest, const char* src, size_t count);
void* PS4_SYSV_ABI memmove(void* dest, const void* src, std::size_t count);
char* PS4_SYSV_ABI strcpy(char* destination, const char* source);
char* PS4_SYSV_ABI strcat(char* dest, const char* src);
float PS4_SYSV_ABI atan2f(float y, float x);
float PS4_SYSV_ABI acosf(float num);
float PS4_SYSV_ABI tanf(float num);
float PS4_SYSV_ABI asinf(float num);
double PS4_SYSV_ABI pow(double base, double exponent);
double PS4_SYSV_ABI _Sin(double x);
} // namespace Emulator::HLE::Libraries::LibC

View File

@ -0,0 +1,147 @@
#include "libc_cxa.h"
#include "debug.h"
#include "Util/log.h"
// adapted from https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html
namespace Emulator::HLE::Libraries::LibC::Cxa {
constexpr bool log_file_cxa = true; // disable it to disable logging
// This file implements the __cxa_guard_* functions as defined at:
// http://www.codesourcery.com/public/cxx-abi/abi.html
//
// The goal of these functions is to support thread-safe, one-time
// initialization of function scope variables. The compiler will generate
// code like the following:
//
// if ( obj_guard.first_byte == 0 ) {
// if ( __cxa_guard_acquire(&obj_guard) ) {
// try {
// ... initialize the object ...;
// }
// catch (...) {
// __cxa_guard_abort(&obj_guard);
// throw;
// }
// ... queue object destructor with __cxa_atexit() ...;
// __cxa_guard_release(&obj_guard);
// }
// }
//
// Notes:
// ojb_guard is a 64-bytes in size and statically initialized to zero.
//
// Section 6.7 of the C++ Spec says "If control re-enters the declaration
// recursively while the object is being initialized, the behavior is
// undefined". This implementation calls abort().
//
// Note don't use function local statics to avoid use of cxa functions...
static pthread_mutex_t __guard_mutex;
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
static void makeRecusiveMutex() {
pthread_mutexattr_t recursiveMutexAttr;
pthread_mutexattr_init(&recursiveMutexAttr);
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
}
__attribute__((noinline)) static pthread_mutex_t* guard_mutex() {
pthread_once(&__once_control, &makeRecusiveMutex);
return &__guard_mutex;
}
// helper functions for getting/setting flags in guard_object
static bool initializerHasRun(u64* guard_object) { return (*((u08*)guard_object) != 0); }
static void setInitializerHasRun(u64* guard_object) { *((u08*)guard_object) = 1; }
static bool inUse(u64* guard_object) { return (((u08*)guard_object)[1] != 0); }
static void setInUse(u64* guard_object) { ((u08*)guard_object)[1] = 1; }
static void setNotInUse(u64* guard_object) { ((u08*)guard_object)[1] = 0; }
//
// Returns 1 if the caller needs to run the initializer and then either
// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned,
// then the initializer has already been run. This function blocks
// if another thread is currently running the initializer. This function
// aborts if called again on the same guard object without an intervening
// call to __cxa_guard_release() or __cxa_guard_abort().
//
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) {
// Double check that the initializer has not already been run
if (initializerHasRun(guard_object)) return 0;
// We now need to acquire a lock that allows only one thread
// to run the initializer. If a different thread calls
// __cxa_guard_acquire() with the same guard object, we want
// that thread to block until this thread is done running the
// initializer and calls __cxa_guard_release(). But if the same
// thread calls __cxa_guard_acquire() with the same guard object,
// we want to abort.
// To implement this we have one global pthread recursive mutex
// shared by all guard objects, but only one at a time.
int result = ::pthread_mutex_lock(guard_mutex());
if (result != 0) {
LOG_TRACE_IF(log_file_cxa, "__cxa_guard_acquire(): pthread_mutex_lock failed with {}\n",result);
}
// At this point all other threads will block in __cxa_guard_acquire()
// Check if another thread has completed initializer run
if (initializerHasRun(guard_object)) {
int result = ::pthread_mutex_unlock(guard_mutex());
if (result != 0) {
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result);
}
return 0;
}
// The pthread mutex is recursive to allow other lazy initialized
// function locals to be evaluated during evaluation of this one.
// But if the same thread can call __cxa_guard_acquire() on the
// *same* guard object again, we call abort();
if (inUse(guard_object)) {
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): initializer for function local static variable called enclosing function\n");
}
// mark this guard object as being in use
setInUse(guard_object);
// return non-zero to tell caller to run initializer
return 1;
}
//
// Sets the first byte of the guard_object to a non-zero value.
// Releases any locks acquired by __cxa_guard_acquire().
//
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object) {
// first mark initalizer as having been run, so
// other threads won't try to re-run it.
setInitializerHasRun(guard_object);
// release global mutex
int result = ::pthread_mutex_unlock(guard_mutex());
if (result != 0) {
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result);
}
}
//
// Releases any locks acquired by __cxa_guard_acquire().
//
void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object) {
int result = ::pthread_mutex_unlock(guard_mutex());
if (result != 0) {
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_abort(): pthread_mutex_unlock failed with {}\n",result);
}
// now reset state, so possible to try to initialize again
setNotInUse(guard_object);
}
} // namespace Emulator::HLE::Libraries::LibC::Cxa

View File

@ -0,0 +1,11 @@
#pragma once
#define _TIMESPEC_DEFINED
#include <pthread.h>
#include <types.h>
namespace Emulator::HLE::Libraries::LibC::Cxa {
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object);
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object);
void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object);
} // namespace Emulator::HLE::Libraries::LibC::Cxa

View File

@ -0,0 +1,702 @@
///////////////////////////////////////////////////////////////////////////////
// \author (c) Marco Paland (info@paland.com)
// 2014-2018, PALANDesign Hannover, Germany
//
// \license The MIT License (MIT)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
// embedded systems with a very limited resources.
// Use this instead of bloated standard/newlib printf.
// These routines are thread safe and reentrant!
//
///////////////////////////////////////////////////////////////////////////////
// Vita3K emulator project
// Copyright (C) 2023 Vita3K team
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
// copied from Vita3k project at 6/10/2023 (latest update 30/06/2023)
// modifications for adapting va_args parameters
#pragma once
#include <stdio.h>
#include <cstdarg>
#include <cstdbool>
#include <cstddef>
#include <cstdint>
#include <string>
#include "va_ctx.h"
namespace Emulator::HLE::Libraries::LibC {
// ntoa conversion buffer size, this must be big enough to hold
// one converted numeric number including padded zeros (dynamically created on stack)
// 32 byte is a good default
#define PRINTF_NTOA_BUFFER_SIZE 32U
// ftoa conversion buffer size, this must be big enough to hold
// one converted float number including padded zeros (dynamically created on stack)
// 32 byte is a good default
#define PRINTF_FTOA_BUFFER_SIZE 32U
// define this to support floating point (%f)
#define PRINTF_SUPPORT_FLOAT
// define this to support long long types (%llu or %p)
#define PRINTF_SUPPORT_LONG_LONG
// define this to support the ptrdiff_t type (%t)
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
#define PRINTF_SUPPORT_PTRDIFF_T
///////////////////////////////////////////////////////////////////////////////
// internal flag definitions
#define FLAGS_ZEROPAD (1U << 0U)
#define FLAGS_LEFT (1U << 1U)
#define FLAGS_PLUS (1U << 2U)
#define FLAGS_SPACE (1U << 3U)
#define FLAGS_HASH (1U << 4U)
#define FLAGS_UPPERCASE (1U << 5U)
#define FLAGS_CHAR (1U << 6U)
#define FLAGS_SHORT (1U << 7U)
#define FLAGS_LONG (1U << 8U)
#define FLAGS_LONG_LONG (1U << 9U)
#define FLAGS_PRECISION (1U << 10U)
#define FLAGS_WIDTH (1U << 11U)
// output function type
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
// wrapper (used as buffer) for output function type
typedef struct {
void (*fct)(char character, void* arg);
void* arg;
} out_fct_wrap_type;
// internal buffer output
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) {
if (idx < maxlen) {
((char*)buffer)[idx] = character;
}
}
// internal null output
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) {
(void)character;
(void)buffer;
(void)idx;
(void)maxlen;
}
// internal output function wrapper
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) {
(void)idx;
(void)maxlen;
// buffer is the output fct pointer
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
}
// internal strlen
// \return The length of the string (excluding the terminating 0)
static inline unsigned int _strlen(const char* str) {
const char* s;
for (s = str; *s; ++s)
;
return (unsigned int)(s - str);
}
// internal test if char is a digit (0-9)
// \return true if char is a digit
static inline bool _is_digit(char ch) { return (ch >= '0') && (ch <= '9'); }
// internal ASCII string to unsigned int conversion
static inline unsigned int _atoi(const char** str) {
unsigned int i = 0U;
while (_is_digit(**str)) {
i = i * 10U + (unsigned int)(*((*str)++) - '0');
}
return i;
}
// internal itoa format
static inline size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base,
unsigned int prec, unsigned int width, unsigned int flags) {
const size_t start_idx = idx;
// pad leading zeros
while (!(flags & FLAGS_LEFT) && (len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
// handle hash
if (flags & FLAGS_HASH) {
if (((len == prec) || (len == width)) && (len > 0U)) {
len--;
if ((base == 16U) && (len > 0U)) {
len--;
}
}
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'x';
}
if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
buf[len++] = 'X';
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
buf[len++] = '0';
}
}
// handle sign
if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
len--;
}
if (len < PRINTF_NTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
for (size_t i = 0U; i < len; i++) {
out(buf[len - i - 1U], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx - start_idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
// internal itoa for 'long' type
static inline size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base,
unsigned int prec, unsigned int width, unsigned int flags) {
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
// internal itoa for 'long long' type
#if defined(PRINTF_SUPPORT_LONG_LONG)
static inline size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative,
unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) {
char buf[PRINTF_NTOA_BUFFER_SIZE];
size_t len = 0U;
// write if precision != 0 and value is != 0
if (!(flags & FLAGS_PRECISION) || value) {
do {
const char digit = (char)(value % base);
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
value /= base;
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
}
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
}
#endif // PRINTF_SUPPORT_LONG_LONG
#if defined(PRINTF_SUPPORT_FLOAT)
static inline size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width,
unsigned int flags) {
char buf[PRINTF_FTOA_BUFFER_SIZE];
size_t len = 0U;
double diff = 0.0;
// if input is larger than thres_max, revert to exponential
const double thres_max = (double)0x7FFFFFFF;
// powers of 10
static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
// test for negative
bool negative = false;
if (value < 0) {
negative = true;
value = 0 - value;
}
// set default precision to 6, if not set explicitly
if (!(flags & FLAGS_PRECISION)) {
prec = 6U;
}
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
buf[len++] = '0';
prec--;
}
int whole = (int)value;
double tmp = (value - whole) * pow10[prec];
unsigned long frac = (unsigned long)tmp;
diff = tmp - frac;
if (diff > 0.5) {
++frac;
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
if (frac >= pow10[prec]) {
frac = 0;
++whole;
}
} else if ((diff == 0.5) && ((frac == 0U) || (frac & 1U))) {
// if halfway, round up if odd, OR if last digit is 0
++frac;
}
// TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to write code to replace this?
// Normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad
if (value > thres_max) {
return 0U;
}
if (prec == 0U) {
diff = value - (double)whole;
if (diff > 0.5) {
// greater than 0.5, round up, e.g. 1.6 -> 2
++whole;
} else if ((diff == 0.5) && (whole & 1)) {
// exactly 0.5 and ODD, then round up
// 1.5 -> 2, but 2.5 -> 2
++whole;
}
} else {
unsigned int count = prec;
// now do fractional part, as an unsigned number
while (len < PRINTF_FTOA_BUFFER_SIZE) {
--count;
buf[len++] = (char)(48U + (frac % 10U));
if (!(frac /= 10U)) {
break;
}
}
// add extra 0s
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
buf[len++] = '0';
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
// add decimal
buf[len++] = '.';
}
}
// do whole part, number is reversed
while (len < PRINTF_FTOA_BUFFER_SIZE) {
buf[len++] = (char)(48 + (whole % 10));
if (!(whole /= 10)) {
break;
}
}
// pad leading zeros
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
buf[len++] = '0';
}
// handle sign
if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
len--;
}
if (len < PRINTF_FTOA_BUFFER_SIZE) {
if (negative) {
buf[len++] = '-';
} else if (flags & FLAGS_PLUS) {
buf[len++] = '+'; // ignore the space if the '+' exists
} else if (flags & FLAGS_SPACE) {
buf[len++] = ' ';
}
}
// pad spaces up to given width
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
for (size_t i = len; i < width; i++) {
out(' ', buffer, idx++, maxlen);
}
}
// reverse string
for (size_t i = 0U; i < len; i++) {
out(buf[len - i - 1U], buffer, idx++, maxlen);
}
// append pad spaces up to given width
if (flags & FLAGS_LEFT) {
while (idx < width) {
out(' ', buffer, idx++, maxlen);
}
}
return idx;
}
#endif // PRINTF_SUPPORT_FLOAT
// internal vsnprintf
static inline int _vsnprintf(out_fct_type out, char* buffer, const char* format, VaList* va_list) {
unsigned int flags, width, precision, n;
size_t idx = 0U;
auto maxlen = static_cast<size_t>(-1);
if (!buffer) {
// use null output function
out = _out_null;
}
while (*format) {
// format specifier? %[flags][width][.precision][length]
if (*format != '%') {
// no
out(*format, buffer, idx++, maxlen);
format++;
continue;
} else {
// yes, evaluate it
format++;
}
// evaluate flags
flags = 0U;
do {
switch (*format) {
case '0':
flags |= FLAGS_ZEROPAD;
format++;
n = 1U;
break;
case '-':
flags |= FLAGS_LEFT;
format++;
n = 1U;
break;
case '+':
flags |= FLAGS_PLUS;
format++;
n = 1U;
break;
case ' ':
flags |= FLAGS_SPACE;
format++;
n = 1U;
break;
case '#':
flags |= FLAGS_HASH;
format++;
n = 1U;
break;
default: n = 0U; break;
}
} while (n);
// evaluate width field
width = 0U;
if (_is_digit(*format)) {
width = _atoi(&format);
} else if (*format == '*') {
const int w = vaArgInteger(va_list); // const int w = va.next<int>(cpu, mem);
if (w < 0) {
flags |= FLAGS_LEFT; // reverse padding
width = (unsigned int)-w;
} else {
width = (unsigned int)w;
}
format++;
}
// evaluate precision field
precision = 0U;
if (*format == '.') {
flags |= FLAGS_PRECISION;
format++;
if (_is_digit(*format)) {
precision = _atoi(&format);
} else if (*format == '*') {
precision = vaArgInteger(va_list); // precision = (unsigned int)va.next<int>(cpu, mem);
format++;
}
}
// evaluate length field
switch (*format) {
case 'l':
flags |= FLAGS_LONG;
format++;
if (*format == 'l') {
flags |= FLAGS_LONG_LONG;
format++;
}
break;
case 'h':
flags |= FLAGS_SHORT;
format++;
if (*format == 'h') {
flags |= FLAGS_CHAR;
format++;
}
break;
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
case 't':
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
#endif
case 'j':
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
case 'z':
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
format++;
break;
default: break;
}
// evaluate specifier
switch (*format) {
case 'd':
case 'i':
case 'u':
case 'x':
case 'X':
case 'o':
case 'b': {
// set the base
unsigned int base;
if (*format == 'x' || *format == 'X') {
base = 16U;
} else if (*format == 'o') {
base = 8U;
} else if (*format == 'b') {
base = 2U;
flags &= ~FLAGS_HASH; // no hash for bin format
} else {
base = 10U;
flags &= ~FLAGS_HASH; // no hash for dec format
}
// uppercase
if (*format == 'X') {
flags |= FLAGS_UPPERCASE;
}
// no plus or space flag for u, x, X, o, b
if ((*format != 'i') && (*format != 'd')) {
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
}
// convert the integer
if ((*format == 'i') || (*format == 'd')) {
// signed
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
auto value = vaArgLongLong(va_list); // const long long value = va.next<long long>(cpu, mem);
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base,
precision, width, flags);
#endif
} else if (flags & FLAGS_LONG) {
auto value = vaArgLong(va_list); // const long value = va.next<long>(cpu, mem);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width,
flags);
} else {
// const int value = (flags & FLAGS_CHAR) ? (char)va.next<int>(cpu, mem) : (flags & FLAGS_SHORT) ? (short
// int)va.next<int>(cpu, mem): va.next<int>(cpu, mem);
const int value = (flags & FLAGS_CHAR) ? static_cast<char>(vaArgInteger(va_list))
: (flags & FLAGS_SHORT) ? static_cast<int16_t>(vaArgInteger(va_list))
: vaArgInteger(va_list);
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width,
flags);
}
} else {
// unsigned
if (flags & FLAGS_LONG_LONG) {
#if defined(PRINTF_SUPPORT_LONG_LONG)
// idx = _ntoa_long_long(out, buffer, idx, maxlen, va.next<unsigned long long>(cpu, mem), false, base, precision, width,
// flags);
idx =
_ntoa_long_long(out, buffer, idx, maxlen, static_cast<u64>(vaArgLongLong(va_list)), false, base, precision, width, flags);
#endif
} else if (flags & FLAGS_LONG) {
// idx = _ntoa_long(out, buffer, idx, maxlen, va.next<unsigned long>(cpu, mem), false, base, precision, width, flags);
idx = _ntoa_long(out, buffer, idx, maxlen, static_cast<u32>(vaArgLong(va_list)), false, base, precision, width, flags);
} else {
// const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va.next<unsigned int>(cpu, mem) : (flags & FLAGS_SHORT) ?
// (unsigned short int)va.next<unsigned int>(cpu, mem) : va.next<unsigned int>(cpu, mem);
const unsigned int value = (flags & FLAGS_CHAR) ? static_cast<u08>(vaArgInteger(va_list))
: (flags & FLAGS_SHORT) ? static_cast<u16>(vaArgInteger(va_list))
: static_cast<u32>(vaArgInteger(va_list));
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
}
}
format++;
break;
}
#if defined(PRINTF_SUPPORT_FLOAT)
case 'f':
case 'F':
// idx = _ftoa(out, buffer, idx, maxlen, va.next<double>(cpu, mem), precision, width, flags);
idx = _ftoa(out, buffer, idx, maxlen, vaArgDouble(va_list), precision, width, flags);
format++;
break;
#endif // PRINTF_SUPPORT_FLOAT
case 'c': {
unsigned int l = 1U;
// pre padding
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// char output
// out((char)va.next<int>(cpu, mem), buffer, idx++, maxlen);
out(static_cast<char>(vaArgInteger(va_list)), buffer, idx++, maxlen);
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 's': {
const char* p = vaArgPtr<const char>(va_list); // const char *p = va.next<Ptr<char>>(cpu, mem).get(mem);
p = p != nullptr ? p : "(null)";
unsigned int l = _strlen(p);
// pre padding
if (flags & FLAGS_PRECISION) {
l = (l < precision ? l : precision);
}
if (!(flags & FLAGS_LEFT)) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
// string output
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
out(*(p++), buffer, idx++, maxlen);
}
// post padding
if (flags & FLAGS_LEFT) {
while (l++ < width) {
out(' ', buffer, idx++, maxlen);
}
}
format++;
break;
}
case 'p': {
width = sizeof(void*) * 2U;
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
#if defined(PRINTF_SUPPORT_LONG_LONG)
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
if (is_ll) {
// idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va.next<Ptr<void>>(cpu, mem).address(), false, 16U, precision,
// width, flags);
idx = _ntoa_long_long(out, buffer, idx, maxlen, reinterpret_cast<uintptr_t>(vaArgPtr<void>(va_list)), false, 16U, precision,
width, flags);
} else {
#endif
// idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va.next<Ptr<void>>(cpu, mem).address()), false, 16U,
// precision, width, flags);
idx = _ntoa_long(out, buffer, idx, maxlen, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(vaArgPtr<void>(va_list))), false,
16U, precision, width, flags);
#if defined(PRINTF_SUPPORT_LONG_LONG)
}
#endif
format++;
break;
}
case '%':
out('%', buffer, idx++, maxlen);
format++;
break;
default:
out(*format, buffer, idx++, maxlen);
format++;
break;
}
}
// termination
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return (int)idx;
}
static int printf_ctx(VaCtx* ctx) {
const char* format = vaArgPtr<const char>(&ctx->va_list);
char buffer[256];
int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list);
printf("%s", buffer);
return result;
}
static int vsnprintf_ctx(char* s, size_t n, const char* format, VaList* arg) {
char buffer[n];
int result = _vsnprintf(_out_buffer, buffer, format, arg);
std::strcpy(s, buffer);
return result;
}
} // namespace Emulator::HLE::Libraries::LibC

View File

@ -0,0 +1,106 @@
#include <types.h>
#include <xmmintrin.h>
#define VA_ARGS \
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, \
__m128 xmm2, __m128 xmm3, __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ...
#define VA_CTX(ctx) \
alignas(16) VaCtx ctx; \
(ctx).reg_save_area.gp[0] = rdi; \
(ctx).reg_save_area.gp[1] = rsi; \
(ctx).reg_save_area.gp[2] = rdx; \
(ctx).reg_save_area.gp[3] = rcx; \
(ctx).reg_save_area.gp[4] = r8; \
(ctx).reg_save_area.gp[5] = r9; \
(ctx).reg_save_area.fp[0] = xmm0; \
(ctx).reg_save_area.fp[1] = xmm1; \
(ctx).reg_save_area.fp[2] = xmm2; \
(ctx).reg_save_area.fp[3] = xmm3; \
(ctx).reg_save_area.fp[4] = xmm4; \
(ctx).reg_save_area.fp[5] = xmm5; \
(ctx).reg_save_area.fp[6] = xmm6; \
(ctx).reg_save_area.fp[7] = xmm7; \
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
(ctx).va_list.gp_offset = offsetof(VaRegSave, gp); \
(ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
namespace Emulator::HLE::Libraries::LibC {
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
struct VaList {
u32 gp_offset;
u32 fp_offset;
void* overflow_arg_area;
void* reg_save_area;
};
struct VaRegSave {
u64 gp[6];
__m128 fp[8];
};
struct VaCtx {
VaRegSave reg_save_area;
VaList va_list;
};
template <class T, uint32_t Size>
T vaArgRegSaveAreaGp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u08*>(l->reg_save_area) + l->gp_offset);
l->gp_offset += Size;
return *addr;
}
template <class T, u64 Align, u64 Size>
T vaArgOverflowArgArea(VaList* l) {
auto ptr = ((reinterpret_cast<u64>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
auto* addr = reinterpret_cast<T*>(ptr);
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
return *addr;
}
template <class T, uint32_t Size>
T vaArgRegSaveAreaFp(VaList* l) {
auto* addr = reinterpret_cast<T*>(static_cast<u08*>(l->reg_save_area) + l->fp_offset);
l->fp_offset += Size;
return *addr;
}
inline int vaArgInteger(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<int, 8>(l);
}
return vaArgOverflowArgArea<int, 1, 8>(l);
}
inline long long vaArgLongLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long long, 8>(l);
}
return vaArgOverflowArgArea<long long, 1, 8>(l);
}
inline long vaArgLong(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<long, 8>(l);
}
return vaArgOverflowArgArea<long, 1, 8>(l);
}
inline double vaArgDouble(VaList* l) {
if (l->fp_offset <= 160) {
return vaArgRegSaveAreaFp<double, 16>(l);
}
return vaArgOverflowArgArea<double, 1, 8>(l);
}
template <class T>
T* vaArgPtr(VaList* l) {
if (l->gp_offset <= 40) {
return vaArgRegSaveAreaGp<T*, 8>(l);
}
return vaArgOverflowArgArea<T*, 1, 8>(l);
}
} // namespace Emulator::HLE::Libraries::LibC

View File

@ -0,0 +1,13 @@
#include "file_system.h"
#include <debug.h>
#include <Util/log.h>
namespace Emulator::HLE::Libraries::LibKernel::FileSystem {
constexpr bool log_file_fs = true; // disable it to disable logging
int PS4_SYSV_ABI sceKernelOpen(const char* path, int flags, u16 mode) {
LOG_INFO_IF(log_file_fs, "sceKernelOpen path = {} flags = {} mode = {}\n", path, log_hex_full(flags), log_hex_full(mode));
return 0;
}
} // namespace Emulator::HLE::Libraries::LibKernel::FileSystem

View File

@ -0,0 +1,7 @@
#pragma once
#include <types.h>
namespace Emulator::HLE::Libraries::LibKernel::FileSystem {
int PS4_SYSV_ABI sceKernelOpen(const char *path, int flags, /* SceKernelMode*/ u16 mode);
}

View File

@ -0,0 +1,27 @@
#pragma once
#include <types.h>
#include <string>
namespace Emulator::Host::GenericFS {
enum FileAccess {
FILEACCESS_READ = 0,
FILEACCESS_WRITE = 1,
FILEACCESS_READWRITE = 2
};
class GenericHandleAllocator {
public:
virtual u32 requestHandle() = 0;
virtual void releaseHandle(u32 handle) = 0;
};
class AbstractFileSystem {
public:
virtual bool ownsHandle(u32 handle) = 0;
virtual u32 openFile(std::string filename, FileAccess access) = 0;
virtual void closeFile(u32 handle) = 0;
};
} // namespace Emulator::Host::GenericFS

View File

@ -0,0 +1,52 @@
#include "meta_file_system.h"
namespace Emulator::Host::GenericFS {
void MetaFileSystem::mount(std::string prefix, AbstractFileSystem* system) {
System x;
x.prefix = prefix;
x.system = system;
fileSystems.push_back(x);
}
void MetaFileSystem::unMount(AbstractFileSystem* system) {}
void MetaFileSystem::unMountAll() { fileSystems.clear(); }
AbstractFileSystem* MetaFileSystem::getHandleOwner(u32 handle) {
for (u32 i = 0; i < fileSystems.size(); i++) {
if (fileSystems[i].system->ownsHandle(handle)) return fileSystems[i].system; // got it!
}
return nullptr;
}
bool MetaFileSystem::mapFilePath(std::string inpath, std::string* outpath, AbstractFileSystem** system) {
for (unsigned int i = 0; i < fileSystems.size(); i++) {
int prefLen = fileSystems[i].prefix.size();
if (fileSystems[i].prefix == inpath.substr(0, prefLen))
{
*outpath = inpath.substr(prefLen);
*system = fileSystems[i].system;
return true;
}
}
return false;
}
u32 MetaFileSystem::openFile(std::string filename, FileAccess access) {
AbstractFileSystem* system;
std::string of;
if (mapFilePath(filename, &of, &system)) {
return system->openFile(of, access);
}
return 0;
}
void MetaFileSystem::closeFile(u32 handle) {
AbstractFileSystem* sys = getHandleOwner(handle);
if (sys) sys->closeFile(handle);
}
} // namespace Emulator::Host::GenericFS

View File

@ -0,0 +1,40 @@
#pragma once
#include <types.h>
#include <string>
#include <vector>
#include "generic_file_system.h"
namespace Emulator::Host::GenericFS {
class MetaFileSystem : public GenericHandleAllocator, AbstractFileSystem {
struct System {
std::string prefix;
AbstractFileSystem *system;
};
u32 current;
std::vector<System> fileSystems;
std::string currentDirectory;
std::vector<u32> handler;
public:
MetaFileSystem() : current(0) {}
void mount(std::string prefix, AbstractFileSystem *system);
void unMount(AbstractFileSystem *system);
void unMountAll();
AbstractFileSystem *getHandleOwner(u32 handle);
bool mapFilePath(std::string inpath, std::string *outpath, AbstractFileSystem **system);
u32 requestHandle() {
handler.push_back(current);
current++;
return handler.back();
}
void releaseHandle(u32 handle) { handler.erase(std::remove(handler.begin(), handler.end(), handle), handler.end()); }
bool ownsHandle(u32 handle) { return false; }
u32 openFile(std::string filename, FileAccess access);
void closeFile(u32 handle);
};
} // namespace Emulator::Host::GenericFS

View File

@ -0,0 +1,15 @@
#include "posix_file_system.h"
#include <debug.h>
#include "file_system.h"
namespace Emulator::HLE::Libraries::LibKernel::FileSystem::POSIX {
int PS4_SYSV_ABI open(const char* path, int flags, /* SceKernelMode*/ u16 mode) {
int result = sceKernelOpen(path, flags, mode);
if (result < 0) {
BREAKPOINT(); // posix calls different only for their return values
}
return result;
}
} // namespace Emulator::HLE::Libraries::LibKernel::FileSystem::POSIX

View File

@ -0,0 +1,6 @@
#pragma once
#include "types.h"
namespace Emulator::HLE::Libraries::LibKernel::FileSystem::POSIX {
int PS4_SYSV_ABI open(const char *path, int flags, /* SceKernelMode*/ u16 mode);
}

View File

@ -0,0 +1,55 @@
#include "pad.h"
#include <Core/PS4/HLE/ErrorCodes.h>
#include <Core/PS4/HLE/Libs.h>
#include "Emulator/Util/singleton.h"
#include "Emulator/Host/controller.h"
#include <debug.h>
#include <Util/log.h>
namespace Emulator::HLE::Libraries::LibPad {
constexpr bool log_file_pad = true; // disable it to disable logging
int PS4_SYSV_ABI scePadInit() { return SCE_OK; }
int PS4_SYSV_ABI scePadOpen(Emulator::HLE::Libraries::LibUserService::SceUserServiceUserId userId, s32 type, s32 index,
const ScePadOpenParam* pParam) {
return 1; // dummy
}
int PS4_SYSV_ABI scePadReadState(int32_t handle, ScePadData* pData) {
auto* controller = singleton<Emulator::Host::Controller::GameController>::instance();
int connectedCount = 0;
bool isConnected = false;
Emulator::Host::Controller::State state;
controller->readState(&state, &isConnected, &connectedCount);
pData->buttons = state.buttonsState;
pData->leftStick.x = 128; // dummy
pData->leftStick.y = 128; // dummy
pData->rightStick.x = 0; // dummy
pData->rightStick.y = 0; // dummy
pData->analogButtons.r2 = 0;//dummy
pData->analogButtons.l2 = 0;//dummy
pData->orientation.x = 0;
pData->orientation.y = 0;
pData->orientation.z = 0;
pData->orientation.w = 0;
pData->connected = true; // isConnected; //TODO fix me proper
pData->connectedCount = 1;//connectedCount;
pData->deviceUniqueDataLen = 0;
return SCE_OK;
}
void libPad_Register(SymbolsResolver* sym) {
LIB_FUNCTION("hv1luiJrqQM", "libScePad", 1, "libScePad", 1, 1, scePadInit);
LIB_FUNCTION("xk0AcarP3V4", "libScePad", 1, "libScePad", 1, 1, scePadOpen);
LIB_FUNCTION("YndgXqQVV7c", "libScePad", 1, "libScePad", 1, 1, scePadReadState);
}
} // namespace Emulator::HLE::Libraries::LibPad

View File

@ -0,0 +1,98 @@
#pragma once
#include <Emulator/HLE/Libraries/LibUserService/user_service.h>
#include <types.h>
#include "Core/PS4/Loader/SymbolsResolver.h"
namespace Emulator::HLE::Libraries::LibPad {
typedef enum : u32 {
SCE_PAD_BUTTON_L3 = 0x00000002,
SCE_PAD_BUTTON_R3 = 0x00000004,
SCE_PAD_BUTTON_OPTIONS = 0x00000008,
SCE_PAD_BUTTON_UP = 0x00000010,
SCE_PAD_BUTTON_RIGHT = 0x00000020,
SCE_PAD_BUTTON_DOWN = 0x00000040,
SCE_PAD_BUTTON_LEFT = 0x00000080,
SCE_PAD_BUTTON_L2 = 0x00000100,
SCE_PAD_BUTTON_R2 = 0x00000200,
SCE_PAD_BUTTON_L1 = 0x00000400,
SCE_PAD_BUTTON_R1 = 0x00000800,
SCE_PAD_BUTTON_TRIANGLE = 0x00001000,
SCE_PAD_BUTTON_CIRCLE = 0x00002000,
SCE_PAD_BUTTON_CROSS = 0x00004000,
SCE_PAD_BUTTON_SQUARE = 0x00008000,
SCE_PAD_BUTTON_TOUCH_PAD = 0x00100000,
SCE_PAD_BUTTON_INTERCEPTED = 0x80000000,
} ScePadButton;
struct ScePadOpenParam {
u08 reserve[8];
};
struct ScePadAnalogStick {
u08 x;
u08 y;
};
struct ScePadAnalogButtons {
u08 l2;
u08 r2;
u08 padding[2];
};
struct SceFQuaternion {
float x, y, z, w;
};
struct SceFVector3 {
float x, y, z;
};
struct ScePadTouch {
u16 x;
u16 y;
u08 id;
u08 reserve[3];
};
constexpr int SCE_PAD_MAX_TOUCH_NUM = 2;
typedef struct ScePadTouchData {
u08 touchNum;
u08 reserve[3];
u32 reserve1;
ScePadTouch touch[SCE_PAD_MAX_TOUCH_NUM];
} ScePadTouchData;
struct ScePadExtensionUnitData {
u32 extensionUnitId;
u08 reserve[1];
u08 dataLength;
u08 data[10];
};
struct ScePadData {
u32 buttons;
ScePadAnalogStick leftStick;
ScePadAnalogStick rightStick;
ScePadAnalogButtons analogButtons;
SceFQuaternion orientation;
SceFVector3 acceleration;
SceFVector3 angularVelocity;
ScePadTouchData touchData;
bool connected;
u64 timestamp;
ScePadExtensionUnitData extensionUnitData;
uint8_t connectedCount;
uint8_t reserve[2];
uint8_t deviceUniqueDataLen;
uint8_t deviceUniqueData[12];
};
// hle functions
int PS4_SYSV_ABI scePadInit();
int PS4_SYSV_ABI scePadOpen(Emulator::HLE::Libraries::LibUserService::SceUserServiceUserId userId, s32 type, s32 index,
const ScePadOpenParam* pParam);
int PS4_SYSV_ABI scePadReadState(int32_t handle, ScePadData* pData);
void libPad_Register(SymbolsResolver* sym);
}; // namespace Emulator::HLE::Libraries::LibPad

View File

@ -0,0 +1,16 @@
#include <Core/PS4/HLE/ErrorCodes.h>
#include <Core/PS4/HLE/Libs.h>
#include "system_service.h"
namespace Emulator::HLE::Libraries::LibSystemService {
s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen() {
// dummy
return SCE_OK;
}
void libSystemService_Register(SymbolsResolver* sym) {
LIB_FUNCTION("Vo5V8KAwCmk", "libSceSystemService", 1, "libSceSystemService", 1, 1, sceSystemServiceHideSplashScreen);
}
}; // namespace Emulator::HLE::Libraries::LibUserService

View File

@ -0,0 +1,11 @@
#pragma once
#include "Core/PS4/Loader/SymbolsResolver.h"
namespace Emulator::HLE::Libraries::LibSystemService {
//HLE functions
s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen();
void libSystemService_Register(SymbolsResolver* sym);
}; // namespace Emulator::HLE::Libraries::LibUserService

View File

@ -0,0 +1,26 @@
#include "user_service.h"
#include <Core/PS4/HLE/ErrorCodes.h>
#include <Core/PS4/HLE/Libs.h>
namespace Emulator::HLE::Libraries::LibUserService {
s32 PS4_SYSV_ABI sceUserServiceInitialize(const SceUserServiceInitializeParams* initParams) {
// dummy
return SCE_OK;
}
s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(SceUserServiceLoginUserIdList* userIdList) {
// dummy
userIdList->user_id[0] = 1;
userIdList->user_id[1] = -1;
userIdList->user_id[2] = -1;
userIdList->user_id[3] = -1;
return SCE_OK;
}
void libUserService_Register(SymbolsResolver* sym) {
LIB_FUNCTION("j3YMu1MVNNo", "libSceUserService", 1, "libSceUserService", 1, 1, sceUserServiceInitialize);
LIB_FUNCTION("fPhymKNvK-A", "libSceUserService", 1, "libSceUserService", 1, 1, sceUserServiceGetLoginUserIdList);
}
}; // namespace Emulator::HLE::Libraries::LibUserService

View File

@ -0,0 +1,20 @@
#pragma once
#include "Core/PS4/Loader/SymbolsResolver.h"
namespace Emulator::HLE::Libraries::LibUserService {
using SceUserServiceUserId = s32;
struct SceUserServiceInitializeParams {
s32 priority;
};
struct SceUserServiceLoginUserIdList {
int user_id[4];
};
s32 PS4_SYSV_ABI sceUserServiceInitialize(const SceUserServiceInitializeParams* initParams);
s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(SceUserServiceLoginUserIdList* userIdList);
void libUserService_Register(SymbolsResolver* sym);
}; // namespace Emulator::HLE::Libraries::LibUserService

View File

@ -0,0 +1,51 @@
#include "controller.h"
namespace Emulator::Host::Controller {
GameController::GameController() { m_states_num = 0;
m_last_state = State();
}
void GameController::readState(State* state, bool* isConnected, int* connectedCount) {
std::scoped_lock lock{m_mutex};
*isConnected = m_connected;
*connectedCount = m_connected_count;
*state = getLastState();
}
State GameController::getLastState() const {
if (m_states_num == 0) {
return m_last_state;
}
auto last = (m_first_state + m_states_num - 1) % MAX_STATES;
return m_states[last];
}
void GameController::addState(const State& state) {
if (m_states_num >= MAX_STATES) {
m_states_num = MAX_STATES - 1;
m_first_state = (m_first_state + 1) % MAX_STATES;
}
auto index = (m_first_state + m_states_num) % MAX_STATES;
m_states[index] = state;
m_last_state = state;
m_states_num++;
}
void GameController::checKButton(int id, u32 button, bool isPressed) {
std::scoped_lock lock{m_mutex};
auto state = getLastState();
if (isPressed) {
state.buttonsState |= button;
} else {
state.buttonsState &= ~button;
}
addState(state);
}
} // namespace Emulator::Host::Controller

View File

@ -0,0 +1,33 @@
#pragma once
#include <types.h>
#include <mutex>
namespace Emulator::Host::Controller {
struct State {
u32 buttonsState =0;
};
constexpr u32 MAX_STATES = 64;
class GameController {
public:
GameController();
virtual ~GameController() = default;
void readState(State* state, bool* isConnected, int* connectedCount);
State getLastState() const;
void checKButton(int id, u32 button, bool isPressed);
void addState(const State& state);
private:
std::mutex m_mutex;
bool m_connected = false;
State m_last_state;
int m_connected_count = 0;
u32 m_states_num = 0;
u32 m_first_state = 0;
State m_states[MAX_STATES];
};
} // namespace Emulator::HLE::Libraries::Controller

View File

@ -0,0 +1,24 @@
#pragma once
#include <cstdlib>
#include <new>
template <class T>
class singleton {
public:
static T* instance() {
if (!m_instance) {
m_instance = static_cast<T*>(std::malloc(sizeof(T)));
new (m_instance) T;
}
return m_instance;
}
protected:
singleton();
~singleton();
private:
static inline T* m_instance = nullptr;
};

View File

@ -1,79 +0,0 @@
#include "Threads.h"
#include <sstream>
static std::thread::id g_main_thread;
static int g_main_thread_int;
static std::atomic<int> g_thread_counter = 0;
void Lib::InitThreads() {
g_main_thread = std::this_thread::get_id();
g_main_thread_int = Lib::Thread::GetThreadIdUnique();
}
Lib::Thread::Thread(thread_func_t func, void* arg) {
m_thread = new ThreadStructInternal(func, arg);
while (!m_thread->started) {
SleepThreadMicro(1000);
}
}
Lib::Thread::~Thread() { delete m_thread; }
void Lib::Thread::JoinThread() { m_thread->m_thread.join(); }
void Lib::Thread::DetachThread() { m_thread->m_thread.detach(); }
std::string Lib::Thread::GetId() const {
std::stringstream ss;
ss << m_thread->m_thread.get_id();
return ss.str().c_str();
}
int Lib::Thread::GetUniqueId() const { return m_thread->unique_id; }
void Lib::Thread::SleepThread(u32 millis) { std::this_thread::sleep_for(std::chrono::milliseconds(millis)); }
void Lib::Thread::SleepThreadMicro(u32 micros) { std::this_thread::sleep_for(std::chrono::microseconds(micros)); }
void Lib::Thread::SleepThreadNano(u64 nanos) { std::this_thread::sleep_for(std::chrono::nanoseconds(nanos)); }
bool Lib::Thread::IsMainThread() { return g_main_thread == std::this_thread::get_id(); }
std::string Lib::Thread::GetThreadId() {
std::stringstream ss;
ss << std::this_thread::get_id();
return ss.str().c_str();
}
int Lib::Thread::GetThreadIdUnique() {
static thread_local int tid = ++g_thread_counter;
return tid;
}
Lib::Mutex::Mutex() { m_mutex = new MutexStructInternal(); }
Lib::Mutex::~Mutex() { delete m_mutex; }
void Lib::Mutex::LockMutex() { EnterCriticalSection(&m_mutex->m_cs); }
void Lib::Mutex::UnlockMutex() { LeaveCriticalSection(&m_mutex->m_cs); }
bool Lib::Mutex::TryLockMutex() { return (TryEnterCriticalSection(&m_mutex->m_cs) != 0); }
Lib::ConditionVariable::ConditionVariable() { m_cond_var = new ConditionVariableStructInternal(); }
Lib::ConditionVariable::~ConditionVariable() { delete m_cond_var; }
void Lib::ConditionVariable::WaitCondVar(Mutex* mutex) { SleepConditionVariableCS(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, INFINITE); }
bool Lib::ConditionVariable::WaitCondVarFor(Mutex* mutex, u32 micros) {
bool ok = false;
ok = !(SleepConditionVariableCS(&m_cond_var->m_cv, &mutex->m_mutex->m_cs, (micros < 1000 ? 1 : micros / 1000)) == 0 &&
GetLastError() == ERROR_TIMEOUT);
return ok;
}
void Lib::ConditionVariable::SignalCondVar() { WakeConditionVariable(&m_cond_var->m_cv); }
void Lib::ConditionVariable::SignalCondVarAll() { WakeAllConditionVariable(&m_cond_var->m_cv); }

View File

@ -1,131 +0,0 @@
#pragma once
#include "windows.h"
#include <synchapi.h>
#include <atomic>
#include <condition_variable>
#include <string>
#include <thread>
#include "../types.h"
namespace Lib {
using thread_func_t = void (*)(void*);
void InitThreads();
struct ThreadStructInternal;
struct MutexStructInternal;
struct ConditionVariableStructInternal;
class Thread {
public:
Thread(thread_func_t func, void* arg);
virtual ~Thread();
void JoinThread();
void DetachThread();
std::string GetId() const;
int GetUniqueId() const;
static void SleepThread(u32 millis);
static void SleepThreadMicro(u32 micros);
static void SleepThreadNano(u64 nanos);
static bool IsMainThread();
// Get current thread id (reusable id)
static std::string GetThreadId();
// Get current thread id (unique id)
static int GetThreadIdUnique();
public:
Thread(const Thread&) = delete;
Thread& operator=(const Thread&) = delete;
Thread(Thread&&) = delete;
Thread& operator=(Thread&&) = delete;
private:
ThreadStructInternal* m_thread;
};
struct ThreadStructInternal {
ThreadStructInternal(thread_func_t f, void* a) : func(f), arg(a), m_thread(&Run, this) {}
static void Run(ThreadStructInternal* t) {
t->unique_id = Lib::Thread::GetThreadIdUnique();
t->started = true;
t->func(t->arg);
}
thread_func_t func;
void* arg;
std::atomic_bool started = false;
int unique_id = 0;
std::thread m_thread;
};
class Mutex {
public:
Mutex();
virtual ~Mutex();
void LockMutex();
void UnlockMutex();
bool TryLockMutex();
friend class ConditionVariable;
public:
Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;
Mutex(Mutex&&) = delete;
Mutex& operator=(Mutex&&) = delete;
private:
MutexStructInternal* m_mutex;
};
struct MutexStructInternal {
MutexStructInternal() { InitializeCriticalSectionAndSpinCount(&m_cs, 4000); }
~MutexStructInternal() { DeleteCriticalSection(&m_cs); }
CRITICAL_SECTION m_cs{};
};
class ConditionVariable {
public:
ConditionVariable();
virtual ~ConditionVariable();
void WaitCondVar(Mutex* mutex);
bool WaitCondVarFor(Mutex* mutex, u32 micros);
void SignalCondVar();
void SignalCondVarAll();
private:
ConditionVariableStructInternal* m_cond_var;
};
struct ConditionVariableStructInternal {
ConditionVariableStructInternal() { InitializeConditionVariable(&m_cv); }
~ConditionVariableStructInternal() = default;
CONDITION_VARIABLE m_cv{};
};
class LockMutexGuard {
public:
explicit LockMutexGuard(Mutex& m) : m_mutex(m) { m_mutex.LockMutex(); }
~LockMutexGuard() { m_mutex.UnlockMutex(); }
private:
Mutex& m_mutex;
public:
LockMutexGuard(const LockMutexGuard&) = delete;
LockMutexGuard& operator=(const LockMutexGuard&) = delete;
LockMutexGuard(LockMutexGuard&&) noexcept = delete;
LockMutexGuard& operator=(LockMutexGuard&&) noexcept = delete;
};
} // namespace Lib

View File

@ -1,103 +0,0 @@
#include "Timer.h"
#ifdef _WIN64
#include <windows.h>
#endif
Lib::Timer::Timer() {
#ifdef _WIN64
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
m_Frequency = f.QuadPart;
#else
#error Unimplemented Timer constructor
#endif
}
void Lib::Timer::Start() {
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
m_StartTime = c.QuadPart;
#else
#error Unimplemented Timer::Start()
#endif
m_is_timer_paused = false;
}
void Lib::Timer::Pause() {
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
m_PauseTime = c.QuadPart;
#else
#error Unimplemented Timer::Pause()
#endif
m_is_timer_paused = true;
}
void Lib::Timer::Resume() {
u64 current_time = 0;
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
current_time = c.QuadPart;
#else
#error Unimplemented Timer::Resume()
#endif
m_StartTime += current_time - m_PauseTime;
m_is_timer_paused = false;
}
bool Lib::Timer::IsPaused() const { return m_is_timer_paused; }
double Lib::Timer::GetTimeMsec() const {
if (m_is_timer_paused) {
return 1000.0 * (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
u64 current_time = 0;
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
current_time = c.QuadPart;
#else
#error Unimplemented Timer::GetTimeMsec()
#endif
return 1000.0 * (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
double Lib::Timer::GetTimeSec() const {
if (m_is_timer_paused) {
return (static_cast<double>(m_PauseTime - m_StartTime)) / static_cast<double>(m_Frequency);
}
u64 current_time = 0;
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
current_time = c.QuadPart;
#else
#error Unimplemented Timer::GetTimeSec()
#endif
return (static_cast<double>(current_time - m_StartTime)) / static_cast<double>(m_Frequency);
}
u64 Lib::Timer::GetTicks() const {
if (m_is_timer_paused) {
return (m_PauseTime - m_StartTime);
}
u64 current_time = 0;
#ifdef _WIN64
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
current_time = c.QuadPart;
#else
#error Unimplemented Timer::GetTicks()
#endif
return (current_time - m_StartTime);
}
u64 Lib::Timer::GetFrequency() const { return m_Frequency; }

View File

@ -1,34 +0,0 @@
#pragma once
#include "../types.h"
namespace Lib {
class Timer final
{
public:
Timer();
~Timer() = default;
void Start();
void Pause();
void Resume();
bool IsPaused() const;
double GetTimeMsec() const;// return time in milliseconds
double GetTimeSec() const;// return time in seconds
u64 GetTicks() const;// return time in ticks
u64 GetFrequency() const;// return ticks frequency
public:
Timer(const Timer&) = delete;
Timer& operator=(const Timer&) = delete;
Timer(Timer&&) = delete;
Timer& operator=(Timer&&) = delete;
private:
bool m_is_timer_paused = true;
u64 m_Frequency = 0;
u64 m_StartTime = 0;
u64 m_PauseTime = 0;
};
}

View File

@ -1,27 +0,0 @@
#pragma once
#include <cstdlib>
#include <new>
template <class T>
class Singleton
{
public:
static T* Instance()
{
if (!m_instance)
{
m_instance = static_cast<T*>(std::malloc(sizeof(T)));
new (m_instance) T;
}
return m_instance;
}
protected:
Singleton();
~Singleton();
private:
static inline T* m_instance = nullptr;
};

View File

@ -1,29 +1,31 @@
#include "emulator.h"
#include <Core/PS4/HLE/Graphics/graphics_render.h>
#include <Util/Singleton.h>
#include <Emulator/Host/controller.h>
#include "Emulator/Util/singleton.h"
#include <vulkan_util.h>
#include "Core/PS4/HLE/Graphics/video_out.h"
#include "Emulator/HLE/Libraries/LibPad/pad.h"
#include "version.h"
namespace Emulator {
namespace Emu {
bool m_emu_needs_exit = false;
void emuInit(u32 width, u32 height) {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
window_ctx->m_graphic_ctx.screen_width = width;
window_ctx->m_graphic_ctx.screen_height = height;
}
void checkAndWaitForGraphicsInit() {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
Lib::LockMutexGuard lock(window_ctx->m_mutex);
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
std::unique_lock lock{window_ctx->m_mutex};
while (!window_ctx->m_is_graphic_initialized) {
window_ctx->m_graphic_initialized_cond.WaitCondVar(&window_ctx->m_mutex);
window_ctx->m_graphic_initialized_cond.wait(lock);
}
}
static void CreateSdlWindow(WindowCtx* ctx) {
@ -48,16 +50,15 @@ static void CreateSdlWindow(WindowCtx* ctx) {
SDL_SetWindowResizable(ctx->m_window, SDL_FALSE); // we don't support resizable atm
}
void emuRun() {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
window_ctx->m_mutex.LockMutex();
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
{
// init window and wait until init finishes
std::scoped_lock lock{window_ctx->m_mutex};
CreateSdlWindow(window_ctx);
Graphics::Vulkan::vulkanCreate(window_ctx);
window_ctx->m_is_graphic_initialized = true;
window_ctx->m_graphic_initialized_cond.SignalCondVar();
window_ctx->m_graphic_initialized_cond.notify_one();
}
window_ctx->m_mutex.UnlockMutex();
bool exit_loop = false;
@ -82,7 +83,7 @@ void emuRun() {
case SDL_EVENT_DID_ENTER_FOREGROUND: break;
case SDL_EVENT_KEY_DOWN:
case SDL_EVENT_KEY_UP: break;
case SDL_EVENT_KEY_UP: keyboardEvent(&event); break;
}
continue;
}
@ -96,14 +97,13 @@ void emuRun() {
}
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
Lib::LockMutexGuard lock(window_ctx->m_mutex);
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
std::scoped_lock lock{window_ctx->m_mutex};
return &window_ctx->m_graphic_ctx;
}
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
if (window_ctx->is_window_hidden) {
SDL_ShowWindow(window_ctx->m_window);
window_ctx->is_window_hidden = false;
@ -198,4 +198,29 @@ void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) {
}
}
} // namespace Emulator
void keyboardEvent(SDL_Event* event) {
using Emulator::HLE::Libraries::LibPad::ScePadButton;
if (event->type == SDL_EVENT_KEY_DOWN || event->type == SDL_EVENT_KEY_UP) {
u32 button = 0;
switch (event->key.keysym.sym) {
case SDLK_UP: button = ScePadButton::SCE_PAD_BUTTON_UP; break;
case SDLK_DOWN: button = ScePadButton::SCE_PAD_BUTTON_DOWN; break;
case SDLK_LEFT: button = ScePadButton::SCE_PAD_BUTTON_LEFT; break;
case SDLK_RIGHT: button = ScePadButton::SCE_PAD_BUTTON_RIGHT; break;
case SDLK_KP_8: button = ScePadButton ::SCE_PAD_BUTTON_TRIANGLE; break;
case SDLK_KP_6: button = ScePadButton ::SCE_PAD_BUTTON_CIRCLE; break;
case SDLK_KP_2: button = ScePadButton ::SCE_PAD_BUTTON_CROSS; break;
case SDLK_KP_4: button = ScePadButton ::SCE_PAD_BUTTON_SQUARE; break;
case SDLK_RETURN: button = ScePadButton ::SCE_PAD_BUTTON_OPTIONS; break;
default: break;
}
if (button != 0) {
auto* controller = singleton<Emulator::Host::Controller::GameController>::instance();
controller->checKButton(0, button, event->type == SDL_EVENT_KEY_DOWN);
}
}
}
} // namespace Emu

View File

@ -1,11 +1,13 @@
#pragma once
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
#include <Lib/Threads.h>
#include <SDL.h>
#include <mutex>
#include <condition_variable>
#include <vector>
namespace Emulator {
namespace Emu {
struct VulkanExt {
bool enable_validation_layers = false;
@ -57,9 +59,9 @@ struct VulkanSwapchain {
struct WindowCtx {
HLE::Libs::Graphics::GraphicCtx m_graphic_ctx;
Lib::Mutex m_mutex;
std::mutex m_mutex;
bool m_is_graphic_initialized = false;
Lib::ConditionVariable m_graphic_initialized_cond;
std::condition_variable m_graphic_initialized_cond;
SDL_Window* m_window = nullptr;
bool is_window_hidden = true;
VkSurfaceKHR m_surface = nullptr;
@ -69,7 +71,7 @@ struct WindowCtx {
struct EmuPrivate {
EmuPrivate() = default;
Lib::Mutex m_mutex;
std::mutex m_mutex;
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
void* data1 = nullptr;
void* data2 = nullptr;
@ -81,4 +83,5 @@ void emuRun();
void checkAndWaitForGraphicsInit();
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx();
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image);
void keyboardEvent(SDL_Event* event);
} // namespace Emulator

View File

@ -3,41 +3,43 @@
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
// Read online: https://github.com/ocornut/imgui/tree/master/docs
#include "imgui.h"
#include "imgui_impl_sdl3.h"
#include "imgui_impl_opengl3.h"
#include <stdio.h>
#include <SDL3/SDL.h>
#include <stdio.h>
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "imgui_impl_sdl3.h"
#if defined(IMGUI_IMPL_OPENGL_ES2)
#include <SDL3/SDL_opengles2.h>
#else
#include <SDL3/SDL_opengl.h>
#endif
#include <Util/log.h>
#include "GUI/ElfViewer.h"
#include "spdlog/spdlog.h"
#include "types.h"
#include "GUI/ElfViewer.h"
#include <Util/log.h>
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
#ifdef __EMSCRIPTEN__
#include "../libs/emscripten/emscripten_mainloop_stub.h"
#endif
#include "Core/PS4/Linker.h"
#include "Util/Singleton.h"
#include <stdio.h>
#include <inttypes.h>
#include <Zydis/Zydis.h>
#include "Core/PS4/HLE/Libs.h"
#include "Lib/Threads.h"
#include <emulator.h>
#include "discord.h"
#include <Util/config.h>
#include <Core/PS4/HLE/Graphics/video_out.h>
#include <Util/config.h>
#include <Zydis/Zydis.h>
#include <emulator.h>
#include <inttypes.h>
#include <stdio.h>
#include <thread>
#include "Core/PS4/HLE/Libs.h"
#include "Core/PS4/Linker.h"
#include "Emulator/Util\singleton.h"
#include "discord.h"
// Main code
int main(int argc, char* argv[])
{
int main(int argc, char* argv[]) {
if (argc == 1) {
printf("Usage: %s <elf or eboot.bin path>\n", argv[0]);
return -1;
@ -46,30 +48,24 @@ int main(int argc, char* argv[])
logging::init(true); // init logging
auto width = Config::getScreenWidth();
auto height = Config::getScreenHeight();
Emulator::emuInit(width, height);
Emu::emuInit(width, height);
HLE::Libs::Graphics::VideoOut::videoOutInit(width, height);
Lib::InitThreads();
const char* const path = argv[1]; // argument 1 is the path of self file to boot
auto* linker = Singleton<Linker>::Instance();
auto* linker = singleton<Linker>::instance();
HLE::Libs::Init_HLE_Libs(linker->getHLESymbols());
auto *module =linker->LoadModule(path);//load main executable
Lib::Thread mainthread(
[](void*) {
auto* linker = Singleton<Linker>::Instance();
auto* module = linker->LoadModule(path); // load main executable
std::jthread mainthread(
[](std::stop_token stop_token, void*) {
auto* linker = singleton<Linker>::instance();
linker->Execute();
},
nullptr);
mainthread.DetachThread();
Discord::RPC discordRPC;
discordRPC.init();
discordRPC.update(Discord::RPCStatus::Idling, "");
Emulator::emuRun();
mainthread.JoinThread();
Emu::emuRun();
#if 0
// Setup SDL

View File

@ -3,5 +3,5 @@
#include <string_view>
namespace Emulator {
constexpr char VERSION[] = "0.0.1";
constexpr char VERSION[] = "0.0.3 WIP";
}

View File

@ -2,7 +2,7 @@
#include <Core/PS4/GPU/gpu_memory.h>
#include <SDL_vulkan.h>
#include <Util/Singleton.h>
#include <Emulator/Util/singleton.h>
#include <Util/log.h>
#include <debug.h>
#include <vulkan/vk_enum_string_helper.h>
@ -12,8 +12,8 @@
constexpr bool log_file_vulkanutil = true; // disable it to disable logging
void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
Emulator::VulkanExt ext;
void Graphics::Vulkan::vulkanCreate(Emu::WindowCtx* ctx) {
Emu::VulkanExt ext;
vulkanGetInstanceExtensions(&ext);
VkApplicationInfo app_info{};
@ -53,8 +53,8 @@ void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
std::vector<const char*> device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, "VK_KHR_maintenance1"};
ctx->m_surface_capabilities = new Emulator::VulkanSurfaceCapabilities{};
Emulator::VulkanQueues queues;
ctx->m_surface_capabilities = new Emu::VulkanSurfaceCapabilities{};
Emu::VulkanQueues queues;
vulkanFindCompatiblePhysicalDevice(ctx->m_graphic_ctx.m_instance, ctx->m_surface, device_extensions, ctx->m_surface_capabilities,
&ctx->m_graphic_ctx.m_physical_device, &queues);
@ -79,11 +79,9 @@ void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
ctx->swapchain = vulkanCreateSwapchain(&ctx->m_graphic_ctx, 2);
}
Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
Lib::LockMutexGuard lock(window_ctx->m_mutex);
auto* s = new Emulator::VulkanSwapchain;
Emu::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
auto* s = new Emu::VulkanSwapchain;
VkExtent2D extent{};
extent.width = clamp(ctx->screen_width, window_ctx->m_surface_capabilities->capabilities.minImageExtent.width,
@ -183,13 +181,13 @@ Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Gr
return s;
}
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues) {
auto get_queue = [ctx](int id, const Emulator::VulkanQueueInfo& info, bool with_mutex = false) {
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues) {
auto get_queue = [ctx](int id, const Emu::VulkanQueueInfo& info, bool with_mutex = false) {
ctx->queues[id].family = info.family;
ctx->queues[id].index = info.index;
vkGetDeviceQueue(ctx->m_device, ctx->queues[id].family, ctx->queues[id].index, &ctx->queues[id].vk_queue);
if (with_mutex) {
ctx->queues[id].mutex = new Lib::Mutex;
ctx->queues[id].mutex = new std::mutex;
}
};
@ -203,8 +201,8 @@ void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx,
}
}
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
std::vector<VkDeviceQueueCreateInfo> queue_create_info(queues.family_count);
std::vector<std::vector<float>> queue_priority(queues.family_count);
uint32_t queue_create_info_num = 0;
@ -247,7 +245,7 @@ VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device,
return device;
}
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emu::VulkanExt* ext) {
u32 required_extensions_count = 0;
u32 available_extensions_count = 0;
u32 available_layers_count = 0;
@ -283,8 +281,8 @@ void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface,
const std::vector<const char*>& device_extensions,
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
Emulator::VulkanQueues* out_queues) {
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
Emu::VulkanQueues* out_queues) {
u32 count_devices = 0;
vkEnumeratePhysicalDevices(instance, &count_devices, nullptr);
@ -292,7 +290,7 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
vkEnumeratePhysicalDevices(instance, &count_devices, devices.data());
VkPhysicalDevice found_best_device = nullptr;
Emulator::VulkanQueues found_best_queues;
Emu::VulkanQueues found_best_queues;
for (const auto& device : devices) {
VkPhysicalDeviceProperties device_properties{};
@ -316,8 +314,8 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
*out_queues = found_best_queues;
}
Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
Emulator::VulkanQueues qs;
Emu::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
Emu::VulkanQueues qs;
u32 queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
@ -334,7 +332,7 @@ Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice devic
LOG_INFO_IF(log_file_vulkanutil, "queue family: {}, count = {}, present = {}\n", string_VkQueueFlags(f.queueFlags).c_str(), f.queueCount,
(presentation_supported == VK_TRUE ? "true" : "false"));
for (uint32_t i = 0; i < f.queueCount; i++) {
Emulator::VulkanQueueInfo info;
Emu::VulkanQueueInfo info;
info.family = family;
info.index = i;
info.is_graphics = (f.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0;
@ -401,7 +399,7 @@ Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice devic
}
void Graphics::Vulkan::vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
Emulator::VulkanSurfaceCapabilities* surfaceCap) {
Emu::VulkanSurfaceCapabilities* surfaceCap) {
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfaceCap->capabilities);
uint32_t formats_count = 0;
@ -488,7 +486,7 @@ static void set_image_layout(VkCommandBuffer buffer, HLE::Libs::Graphics::Vulkan
}
void Graphics::Vulkan::vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image,
Emulator::VulkanSwapchain* dst_swapchain) {
Emu::VulkanSwapchain* dst_swapchain) {
auto* vk_buffer = buffer->getPool()->buffers[buffer->getIndex()];
HLE::Libs::Graphics::VulkanImage swapchain_image(HLE::Libs::Graphics::VulkanImageType::Unknown);

View File

@ -26,18 +26,18 @@ const T& clamp(const T& x, const T& min, const T& max) {
return x;
}
void vulkanCreate(Emulator::WindowCtx* ctx);
void vulkanGetInstanceExtensions(Emulator::VulkanExt* ext);
void vulkanCreate(Emu::WindowCtx* ctx);
void vulkanGetInstanceExtensions(Emu::VulkanExt* ext);
void vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const std::vector<const char*>& device_extensions,
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
Emulator::VulkanQueues* out_queues);
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
Emulator::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emulator::VulkanSurfaceCapabilities* surfaceCap);
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues);
Emulator::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
void vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image, Emulator::VulkanSwapchain* dst_swapchain);
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
Emu::VulkanQueues* out_queues);
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
Emu::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emu::VulkanSurfaceCapabilities* surfaceCap);
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues);
Emu::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
void vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image, Emu::VulkanSwapchain* dst_swapchain);
void vulkanFillImage(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanImage* dst_image, const void* src_data, u64 size, u32 src_pitch,
u64 dst_layout);
void vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,

201
third-party/result/LICENSE vendored Normal file
View File

@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {2016} {Mathieu Stefani}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

132
third-party/result/README.md vendored Normal file
View File

@ -0,0 +1,132 @@
# Result
This is an adaption of [https://github.com/oktal/result](https://github.com/oktal/result). Make sure to support the original library!
## Overview
`Result<T, E>` is a template type that can be used to return and propage errors. It can be used to replace
exceptions in context where they are not allowed or too slow to be used. `Result<T, E>` is an algebraic data
type of `Ok(T)` that represents success and `Err(E)` representing an error.
Design of this class has been mainly inspired by Rust's [std::result](https://doc.rust-lang.org/std/result/)
```
struct Request {
};
struct Error {
enum class Kind {
Timeout,
Invalid,
TooLong
}
Error(Kind kind, std::string text);
Kind kind;
std::string text;
};
Result<Request, Error> parseRequest(const std::string& payload) {
if (payload.size() > 512) return Err(Error(Kind::TooLong, "Request exceeded maximum allowed size (512 bytes)"));
Request request;
return Ok(request);
}
std::string payload = receivePayload();
auto request = parseRequest(payload).expect("Failed to parse request");
```
To return a successfull `Result`, use the `Ok()` function. To return an error one, use the `Err()` function.
## Extract and unwrap
To extract the value from a `Result<T, E>` type, you can use the `expect()` function that will yield the value
of an `Ok(T)` or terminate the program with an error message passed as a parameter.
```
Result<uint32_t, uint32_t> r1 = Ok(3u);
auto val = r1.expect("Failed to retrieve the value");
assert(val == 3);
```
`unwrap()` can also be used to extract the value of a `Result`, yielding the value of an `Ok(T)` value or terminating
the program otherwise:
```
Result<uint32_t, uint32_t> r1 = Ok(3u);
auto val = r1.unwrap();
assert(val == 3);
```
Instead a terminating the program, `unwrapOr` can be used to return a default value for an `Err(E)` Result:
```
Result<uint32_t, uint32_t> r1 = Err(9u);
auto val = r1.unwrapOr(0);
assert(val == 0);
```
## Map and bind
To transform (or map) a `Result<T, E>` to a `Result<U, E>`, `Result` provides a `map` member function.
`map` will apply a function to a contained `Ok(T)` value and will return the result of the transformation,
and will leave an `Err(E)` untouched:
```
std::string stringify(int val) { return std::to_string(val); }
Result<uint32_t, uint32_t> r1 = Ok(2u);
auto r2 = r1.map(stringify); // Maps a Result<uint32_t, uint32_t> to Result<std::string, uint32_t>
assert(r2.unwrap(), "2");
```
Note that `map` should return a simple value and not a `Result<U, E>`. A function returning nothing (`void`)
applied to a `Result<T, E>` will yield a `Result<void, E>`.
To map a function to a contained `Err(E)` value, use the `mapError` function.
To *bind* a `Result<T, E>` to a `Result<U, E>`, you can use the `andThen` member function:
```
Result<uint32_t, uint32_t> square(uint32_t val) { return Ok(val * val); }
Result<uint32_t, uint32_t> r1 = Ok(3u);
auto r2 = r1.andThen(square);
assert(r2.unwrap(), 9);
```
Use `orElse` to apply a function to a contained `Err(E)` value:
```
Result<uint32_t, uint32_t> identity(uint32_t val) { return Ok(val); }
Result<uint32_t, uint32_t> r1 = Err(3u);
assert(r1.andThen(identity).orElse(square).unwrap(), 9);
```
## The TRY macro
Like Rust, a `TRY` macro is also provided that comes in handy when writing code that calls a lot of functions returning a `Result`.
the `TRY` macro will simply call its argument and short-cirtcuit the function returning an `Err(E)` if the operation returned an error `Result`:
```
Result<void, IoError> copy(int srcFd, const char* dstFile) {
auto fd = TRY(open(dstFile));
auto data = TRY(read(srcFd));
TRY(write(fd, data));
return Ok();
}
```
Note that this macro uses a special extension called *compound statement* only supported by gcc and clang

910
third-party/result/include/result.hpp vendored Normal file
View File

@ -0,0 +1,910 @@
/*
Mathieu Stefani, 03 mai 2016
This header provides a Result type that can be used to replace exceptions in code
that has to handle error.
Result<T, E> can be used to return and propagate an error to the caller. Result<T, E> is an algebraic
data type that can either Ok(T) to represent success or Err(E) to represent an error.
*/
#pragma once
#include <iostream>
#include <functional>
#include <type_traits>
namespace types {
template<typename T>
struct Ok {
Ok(const T& val) : val(val) { }
Ok(T&& val) : val(std::move(val)) { }
T val;
};
template<>
struct Ok<void> { };
template<typename E>
struct Err {
Err(const E& val) : val(val) { }
Err(E&& val) : val(std::move(val)) { }
E val;
};
}
template<typename T, typename CleanT = typename std::decay<T>::type>
types::Ok<CleanT> Ok(T&& val) {
return types::Ok<CleanT>(std::forward<T>(val));
}
inline types::Ok<void> Ok() {
return types::Ok<void>();
}
template<typename E, typename CleanE = typename std::decay<E>::type>
types::Err<CleanE> Err(E&& val) {
return types::Err<CleanE>(std::forward<E>(val));
}
namespace Rust {
template<typename T, typename E> struct Result;
}
namespace details {
template<typename ...> struct void_t { typedef void type; };
namespace impl {
template<typename Func> struct result_of;
template<typename Ret, typename Cls, typename... Args>
struct result_of<Ret (Cls::*)(Args...)> : public result_of<Ret (Args...)> { };
template<typename Ret, typename... Args>
struct result_of<Ret (Args...)> {
typedef Ret type;
};
}
template<typename Func>
struct result_of : public impl::result_of<decltype(&Func::operator())> { };
template<typename Ret, typename Cls, typename... Args>
struct result_of<Ret (Cls::*) (Args...) const> {
typedef Ret type;
};
template<typename Ret, typename... Args>
struct result_of<Ret (*)(Args...)> {
typedef Ret type;
};
template<typename R>
struct ResultOkType { typedef typename std::decay<R>::type type; };
template<typename T, typename E>
struct ResultOkType<Rust::Result<T, E>> {
typedef T type;
};
template<typename R>
struct ResultErrType { typedef R type; };
template<typename T, typename E>
struct ResultErrType<Rust::Result<T, E>> {
typedef typename std::remove_reference<E>::type type;
};
template<typename R> struct IsResult : public std::false_type { };
template<typename T, typename E>
struct IsResult<Rust::Result<T, E>> : public std::true_type { };
namespace ok {
namespace impl {
template<typename T> struct Map;
template<typename Ret, typename Cls, typename Arg>
struct Map<Ret (Cls::*)(Arg) const> : public Map<Ret (Arg)> { };
template<typename Ret, typename Cls, typename Arg>
struct Map<Ret (Cls::*)(Arg)> : public Map<Ret (Arg)> { };
// General implementation
template<typename Ret, typename Arg>
struct Map<Ret (Arg)> {
static_assert(!IsResult<Ret>::value,
"Can not map a callback returning a Result, use andThen instead");
template<typename T, typename E, typename Func>
static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) {
static_assert(
std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
if (result.isOk()) {
auto res = func(result.storage().template get<T>());
return types::Ok<Ret>(std::move(res));
}
return types::Err<E>(result.storage().template get<E>());
}
};
// Specialization for callback returning void
template<typename Arg>
struct Map<void (Arg)> {
template<typename T, typename E, typename Func>
static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) {
if (result.isOk()) {
func(result.storage().template get<T>());
return types::Ok<void>();
}
return types::Err<E>(result.storage().template get<E>());
}
};
// Specialization for a void Result
template<typename Ret>
struct Map<Ret (void)> {
template<typename T, typename E, typename Func>
static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) {
static_assert(std::is_same<T, void>::value,
"Can not map a void callback on a non-void Result");
if (result.isOk()) {
auto ret = func();
return types::Ok<Ret>(std::move(ret));
}
return types::Err<E>(result.storage().template get<E>());
}
};
// Specialization for callback returning void on a void Result
template<>
struct Map<void (void)> {
template<typename T, typename E, typename Func>
static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) {
static_assert(std::is_same<T, void>::value,
"Can not map a void callback on a non-void Result");
if (result.isOk()) {
func();
return types::Ok<void>();
}
return types::Err<E>(result.storage().template get<E>());
}
};
// General specialization for a callback returning a Result
template<typename U, typename E, typename Arg>
struct Map<Rust::Result<U, E> (Arg)> {
template<typename T, typename Func>
static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) {
static_assert(
std::is_same<T, Arg>::value ||
std::is_convertible<T, Arg>::value,
"Incompatible types detected");
if (result.isOk()) {
auto res = func(result.storage().template get<T>());
return res;
}
return types::Err<E>(result.storage().template get<E>());
}
};
// Specialization for a void callback returning a Result
template<typename U, typename E>
struct Map<Rust::Result<U, E> (void)> {
template<typename T, typename Func>
static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) {
static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result");
if (result.isOk()) {
auto res = func();
return res;
}
return types::Err<E>(result.storage().template get<E>());
}
};
} // namespace impl
template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { };
template<typename Ret, typename... Args>
struct Map<Ret (*) (Args...)> : public impl::Map<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Map<Ret (Cls::*) (Args...)> : public impl::Map<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Map<Ret (Cls::*) (Args...) const> : public impl::Map<Ret (Args...)> { };
template<typename Ret, typename... Args>
struct Map<std::function<Ret (Args...)>> : public impl::Map<Ret (Args...)> { };
} // namespace ok
namespace err {
namespace impl {
template<typename T> struct Map;
template<typename Ret, typename Cls, typename Arg>
struct Map<Ret (Cls::*)(Arg) const> {
static_assert(!IsResult<Ret>::value,
"Can not map a callback returning a Result, use orElse instead");
template<typename T, typename E, typename Func>
static Rust::Result<T, Ret> map(const Rust::Result<T, E>& result, Func func) {
if (result.isErr()) {
auto res = func(result.storage().template get<E>());
return types::Err<Ret>(res);
}
return types::Ok<T>(result.storage().template get<T>());
}
template<typename E, typename Func>
static Rust::Result<void, Ret> map(const Rust::Result<void, E>& result, Func func) {
if (result.isErr()) {
auto res = func(result.storage().template get<E>());
return types::Err<Ret>(res);
}
return types::Ok<void>();
}
};
} // namespace impl
template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { };
} // namespace err;
namespace And {
namespace impl {
template<typename Func> struct Then;
template<typename Ret, typename... Args>
struct Then<Ret (*)(Args...)> : public Then<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Then<Ret (Cls::*)(Args...)> : public Then<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Then<Ret (Cls::*)(Args...) const> : public Then<Ret (Args...)> { };
template<typename Ret, typename Arg>
struct Then<Ret (Arg)> {
static_assert(std::is_same<Ret, void>::value,
"then() should not return anything, use map() instead");
template<typename T, typename E, typename Func>
static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
if (result.isOk()) {
func(result.storage().template get<T>());
}
return result;
}
};
template<typename Ret>
struct Then<Ret (void)> {
static_assert(std::is_same<Ret, void>::value,
"then() should not return anything, use map() instead");
template<typename T, typename E, typename Func>
static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result");
if (result.isOk()) {
func();
}
return result;
}
};
} // namespace impl
template<typename Func>
struct Then : public impl::Then<decltype(&Func::operator())> { };
template<typename Ret, typename... Args>
struct Then<Ret (*) (Args...)> : public impl::Then<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Then<Ret (Cls::*)(Args...)> : public impl::Then<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Then<Ret (Cls::*)(Args...) const> : public impl::Then<Ret (Args...)> { };
} // namespace And
namespace Or {
namespace impl {
template<typename Func> struct Else;
template<typename Ret, typename... Args>
struct Else<Ret (*)(Args...)> : public Else<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Else<Ret (Cls::*)(Args...)> : public Else<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Else<Ret (Cls::*)(Args...) const> : public Else<Ret (Args...)> { };
template<typename T, typename F, typename Arg>
struct Else<Rust::Result<T, F> (Arg)> {
template<typename E, typename Func>
static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) {
static_assert(
std::is_same<E, Arg>::value ||
std::is_convertible<E, Arg>::value,
"Incompatible types detected");
if (result.isErr()) {
auto res = func(result.storage().template get<E>());
return res;
}
return types::Ok<T>(result.storage().template get<T>());
}
template<typename E, typename Func>
static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) {
if (result.isErr()) {
auto res = func(result.storage().template get<E>());
return res;
}
return types::Ok<void>();
}
};
template<typename T, typename F>
struct Else<Rust::Result<T, F> (void)> {
template<typename E, typename Func>
static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) {
static_assert(std::is_same<T, void>::value,
"Can not call a void-callback on a non-void Result");
if (result.isErr()) {
auto res = func();
return res;
}
return types::Ok<T>(result.storage().template get<T>());
}
template<typename E, typename Func>
static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) {
if (result.isErr()) {
auto res = func();
return res;
}
return types::Ok<void>();
}
};
} // namespace impl
template<typename Func>
struct Else : public impl::Else<decltype(&Func::operator())> { };
template<typename Ret, typename... Args>
struct Else<Ret (*) (Args...)> : public impl::Else<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Else<Ret (Cls::*)(Args...)> : public impl::Else<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Else<Ret (Cls::*)(Args...) const> : public impl::Else<Ret (Args...)> { };
} // namespace Or
namespace Other {
namespace impl {
template<typename Func> struct Wise;
template<typename Ret, typename... Args>
struct Wise<Ret (*)(Args...)> : public Wise<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Wise<Ret (Cls::*)(Args...)> : public Wise<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Wise<Ret (Cls::*)(Args...) const> : public Wise<Ret (Args...)> { };
template<typename Ret, typename Arg>
struct Wise<Ret (Arg)> {
template<typename T, typename E, typename Func>
static Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) {
static_assert(
std::is_same<E, Arg>::value ||
std::is_convertible<E, Arg>::value,
"Incompatible types detected");
static_assert(std::is_same<Ret, void>::value,
"callback should not return anything, use mapError() for that");
if (result.isErr()) {
func(result.storage().template get<E>());
}
return result;
}
};
} // namespace impl
template<typename Func>
struct Wise : public impl::Wise<decltype(&Func::operator())> { };
template<typename Ret, typename... Args>
struct Wise<Ret (*) (Args...)> : public impl::Wise<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Wise<Ret (Cls::*)(Args...)> : public impl::Wise<Ret (Args...)> { };
template<typename Ret, typename Cls, typename... Args>
struct Wise<Ret (Cls::*)(Args...) const> : public impl::Wise<Ret (Args...)> { };
} // namespace Other
template<typename T, typename E, typename Func,
typename Ret =
Rust::Result<
typename details::ResultOkType<
typename details::result_of<Func>::type
>::type,
E>
>
Ret map(const Rust::Result<T, E>& result, Func func) {
return ok::Map<Func>::map(result, func);
}
template<typename T, typename E, typename Func,
typename Ret =
Rust::Result<T,
typename details::ResultErrType<
typename details::result_of<Func>::type
>::type
>
>
Ret mapError(const Rust::Result<T, E>& result, Func func) {
return err::Map<Func>::map(result, func);
}
template<typename T, typename E, typename Func>
Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
return And::Then<Func>::then(result, func);
}
template<typename T, typename E, typename Func>
Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) {
return Other::Wise<Func>::otherwise(result, func);
}
template<typename T, typename E, typename Func,
typename Ret =
Rust::Result<T,
typename details::ResultErrType<
typename details::result_of<Func>::type
>::type
>
>
Ret orElse(const Rust::Result<T, E>& result, Func func) {
return Or::Else<Func>::orElse(result, func);
}
struct ok_tag { };
struct err_tag { };
template<typename T, typename E>
struct Storage {
static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E);
static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E);
typedef typename std::aligned_storage<Size, Align>::type type;
Storage()
: initialized_(false)
{ }
void construct(types::Ok<T> ok)
{
new (&storage_) T(ok.val);
initialized_ = true;
}
void construct(types::Err<E> err)
{
new (&storage_) E(err.val);
initialized_ = true;
}
template<typename U>
void rawConstruct(U&& val) {
typedef typename std::decay<U>::type CleanU;
new (&storage_) CleanU(std::forward<U>(val));
initialized_ = true;
}
template<typename U>
const U& get() const {
return *reinterpret_cast<const U *>(&storage_);
}
template<typename U>
U& get() {
return *reinterpret_cast<U *>(&storage_);
}
void destroy(ok_tag) {
if (initialized_) {
get<T>().~T();
initialized_ = false;
}
}
void destroy(err_tag) {
if (initialized_) {
get<E>().~E();
initialized_ = false;
}
}
type storage_;
bool initialized_;
};
template<typename E>
struct Storage<void, E> {
typedef typename std::aligned_storage<sizeof(E), alignof(E)>::type type;
void construct(types::Ok<void>)
{
initialized_ = true;
}
void construct(types::Err<E> err)
{
new (&storage_) E(err.val);
initialized_ = true;
}
template<typename U>
void rawConstruct(U&& val) {
typedef typename std::decay<U>::type CleanU;
new (&storage_) CleanU(std::forward<U>(val));
initialized_ = true;
}
void destroy(ok_tag) { initialized_ = false; }
void destroy(err_tag) {
if (initialized_) {
get<E>().~E(); initialized_ = false;
}
}
template<typename U>
const U& get() const {
return *reinterpret_cast<const U *>(&storage_);
}
template<typename U>
U& get() {
return *reinterpret_cast<U *>(&storage_);
}
type storage_;
bool initialized_;
};
template<typename T, typename E>
struct Constructor {
static void move(Storage<T, E>&& src, Storage<T, E>& dst, ok_tag) {
dst.rawConstruct(std::move(src.template get<T>()));
src.destroy(ok_tag());
}
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, ok_tag) {
dst.rawConstruct(src.template get<T>());
}
static void move(Storage<T, E>&& src, Storage<T, E>& dst, err_tag) {
dst.rawConstruct(std::move(src.template get<E>()));
src.destroy(err_tag());
}
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, err_tag) {
dst.rawConstruct(src.template get<E>());
}
};
template<typename E>
struct Constructor<void, E> {
static void move(Storage<void, E>&& src, Storage<void, E>& dst, ok_tag) {
}
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, ok_tag) {
}
static void move(Storage<void, E>&& src, Storage<void, E>& dst, err_tag) {
dst.rawConstruct(std::move(src.template get<E>()));
src.destroy(err_tag());
}
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, err_tag) {
dst.rawConstruct(src.template get<E>());
}
};
} // namespace details
namespace rpog {
template<typename T, typename = void> struct EqualityComparable : std::false_type { };
template<typename T>
struct EqualityComparable<T,
typename std::enable_if<
true,
typename details::void_t<decltype(std::declval<T>() == std::declval<T>())>::type
>::type
> : std::true_type
{
};
} // namespace rpog
namespace Rust {
template<typename T, typename E>
struct Result {
static_assert(!std::is_same<E, void>::value, "void error type is not allowed");
typedef details::Storage<T, E> storage_type;
Result(types::Ok<T> ok)
: ok_(true)
{
storage_.construct(std::move(ok));
}
Result(types::Err<E> err)
: ok_(false)
{
storage_.construct(std::move(err));
}
Result(Result&& other) {
if (other.isOk()) {
details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::ok_tag());
ok_ = true;
} else {
details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::err_tag());
ok_ = false;
}
}
Result(const Result& other) {
if (other.isOk()) {
details::Constructor<T, E>::copy(other.storage_, storage_, details::ok_tag());
ok_ = true;
} else {
details::Constructor<T, E>::copy(other.storage_, storage_, details::err_tag());
ok_ = false;
}
}
~Result() {
if (ok_)
storage_.destroy(details::ok_tag());
else
storage_.destroy(details::err_tag());
}
bool isOk() const {
return ok_;
}
bool isErr() const {
return !ok_;
}
T expect(const char* str) const {
if (!isOk()) {
std::fprintf(stderr, "%s\n", str);
std::terminate();
}
return expect_impl(std::is_same<T, void>());
}
template<typename Func,
typename Ret =
Result<
typename details::ResultOkType<
typename details::result_of<Func>::type
>::type,
E>
>
Ret map(Func func) const {
return details::map(*this, func);
}
template<typename Func,
typename Ret =
Result<T,
typename details::ResultErrType<
typename details::result_of<Func>::type
>::type
>
>
Ret mapError(Func func) const {
return details::mapError(*this, func);
}
template<typename Func>
Result<T, E> then(Func func) const {
return details::then(*this, func);
}
template<typename Func>
Result<T, E> otherwise(Func func) const {
return details::otherwise(*this, func);
}
template<typename Func,
typename Ret =
Result<T,
typename details::ResultErrType<
typename details::result_of<Func>::type
>::type
>
>
Ret orElse(Func func) const {
return details::orElse(*this, func);
}
storage_type& storage() {
return storage_;
}
const storage_type& storage() const {
return storage_;
}
template<typename U = T>
typename std::enable_if<
!std::is_same<U, void>::value,
U
>::type
unwrapOr(const U& defaultValue) const {
if (isOk()) {
return storage().template get<U>();
}
return defaultValue;
}
template<typename U = T>
typename std::enable_if<
!std::is_same<U, void>::value,
U
>::type
unwrap() const {
if (isOk()) {
return storage().template get<U>();
}
std::fprintf(stderr, "Attempting to unwrap an error Result\n");
std::terminate();
}
E unwrapErr() const {
if (isErr()) {
return storage().template get<E>();
}
std::fprintf(stderr, "Attempting to unwrapErr an ok Result\n");
std::terminate();
}
private:
T expect_impl(std::true_type) const { }
T expect_impl(std::false_type) const { return storage_.template get<T>(); }
bool ok_;
storage_type storage_;
};
template<typename T, typename E>
bool operator==(const Rust::Result<T, E>& lhs, const Rust::Result<T, E>& rhs) {
static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable");
static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable");
if (lhs.isOk() && rhs.isOk()) {
return lhs.storage().template get<T>() == rhs.storage().template get<T>();
}
if (lhs.isErr() && rhs.isErr()) {
return lhs.storage().template get<E>() == rhs.storage().template get<E>();
}
}
template<typename T, typename E>
bool operator==(const Rust::Result<T, E>& lhs, types::Ok<T> ok) {
static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable");
if (!lhs.isOk()) return false;
return lhs.storage().template get<T>() == ok.val;
}
template<typename E>
bool operator==(const Rust::Result<void, E>& lhs, types::Ok<void>) {
return lhs.isOk();
}
template<typename T, typename E>
bool operator==(const Rust::Result<T, E>& lhs, types::Err<E> err) {
static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable");
if (!lhs.isErr()) return false;
return lhs.storage().template get<E>() == err.val;
}
} // end namespace Rust
#define TRY(...) \
({ \
auto res = __VA_ARGS__; \
if (!res.isOk()) { \
typedef details::ResultErrType<decltype(res)>::type E; \
return types::Err<E>(res.storage().get<E>()); \
} \
typedef details::ResultOkType<decltype(res)>::type T; \
res.storage().get<T>(); \
})