Merge branch 'main' into hello_world_OpenOrbis
This commit is contained in:
commit
c3ce005e62
|
@ -19,11 +19,51 @@ include_directories(third-party/zydis/include/Zydis)
|
||||||
include_directories(third-party/winpthread/include)
|
include_directories(third-party/winpthread/include)
|
||||||
include_directories(third-party/vulkan/include)
|
include_directories(third-party/vulkan/include)
|
||||||
include_directories(third-party/xxhash/include)
|
include_directories(third-party/xxhash/include)
|
||||||
|
include_directories(third-party/result/include)
|
||||||
add_subdirectory("third-party")
|
add_subdirectory("third-party")
|
||||||
#=================== EXAMPLE ===================
|
#=================== EXAMPLE ===================
|
||||||
include_directories(src)
|
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
|
add_executable(shadps4
|
||||||
|
${LIBC_SOURCES}
|
||||||
|
${USERSERVICE_SOURCES}
|
||||||
|
${PAD_SOURCES}
|
||||||
|
${SYSTEMSERVICE_SOURCES}
|
||||||
|
${FILESYSTEM_SOURCES}
|
||||||
|
${HOST_SOURCES}
|
||||||
|
${UTIL_SOURCES}
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/types.h
|
src/types.h
|
||||||
src/Core/FsFile.cpp
|
src/Core/FsFile.cpp
|
||||||
|
@ -40,8 +80,9 @@ add_executable(shadps4
|
||||||
src/Core/virtual_memory.h
|
src/Core/virtual_memory.h
|
||||||
src/Core/PS4/Linker.cpp
|
src/Core/PS4/Linker.cpp
|
||||||
src/Core/PS4/Linker.h
|
src/Core/PS4/Linker.h
|
||||||
src/Lib/Threads.cpp
|
src/Core/PS4/Stubs.cpp
|
||||||
src/Lib/Threads.h
|
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.h
|
||||||
src/Core/PS4/HLE/Kernel/Objects/physical_memory.cpp
|
src/Core/PS4/HLE/Kernel/Objects/physical_memory.cpp
|
||||||
src/Util/string_util.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.cpp
|
||||||
src/Core/PS4/HLE/Kernel/cpu_management.h
|
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)
|
find_package(OpenGL REQUIRED)
|
||||||
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY} vulkan-1 spirv-tools-opt spirv-tools)
|
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY} vulkan-1 spirv-tools-opt spirv-tools)
|
||||||
|
|
|
@ -1,15 +1,13 @@
|
||||||
# shadPS4
|
# shadPS4
|
||||||
|
|
||||||
An early PS4 emulator for Windows and Linux written in C++
|
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")
|
[Check us on twitter](https://twitter.com/shadps4 "Check us on twitter")
|
||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
Currently, it can only load PS4 ELF files.
|
Early progress , a small amount of ps4 sdk demos and homebrew games working
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
 Elf Loader
|
 Elf Loader
|
||||||
|
|
||||||
|
|
|
@ -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
|
v0.0.1 29/09/2023
|
||||||
=================
|
=================
|
||||||
First public release . Everything is new
|
First public release . Everything is new
|
File diff suppressed because it is too large
Load Diff
|
@ -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)
|
|
@ -2,19 +2,19 @@
|
||||||
|
|
||||||
#include <xxhash/xxh3.h>
|
#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,
|
void* GPU::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo /*CommandBuffer?*/, u64 virtual_addr, u64 size,
|
||||||
const GPUObject& info) {
|
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);
|
return gpumemory->memoryCreateObj(submit_id, ctx, nullptr, &virtual_addr, &size, 1, info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::memorySetAllocArea(u64 virtual_addr, u64 size) {
|
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;
|
MemoryHeap h;
|
||||||
h.allocated_virtual_addr = virtual_addr;
|
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) {
|
void GPU::flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx) {
|
||||||
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
auto* gpumemory = singleton<GPUMemory>::instance();
|
||||||
gpumemory->flushAllHeaps(ctx);
|
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,
|
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) {
|
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]);
|
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) {
|
void GPU::GPUMemory::flushAllHeaps(HLE::Libs::Graphics::GraphicCtx* ctx) {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
int heap_id = 0;
|
int heap_id = 0;
|
||||||
for (auto& heap : m_heaps) {
|
for (auto& heap : m_heaps) {
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace GPU {
|
namespace GPU {
|
||||||
|
@ -66,7 +67,7 @@ class GPUMemory {
|
||||||
GPUMemory() {}
|
GPUMemory() {}
|
||||||
virtual ~GPUMemory() {}
|
virtual ~GPUMemory() {}
|
||||||
int getHeapId(u64 vaddr, u64 size);
|
int getHeapId(u64 vaddr, u64 size);
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::vector<MemoryHeap> m_heaps;
|
std::vector<MemoryHeap> m_heaps;
|
||||||
void* memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, /*CommandBuffer* buffer*/ void* todo, const u64* virtual_addr,
|
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);
|
const u64* size, int virtual_addr_num, const GPUObject& info);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#include "tile_manager.h"
|
#include "tile_manager.h"
|
||||||
#include "Lib/Threads.h"
|
#include "Emulator/Util/singleton.h"
|
||||||
#include "Util/Singleton.h"
|
#include <mutex>
|
||||||
|
|
||||||
namespace GPU {
|
namespace GPU {
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ class TileManager {
|
||||||
public:
|
public:
|
||||||
TileManager(){}
|
TileManager(){}
|
||||||
virtual ~TileManager() { }
|
virtual ~TileManager() { }
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
class TileManager32 {
|
class TileManager32 {
|
||||||
|
@ -143,9 +143,9 @@ void convertTileToLinear(void* dst, const void* src,u32 width, u32 height, bool
|
||||||
TileManager32 t;
|
TileManager32 t;
|
||||||
t.Init(width, height, is_neo);
|
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++) {
|
for (u32 y = 0; y < height; y++) {
|
||||||
u32 x = 0;
|
u32 x = 0;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#include "video_out_ctx.h"
|
#include "video_out_ctx.h"
|
||||||
|
|
||||||
#include <Core/PS4/HLE/LibKernel.h>
|
#include <Core/PS4/HLE/LibKernel.h>
|
||||||
|
#include <debug.h>
|
||||||
|
|
||||||
namespace HLE::Graphics::Objects {
|
namespace HLE::Graphics::Objects {
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ void VideoOutCtx::Init(u32 width, u32 height) {
|
||||||
m_video_out_ctx.m_resolution.paneHeight = height;
|
m_video_out_ctx.m_resolution.paneHeight = height;
|
||||||
}
|
}
|
||||||
int VideoOutCtx::Open() {
|
int VideoOutCtx::Open() {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
int handle = -1;
|
int handle = -1;
|
||||||
|
|
||||||
|
@ -28,20 +29,45 @@ int VideoOutCtx::Open() {
|
||||||
|
|
||||||
return handle;
|
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) {
|
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
|
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) {
|
void FlipQueue::getFlipStatus(VideoConfigInternal* cfg, SceVideoOutFlipStatus* out) {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock(m_mutex);
|
||||||
|
|
||||||
*out = cfg->m_flip_status;
|
*out = cfg->m_flip_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlipQueue::submitFlip(VideoConfigInternal* cfg, s32 index, s64 flip_arg) {
|
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) {
|
if (m_requests.size() >= 2) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -58,50 +84,51 @@ 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.flipPendingNum = static_cast<int>(m_requests.size());
|
||||||
cfg->m_flip_status.gcQueueNum = 0;
|
cfg->m_flip_status.gcQueueNum = 0;
|
||||||
|
|
||||||
m_submit_cond.SignalCondVar();
|
m_submit_cond.notify_one();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FlipQueue::flip(u32 micros) {
|
bool FlipQueue::flip(u32 micros) {
|
||||||
m_mutex.LockMutex();
|
const auto request = [&]() -> Request* {
|
||||||
if (m_requests.size() == 0) {
|
std::unique_lock lock{m_mutex};
|
||||||
m_submit_cond.WaitCondVarFor(&m_mutex, micros);
|
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) {
|
if (!request) {
|
||||||
m_mutex.UnlockMutex();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
auto request = m_requests.at(0); // proceed first request
|
|
||||||
m_mutex.UnlockMutex();
|
|
||||||
|
|
||||||
auto* buffer = request.cfg->buffers[request.index].buffer_render;
|
const auto buffer = request->cfg->buffers[request->index].buffer_render;
|
||||||
|
Emu::DrawBuffer(buffer);
|
||||||
|
|
||||||
Emulator::DrawBuffer(buffer);
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
m_mutex.LockMutex();
|
{
|
||||||
|
std::scoped_lock cfg_lock{request->cfg->m_mutex};
|
||||||
request.cfg->m_mutex.LockMutex();
|
for (auto& flip_eq : request->cfg->m_flip_evtEq) {
|
||||||
for (auto& flip_eq : request.cfg->m_flip_evtEq) {
|
|
||||||
if (flip_eq != nullptr) {
|
if (flip_eq != nullptr) {
|
||||||
flip_eq->triggerEvent(SCE_VIDEO_OUT_EVENT_FLIP, HLE::Kernel::Objects::EVFILT_VIDEO_OUT, reinterpret_cast<void*>(request.flip_arg));
|
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_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();
|
//TODO request.cfg->m_flip_status.processTime = LibKernel::KernelGetProcessTime();
|
||||||
request.cfg->m_flip_status.tsc = HLE::Libs::LibKernel::sceKernelReadTsc();
|
request->cfg->m_flip_status.tsc = HLE::Libs::LibKernel::sceKernelReadTsc();
|
||||||
request.cfg->m_flip_status.submitTsc = request.submit_tsc;
|
request->cfg->m_flip_status.submitTsc = request->submit_tsc;
|
||||||
request.cfg->m_flip_status.flipArg = request.flip_arg;
|
request->cfg->m_flip_status.flipArg = request->flip_arg;
|
||||||
request.cfg->m_flip_status.currentBuffer = request.index;
|
request->cfg->m_flip_status.currentBuffer = request->index;
|
||||||
request.cfg->m_flip_status.flipPendingNum = static_cast<int>(m_requests.size());
|
request->cfg->m_flip_status.flipPendingNum = static_cast<int>(m_requests.size());
|
||||||
|
|
||||||
m_mutex.UnlockMutex();
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <condition_variable>
|
||||||
|
#include <mutex>
|
||||||
#include <Core/PS4/HLE/Graphics/video_out.h>
|
#include <Core/PS4/HLE/Graphics/video_out.h>
|
||||||
#include <Lib/Threads.h>
|
|
||||||
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
||||||
#include <emulator.h>
|
#include <emulator.h>
|
||||||
|
|
||||||
|
@ -17,7 +19,7 @@ struct VideoOutBufferInfo {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VideoConfigInternal {
|
struct VideoConfigInternal {
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
SceVideoOutResolutionStatus m_resolution;
|
SceVideoOutResolutionStatus m_resolution;
|
||||||
bool isOpened = false;
|
bool isOpened = false;
|
||||||
SceVideoOutFlipStatus m_flip_status;
|
SceVideoOutFlipStatus m_flip_status;
|
||||||
|
@ -45,9 +47,9 @@ class FlipQueue {
|
||||||
uint64_t submit_tsc;
|
uint64_t submit_tsc;
|
||||||
};
|
};
|
||||||
|
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
Lib::ConditionVariable m_submit_cond;
|
std::condition_variable m_submit_cond;
|
||||||
Lib::ConditionVariable m_done_cond;
|
std::condition_variable m_done_cond;
|
||||||
std::vector<Request> m_requests;
|
std::vector<Request> m_requests;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -58,19 +60,20 @@ class VideoOutCtx {
|
||||||
virtual ~VideoOutCtx() {}
|
virtual ~VideoOutCtx() {}
|
||||||
void Init(u32 width, u32 height);
|
void Init(u32 width, u32 height);
|
||||||
int Open();
|
int Open();
|
||||||
|
void Close(s32 handle);
|
||||||
VideoConfigInternal* getCtx(int handle);
|
VideoConfigInternal* getCtx(int handle);
|
||||||
FlipQueue& getFlipQueue() { return m_flip_queue; }
|
FlipQueue& getFlipQueue() { return m_flip_queue; }
|
||||||
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
if (m_graphic_ctx == nullptr) {
|
if (!m_graphic_ctx) {
|
||||||
m_graphic_ctx = Emulator::getGraphicCtx();
|
m_graphic_ctx = Emu::getGraphicCtx();
|
||||||
}
|
}
|
||||||
|
|
||||||
return m_graphic_ctx;
|
return m_graphic_ctx;
|
||||||
}
|
}
|
||||||
private:
|
private:
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
VideoConfigInternal m_video_out_ctx;
|
VideoConfigInternal m_video_out_ctx;
|
||||||
FlipQueue m_flip_queue;
|
FlipQueue m_flip_queue;
|
||||||
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
||||||
|
|
|
@ -2,13 +2,12 @@
|
||||||
|
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
#include <vulkan/vulkan_core.h>
|
#include <vulkan/vulkan_core.h>
|
||||||
|
#include <mutex>
|
||||||
#include "Lib/Threads.h"
|
|
||||||
|
|
||||||
namespace HLE::Libs::Graphics {
|
namespace HLE::Libs::Graphics {
|
||||||
|
|
||||||
struct VulkanCommandPool {
|
struct VulkanCommandPool {
|
||||||
Lib::Mutex mutex;
|
std::mutex mutex;
|
||||||
VkCommandPool pool = nullptr;
|
VkCommandPool pool = nullptr;
|
||||||
VkCommandBuffer* buffers = nullptr;
|
VkCommandBuffer* buffers = nullptr;
|
||||||
VkFence* fences = nullptr;
|
VkFence* fences = nullptr;
|
||||||
|
@ -18,7 +17,7 @@ struct VulkanCommandPool {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct VulkanQueueInfo {
|
struct VulkanQueueInfo {
|
||||||
Lib::Mutex* mutex = nullptr;
|
std::mutex* mutex = nullptr;
|
||||||
u32 family = static_cast<u32>(-1);
|
u32 family = static_cast<u32>(-1);
|
||||||
u32 index = static_cast<u32>(-1);
|
u32 index = static_cast<u32>(-1);
|
||||||
VkQueue vk_queue = nullptr;
|
VkQueue vk_queue = nullptr;
|
||||||
|
|
|
@ -1,20 +1,20 @@
|
||||||
#include "graphics_render.h"
|
#include "graphics_render.h"
|
||||||
|
|
||||||
#include "Util/Singleton.h"
|
#include "Emulator/Util/singleton.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
|
|
||||||
static thread_local GPU::CommandPool g_command_pool;
|
static thread_local GPU::CommandPool g_command_pool;
|
||||||
|
|
||||||
void GPU::renderCreateCtx() {
|
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() {
|
void GPU::CommandBuffer::allocateBuffer() {
|
||||||
m_pool = g_command_pool.getPool(m_queue);
|
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++) {
|
for (uint32_t i = 0; i < m_pool->buffers_count; i++) {
|
||||||
if (!m_pool->busy[i]) {
|
if (!m_pool->busy[i]) {
|
||||||
|
@ -27,7 +27,7 @@ void GPU::CommandBuffer::allocateBuffer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::CommandBuffer::freeBuffer() {
|
void GPU::CommandBuffer::freeBuffer() {
|
||||||
Lib::LockMutexGuard lock(m_pool->mutex);
|
std::scoped_lock lock{m_pool->mutex};
|
||||||
|
|
||||||
waitForFence();
|
waitForFence();
|
||||||
|
|
||||||
|
@ -37,7 +37,7 @@ void GPU::CommandBuffer::freeBuffer() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::CommandBuffer::waitForFence() {
|
void GPU::CommandBuffer::waitForFence() {
|
||||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||||
|
|
||||||
if (m_execute) {
|
if (m_execute) {
|
||||||
auto* device = render_ctx->getGraphicCtx()->m_device;
|
auto* device = render_ctx->getGraphicCtx()->m_device;
|
||||||
|
@ -89,19 +89,10 @@ void GPU::CommandBuffer::executeWithSemaphore() {
|
||||||
submit_info.signalSemaphoreCount = 1;
|
submit_info.signalSemaphoreCount = 1;
|
||||||
submit_info.pSignalSemaphores = &m_pool->semaphores[m_index];
|
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];
|
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);
|
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
|
||||||
|
|
||||||
if (queue.mutex != nullptr) {
|
|
||||||
queue.mutex->LockMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_execute = true;
|
m_execute = true;
|
||||||
|
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
|
@ -124,19 +115,10 @@ void GPU::CommandBuffer::execute() {
|
||||||
submit_info.signalSemaphoreCount = 0;
|
submit_info.signalSemaphoreCount = 0;
|
||||||
submit_info.pSignalSemaphores = nullptr;
|
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];
|
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);
|
auto result = vkQueueSubmit(queue.vk_queue, 1, &submit_info, fence);
|
||||||
|
|
||||||
if (queue.mutex != nullptr) {
|
|
||||||
queue.mutex->UnlockMutex();
|
|
||||||
}
|
|
||||||
|
|
||||||
m_execute = true;
|
m_execute = true;
|
||||||
|
|
||||||
if (result != VK_SUCCESS) {
|
if (result != VK_SUCCESS) {
|
||||||
|
@ -145,7 +127,7 @@ void GPU::CommandBuffer::execute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void GPU::CommandPool::createPool(int id) {
|
void GPU::CommandPool::createPool(int id) {
|
||||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||||
auto* ctx = render_ctx->getGraphicCtx();
|
auto* ctx = render_ctx->getGraphicCtx();
|
||||||
|
|
||||||
m_pool[id] = new HLE::Libs::Graphics::VulkanCommandPool;
|
m_pool[id] = new HLE::Libs::Graphics::VulkanCommandPool;
|
||||||
|
@ -206,7 +188,7 @@ void GPU::CommandPool::createPool(int id) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void GPU::CommandPool::deleteAllPool() {
|
void GPU::CommandPool::deleteAllPool() {
|
||||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||||
auto* ctx = render_ctx->getGraphicCtx();
|
auto* ctx = render_ctx->getGraphicCtx();
|
||||||
|
|
||||||
for (auto& pool : m_pool) {
|
for (auto& pool : m_pool) {
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#include "video_out.h"
|
#include "video_out.h"
|
||||||
|
|
||||||
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
#include <Core/PS4/GPU/video_out_buffer.h>
|
#include <Core/PS4/GPU/video_out_buffer.h>
|
||||||
#include <Core/PS4/HLE/ErrorCodes.h>
|
#include <Core/PS4/HLE/ErrorCodes.h>
|
||||||
|
#include <Core/PS4/HLE/LibSceGnmDriver.h>
|
||||||
#include <Core/PS4/HLE/Libs.h>
|
#include <Core/PS4/HLE/Libs.h>
|
||||||
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
|
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
|
||||||
#include <Util/config.h>
|
#include <Util/config.h>
|
||||||
|
@ -13,9 +15,8 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "Objects/video_out_ctx.h"
|
#include "Objects/video_out_ctx.h"
|
||||||
#include "Util/Singleton.h"
|
#include "Emulator/Util/singleton.h"
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
#include <Core/PS4/GPU/gpu_memory.h>
|
|
||||||
#include "graphics_render.h"
|
#include "graphics_render.h"
|
||||||
#include <Core/PS4/HLE/LibSceGnmDriver.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
|
constexpr bool log_file_videoout = true; // disable it to disable logging
|
||||||
|
|
||||||
void videoOutInit(u32 width, u32 height) {
|
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);
|
videoOut->Init(width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool videoOutFlip(u32 micros) {
|
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);
|
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) {
|
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
|
|
||||||
auto* ctx = videoOut->getCtx(handle);
|
auto* ctx = videoOut->getCtx(handle);
|
||||||
|
|
||||||
if (ctx == nullptr) {
|
if (ctx == nullptr) {
|
||||||
return SCE_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
return SCE_VIDEO_OUT_ERROR_INVALID_HANDLE;
|
||||||
}
|
}
|
||||||
Lib::LockMutexGuard lock(ctx->m_mutex);
|
std::scoped_lock lock(ctx->m_mutex);
|
||||||
|
|
||||||
if (eq == nullptr) {
|
if (eq == nullptr) {
|
||||||
return SCE_VIDEO_OUT_ERROR_INVALID_EVENT_QUEUE;
|
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,
|
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum,
|
||||||
const SceVideoOutBufferAttribute* attribute) {
|
const SceVideoOutBufferAttribute* attribute) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
auto* ctx = videoOut->getCtx(handle);
|
auto* ctx = videoOut->getCtx(handle);
|
||||||
|
|
||||||
if (handle == 1) { // main port
|
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++;
|
int registration_index = ctx->buffers_registration_index++;
|
||||||
|
|
||||||
Emulator::checkAndWaitForGraphicsInit();
|
Emu::checkAndWaitForGraphicsInit();
|
||||||
GPU::renderCreateCtx();
|
GPU::renderCreateCtx();
|
||||||
|
|
||||||
// try to calculate buffer size
|
// 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_size = buffer_size;
|
||||||
ctx->buffers[i + startIndex].buffer_pitch = buffer_pitch;
|
ctx->buffers[i + startIndex].buffer_pitch = buffer_pitch;
|
||||||
ctx->buffers[i + startIndex].buffer_render = static_cast<Graphics::VideoOutVulkanImage*>(
|
ctx->buffers[i + startIndex].buffer_render = static_cast<Graphics::VideoOutVulkanImage*>(
|
||||||
GPU::memoryCreateObj(
|
GPU::memoryCreateObj(0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
|
||||||
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])));
|
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) {
|
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
||||||
PRINT_FUNCTION_NAME();
|
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;
|
videoOut->getCtx(handle)->m_flip_rate = rate;
|
||||||
return SCE_OK;
|
return SCE_OK;
|
||||||
}
|
}
|
||||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
||||||
// BREAKPOINT();
|
PRINT_FUNCTION_NAME();
|
||||||
PRINT_DUMMY_FUNCTION_NAME();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
return 0;
|
s32 pending = videoOut->getCtx(handle)->m_flip_status.flipPendingNum;
|
||||||
|
return pending;
|
||||||
}
|
}
|
||||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg) {
|
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
auto* ctx = videoOut->getCtx(handle);
|
auto* ctx = videoOut->getCtx(handle);
|
||||||
|
|
||||||
if (flipMode != 1) {
|
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) {
|
if (bufferIndex == -1) {
|
||||||
BREAKPOINT(); // blank output not supported
|
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");
|
LOG_TRACE_IF(log_file_videoout, "sceVideoOutSubmitFlip flip queue is full\n");
|
||||||
return SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
|
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;
|
return SCE_OK;
|
||||||
}
|
}
|
||||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status) {
|
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
auto* ctx = videoOut->getCtx(handle);
|
auto* ctx = videoOut->getCtx(handle);
|
||||||
videoOut->getFlipQueue().getFlipStatus(ctx, status);
|
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) {
|
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
|
||||||
PRINT_FUNCTION_NAME();
|
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;
|
*status = videoOut->getCtx(handle)->m_resolution;
|
||||||
return SCE_OK;
|
return SCE_OK;
|
||||||
}
|
}
|
||||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param) {
|
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param) {
|
||||||
PRINT_FUNCTION_NAME();
|
PRINT_FUNCTION_NAME();
|
||||||
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM) {
|
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM && userId != 0) {
|
||||||
BREAKPOINT();
|
BREAKPOINT();
|
||||||
}
|
}
|
||||||
if (busType != SCE_VIDEO_OUT_BUS_TYPE_MAIN) {
|
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) {
|
if (param != nullptr) {
|
||||||
BREAKPOINT();
|
BREAKPOINT();
|
||||||
}
|
}
|
||||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||||
int handle = videoOut->Open();
|
int handle = videoOut->Open();
|
||||||
|
|
||||||
if (handle < 0) {
|
if (handle < 0) {
|
||||||
|
@ -298,6 +302,12 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
|
||||||
|
|
||||||
return handle;
|
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) {
|
void videoOutRegisterLib(SymbolsResolver* sym) {
|
||||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetFlipStatus);
|
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("6kPnj51T62Y", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetResolutionStatus);
|
||||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutOpen);
|
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutOpen);
|
||||||
LIB_FUNCTION("zgXifHT9ErY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutIsFlipPending);
|
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
|
//openOrbis appears to have libSceVideoOut_v1 module libSceVideoOut_v1.1
|
||||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutOpen);
|
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("w3BY+tAEiQY", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutRegisterBuffers);
|
||||||
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip);
|
LIB_FUNCTION("U46NwOiJpys", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutSubmitFlip);
|
||||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutGetFlipStatus);
|
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 1, 1, sceVideoOutGetFlipStatus);
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
} // namespace HLE::Libs::Graphics::VideoOut
|
} // namespace HLE::Libs::Graphics::VideoOut
|
|
@ -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 sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status);
|
||||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* 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 sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param);
|
||||||
|
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||||
} // namespace HLE::Libs::Graphics::VideoOut
|
} // namespace HLE::Libs::Graphics::VideoOut
|
|
@ -1,14 +1,13 @@
|
||||||
#include "event_queue.h"
|
#include "event_queue.h"
|
||||||
|
|
||||||
#include <Lib/Timer.h>
|
|
||||||
|
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
namespace HLE::Kernel::Objects {
|
namespace HLE::Kernel::Objects {
|
||||||
EqueueInternal::~EqueueInternal() {}
|
EqueueInternal::~EqueueInternal() {}
|
||||||
|
|
||||||
int EqueueInternal::addEvent(const EqueueEvent& event) {
|
int EqueueInternal::addEvent(const EqueueEvent& event) {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock{m_mutex};
|
||||||
|
|
||||||
if (m_events.size() > 0) {
|
if (m_events.size() > 0) {
|
||||||
BREAKPOINT();
|
BREAKPOINT();
|
||||||
|
@ -24,11 +23,10 @@ int EqueueInternal::addEvent(const EqueueEvent& event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::unique_lock lock{m_mutex};
|
||||||
|
|
||||||
u32 timeElapsed = 0;
|
u64 timeElapsed = 0;
|
||||||
Lib::Timer t;
|
const auto start = std::chrono::high_resolution_clock::now();
|
||||||
t.Start();
|
|
||||||
|
|
||||||
for (;;) {
|
for (;;) {
|
||||||
int ret = getTriggeredEvents(ev, num);
|
int ret = getTriggeredEvents(ev, num);
|
||||||
|
@ -38,19 +36,20 @@ int EqueueInternal::waitForEvents(SceKernelEvent* ev, int num, u32 micros) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (micros == 0) {
|
if (micros == 0) {
|
||||||
m_cond.WaitCondVar(&m_mutex);
|
m_cond.wait(lock);
|
||||||
} else {
|
} 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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
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) {
|
if (m_events.size() > 1) {
|
||||||
BREAKPOINT(); // we currently support one event
|
BREAKPOINT(); // we currently support one event
|
||||||
|
@ -63,14 +62,12 @@ bool EqueueInternal::triggerEvent(u64 ident, s16 filter, void* trigger_data) {
|
||||||
event.isTriggered = true;
|
event.isTriggered = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_cond.SignalCondVar();
|
m_cond.notify_one();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
|
int EqueueInternal::getTriggeredEvents(SceKernelEvent* ev, int num) {
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
|
||||||
|
|
||||||
int ret = 0;
|
int ret = 0;
|
||||||
|
|
||||||
if (m_events.size() > 1) {
|
if (m_events.size() > 1) {
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
#include <Lib/Threads.h>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
@ -71,8 +72,8 @@ class EqueueInternal {
|
||||||
int getTriggeredEvents(SceKernelEvent* ev, int num);
|
int getTriggeredEvents(SceKernelEvent* ev, int num);
|
||||||
private:
|
private:
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
std::vector<EqueueEvent> m_events;
|
std::vector<EqueueEvent> m_events;
|
||||||
Lib::ConditionVariable m_cond;
|
std::condition_variable m_cond;
|
||||||
};
|
};
|
||||||
}; // namespace HLE::Kernel::Objects
|
}; // namespace HLE::Kernel::Objects
|
|
@ -5,7 +5,7 @@ namespace HLE::Kernel::Objects {
|
||||||
static u64 AlignUp(u64 pos, u64 align) { return (align != 0 ? (pos + (align - 1)) & ~(align - 1) : pos); }
|
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) {
|
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;
|
u64 find_free_pos = 0;
|
||||||
|
|
||||||
// iterate through allocated blocked and find the next free position
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
bool PhysicalMemory::Map(u64 virtual_addr, u64 phys_addr, u64 len, int prot, VirtualMemory::MemoryMode cpu_mode, GPU::MemoryMode gpu_mode) {
|
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) {
|
for (auto& b : m_allocatedBlocks) {
|
||||||
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size) {
|
if (phys_addr >= b.start_addr && phys_addr < b.start_addr + b.size) {
|
||||||
if (b.map_virtual_addr != 0 || b.map_size != 0) {
|
if (b.map_virtual_addr != 0 || b.map_size != 0) {
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
#include <types.h>
|
#include <types.h>
|
||||||
#include <Core/virtual_memory.h>
|
#include <Core/virtual_memory.h>
|
||||||
#include <Core/PS4/GPU/gpu_memory.h>
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
|
#include <mutex>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "Lib/Threads.h"
|
|
||||||
|
|
||||||
namespace HLE::Kernel::Objects {
|
namespace HLE::Kernel::Objects {
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ class PhysicalMemory {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<AllocatedBlock> m_allocatedBlocks;
|
std::vector<AllocatedBlock> m_allocatedBlocks;
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace HLE::Kernel::Objects
|
} // namespace HLE::Kernel::Objects
|
|
@ -2,13 +2,13 @@
|
||||||
|
|
||||||
#include <Core/PS4/GPU/gpu_memory.h>
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
#include <Core/virtual_memory.h>
|
#include <Core/virtual_memory.h>
|
||||||
|
#include <Util/log.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
|
|
||||||
#include <bit>
|
#include <bit>
|
||||||
#include <magic_enum.hpp>
|
#include <magic_enum.hpp>
|
||||||
|
|
||||||
#include <Util/log.h>
|
#include "Emulator/Util/singleton.h"
|
||||||
#include "../../../../Util/Singleton.h"
|
|
||||||
#include "../ErrorCodes.h"
|
#include "../ErrorCodes.h"
|
||||||
#include "../Libs.h"
|
#include "../Libs.h"
|
||||||
#include "Objects/physical_memory.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()));
|
LOG_INFO_IF(log_file_memory, "memory_type = {}\n", magic_enum::enum_name(memtype.value()));
|
||||||
|
|
||||||
u64 physical_addr = 0;
|
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)) {
|
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");
|
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EAGAIN can't allocate physical memory\n");
|
||||||
return SCE_KERNEL_ERROR_EAGAIN;
|
return SCE_KERNEL_ERROR_EAGAIN;
|
||||||
|
@ -76,7 +76,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||||
return SCE_KERNEL_ERROR_EINVAL;
|
return SCE_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
if (alignment != 0) {
|
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");
|
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL alignment invalid\n");
|
||||||
return SCE_KERNEL_ERROR_EINVAL;
|
return SCE_KERNEL_ERROR_EINVAL;
|
||||||
}
|
}
|
||||||
|
@ -92,6 +92,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||||
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
|
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
|
||||||
|
|
||||||
switch (prot) {
|
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
|
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;
|
cpu_mode = VirtualMemory::MemoryMode::ReadWrite;
|
||||||
gpu_mode = GPU::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;
|
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)) {
|
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode, gpu_mode)) {
|
||||||
BREAKPOINT();
|
BREAKPOINT();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,110 +1,98 @@
|
||||||
#include "LibC.h"
|
#include "LibC.h"
|
||||||
#include "Libs.h"
|
|
||||||
#include "../Loader/Elf.h"
|
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <pthread.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 {
|
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
|
static PS4_SYSV_ABI void init_env() // every game/demo should probably
|
||||||
{
|
{
|
||||||
//dummy no need atm
|
// dummy no need atm
|
||||||
}
|
}
|
||||||
|
|
||||||
static pthread_mutex_t __guard_mutex;
|
static PS4_SYSV_ABI void catchReturnFromMain(int status) {
|
||||||
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
|
// dummy
|
||||||
|
}
|
||||||
|
|
||||||
static void recursiveMutex()
|
static PS4_SYSV_ABI void _Assert() { BREAKPOINT(); }
|
||||||
{
|
|
||||||
pthread_mutexattr_t recursiveMutexAttr;
|
|
||||||
pthread_mutexattr_init(&recursiveMutexAttr);
|
|
||||||
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
|
|
||||||
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_mutex_t* mutex_quard() {
|
PS4_SYSV_ABI int puts(const char* s) {
|
||||||
pthread_once(&__once_control, &recursiveMutex);
|
std::puts(s);
|
||||||
return &__guard_mutex;
|
return SCE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) {
|
PS4_SYSV_ABI int rand() { return std::rand(); }
|
||||||
if ((*((uint8_t*)guard_object) != 0)) // low 8 bits checks if its already init
|
|
||||||
{
|
PS4_SYSV_ABI void _ZdlPv(void* ptr) { std::free(ptr); }
|
||||||
return 0;
|
PS4_SYSV_ABI void _ZSt11_Xbad_allocv() { BREAKPOINT(); }
|
||||||
}
|
PS4_SYSV_ABI void _ZSt14_Xlength_errorPKc() { BREAKPOINT(); }
|
||||||
int result = ::pthread_mutex_lock(mutex_quard());
|
PS4_SYSV_ABI void* _Znwm(u64 count) {
|
||||||
if (result != 0) {
|
if (count == 0) {
|
||||||
BREAKPOINT();
|
BREAKPOINT();
|
||||||
}
|
}
|
||||||
|
void* ptr = std::malloc(count);
|
||||||
|
return ptr;
|
||||||
|
}
|
||||||
|
|
||||||
// Check if another thread has completed initializer run
|
float PS4_SYSV_ABI _Fsin(float arg) { return std::sinf(arg); }
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((uint8_t*)guard_object)[1] != 0) { // the second lowest byte marks if it's being used by a thread
|
typedef int(PS4_SYSV_ABI* pfunc_QsortCmp)(const void*, const void*);
|
||||||
BREAKPOINT();
|
thread_local static pfunc_QsortCmp compair_ps4;
|
||||||
}
|
|
||||||
|
|
||||||
// mark this guard object as being in use
|
int qsort_compair(const void* arg1, const void* arg2) { return compair_ps4(arg1, arg2); }
|
||||||
((uint8_t*)guard_object)[1] = 1;
|
|
||||||
|
|
||||||
return 1;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object)
|
void LibC_Register(SymbolsResolver* sym) {
|
||||||
{
|
|
||||||
*((uint8_t*)guard_object) = 1;//mark it as done
|
|
||||||
|
|
||||||
// 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("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
|
||||||
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, __cxa_guard_acquire);
|
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_acquire);
|
||||||
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, __cxa_guard_release);
|
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_release);
|
||||||
LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, memcmp);
|
LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_abort);
|
||||||
LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, memcpy);
|
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("XKRegsFpEpk", "libc", 1, "libc", 1, 1, catchReturnFromMain);
|
||||||
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, exit);
|
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::exit);
|
||||||
LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, atexit);
|
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("-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);
|
||||||
|
|
||||||
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
|
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
|
||||||
}
|
|
||||||
|
|
||||||
};
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}; // namespace HLE::Libs::LibC
|
|
@ -7,12 +7,7 @@ namespace HLE::Libs::LibC {
|
||||||
|
|
||||||
//functions
|
//functions
|
||||||
static PS4_SYSV_ABI void init_env();
|
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 _Assert();
|
||||||
static PS4_SYSV_ABI void catchReturnFromMain(int status);
|
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);
|
|
||||||
|
|
||||||
};
|
};
|
|
@ -5,8 +5,7 @@
|
||||||
#include <io.h>
|
#include <io.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
#include "Emulator/Util/singleton.h"
|
||||||
#include "../../../Util/Singleton.h"
|
|
||||||
#include "../Loader/Elf.h"
|
#include "../Loader/Elf.h"
|
||||||
#include "Kernel/Objects/physical_memory.h"
|
#include "Kernel/Objects/physical_memory.h"
|
||||||
#include "Kernel/ThreadManagement.h"
|
#include "Kernel/ThreadManagement.h"
|
||||||
|
@ -14,6 +13,8 @@
|
||||||
#include "Kernel/event_queues.h"
|
#include "Kernel/event_queues.h"
|
||||||
#include "Kernel/memory_management.h"
|
#include "Kernel/memory_management.h"
|
||||||
#include "Libs.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 {
|
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("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelGetDirectMemorySize);
|
||||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelMapDirectMemory);
|
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelMapDirectMemory);
|
||||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||||
|
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
|
||||||
// equeue
|
// equeue
|
||||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelCreateEqueue);
|
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelCreateEqueue);
|
||||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelWaitEqueue);
|
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);
|
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||||
// time
|
// time
|
||||||
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
||||||
|
|
||||||
// Pthreads
|
// Pthreads
|
||||||
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadMutexattrInit);
|
LIB_FUNCTION("F8bUHwAG284", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadMutexattrInit);
|
||||||
LIB_FUNCTION("iMp8QpE+XO4", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadMutexattrSettype);
|
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("2Tb92quprl0", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadCondInit);
|
||||||
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadCondBroadcast);
|
LIB_FUNCTION("JGgj7Uvrl+A", "libkernel", 1, "libkernel", 1, 1, ThreadManagement::scePthreadCondBroadcast);
|
||||||
|
|
||||||
// TODO
|
|
||||||
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
|
LIB_FUNCTION("6XG4B33N09g", "libkernel", 1, "libkernel", 1, 1, sched_yield);
|
||||||
LIB_FUNCTION("mkawd0NA9ts", "libkernel", 1, "libkernel", 1, 1, sysconf);
|
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("t0fXUzq61Z4", "libkernel", 1, "libkernel", 1, 1, POSIX::_fcntl);
|
||||||
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, POSIX::_readv);
|
LIB_FUNCTION("+WRlkKjZvag", "libkernel", 1, "libkernel", 1, 1, POSIX::_readv);
|
||||||
LIB_FUNCTION("DRuBt2pvICk", "libkernel", 1, "libkernel", 1, 1, POSIX::_read);
|
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
|
}; // namespace HLE::Libs::LibKernel
|
|
@ -8,161 +8,20 @@
|
||||||
|
|
||||||
namespace HLE::Libs::LibSceGnmDriver {
|
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()
|
int32_t sceGnmSubmitDone()
|
||||||
{
|
{
|
||||||
PRINT_DUMMY_FUNCTION_NAME();
|
PRINT_DUMMY_FUNCTION_NAME();
|
||||||
return 0;
|
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();
|
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)
|
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("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("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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
|
@ -4,55 +4,6 @@
|
||||||
namespace HLE::Libs::LibSceGnmDriver {
|
namespace HLE::Libs::LibSceGnmDriver {
|
||||||
|
|
||||||
void LibSceGnmDriver_Register(SymbolsResolver* sym);
|
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();
|
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 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
|
}; // namespace HLE::Libs::LibSceGnmDriver
|
|
@ -4,6 +4,9 @@
|
||||||
#include "LibKernel.h"
|
#include "LibKernel.h"
|
||||||
#include "LibSceGnmDriver.h"
|
#include "LibSceGnmDriver.h"
|
||||||
#include <Core/PS4/HLE/Graphics/video_out.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 {
|
namespace HLE::Libs {
|
||||||
|
|
||||||
|
@ -12,5 +15,8 @@ void Init_HLE_Libs(SymbolsResolver *sym) {
|
||||||
LibKernel::LibKernel_Register(sym);
|
LibKernel::LibKernel_Register(sym);
|
||||||
Graphics::VideoOut::videoOutRegisterLib(sym);
|
Graphics::VideoOut::videoOutRegisterLib(sym);
|
||||||
LibSceGnmDriver::LibSceGnmDriver_Register(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
|
} // namespace HLE::Libs
|
|
@ -6,7 +6,7 @@
|
||||||
#include "Util/aerolib.h"
|
#include "Util/aerolib.h"
|
||||||
#include "Loader/SymbolsResolver.h"
|
#include "Loader/SymbolsResolver.h"
|
||||||
#include "HLE/Kernel/ThreadManagement.h"
|
#include "HLE/Kernel/ThreadManagement.h"
|
||||||
|
#include "Stubs.h"
|
||||||
|
|
||||||
constexpr bool debug_loader = true;
|
constexpr bool debug_loader = true;
|
||||||
|
|
||||||
|
@ -70,7 +70,7 @@ static std::string encodeId(u64 nVal)
|
||||||
}
|
}
|
||||||
Module* Linker::LoadModule(const std::string& elf_name)
|
Module* Linker::LoadModule(const std::string& elf_name)
|
||||||
{
|
{
|
||||||
Lib::LockMutexGuard lock(m_mutex);
|
std::scoped_lock lock{m_mutex};
|
||||||
auto* m = new Module;
|
auto* m = new Module;
|
||||||
m->linker = this;
|
m->linker = this;
|
||||||
m->elf = new Elf;
|
m->elf = new Elf;
|
||||||
|
@ -469,14 +469,18 @@ void Linker::LoadSymbols(Module* m)
|
||||||
//if st_value!=0 then it's export symbol
|
//if st_value!=0 then it's export symbol
|
||||||
bool is_sym_export = sym->st_value != 0;
|
bool is_sym_export = sym->st_value != 0;
|
||||||
std::string nidName = "";
|
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
|
else
|
||||||
{
|
{
|
||||||
nidName = "UNK";
|
nidName = "UNK";
|
||||||
}
|
}
|
||||||
|
|
||||||
SymbolRes sym_r{};
|
SymbolRes sym_r{};
|
||||||
sym_r.name = ids.at(0);
|
sym_r.name = ids.at(0);
|
||||||
sym_r.nidName = nidName;
|
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) {
|
void Linker::Resolve(const std::string& name, int Symtype, Module* m, SymbolRecord* return_info) {
|
||||||
auto ids = StringUtil::split_string(name, '#');
|
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) {
|
if (rec != nullptr) {
|
||||||
*return_info = *rec;
|
*return_info = *rec;
|
||||||
} else {
|
} else {
|
||||||
return_info->virtual_address = 0;
|
auto aeronid = aerolib::find_by_nid(sr.name.c_str());
|
||||||
return_info->name = "Unresolved!!!";
|
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
|
else
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
#include "Loader/Elf.h"
|
#include "Loader/Elf.h"
|
||||||
#include "Loader/SymbolsResolver.h"
|
#include "Loader/SymbolsResolver.h"
|
||||||
#include "Lib/Threads.h"
|
|
||||||
|
|
||||||
struct DynamicModuleInfo;
|
struct DynamicModuleInfo;
|
||||||
class Linker;
|
class Linker;
|
||||||
|
@ -130,5 +130,5 @@ public:
|
||||||
|
|
||||||
std::vector<Module*> m_modules;
|
std::vector<Module*> m_modules;
|
||||||
SymbolsResolver* m_HLEsymbols = nullptr;
|
SymbolsResolver* m_HLEsymbols = nullptr;
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
};
|
};
|
|
@ -206,8 +206,8 @@ bool Elf::isElfFile() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_elf_header->e_type != ET_SCE_DYNEXEC && m_elf_header->e_type != ET_SCE_DYNAMIC) {
|
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 is (%04x)\n", m_elf_header->e_type);
|
printf("ERROR:e_type expected 0xFE10 OR 0xFE18 OR 0xfe00 is (%04x)\n", m_elf_header->e_type);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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++];
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#include "types.h"
|
||||||
|
|
||||||
|
u64 UnresolvedStub();
|
||||||
|
u64 GetStub(const char *nid);
|
|
@ -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
File diff suppressed because it is too large
Load Diff
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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);
|
||||||
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||||
|
};
|
|
@ -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); }
|
|
|
@ -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
|
|
|
@ -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; }
|
|
||||||
|
|
|
@ -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;
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -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;
|
|
||||||
};
|
|
|
@ -1,29 +1,31 @@
|
||||||
#include "emulator.h"
|
#include "emulator.h"
|
||||||
|
|
||||||
#include <Core/PS4/HLE/Graphics/graphics_render.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 <vulkan_util.h>
|
||||||
|
|
||||||
#include "Core/PS4/HLE/Graphics/video_out.h"
|
#include "Core/PS4/HLE/Graphics/video_out.h"
|
||||||
|
#include "Emulator/HLE/Libraries/LibPad/pad.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emu {
|
||||||
|
|
||||||
bool m_emu_needs_exit = false;
|
bool m_emu_needs_exit = false;
|
||||||
|
|
||||||
void emuInit(u32 width, u32 height) {
|
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_width = width;
|
||||||
window_ctx->m_graphic_ctx.screen_height = height;
|
window_ctx->m_graphic_ctx.screen_height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
void checkAndWaitForGraphicsInit() {
|
void checkAndWaitForGraphicsInit() {
|
||||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||||
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
std::unique_lock lock{window_ctx->m_mutex};
|
||||||
|
|
||||||
while (!window_ctx->m_is_graphic_initialized) {
|
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) {
|
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
|
SDL_SetWindowResizable(ctx->m_window, SDL_FALSE); // we don't support resizable atm
|
||||||
}
|
}
|
||||||
void emuRun() {
|
void emuRun() {
|
||||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||||
window_ctx->m_mutex.LockMutex();
|
|
||||||
{
|
{
|
||||||
// init window and wait until init finishes
|
// init window and wait until init finishes
|
||||||
|
std::scoped_lock lock{window_ctx->m_mutex};
|
||||||
CreateSdlWindow(window_ctx);
|
CreateSdlWindow(window_ctx);
|
||||||
Graphics::Vulkan::vulkanCreate(window_ctx);
|
Graphics::Vulkan::vulkanCreate(window_ctx);
|
||||||
window_ctx->m_is_graphic_initialized = true;
|
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;
|
bool exit_loop = false;
|
||||||
|
|
||||||
|
@ -82,7 +83,7 @@ void emuRun() {
|
||||||
case SDL_EVENT_DID_ENTER_FOREGROUND: break;
|
case SDL_EVENT_DID_ENTER_FOREGROUND: break;
|
||||||
|
|
||||||
case SDL_EVENT_KEY_DOWN:
|
case SDL_EVENT_KEY_DOWN:
|
||||||
case SDL_EVENT_KEY_UP: break;
|
case SDL_EVENT_KEY_UP: keyboardEvent(&event); break;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -96,14 +97,13 @@ void emuRun() {
|
||||||
}
|
}
|
||||||
|
|
||||||
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
||||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||||
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
std::scoped_lock lock{window_ctx->m_mutex};
|
||||||
|
|
||||||
return &window_ctx->m_graphic_ctx;
|
return &window_ctx->m_graphic_ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) {
|
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) {
|
if (window_ctx->is_window_hidden) {
|
||||||
SDL_ShowWindow(window_ctx->m_window);
|
SDL_ShowWindow(window_ctx->m_window);
|
||||||
window_ctx->is_window_hidden = false;
|
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
|
|
@ -1,11 +1,13 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
#include <Core/PS4/HLE/Graphics/graphics_ctx.h>
|
||||||
#include <Lib/Threads.h>
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
|
||||||
|
#include <mutex>
|
||||||
|
#include <condition_variable>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emu {
|
||||||
|
|
||||||
struct VulkanExt {
|
struct VulkanExt {
|
||||||
bool enable_validation_layers = false;
|
bool enable_validation_layers = false;
|
||||||
|
@ -57,9 +59,9 @@ struct VulkanSwapchain {
|
||||||
|
|
||||||
struct WindowCtx {
|
struct WindowCtx {
|
||||||
HLE::Libs::Graphics::GraphicCtx m_graphic_ctx;
|
HLE::Libs::Graphics::GraphicCtx m_graphic_ctx;
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
bool m_is_graphic_initialized = false;
|
bool m_is_graphic_initialized = false;
|
||||||
Lib::ConditionVariable m_graphic_initialized_cond;
|
std::condition_variable m_graphic_initialized_cond;
|
||||||
SDL_Window* m_window = nullptr;
|
SDL_Window* m_window = nullptr;
|
||||||
bool is_window_hidden = true;
|
bool is_window_hidden = true;
|
||||||
VkSurfaceKHR m_surface = nullptr;
|
VkSurfaceKHR m_surface = nullptr;
|
||||||
|
@ -69,7 +71,7 @@ struct WindowCtx {
|
||||||
|
|
||||||
struct EmuPrivate {
|
struct EmuPrivate {
|
||||||
EmuPrivate() = default;
|
EmuPrivate() = default;
|
||||||
Lib::Mutex m_mutex;
|
std::mutex m_mutex;
|
||||||
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
HLE::Libs::Graphics::GraphicCtx* m_graphic_ctx = nullptr;
|
||||||
void* data1 = nullptr;
|
void* data1 = nullptr;
|
||||||
void* data2 = nullptr;
|
void* data2 = nullptr;
|
||||||
|
@ -81,4 +83,5 @@ void emuRun();
|
||||||
void checkAndWaitForGraphicsInit();
|
void checkAndWaitForGraphicsInit();
|
||||||
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx();
|
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx();
|
||||||
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image);
|
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image);
|
||||||
|
void keyboardEvent(SDL_Event* event);
|
||||||
} // namespace Emulator
|
} // namespace Emulator
|
58
src/main.cpp
58
src/main.cpp
|
@ -3,41 +3,43 @@
|
||||||
// If you are new to Dear ImGui, read documentation from the docs/ folder + read the top of imgui.cpp.
|
// 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
|
// 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 <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)
|
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||||
#include <SDL3/SDL_opengles2.h>
|
#include <SDL3/SDL_opengles2.h>
|
||||||
#else
|
#else
|
||||||
#include <SDL3/SDL_opengl.h>
|
#include <SDL3/SDL_opengl.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <Util/log.h>
|
||||||
|
|
||||||
|
#include "GUI/ElfViewer.h"
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
#include "types.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.
|
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
|
||||||
#ifdef __EMSCRIPTEN__
|
#ifdef __EMSCRIPTEN__
|
||||||
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
||||||
#endif
|
#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 <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
|
// Main code
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[]) {
|
||||||
{
|
|
||||||
if (argc == 1) {
|
if (argc == 1) {
|
||||||
printf("Usage: %s <elf or eboot.bin path>\n", argv[0]);
|
printf("Usage: %s <elf or eboot.bin path>\n", argv[0]);
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -46,30 +48,24 @@ int main(int argc, char* argv[])
|
||||||
logging::init(true); // init logging
|
logging::init(true); // init logging
|
||||||
auto width = Config::getScreenWidth();
|
auto width = Config::getScreenWidth();
|
||||||
auto height = Config::getScreenHeight();
|
auto height = Config::getScreenHeight();
|
||||||
Emulator::emuInit(width, height);
|
Emu::emuInit(width, height);
|
||||||
HLE::Libs::Graphics::VideoOut::videoOutInit(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
|
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());
|
HLE::Libs::Init_HLE_Libs(linker->getHLESymbols());
|
||||||
auto *module =linker->LoadModule(path);//load main executable
|
auto* module = linker->LoadModule(path); // load main executable
|
||||||
Lib::Thread mainthread(
|
std::jthread mainthread(
|
||||||
[](void*) {
|
[](std::stop_token stop_token, void*) {
|
||||||
auto* linker = Singleton<Linker>::Instance();
|
auto* linker = singleton<Linker>::instance();
|
||||||
linker->Execute();
|
linker->Execute();
|
||||||
},
|
},
|
||||||
nullptr);
|
nullptr);
|
||||||
mainthread.DetachThread();
|
|
||||||
Discord::RPC discordRPC;
|
Discord::RPC discordRPC;
|
||||||
discordRPC.init();
|
discordRPC.init();
|
||||||
discordRPC.update(Discord::RPCStatus::Idling, "");
|
discordRPC.update(Discord::RPCStatus::Idling, "");
|
||||||
Emulator::emuRun();
|
Emu::emuRun();
|
||||||
mainthread.JoinThread();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#if 0
|
#if 0
|
||||||
// Setup SDL
|
// Setup SDL
|
||||||
|
|
|
@ -3,5 +3,5 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
constexpr char VERSION[] = "0.0.1";
|
constexpr char VERSION[] = "0.0.3 WIP";
|
||||||
}
|
}
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
#include <Core/PS4/GPU/gpu_memory.h>
|
#include <Core/PS4/GPU/gpu_memory.h>
|
||||||
#include <SDL_vulkan.h>
|
#include <SDL_vulkan.h>
|
||||||
#include <Util/Singleton.h>
|
#include <Emulator/Util/singleton.h>
|
||||||
#include <Util/log.h>
|
#include <Util/log.h>
|
||||||
#include <debug.h>
|
#include <debug.h>
|
||||||
#include <vulkan/vk_enum_string_helper.h>
|
#include <vulkan/vk_enum_string_helper.h>
|
||||||
|
@ -12,8 +12,8 @@
|
||||||
|
|
||||||
constexpr bool log_file_vulkanutil = true; // disable it to disable logging
|
constexpr bool log_file_vulkanutil = true; // disable it to disable logging
|
||||||
|
|
||||||
void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
|
void Graphics::Vulkan::vulkanCreate(Emu::WindowCtx* ctx) {
|
||||||
Emulator::VulkanExt ext;
|
Emu::VulkanExt ext;
|
||||||
vulkanGetInstanceExtensions(&ext);
|
vulkanGetInstanceExtensions(&ext);
|
||||||
|
|
||||||
VkApplicationInfo app_info{};
|
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,
|
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"};
|
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, "VK_KHR_maintenance1"};
|
||||||
|
|
||||||
ctx->m_surface_capabilities = new Emulator::VulkanSurfaceCapabilities{};
|
ctx->m_surface_capabilities = new Emu::VulkanSurfaceCapabilities{};
|
||||||
Emulator::VulkanQueues queues;
|
Emu::VulkanQueues queues;
|
||||||
|
|
||||||
vulkanFindCompatiblePhysicalDevice(ctx->m_graphic_ctx.m_instance, ctx->m_surface, device_extensions, ctx->m_surface_capabilities,
|
vulkanFindCompatiblePhysicalDevice(ctx->m_graphic_ctx.m_instance, ctx->m_surface, device_extensions, ctx->m_surface_capabilities,
|
||||||
&ctx->m_graphic_ctx.m_physical_device, &queues);
|
&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);
|
ctx->swapchain = vulkanCreateSwapchain(&ctx->m_graphic_ctx, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
|
Emu::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
|
||||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||||
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
auto* s = new Emu::VulkanSwapchain;
|
||||||
|
|
||||||
auto* s = new Emulator::VulkanSwapchain;
|
|
||||||
|
|
||||||
VkExtent2D extent{};
|
VkExtent2D extent{};
|
||||||
extent.width = clamp(ctx->screen_width, window_ctx->m_surface_capabilities->capabilities.minImageExtent.width,
|
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;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues) {
|
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues) {
|
||||||
auto get_queue = [ctx](int id, const Emulator::VulkanQueueInfo& info, bool with_mutex = false) {
|
auto get_queue = [ctx](int id, const Emu::VulkanQueueInfo& info, bool with_mutex = false) {
|
||||||
ctx->queues[id].family = info.family;
|
ctx->queues[id].family = info.family;
|
||||||
ctx->queues[id].index = info.index;
|
ctx->queues[id].index = info.index;
|
||||||
vkGetDeviceQueue(ctx->m_device, ctx->queues[id].family, ctx->queues[id].index, &ctx->queues[id].vk_queue);
|
vkGetDeviceQueue(ctx->m_device, ctx->queues[id].family, ctx->queues[id].index, &ctx->queues[id].vk_queue);
|
||||||
if (with_mutex) {
|
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,
|
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
|
||||||
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
|
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
|
||||||
std::vector<VkDeviceQueueCreateInfo> queue_create_info(queues.family_count);
|
std::vector<VkDeviceQueueCreateInfo> queue_create_info(queues.family_count);
|
||||||
std::vector<std::vector<float>> queue_priority(queues.family_count);
|
std::vector<std::vector<float>> queue_priority(queues.family_count);
|
||||||
uint32_t queue_create_info_num = 0;
|
uint32_t queue_create_info_num = 0;
|
||||||
|
@ -247,7 +245,7 @@ VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device,
|
||||||
|
|
||||||
return device;
|
return device;
|
||||||
}
|
}
|
||||||
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
|
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emu::VulkanExt* ext) {
|
||||||
u32 required_extensions_count = 0;
|
u32 required_extensions_count = 0;
|
||||||
u32 available_extensions_count = 0;
|
u32 available_extensions_count = 0;
|
||||||
u32 available_layers_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,
|
void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface,
|
||||||
const std::vector<const char*>& device_extensions,
|
const std::vector<const char*>& device_extensions,
|
||||||
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||||
Emulator::VulkanQueues* out_queues) {
|
Emu::VulkanQueues* out_queues) {
|
||||||
u32 count_devices = 0;
|
u32 count_devices = 0;
|
||||||
vkEnumeratePhysicalDevices(instance, &count_devices, nullptr);
|
vkEnumeratePhysicalDevices(instance, &count_devices, nullptr);
|
||||||
|
|
||||||
|
@ -292,7 +290,7 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
|
||||||
vkEnumeratePhysicalDevices(instance, &count_devices, devices.data());
|
vkEnumeratePhysicalDevices(instance, &count_devices, devices.data());
|
||||||
|
|
||||||
VkPhysicalDevice found_best_device = nullptr;
|
VkPhysicalDevice found_best_device = nullptr;
|
||||||
Emulator::VulkanQueues found_best_queues;
|
Emu::VulkanQueues found_best_queues;
|
||||||
|
|
||||||
for (const auto& device : devices) {
|
for (const auto& device : devices) {
|
||||||
VkPhysicalDeviceProperties device_properties{};
|
VkPhysicalDeviceProperties device_properties{};
|
||||||
|
@ -316,8 +314,8 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
|
||||||
*out_queues = found_best_queues;
|
*out_queues = found_best_queues;
|
||||||
}
|
}
|
||||||
|
|
||||||
Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
Emu::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||||
Emulator::VulkanQueues qs;
|
Emu::VulkanQueues qs;
|
||||||
|
|
||||||
u32 queue_family_count = 0;
|
u32 queue_family_count = 0;
|
||||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
|
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,
|
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"));
|
(presentation_supported == VK_TRUE ? "true" : "false"));
|
||||||
for (uint32_t i = 0; i < f.queueCount; i++) {
|
for (uint32_t i = 0; i < f.queueCount; i++) {
|
||||||
Emulator::VulkanQueueInfo info;
|
Emu::VulkanQueueInfo info;
|
||||||
info.family = family;
|
info.family = family;
|
||||||
info.index = i;
|
info.index = i;
|
||||||
info.is_graphics = (f.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0;
|
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,
|
void Graphics::Vulkan::vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
|
||||||
Emulator::VulkanSurfaceCapabilities* surfaceCap) {
|
Emu::VulkanSurfaceCapabilities* surfaceCap) {
|
||||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfaceCap->capabilities);
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfaceCap->capabilities);
|
||||||
|
|
||||||
uint32_t formats_count = 0;
|
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,
|
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()];
|
auto* vk_buffer = buffer->getPool()->buffers[buffer->getIndex()];
|
||||||
|
|
||||||
HLE::Libs::Graphics::VulkanImage swapchain_image(HLE::Libs::Graphics::VulkanImageType::Unknown);
|
HLE::Libs::Graphics::VulkanImage swapchain_image(HLE::Libs::Graphics::VulkanImageType::Unknown);
|
||||||
|
|
|
@ -26,18 +26,18 @@ const T& clamp(const T& x, const T& min, const T& max) {
|
||||||
return x;
|
return x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vulkanCreate(Emulator::WindowCtx* ctx);
|
void vulkanCreate(Emu::WindowCtx* ctx);
|
||||||
void vulkanGetInstanceExtensions(Emulator::VulkanExt* ext);
|
void vulkanGetInstanceExtensions(Emu::VulkanExt* ext);
|
||||||
void vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const std::vector<const char*>& device_extensions,
|
void vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const std::vector<const char*>& device_extensions,
|
||||||
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||||
Emulator::VulkanQueues* out_queues);
|
Emu::VulkanQueues* out_queues);
|
||||||
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
|
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
|
||||||
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
|
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
|
||||||
Emulator::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
Emu::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||||
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emulator::VulkanSurfaceCapabilities* surfaceCap);
|
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emu::VulkanSurfaceCapabilities* surfaceCap);
|
||||||
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues);
|
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues);
|
||||||
Emulator::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
|
Emu::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);
|
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,
|
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);
|
u64 dst_layout);
|
||||||
void vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,
|
void vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,
|
||||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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>(); \
|
||||||
|
})
|
Loading…
Reference in New Issue