mirror of
https://github.com/shadps4-emu/shadPS4.git
synced 2025-12-13 15:19:11 +00:00
Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f77fd1a342 | ||
|
|
1b483f1f03 | ||
|
|
e9d2d37aae | ||
|
|
08b0057754 | ||
|
|
e3898029c5 | ||
|
|
077b254a4f | ||
|
|
ec362948f4 | ||
|
|
eb307b9cd9 | ||
|
|
90d24c3a3c | ||
|
|
e3effe8c0a | ||
|
|
84393e6acc | ||
|
|
189d177692 | ||
|
|
ad83c454a7 | ||
|
|
f6c97963bd | ||
|
|
044628ab13 | ||
|
|
f916fd48b0 | ||
|
|
675193fec3 | ||
|
|
2d4d39c54b | ||
|
|
fb4f7b79c0 | ||
|
|
d70c1f3f95 | ||
|
|
58721d84a0 | ||
|
|
0f80805d69 | ||
|
|
c0649b8d24 | ||
|
|
22cbfd860d | ||
|
|
470881dc11 | ||
|
|
13709121ac | ||
|
|
5a905b0c4f | ||
|
|
e1b64617a8 | ||
|
|
da9adf84c0 | ||
|
|
a25c5f738e | ||
|
|
f1ce6fe669 | ||
|
|
95f1c14dcf | ||
|
|
1e18efcd05 | ||
|
|
bc6b740ffe | ||
|
|
fe62e435ed | ||
|
|
b7c4bb7ba0 | ||
|
|
9337e859ac | ||
|
|
372ac5d160 | ||
|
|
b7e5714990 | ||
|
|
d96721e753 | ||
|
|
03f19c4d13 | ||
|
|
67da986e19 | ||
|
|
830542e870 | ||
|
|
5a2ee268f8 | ||
|
|
e31365aea3 | ||
|
|
551455e56e | ||
|
|
cefd3d95ed | ||
|
|
b9c6d9d395 | ||
|
|
825d38ef31 | ||
|
|
51641bf132 | ||
|
|
5094d3044c | ||
|
|
809413a880 | ||
|
|
f52b0ad01d | ||
|
|
7791a8eeed |
@@ -19,11 +19,51 @@ include_directories(third-party/zydis/include/Zydis)
|
||||
include_directories(third-party/winpthread/include)
|
||||
include_directories(third-party/vulkan/include)
|
||||
include_directories(third-party/xxhash/include)
|
||||
include_directories(third-party/result/include)
|
||||
add_subdirectory("third-party")
|
||||
#=================== EXAMPLE ===================
|
||||
include_directories(src)
|
||||
|
||||
set(LIBC_SOURCES src/Emulator/HLE/Libraries/LibC/Libc.cpp
|
||||
src/Emulator/HLE/Libraries/LibC/Libc.h
|
||||
src/Emulator/HLE/Libraries/LibC/printf.h
|
||||
src/Emulator/HLE/Libraries/LibC/va_ctx.h
|
||||
src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp
|
||||
src/Emulator/HLE/Libraries/LibC/libc_cxa.h
|
||||
)
|
||||
set(USERSERVICE_SOURCES src/Emulator/HLE/Libraries/LibUserService/user_service.cpp
|
||||
src/Emulator/HLE/Libraries/LibUserService/user_service.h
|
||||
)
|
||||
|
||||
set(PAD_SOURCES src/Emulator/HLE/Libraries/LibPad/pad.cpp
|
||||
src/Emulator/HLE/Libraries/LibPad/pad.h
|
||||
)
|
||||
|
||||
set(SYSTEMSERVICE_SOURCES src/Emulator/HLE/Libraries/LibSystemService/system_service.cpp
|
||||
src/Emulator/HLE/Libraries/LibSystemService/system_service.h
|
||||
)
|
||||
|
||||
set(FILESYSTEM_SOURCES src/Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.cpp
|
||||
src/Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.h
|
||||
src/Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.cpp
|
||||
src/Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.h
|
||||
)
|
||||
|
||||
set(HOST_SOURCES src/Emulator/Host/controller.cpp
|
||||
src/Emulator/Host/controller.h
|
||||
)
|
||||
|
||||
set(UTIL_SOURCES src/Emulator/Util/singleton.h
|
||||
)
|
||||
|
||||
add_executable(shadps4
|
||||
${LIBC_SOURCES}
|
||||
${USERSERVICE_SOURCES}
|
||||
${PAD_SOURCES}
|
||||
${SYSTEMSERVICE_SOURCES}
|
||||
${FILESYSTEM_SOURCES}
|
||||
${HOST_SOURCES}
|
||||
${UTIL_SOURCES}
|
||||
src/main.cpp
|
||||
src/types.h
|
||||
src/Core/FsFile.cpp
|
||||
@@ -40,6 +80,9 @@ add_executable(shadps4
|
||||
src/Core/virtual_memory.h
|
||||
src/Core/PS4/Linker.cpp
|
||||
src/Core/PS4/Linker.h
|
||||
src/Core/PS4/Stubs.cpp
|
||||
src/Core/PS4/Stubs.h
|
||||
src/Core/PS4/Util/aerolib.cpp
|
||||
src/Lib/Threads.cpp
|
||||
src/Lib/Threads.h
|
||||
src/Core/PS4/HLE/Kernel/Objects/physical_memory.h
|
||||
@@ -55,7 +98,7 @@ add_executable(shadps4
|
||||
src/Core/PS4/HLE/Kernel/cpu_management.cpp
|
||||
src/Core/PS4/HLE/Kernel/cpu_management.h
|
||||
|
||||
"src/Util/Singleton.h" "src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/Lib/Timer.cpp" "src/Lib/Timer.h" "src/Core/PS4/HLE/LibKernel.cpp" "src/Core/PS4/HLE/LibKernel.h" "src/Core/PS4/HLE/LibSceGnmDriver.cpp" "src/Core/PS4/HLE/LibSceGnmDriver.h" "src/Core/PS4/HLE/Kernel/ThreadManagement.cpp" "src/Core/PS4/HLE/Kernel/ThreadManagement.h" "src/Core/PS4/HLE/ErrorCodes.h" "src/debug.h" "src/Core/PS4/HLE/Kernel/memory_management.cpp" "src/Core/PS4/HLE/Kernel/memory_management.h" "src/Core/PS4/GPU/gpu_memory.cpp" "src/Core/PS4/GPU/gpu_memory.h" "src/emulator.cpp" "src/emulator.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.h" "src/Core/PS4/HLE/Kernel/Objects/event_queue.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.cpp" "src/Core/PS4/HLE/Graphics/Objects/video_out_ctx.h" "src/Core/PS4/HLE/Graphics/graphics_ctx.h" "src/vulkan_util.cpp" "src/vulkan_util.h" "src/Core/PS4/GPU/video_out_buffer.cpp" "src/Core/PS4/GPU/video_out_buffer.h" "src/Core/PS4/HLE/Graphics/graphics_render.cpp" "src/Core/PS4/HLE/Graphics/graphics_render.h" "src/Core/PS4/GPU/tile_manager.cpp" "src/Core/PS4/GPU/tile_manager.h" "src/version.h")
|
||||
"src/Util/Disassembler.cpp" "src/Util/Disassembler.h" "src/Core/PS4/Util/aerolib.h" "src/Core/PS4/Loader/SymbolsResolver.h" "src/Core/PS4/Loader/SymbolsResolver.cpp" "src/Core/PS4/HLE/Libs.cpp" "src/Core/PS4/HLE/Libs.h" "src/Core/PS4/HLE/LibC.cpp" "src/Core/PS4/HLE/LibC.h" "src/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/Emulator/HLE/Libraries/LibSystemService/system_service.cpp" "src/Emulator/HLE/Libraries/LibSystemService/system_service.h" "src/Emulator/HLE/Libraries/LibKernel/FileSystem/meta_file_system.h" "src/Emulator/HLE/Libraries/LibKernel/FileSystem/meta_file_system.cpp")
|
||||
|
||||
find_package(OpenGL REQUIRED)
|
||||
target_link_libraries(shadps4 PUBLIC fmt mincore spdlog IMGUI SDL3-shared ${OPENGL_LIBRARY} vulkan-1 spirv-tools-opt spirv-tools)
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
# shadPS4
|
||||
|
||||
An early PS4 emulator for Windows and Linux written in C++
|
||||
|
||||
by shadow , skmp , wheremyfoodat
|
||||
|
||||
[Check us on twitter](https://twitter.com/shadps4 "Check us on twitter")
|
||||
|
||||
# Status
|
||||
|
||||
Currently, it can only load PS4 ELF files.
|
||||
|
||||
Progress is focused on videoout_basic.elf from SDK demos, currently, it can load with fully working graphics. Others probably won't run since they might not be able to relocate all necessary functions
|
||||
Early progress , a small amount of ps4 sdk demos and homebrew games working
|
||||
|
||||
 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
|
||||
=================
|
||||
First public release . Everything is new
|
||||
11223
scripts/ps4_names.txt
Normal file
11223
scripts/ps4_names.txt
Normal file
File diff suppressed because it is too large
Load Diff
45
scripts/ps4_names2stubs.py
Normal file
45
scripts/ps4_names2stubs.py
Normal file
@@ -0,0 +1,45 @@
|
||||
# Helper script that generates stub implementation of all known nids + lookup tables
|
||||
# for shadps4
|
||||
|
||||
import sys, os
|
||||
import struct
|
||||
#from hashlib import sha1
|
||||
import hashlib
|
||||
from base64 import b64encode as base64enc
|
||||
from binascii import unhexlify as uhx
|
||||
|
||||
#ref https://github.com/SocraticBliss/ps4_name2nid_plugin
|
||||
#ref derived from https://github.com/zecoxao/sce_symbols.git
|
||||
|
||||
# needs ps4_names.txt (see: https://github.com/zecoxao/sce_symbols.git for full list)
|
||||
# generates "stubs.inl" to include in Core\PS4\Util
|
||||
|
||||
NEW_NIDS = {}
|
||||
NAMES = 'ps4_names.txt'
|
||||
|
||||
def name2nid(name):
|
||||
symbol = hashlib.sha1(name.encode() + uhx('518D64A635DED8C1E6B039B1C3E55230')).digest()
|
||||
id = struct.unpack('<Q', symbol[:8])[0]
|
||||
nid = base64enc(uhx('%016x' % id), b'+-').rstrip(b'=')
|
||||
NEW_NIDS[nid]=name
|
||||
|
||||
def save_stubs(NIDS):
|
||||
nidsSorted=sorted(NIDS.items(), key=lambda x: x[0])
|
||||
|
||||
nidsFile=open("aerolib.inl", "w")
|
||||
nidsFile.writelines('// generated using ps4_names2stubs.py\n\n')
|
||||
for nid, name in nidsSorted:
|
||||
nidsFile.writelines('STUB("%s", %s)\n' % (str(nid,'utf-8'), name))
|
||||
|
||||
nidsFile.close()
|
||||
|
||||
|
||||
|
||||
f = open(NAMES,"r")
|
||||
for line in f.readlines():
|
||||
line = line.strip()
|
||||
name2nid(line)
|
||||
|
||||
f.close()
|
||||
|
||||
save_stubs(NEW_NIDS)
|
||||
@@ -2,17 +2,17 @@
|
||||
|
||||
#include <xxhash/xxh3.h>
|
||||
|
||||
#include "Util/Singleton.h"
|
||||
#include "Emulator/Util/singleton.h"
|
||||
|
||||
void* GPU::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo /*CommandBuffer?*/, u64 virtual_addr, u64 size,
|
||||
const GPUObject& info) {
|
||||
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||
auto* gpumemory = singleton<GPUMemory>::instance();
|
||||
|
||||
return gpumemory->memoryCreateObj(submit_id, ctx, nullptr, &virtual_addr, &size, 1, info);
|
||||
}
|
||||
|
||||
void GPU::memorySetAllocArea(u64 virtual_addr, u64 size) {
|
||||
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||
auto* gpumemory = singleton<GPUMemory>::instance();
|
||||
|
||||
Lib::LockMutexGuard lock(gpumemory->m_mutex);
|
||||
|
||||
@@ -59,7 +59,7 @@ bool GPU::vulkanAllocateMemory(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::
|
||||
}
|
||||
|
||||
void GPU::flushGarlic(HLE::Libs::Graphics::GraphicCtx* ctx) {
|
||||
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||
auto* gpumemory = singleton<GPUMemory>::instance();
|
||||
gpumemory->flushAllHeaps(ctx);
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ int GPU::GPUMemory::getHeapId(u64 virtual_addr, u64 size) {
|
||||
|
||||
void* GPU::GPUMemory::memoryCreateObj(u64 submit_id, HLE::Libs::Graphics::GraphicCtx* ctx, void* todo, const u64* virtual_addr, const u64* size,
|
||||
int virtual_addr_num, const GPUObject& info) {
|
||||
auto* gpumemory = Singleton<GPUMemory>::Instance();
|
||||
auto* gpumemory = singleton<GPUMemory>::instance();
|
||||
|
||||
Lib::LockMutexGuard lock(gpumemory->m_mutex);
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#include "tile_manager.h"
|
||||
#include "Lib/Threads.h"
|
||||
#include "Util/Singleton.h"
|
||||
#include "Emulator/Util/singleton.h"
|
||||
|
||||
namespace GPU {
|
||||
|
||||
@@ -143,7 +143,7 @@ void convertTileToLinear(void* dst, const void* src,u32 width, u32 height, bool
|
||||
TileManager32 t;
|
||||
t.Init(width, height, is_neo);
|
||||
|
||||
auto* g_TileManager = Singleton<TileManager>::Instance();
|
||||
auto* g_TileManager = singleton<TileManager>::instance();
|
||||
|
||||
Lib::LockMutexGuard lock(g_TileManager->m_mutex);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "video_out_ctx.h"
|
||||
|
||||
#include <Core/PS4/HLE/LibKernel.h>
|
||||
#include <debug.h>
|
||||
|
||||
namespace HLE::Graphics::Objects {
|
||||
|
||||
@@ -28,6 +29,30 @@ int VideoOutCtx::Open() {
|
||||
|
||||
return handle;
|
||||
}
|
||||
void VideoOutCtx::Close(s32 handle) {
|
||||
Lib::LockMutexGuard lock(m_mutex);
|
||||
|
||||
m_video_out_ctx.isOpened = false;
|
||||
|
||||
if (m_video_out_ctx.m_flip_evtEq.size() > 0)
|
||||
{
|
||||
BREAKPOINT(); //we need to clear all events if they have been created
|
||||
}
|
||||
|
||||
m_video_out_ctx.m_flip_rate = 0;
|
||||
|
||||
// clear buffers
|
||||
for (auto& buffer : m_video_out_ctx.buffers) {
|
||||
buffer.buffer = nullptr;
|
||||
buffer.buffer_render = nullptr;
|
||||
buffer.buffer_size = 0;
|
||||
buffer.set_id = 0;
|
||||
}
|
||||
|
||||
m_video_out_ctx.buffers_sets.clear();
|
||||
|
||||
m_video_out_ctx.buffers_registration_index = 0;
|
||||
}
|
||||
|
||||
VideoConfigInternal* VideoOutCtx::getCtx(int handle) {
|
||||
if (handle != 1) return nullptr;
|
||||
@@ -78,7 +103,7 @@ bool FlipQueue::flip(u32 micros) {
|
||||
|
||||
auto* buffer = request.cfg->buffers[request.index].buffer_render;
|
||||
|
||||
Emulator::DrawBuffer(buffer);
|
||||
Emu::DrawBuffer(buffer);
|
||||
|
||||
m_mutex.LockMutex();
|
||||
|
||||
|
||||
@@ -58,13 +58,14 @@ class VideoOutCtx {
|
||||
virtual ~VideoOutCtx() {}
|
||||
void Init(u32 width, u32 height);
|
||||
int Open();
|
||||
void Close(s32 handle);
|
||||
VideoConfigInternal* getCtx(int handle);
|
||||
FlipQueue& getFlipQueue() { return m_flip_queue; }
|
||||
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx() {
|
||||
Lib::LockMutexGuard lock(m_mutex);
|
||||
|
||||
if (m_graphic_ctx == nullptr) {
|
||||
m_graphic_ctx = Emulator::getGraphicCtx();
|
||||
m_graphic_ctx = Emu::getGraphicCtx();
|
||||
}
|
||||
|
||||
return m_graphic_ctx;
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#include "graphics_render.h"
|
||||
|
||||
#include "Util/Singleton.h"
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include "emulator.h"
|
||||
|
||||
static thread_local GPU::CommandPool g_command_pool;
|
||||
|
||||
void GPU::renderCreateCtx() {
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
|
||||
render_ctx->setGraphicCtx(Emulator::getGraphicCtx());
|
||||
render_ctx->setGraphicCtx(Emu::getGraphicCtx());
|
||||
}
|
||||
|
||||
void GPU::CommandBuffer::allocateBuffer() {
|
||||
@@ -37,7 +37,7 @@ void GPU::CommandBuffer::freeBuffer() {
|
||||
}
|
||||
|
||||
void GPU::CommandBuffer::waitForFence() {
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
|
||||
if (m_execute) {
|
||||
auto* device = render_ctx->getGraphicCtx()->m_device;
|
||||
@@ -89,7 +89,7 @@ void GPU::CommandBuffer::executeWithSemaphore() {
|
||||
submit_info.signalSemaphoreCount = 1;
|
||||
submit_info.pSignalSemaphores = &m_pool->semaphores[m_index];
|
||||
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
|
||||
|
||||
if (queue.mutex != nullptr) {
|
||||
@@ -124,7 +124,7 @@ void GPU::CommandBuffer::execute() {
|
||||
submit_info.signalSemaphoreCount = 0;
|
||||
submit_info.pSignalSemaphores = nullptr;
|
||||
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
const auto& queue = render_ctx->getGraphicCtx()->queues[m_queue];
|
||||
|
||||
if (queue.mutex != nullptr) {
|
||||
@@ -145,7 +145,7 @@ void GPU::CommandBuffer::execute() {
|
||||
}
|
||||
}
|
||||
void GPU::CommandPool::createPool(int id) {
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
auto* ctx = render_ctx->getGraphicCtx();
|
||||
|
||||
m_pool[id] = new HLE::Libs::Graphics::VulkanCommandPool;
|
||||
@@ -206,7 +206,7 @@ void GPU::CommandPool::createPool(int id) {
|
||||
}
|
||||
|
||||
void GPU::CommandPool::deleteAllPool() {
|
||||
auto* render_ctx = Singleton<RenderCtx>::Instance();
|
||||
auto* render_ctx = singleton<RenderCtx>::instance();
|
||||
auto* ctx = render_ctx->getGraphicCtx();
|
||||
|
||||
for (auto& pool : m_pool) {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include "video_out.h"
|
||||
|
||||
#include <Core/PS4/GPU/gpu_memory.h>
|
||||
#include <Core/PS4/GPU/video_out_buffer.h>
|
||||
#include <Core/PS4/HLE/ErrorCodes.h>
|
||||
#include <Core/PS4/HLE/LibSceGnmDriver.h>
|
||||
#include <Core/PS4/HLE/Libs.h>
|
||||
#include <Core/PS4/HLE/UserManagement/UsrMngCodes.h>
|
||||
#include <Util/config.h>
|
||||
@@ -13,9 +15,8 @@
|
||||
#include <string>
|
||||
|
||||
#include "Objects/video_out_ctx.h"
|
||||
#include "Util/Singleton.h"
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include "emulator.h"
|
||||
#include <Core/PS4/GPU/gpu_memory.h>
|
||||
#include "graphics_render.h"
|
||||
|
||||
namespace HLE::Libs::Graphics::VideoOut {
|
||||
@@ -23,12 +24,12 @@ namespace HLE::Libs::Graphics::VideoOut {
|
||||
constexpr bool log_file_videoout = true; // disable it to disable logging
|
||||
|
||||
void videoOutInit(u32 width, u32 height) {
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
videoOut->Init(width, height);
|
||||
}
|
||||
|
||||
bool videoOutFlip(u32 micros) {
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
return videoOut->getFlipQueue().flip(micros);
|
||||
}
|
||||
|
||||
@@ -86,7 +87,7 @@ static void flip_delete_event_func(LibKernel::EventQueues::SceKernelEqueue eq, H
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue eq, s32 handle, void* udata) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
|
||||
auto* ctx = videoOut->getCtx(handle);
|
||||
|
||||
@@ -121,7 +122,7 @@ s32 PS4_SYSV_ABI sceVideoOutAddFlipEvent(LibKernel::EventQueues::SceKernelEqueue
|
||||
s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* const* addresses, s32 bufferNum,
|
||||
const SceVideoOutBufferAttribute* attribute) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
auto* ctx = videoOut->getCtx(handle);
|
||||
|
||||
if (handle == 1) { // main port
|
||||
@@ -163,11 +164,16 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
|
||||
|
||||
int registration_index = ctx->buffers_registration_index++;
|
||||
|
||||
Emulator::checkAndWaitForGraphicsInit();
|
||||
Emu::checkAndWaitForGraphicsInit();
|
||||
GPU::renderCreateCtx();
|
||||
|
||||
// try to calculate buffer size
|
||||
u64 buffer_size = 1280 * 768 * 4; // TODO hardcoded value should be redone
|
||||
u64 buffer_size = 0; // still calculation is probably partial or wrong :D
|
||||
if (attribute->tilingMode == 0) {
|
||||
buffer_size = 1920 * 1088 * 4;
|
||||
} else {
|
||||
buffer_size = 1920 * 1080 * 4;
|
||||
}
|
||||
u64 buffer_pitch = attribute->pitchInPixel;
|
||||
|
||||
VideoOutBufferSetInternal buf{};
|
||||
@@ -200,8 +206,7 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
|
||||
ctx->buffers[i + startIndex].buffer_size = buffer_size;
|
||||
ctx->buffers[i + startIndex].buffer_pitch = buffer_pitch;
|
||||
ctx->buffers[i + startIndex].buffer_render = static_cast<Graphics::VideoOutVulkanImage*>(
|
||||
GPU::memoryCreateObj(
|
||||
0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
|
||||
GPU::memoryCreateObj(0, videoOut->getGraphicCtx(), nullptr, reinterpret_cast<uint64_t>(addresses[i]), buffer_size, buffer_info));
|
||||
|
||||
LOG_INFO_IF(log_file_videoout, "buffers[{}] = {}\n", i + startIndex, log_hex_full(reinterpret_cast<uint64_t>(addresses[i])));
|
||||
}
|
||||
@@ -210,22 +215,24 @@ s32 PS4_SYSV_ABI sceVideoOutRegisterBuffers(s32 handle, s32 startIndex, void* co
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutSetFlipRate(s32 handle, s32 rate) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
videoOut->getCtx(handle)->m_flip_rate = rate;
|
||||
return SCE_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutIsFlipPending(s32 handle) {
|
||||
// BREAKPOINT();
|
||||
PRINT_DUMMY_FUNCTION_NAME();
|
||||
return 0;
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
s32 pending = videoOut->getCtx(handle)->m_flip_status.flipPendingNum;
|
||||
return pending;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode, s64 flipArg) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
auto* ctx = videoOut->getCtx(handle);
|
||||
|
||||
if (flipMode != 1) {
|
||||
BREAKPOINT(); // only flipmode==1 is supported
|
||||
// BREAKPOINT(); // only flipmode==1 is supported
|
||||
LOG_TRACE_IF(log_file_videoout, "sceVideoOutSubmitFlip flipmode {}\n", bufferIndex);//openBOR needs 2 but seems to work
|
||||
}
|
||||
if (bufferIndex == -1) {
|
||||
BREAKPOINT(); // blank output not supported
|
||||
@@ -242,12 +249,12 @@ s32 PS4_SYSV_ABI sceVideoOutSubmitFlip(s32 handle, s32 bufferIndex, s32 flipMode
|
||||
LOG_TRACE_IF(log_file_videoout, "sceVideoOutSubmitFlip flip queue is full\n");
|
||||
return SCE_VIDEO_OUT_ERROR_FLIP_QUEUE_FULL;
|
||||
}
|
||||
|
||||
HLE::Libs::LibSceGnmDriver::sceGnmFlushGarlic(); // hackish should be done that neccesary for niko's homebrew
|
||||
return SCE_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* status) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
auto* ctx = videoOut->getCtx(handle);
|
||||
videoOut->getFlipQueue().getFlipStatus(ctx, status);
|
||||
|
||||
@@ -263,13 +270,13 @@ s32 PS4_SYSV_ABI sceVideoOutGetFlipStatus(s32 handle, SceVideoOutFlipStatus* sta
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
*status = videoOut->getCtx(handle)->m_resolution;
|
||||
return SCE_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param) {
|
||||
PRINT_FUNCTION_NAME();
|
||||
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM) {
|
||||
if (userId != SCE_USER_SERVICE_USER_ID_SYSTEM && userId != 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
if (busType != SCE_VIDEO_OUT_BUS_TYPE_MAIN) {
|
||||
@@ -282,7 +289,7 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
|
||||
if (param != nullptr) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
auto* videoOut = Singleton<HLE::Graphics::Objects::VideoOutCtx>::Instance();
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
int handle = videoOut->Open();
|
||||
|
||||
if (handle < 0) {
|
||||
@@ -292,6 +299,12 @@ s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 i
|
||||
|
||||
return handle;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle) {
|
||||
auto* videoOut = singleton<HLE::Graphics::Objects::VideoOutCtx>::instance();
|
||||
videoOut->Close(handle);
|
||||
return SCE_OK;
|
||||
}
|
||||
s32 PS4_SYSV_ABI sceVideoOutUnregisterBuffers(s32 handle, s32 attributeIndex) { BREAKPOINT(); }
|
||||
|
||||
void videoOutRegisterLib(SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("SbU3dwp80lQ", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetFlipStatus);
|
||||
@@ -303,5 +316,7 @@ void videoOutRegisterLib(SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("6kPnj51T62Y", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutGetResolutionStatus);
|
||||
LIB_FUNCTION("Up36PTk687E", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutOpen);
|
||||
LIB_FUNCTION("zgXifHT9ErY", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutIsFlipPending);
|
||||
LIB_FUNCTION("N5KDtkIjjJ4", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutUnregisterBuffers);
|
||||
LIB_FUNCTION("uquVH4-Du78", "libSceVideoOut", 1, "libSceVideoOut", 0, 0, sceVideoOutClose);
|
||||
}
|
||||
} // 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 sceVideoOutGetResolutionStatus(s32 handle, SceVideoOutResolutionStatus* status);
|
||||
s32 PS4_SYSV_ABI sceVideoOutOpen(SceUserServiceUserId userId, s32 busType, s32 index, const void* param);
|
||||
|
||||
s32 PS4_SYSV_ABI sceVideoOutClose(s32 handle);
|
||||
} // namespace HLE::Libs::Graphics::VideoOut
|
||||
@@ -2,13 +2,13 @@
|
||||
|
||||
#include <Core/PS4/GPU/gpu_memory.h>
|
||||
#include <Core/virtual_memory.h>
|
||||
#include <Util/log.h>
|
||||
#include <debug.h>
|
||||
|
||||
#include <bit>
|
||||
#include <magic_enum.hpp>
|
||||
|
||||
#include <Util/log.h>
|
||||
#include "../../../../Util/Singleton.h"
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include "../ErrorCodes.h"
|
||||
#include "../Libs.h"
|
||||
#include "Objects/physical_memory.h"
|
||||
@@ -55,7 +55,7 @@ int PS4_SYSV_ABI sceKernelAllocateDirectMemory(s64 searchStart, s64 searchEnd, u
|
||||
LOG_INFO_IF(log_file_memory, "memory_type = {}\n", magic_enum::enum_name(memtype.value()));
|
||||
|
||||
u64 physical_addr = 0;
|
||||
auto* physical_memory = Singleton<HLE::Kernel::Objects::PhysicalMemory>::Instance();
|
||||
auto* physical_memory = singleton<HLE::Kernel::Objects::PhysicalMemory>::instance();
|
||||
if (!physical_memory->Alloc(searchStart, searchEnd, len, alignment, &physical_addr, memoryType)) {
|
||||
LOG_TRACE_IF(log_file_memory, "sceKernelAllocateDirectMemory returned SCE_KERNEL_ERROR_EAGAIN can't allocate physical memory\n");
|
||||
return SCE_KERNEL_ERROR_EAGAIN;
|
||||
@@ -76,10 +76,10 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
if (alignment != 0) {
|
||||
if ((!isPowerOfTwo(alignment) && !is16KBAligned(alignment))){
|
||||
if ((!isPowerOfTwo(alignment) && !is16KBAligned(alignment))) {
|
||||
LOG_TRACE_IF(log_file_memory, "sceKernelMapDirectMemory returned SCE_KERNEL_ERROR_EINVAL alignment invalid\n");
|
||||
return SCE_KERNEL_ERROR_EINVAL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LOG_INFO_IF(log_file_memory, "len = {}\n", log_hex_full(len));
|
||||
@@ -92,6 +92,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||
GPU::MemoryMode gpu_mode = GPU::MemoryMode::NoAccess;
|
||||
|
||||
switch (prot) {
|
||||
case 0x32:
|
||||
case 0x33: // SCE_KERNEL_PROT_CPU_READ|SCE_KERNEL_PROT_CPU_WRITE|SCE_KERNEL_PROT_GPU_READ|SCE_KERNEL_PROT_GPU_ALL
|
||||
cpu_mode = VirtualMemory::MemoryMode::ReadWrite;
|
||||
gpu_mode = GPU::MemoryMode::ReadWrite;
|
||||
@@ -114,7 +115,7 @@ int PS4_SYSV_ABI sceKernelMapDirectMemory(void** addr, u64 len, int prot, int fl
|
||||
return SCE_KERNEL_ERROR_ENOMEM;
|
||||
}
|
||||
|
||||
auto* physical_memory = Singleton<HLE::Kernel::Objects::PhysicalMemory>::Instance();
|
||||
auto* physical_memory = singleton<HLE::Kernel::Objects::PhysicalMemory>::instance();
|
||||
if (!physical_memory->Map(out_addr, directMemoryStart, len, prot, cpu_mode, gpu_mode)) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
|
||||
@@ -1,110 +1,98 @@
|
||||
#include "LibC.h"
|
||||
#include "Libs.h"
|
||||
#include "../Loader/Elf.h"
|
||||
|
||||
#include <debug.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include "../Loader/Elf.h"
|
||||
#include "Emulator/HLE/Libraries/LibC/libc.h"
|
||||
#include "Emulator/HLE/Libraries/LibC/libc_cxa.h"
|
||||
#include "ErrorCodes.h"
|
||||
#include "Libs.h"
|
||||
|
||||
namespace HLE::Libs::LibC {
|
||||
|
||||
static u32 g_need_sceLibc = 1;
|
||||
static u32 g_need_sceLibc = 1;
|
||||
|
||||
static PS4_SYSV_ABI void init_env() // every game/demo should probably
|
||||
{
|
||||
//dummy no need atm
|
||||
}
|
||||
static PS4_SYSV_ABI void init_env() // every game/demo should probably
|
||||
{
|
||||
// dummy no need atm
|
||||
}
|
||||
|
||||
static pthread_mutex_t __guard_mutex;
|
||||
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
|
||||
static PS4_SYSV_ABI void catchReturnFromMain(int status) {
|
||||
// dummy
|
||||
}
|
||||
|
||||
static void recursiveMutex()
|
||||
{
|
||||
pthread_mutexattr_t recursiveMutexAttr;
|
||||
pthread_mutexattr_init(&recursiveMutexAttr);
|
||||
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
|
||||
static PS4_SYSV_ABI void _Assert() { BREAKPOINT(); }
|
||||
|
||||
PS4_SYSV_ABI int puts(const char* s) {
|
||||
std::puts(s);
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
PS4_SYSV_ABI int rand() { return std::rand(); }
|
||||
|
||||
PS4_SYSV_ABI void _ZdlPv(void* ptr) { std::free(ptr); }
|
||||
PS4_SYSV_ABI void _ZSt11_Xbad_allocv() { BREAKPOINT(); }
|
||||
PS4_SYSV_ABI void _ZSt14_Xlength_errorPKc() { BREAKPOINT(); }
|
||||
PS4_SYSV_ABI void* _Znwm(u64 count) {
|
||||
if (count == 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
void* ptr = std::malloc(count);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
static pthread_mutex_t* mutex_quard() {
|
||||
pthread_once(&__once_control, &recursiveMutex);
|
||||
return &__guard_mutex;
|
||||
}
|
||||
float PS4_SYSV_ABI _Fsin(float arg) { return std::sinf(arg); }
|
||||
|
||||
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) {
|
||||
if ((*((uint8_t*)guard_object) != 0)) // low 8 bits checks if its already init
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
int result = ::pthread_mutex_lock(mutex_quard());
|
||||
if (result != 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
typedef int(PS4_SYSV_ABI* pfunc_QsortCmp)(const void*, const void*);
|
||||
thread_local static pfunc_QsortCmp compair_ps4;
|
||||
|
||||
// Check if another thread has completed initializer run
|
||||
if ((*((uint8_t*)guard_object) != 0)) { // check again if other thread init it
|
||||
int result = ::pthread_mutex_unlock(mutex_quard());
|
||||
if (result != 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int qsort_compair(const void* arg1, const void* arg2) { return compair_ps4(arg1, arg2); }
|
||||
|
||||
if (((uint8_t*)guard_object)[1] != 0) { // the second lowest byte marks if it's being used by a thread
|
||||
BREAKPOINT();
|
||||
}
|
||||
void PS4_SYSV_ABI qsort(void* ptr, size_t count,size_t size, int(PS4_SYSV_ABI* comp)(const void*, const void*)) {
|
||||
compair_ps4 = comp;
|
||||
std::qsort(ptr, count, size, qsort_compair);
|
||||
}
|
||||
|
||||
// mark this guard object as being in use
|
||||
((uint8_t*)guard_object)[1] = 1;
|
||||
void LibC_Register(SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
|
||||
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_acquire);
|
||||
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_release);
|
||||
LIB_FUNCTION("2emaaluWzUw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::Cxa::__cxa_guard_abort);
|
||||
LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcmp);
|
||||
LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memcpy);
|
||||
LIB_FUNCTION("8zTFvBIAIN8", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memset);
|
||||
LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, catchReturnFromMain);
|
||||
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::exit);
|
||||
LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::atexit);
|
||||
LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, _Assert);
|
||||
LIB_FUNCTION("hcuQgD53UxM", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::printf);
|
||||
LIB_FUNCTION("Q2V+iqvjgC0", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::vsnprintf);
|
||||
LIB_FUNCTION("YQ0navp+YIc", "libc", 1, "libc", 1, 1, puts);
|
||||
LIB_FUNCTION("cpCOXWMgha0", "libc", 1, "libc", 1, 1, rand);
|
||||
LIB_FUNCTION("ZtjspkJQ+vw", "libc", 1, "libc", 1, 1, _Fsin);
|
||||
LIB_FUNCTION("AEJdIVZTEmo", "libc", 1, "libc", 1, 1, qsort);
|
||||
LIB_FUNCTION("Ovb2dSJOAuE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcmp);
|
||||
LIB_FUNCTION("gQX+4GDQjpM", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::malloc);
|
||||
LIB_FUNCTION("tIhsqj0qsFE", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::free);
|
||||
LIB_FUNCTION("j4ViWNHEgww", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strlen);
|
||||
LIB_FUNCTION("6sJWiWSRuqk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strncpy);
|
||||
LIB_FUNCTION("+P6FRGH4LfA", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::memmove);
|
||||
LIB_FUNCTION("kiZSXIWd9vg", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcpy);
|
||||
LIB_FUNCTION("Ls4tzzhimqQ", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::strcat);
|
||||
LIB_FUNCTION("EH-x713A99c", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::atan2f);
|
||||
LIB_FUNCTION("QI-x0SL8jhw", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::acosf);
|
||||
LIB_FUNCTION("ZE6RNL+eLbk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::tanf);
|
||||
LIB_FUNCTION("GZWjF-YIFFk", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::asinf);
|
||||
LIB_FUNCTION("9LCjpWyQ5Zc", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::pow);
|
||||
LIB_FUNCTION("cCXjU72Z0Ow", "libc", 1, "libc", 1, 1, Emulator::HLE::Libraries::LibC::_Sin);
|
||||
|
||||
return 1;
|
||||
}
|
||||
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
|
||||
|
||||
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object)
|
||||
{
|
||||
*((uint8_t*)guard_object) = 1;//mark it as done
|
||||
LIB_FUNCTION("z+P+xCnWLBk", "libc", 1, "libc", 1, 1, _ZdlPv);
|
||||
LIB_FUNCTION("eT2UsmTewbU", "libc", 1, "libc", 1, 1, _ZSt11_Xbad_allocv);
|
||||
LIB_FUNCTION("tQIo+GIPklo", "libc", 1, "libc", 1, 1, _ZSt14_Xlength_errorPKc);
|
||||
LIB_FUNCTION("fJnpuVVBbKk", "libc", 1, "libc", 1, 1, _Znwm);
|
||||
}
|
||||
|
||||
// release global mutex
|
||||
int result = ::pthread_mutex_unlock(mutex_quard());
|
||||
if (result != 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n) {
|
||||
return ::memcmp(s1, s2, n);
|
||||
}
|
||||
|
||||
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n) {
|
||||
return ::memcpy(dest, src, n);
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void catchReturnFromMain(int status) { BREAKPOINT();
|
||||
}
|
||||
static PS4_SYSV_ABI void exit(int code) { BREAKPOINT();
|
||||
}
|
||||
static PS4_SYSV_ABI int atexit(void (*func)())
|
||||
{
|
||||
int rt = ::atexit(func);
|
||||
if (rt != 0)
|
||||
{
|
||||
BREAKPOINT();
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
static PS4_SYSV_ABI void _Assert() { BREAKPOINT(); }
|
||||
|
||||
void LibC_Register(SymbolsResolver* sym)
|
||||
{
|
||||
LIB_FUNCTION("bzQExy189ZI", "libc", 1, "libc", 1, 1, init_env);
|
||||
LIB_FUNCTION("3GPpjQdAMTw", "libc", 1, "libc", 1, 1, __cxa_guard_acquire);
|
||||
LIB_FUNCTION("9rAeANT2tyE", "libc", 1, "libc", 1, 1, __cxa_guard_release);
|
||||
LIB_FUNCTION("DfivPArhucg", "libc", 1, "libc", 1, 1, memcmp);
|
||||
LIB_FUNCTION("Q3VBxCXhUHs", "libc", 1, "libc", 1, 1, memcpy);
|
||||
LIB_FUNCTION("XKRegsFpEpk", "libc", 1, "libc", 1, 1, catchReturnFromMain);
|
||||
LIB_FUNCTION("uMei1W9uyNo", "libc", 1, "libc", 1, 1, exit);
|
||||
LIB_FUNCTION("8G2LB+A3rzg", "libc", 1, "libc", 1, 1, atexit);
|
||||
LIB_FUNCTION("-QgqOT5u2Vk", "libc", 1, "libc", 1, 1, _Assert);
|
||||
|
||||
LIB_OBJ("P330P3dFF68", "libc", 1, "libc", 1, 1, &HLE::Libs::LibC::g_need_sceLibc);
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace HLE::Libs::LibC
|
||||
@@ -7,12 +7,7 @@ namespace HLE::Libs::LibC {
|
||||
|
||||
//functions
|
||||
static PS4_SYSV_ABI void init_env();
|
||||
static PS4_SYSV_ABI void exit(int code);
|
||||
static PS4_SYSV_ABI void _Assert();
|
||||
static PS4_SYSV_ABI void catchReturnFromMain(int status);
|
||||
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object);
|
||||
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object);
|
||||
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n);
|
||||
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n);
|
||||
|
||||
|
||||
};
|
||||
@@ -1,47 +1,54 @@
|
||||
#include "../Loader/Elf.h"
|
||||
#include "LibKernel.h"
|
||||
#include "Libs.h"
|
||||
#include <debug.h>
|
||||
|
||||
#include <Util/log.h>
|
||||
#include "Kernel/memory_management.h"
|
||||
#include "../../../Util/Singleton.h"
|
||||
#include <debug.h>
|
||||
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include "../Loader/Elf.h"
|
||||
#include "Kernel/Objects/physical_memory.h"
|
||||
#include "Kernel/cpu_management.h"
|
||||
#include "Kernel/event_queues.h"
|
||||
#include "Kernel/memory_management.h"
|
||||
#include "Libs.h"
|
||||
#include "Emulator/HLE/Libraries/LibKernel/FileSystem/file_system.h"
|
||||
#include "Emulator/HLE/Libraries/LibKernel/FileSystem/posix_file_system.h"
|
||||
|
||||
namespace HLE::Libs::LibKernel {
|
||||
|
||||
static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; //dummy return
|
||||
static u64 g_stack_chk_guard = 0xDEADBEEF54321ABC; // dummy return
|
||||
|
||||
int32_t PS4_SYSV_ABI sceKernelReleaseDirectMemory(off_t start, size_t len) {
|
||||
BREAKPOINT();
|
||||
return 0;
|
||||
}
|
||||
int32_t PS4_SYSV_ABI sceKernelReleaseDirectMemory(off_t start, size_t len) {
|
||||
BREAKPOINT();
|
||||
return 0;
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void stack_chk_fail() { BREAKPOINT(); }
|
||||
u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
||||
LARGE_INTEGER c;
|
||||
QueryPerformanceCounter(&c);
|
||||
return c.QuadPart;
|
||||
}
|
||||
int PS4_SYSV_ABI sceKernelMunmap(void* addr, size_t len) { BREAKPOINT(); }
|
||||
void LibKernel_Register(SymbolsResolver* sym) {
|
||||
// obj
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &HLE::Libs::LibKernel::g_stack_chk_guard);
|
||||
// memory
|
||||
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelAllocateDirectMemory);
|
||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelGetDirectMemorySize);
|
||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelMapDirectMemory);
|
||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||
LIB_FUNCTION("cQke9UuBQOk", "libkernel", 1, "libkernel", 1, 1, sceKernelMunmap);
|
||||
// equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelWaitEqueue);
|
||||
// misc
|
||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, CPUManagement::sceKernelIsNeoMode);
|
||||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
// time
|
||||
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
||||
// 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);
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void stack_chk_fail() { BREAKPOINT();
|
||||
}
|
||||
u64 PS4_SYSV_ABI sceKernelReadTsc() {
|
||||
LARGE_INTEGER c;
|
||||
QueryPerformanceCounter(&c);
|
||||
return c.QuadPart;
|
||||
}
|
||||
void LibKernel_Register(SymbolsResolver* sym) {
|
||||
//obj
|
||||
LIB_OBJ("f7uOxY9mM1U", "libkernel", 1, "libkernel", 1, 1, &HLE::Libs::LibKernel::g_stack_chk_guard);
|
||||
//memory
|
||||
LIB_FUNCTION("rTXw65xmLIA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelAllocateDirectMemory);
|
||||
LIB_FUNCTION("pO96TwzOm5E", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelGetDirectMemorySize);
|
||||
LIB_FUNCTION("L-Q3LEjIbgA", "libkernel", 1, "libkernel", 1, 1, MemoryManagement::sceKernelMapDirectMemory);
|
||||
LIB_FUNCTION("MBuItvba6z8", "libkernel", 1, "libkernel", 1, 1, sceKernelReleaseDirectMemory);
|
||||
//equeue
|
||||
LIB_FUNCTION("D0OdFMjp46I", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelCreateEqueue);
|
||||
LIB_FUNCTION("fzyMKs9kim0", "libkernel", 1, "libkernel", 1, 1, EventQueues::sceKernelWaitEqueue);
|
||||
//misc
|
||||
LIB_FUNCTION("WslcK1FQcGI", "libkernel", 1, "libkernel", 1, 1, CPUManagement::sceKernelIsNeoMode);
|
||||
LIB_FUNCTION("Ou3iL1abvng", "libkernel", 1, "libkernel", 1, 1, stack_chk_fail);
|
||||
//time
|
||||
LIB_FUNCTION("-2IRUCO--PM", "libkernel", 1, "libkernel", 1, 1, sceKernelReadTsc);
|
||||
}
|
||||
|
||||
};
|
||||
}; // namespace HLE::Libs::LibKernel
|
||||
@@ -8,161 +8,20 @@
|
||||
|
||||
namespace HLE::Libs::LibSceGnmDriver {
|
||||
|
||||
int sceGnmAddEqEvent(/* SceKernelEqueue eq, EqEventType id,*/ void* udata)
|
||||
{ return 0;
|
||||
}
|
||||
bool sceGnmAreSubmitsAllowed()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int /* WorkloadStatus*/ sceGnmBeginWorkload(uint64_t* workload /*, WorkloadStream stream*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int /* WorkloadStatus*/ sceGnmCreateWorkloadStream(/* WorkloadStream* workloadStream,*/ const char* name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void sceGnmDebugHardwareStatus(/* HardwareStatus flag*/) {
|
||||
|
||||
}
|
||||
void sceGnmSetGsRingSizes(/* GsRingSizeSetup esgsRingSize, GsRingSizeSetup gsvsRingSize*/)
|
||||
{
|
||||
|
||||
}
|
||||
int32_t sceGnmSetWaveLimitMultipliers(uint16_t targetPipeMask, uint8_t gfxRatio, const uint8_t (*pipeRatios)[7])
|
||||
{ return 0;
|
||||
}
|
||||
|
||||
int /*MipStatsError*/ sceGnmSetupMipStatsReport(void* outputBuffer, uint32_t sizeInBytes, uint8_t intervalsBetweenReports,
|
||||
uint8_t numReportsBeforeReset /*, MipStatsResetForce mipStatsResetForce*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sceGnmSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
|
||||
const uint32_t* ccb_sizes_in_bytes)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes,
|
||||
void* ccb_gpu_addrs[], const uint32_t* ccb_sizes_in_bytes, int handle, int index,
|
||||
int flip_mode, int64_t flip_arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
void sceGnmDingDong(u32 ring_id, u32 offset_dw)
|
||||
{
|
||||
|
||||
}
|
||||
bool sceRazorIsLoaded()
|
||||
{ return true;// hmm???
|
||||
}
|
||||
int sceGnmDeleteEqEvent(/* SceKernelEqueue eq, EqEventType id*/)
|
||||
{ return 0;
|
||||
}
|
||||
int32_t sceGnmSubmitDone()
|
||||
{
|
||||
PRINT_DUMMY_FUNCTION_NAME();
|
||||
return 0;
|
||||
}
|
||||
int /* MipStatsError*/ sceGnmDisableMipStatsReport()
|
||||
{ return 0;
|
||||
}
|
||||
int sceGnmSubmitAndFlipCommandBuffersForWorkload()
|
||||
{ return 0;
|
||||
}
|
||||
int sceGnmSubmitCommandBuffersForWorkload()
|
||||
{ return 0;
|
||||
}
|
||||
int /* WorkloadStatus*/ sceGnmDestroyWorkloadStream(/*WorkloadStream workloadStream*/)
|
||||
{ return 0;
|
||||
}
|
||||
void sceGnmDingDongForWorkload()
|
||||
{
|
||||
|
||||
}
|
||||
void sceGnmDriverCaptureInProgress() {}
|
||||
void sceGnmUnmapComputeQueue(){}
|
||||
void sceGnmDriverTraceInProgress(){}
|
||||
void sceGnmDriverTriggerCapture(){}
|
||||
void sceGnmEndWorkload(){}
|
||||
void sceGnmFlushGarlic() { PRINT_FUNCTION_NAME();
|
||||
GPU::flushGarlic(Emulator::getGraphicCtx());
|
||||
GPU::flushGarlic(Emu::getGraphicCtx());
|
||||
}
|
||||
void sceGnmGetEqEventType(){}
|
||||
void sceGnmGetEqTimeStamp(){}
|
||||
void sceGnmGetGpuBlockStatus(){}
|
||||
void sceGnmGetGpuInfoStatus(){}
|
||||
void sceGnmGetLastWaitedAddress(){}
|
||||
void sceGnmGetNumTcaUnits(){}
|
||||
void sceGnmGetOffChipTessellationBufferSize(){}
|
||||
void sceGnmGetPhysicalCounterFromVirtualized(){}
|
||||
void sceGnmGetProtectionFaultTimeStamp(){}
|
||||
void sceGnmGetShaderProgramBaseAddress(){}
|
||||
void sceGnmGetShaderStatus(){}
|
||||
void sceGnmGetTheTessellationFactorRingBufferBaseAddress(){}
|
||||
void sceGnmIsUserPaEnabled(){}
|
||||
void sceGnmLogicalCuIndexToPhysicalCuIndex(){}
|
||||
void sceGnmLogicalCuMaskToPhysicalCuMask(){}
|
||||
void sceGnmMapComputeQueue(){}
|
||||
void sceGnmMapComputeQueueWithPriority(){}
|
||||
void sceRazorCaptureImmediate(){}
|
||||
void sceGnmRequestFlipAndSubmitDone(){}
|
||||
void sceGnmRequestFlipAndSubmitDoneForWorkload(){}
|
||||
void sceGnmRequestMipStatsReportAndReset(){}
|
||||
|
||||
void LibSceGnmDriver_Register(SymbolsResolver* sym)
|
||||
{
|
||||
LIB_FUNCTION("b0xyllnVY-I", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmAddEqEvent);
|
||||
LIB_FUNCTION("b08AgtPlHPg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmAreSubmitsAllowed);
|
||||
LIB_FUNCTION("ihxrbsoSKWc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmBeginWorkload);
|
||||
LIB_FUNCTION("5udAm+6boVg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmCreateWorkloadStream);
|
||||
LIB_FUNCTION("qpGITzPE+Zc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDebugHardwareStatus);
|
||||
LIB_FUNCTION("jtkqXpAOY6w", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetGsRingSizes);
|
||||
LIB_FUNCTION("XiyzNZ9J4nQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetWaveLimitMultipliers);
|
||||
LIB_FUNCTION("+xuDhxlWRPg", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSetupMipStatsReport);
|
||||
LIB_FUNCTION("zwY0YV91TTI", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitCommandBuffers);
|
||||
LIB_FUNCTION("xbxNatawohc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitAndFlipCommandBuffers);
|
||||
LIB_FUNCTION("Ga6r7H6Y0RI", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitAndFlipCommandBuffersForWorkload);
|
||||
LIB_FUNCTION("f33OrruQYbM", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceRazorIsLoaded);
|
||||
LIB_FUNCTION("jRcI8VcgTz4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitCommandBuffersForWorkload);
|
||||
LIB_FUNCTION("PVT+fuoS9gU", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDeleteEqEvent);
|
||||
LIB_FUNCTION("yvZ73uQUqrk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmSubmitDone);
|
||||
LIB_FUNCTION("UtObDRQiGbs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDestroyWorkloadStream);
|
||||
LIB_FUNCTION("bX5IbRvECXk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDingDong);
|
||||
LIB_FUNCTION("byXlqupd8cE", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDingDongForWorkload);
|
||||
LIB_FUNCTION("HHo1BAljZO8", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDisableMipStatsReport);
|
||||
LIB_FUNCTION("TLV4mswiZ4A", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverCaptureInProgress);
|
||||
LIB_FUNCTION("ArSg-TGinhk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmUnmapComputeQueue);
|
||||
LIB_FUNCTION("R6z1xM3pW-w", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverTraceInProgress);
|
||||
LIB_FUNCTION("d88anrgNoKY", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmDriverTriggerCapture);
|
||||
LIB_FUNCTION("Fa3x75OOLRA", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmEndWorkload);
|
||||
LIB_FUNCTION("iBt3Oe00Kvc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmFlushGarlic);
|
||||
LIB_FUNCTION("UoYY0DWMC0U", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetEqEventType);
|
||||
LIB_FUNCTION("H7-fgvEutM0", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetEqTimeStamp);
|
||||
LIB_FUNCTION("oL4hGI1PMpw", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetGpuBlockStatus);
|
||||
LIB_FUNCTION("tZCSL5ulnB4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetGpuInfoStatus);
|
||||
LIB_FUNCTION("iFirFzgYsvw", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetLastWaitedAddress);
|
||||
LIB_FUNCTION("KnldROUkWJY", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetNumTcaUnits);
|
||||
LIB_FUNCTION("FFVZcCu3zWU", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetOffChipTessellationBufferSize);
|
||||
LIB_FUNCTION("dewXw5roLs0", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetPhysicalCounterFromVirtualized);
|
||||
LIB_FUNCTION("fzJdEihTFV4", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetProtectionFaultTimeStamp);
|
||||
LIB_FUNCTION("nEyFbYUloIM", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetShaderProgramBaseAddress);
|
||||
LIB_FUNCTION("k7iGTvDQPLQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetShaderStatus);
|
||||
LIB_FUNCTION("ln33zjBrfjk", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmGetTheTessellationFactorRingBufferBaseAddress);
|
||||
LIB_FUNCTION("jg33rEKLfVs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmIsUserPaEnabled);
|
||||
LIB_FUNCTION("26PM5Mzl8zc", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmLogicalCuIndexToPhysicalCuIndex);
|
||||
LIB_FUNCTION("RU74kek-N0c", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmLogicalCuMaskToPhysicalCuMask);
|
||||
LIB_FUNCTION("29oKvKXzEZo", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmMapComputeQueue);
|
||||
LIB_FUNCTION("A+uGq+3KFtQ", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmMapComputeQueueWithPriority);
|
||||
LIB_FUNCTION("u9YKpRRHe-M", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceRazorCaptureImmediate);
|
||||
LIB_FUNCTION("gObODli-OH8", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestFlipAndSubmitDone);
|
||||
LIB_FUNCTION("6YRHhh5mHCs", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestFlipAndSubmitDoneForWorkload);
|
||||
LIB_FUNCTION("f85orjx7qts", "libSceGnmDriver", 1, "libSceGnmDriver", 1, 1, sceGnmRequestMipStatsReportAndReset);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -4,55 +4,6 @@
|
||||
namespace HLE::Libs::LibSceGnmDriver {
|
||||
|
||||
void LibSceGnmDriver_Register(SymbolsResolver* sym);
|
||||
|
||||
// functions
|
||||
int sceGnmAddEqEvent(/* SceKernelEqueue eq, EqEventType id,*/ void* udata);
|
||||
bool sceGnmAreSubmitsAllowed();
|
||||
int /* WorkloadStatus*/ sceGnmBeginWorkload(uint64_t* workload /*, WorkloadStream stream*/);
|
||||
int /* WorkloadStatus*/ sceGnmCreateWorkloadStream(/* WorkloadStream* workloadStream,*/ const char* name);
|
||||
void sceGnmDebugHardwareStatus(/* HardwareStatus flag*/);
|
||||
void sceGnmSetGsRingSizes(/* GsRingSizeSetup esgsRingSize, GsRingSizeSetup gsvsRingSize*/);
|
||||
int32_t sceGnmSetWaveLimitMultipliers(uint16_t targetPipeMask, uint8_t gfxRatio, const uint8_t (*pipeRatios)[7]);
|
||||
int /*MipStatsError*/ sceGnmSetupMipStatsReport(void* outputBuffer, uint32_t sizeInBytes, uint8_t intervalsBetweenReports,
|
||||
uint8_t numReportsBeforeReset /*, MipStatsResetForce mipStatsResetForce*/);
|
||||
int sceGnmSubmitCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
|
||||
const uint32_t* ccb_sizes_in_bytes);
|
||||
int sceGnmSubmitAndFlipCommandBuffers(uint32_t count, void* dcb_gpu_addrs[], const uint32_t* dcb_sizes_in_bytes, void* ccb_gpu_addrs[],
|
||||
const uint32_t* ccb_sizes_in_bytes, int handle, int index, int flip_mode, int64_t flip_arg);
|
||||
void sceGnmDingDong(u32 ring_id, u32 offset_dw);
|
||||
bool sceRazorIsLoaded();
|
||||
int sceGnmDeleteEqEvent(/* SceKernelEqueue eq, EqEventType id*/);
|
||||
int32_t sceGnmSubmitDone();
|
||||
int /* MipStatsError*/ sceGnmDisableMipStatsReport();
|
||||
int sceGnmSubmitAndFlipCommandBuffersForWorkload();
|
||||
int sceGnmSubmitCommandBuffersForWorkload();
|
||||
int /* WorkloadStatus*/ sceGnmDestroyWorkloadStream(/*WorkloadStream workloadStream*/);
|
||||
void sceGnmDingDongForWorkload();
|
||||
void sceGnmDriverCaptureInProgress();
|
||||
void sceGnmUnmapComputeQueue();
|
||||
void sceGnmDriverTraceInProgress();
|
||||
void sceGnmDriverTriggerCapture();
|
||||
void sceGnmEndWorkload();
|
||||
void sceGnmFlushGarlic();
|
||||
void sceGnmGetEqEventType();
|
||||
void sceGnmGetEqTimeStamp();
|
||||
void sceGnmGetGpuBlockStatus();
|
||||
void sceGnmGetGpuInfoStatus();
|
||||
void sceGnmGetLastWaitedAddress();
|
||||
void sceGnmGetNumTcaUnits();
|
||||
void sceGnmGetOffChipTessellationBufferSize();
|
||||
void sceGnmGetPhysicalCounterFromVirtualized();
|
||||
void sceGnmGetProtectionFaultTimeStamp();
|
||||
void sceGnmGetShaderProgramBaseAddress();
|
||||
void sceGnmGetShaderStatus();
|
||||
void sceGnmGetTheTessellationFactorRingBufferBaseAddress();
|
||||
void sceGnmIsUserPaEnabled();
|
||||
void sceGnmLogicalCuIndexToPhysicalCuIndex();
|
||||
void sceGnmLogicalCuMaskToPhysicalCuMask();
|
||||
void sceGnmMapComputeQueue();
|
||||
void sceGnmMapComputeQueueWithPriority();
|
||||
void sceRazorCaptureImmediate();
|
||||
void sceGnmRequestFlipAndSubmitDone();
|
||||
void sceGnmRequestFlipAndSubmitDoneForWorkload();
|
||||
void sceGnmRequestMipStatsReportAndReset();
|
||||
}; // namespace HLE::Libs::LibSceGnmDriver
|
||||
@@ -4,6 +4,9 @@
|
||||
#include "LibKernel.h"
|
||||
#include "LibSceGnmDriver.h"
|
||||
#include <Core/PS4/HLE/Graphics/video_out.h>
|
||||
#include "Emulator/HLE/Libraries/LibUserService/user_service.h"
|
||||
#include "Emulator/HLE/Libraries/LibPad/pad.h"
|
||||
#include <Emulator/HLE/Libraries/LibSystemService/system_service.h>
|
||||
|
||||
namespace HLE::Libs {
|
||||
|
||||
@@ -12,5 +15,8 @@ void Init_HLE_Libs(SymbolsResolver *sym) {
|
||||
LibKernel::LibKernel_Register(sym);
|
||||
Graphics::VideoOut::videoOutRegisterLib(sym);
|
||||
LibSceGnmDriver::LibSceGnmDriver_Register(sym);
|
||||
Emulator::HLE::Libraries::LibUserService::libUserService_Register(sym);
|
||||
Emulator::HLE::Libraries::LibPad::libPad_Register(sym);
|
||||
Emulator::HLE::Libraries::LibSystemService::libSystemService_Register(sym);
|
||||
}
|
||||
} // namespace HLE::Libs
|
||||
@@ -6,7 +6,7 @@
|
||||
#include "Util/aerolib.h"
|
||||
#include "Loader/SymbolsResolver.h"
|
||||
#include "HLE/Kernel/ThreadManagement.h"
|
||||
|
||||
#include "Stubs.h"
|
||||
|
||||
constexpr bool debug_loader = true;
|
||||
|
||||
@@ -467,14 +467,18 @@ void Linker::LoadSymbols(Module* m)
|
||||
//if st_value!=0 then it's export symbol
|
||||
bool is_sym_export = sym->st_value != 0;
|
||||
std::string nidName = "";
|
||||
if (aerolib::symbolsMap.find(ids.at(0)) != aerolib::symbolsMap.end())
|
||||
|
||||
auto aeronid = aerolib::find_by_nid(ids.at(0).c_str());
|
||||
|
||||
if (aeronid != nullptr)
|
||||
{
|
||||
nidName = aerolib::symbolsMap.at(ids.at(0));
|
||||
nidName = aeronid->name;
|
||||
}
|
||||
else
|
||||
{
|
||||
nidName = "UNK";
|
||||
}
|
||||
|
||||
SymbolRes sym_r{};
|
||||
sym_r.name = ids.at(0);
|
||||
sym_r.nidName = nidName;
|
||||
@@ -589,7 +593,6 @@ void Linker::Relocate(Module* m)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Linker::Resolve(const std::string& name, int Symtype, Module* m, SymbolRecord* return_info) {
|
||||
auto ids = StringUtil::split_string(name, '#');
|
||||
|
||||
@@ -616,8 +619,15 @@ void Linker::Resolve(const std::string& name, int Symtype, Module* m, SymbolReco
|
||||
if (rec != nullptr) {
|
||||
*return_info = *rec;
|
||||
} else {
|
||||
return_info->virtual_address = 0;
|
||||
return_info->name = "Unresolved!!!";
|
||||
auto aeronid = aerolib::find_by_nid(sr.name.c_str());
|
||||
if (aeronid) {
|
||||
return_info->name = aeronid->name;
|
||||
return_info->virtual_address = GetStub(aeronid->nid);
|
||||
} else {
|
||||
return_info->virtual_address = GetStub(sr.name.c_str());
|
||||
return_info->name = "Unknown !!!";
|
||||
}
|
||||
LOG_ERROR_IF(debug_loader, "Linker: Stub resolved {} as {} (lib: {}, mod: {}) \n", sr.name, return_info->name, library->name, module->name);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -640,8 +650,28 @@ static PS4_SYSV_ABI void ProgramExitFunc() {
|
||||
printf("exit function called\n");
|
||||
}
|
||||
|
||||
static PS4_SYSV_ABI void run_main_entry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
||||
reinterpret_cast<entry_func_t>(addr)(params, exit_func);
|
||||
static void run_main_entry(u64 addr, EntryParams* params, exit_func_t exit_func) {
|
||||
//reinterpret_cast<entry_func_t>(addr)(params, exit_func); // can't be used, stack has to have a specific layout
|
||||
|
||||
asm volatile (
|
||||
"andq $-16, %%rsp\n"// Align to 16 bytes
|
||||
"subq $8, %%rsp\n" // videoout_basic expects the stack to be misaligned
|
||||
|
||||
// Kernel also pushes some more things here during process init
|
||||
// at least: environment, auxv, possibly other things
|
||||
|
||||
"pushq 8(%1)\n" // copy EntryParams to top of stack like the kernel does
|
||||
"pushq 0(%1)\n" // OpenOrbis expects to find it there
|
||||
|
||||
"movq %1, %%rdi\n" // also pass params and exit func
|
||||
"movq %2, %%rsi\n" // as before
|
||||
|
||||
"jmp *%0\n" // can't use call here, as that would mangle the prepared stack.
|
||||
// there's no coming back
|
||||
:
|
||||
: "r"(addr), "r"(params), "r"(exit_func)
|
||||
: "rax", "rsi", "rdi", "rsp", "rbp"
|
||||
);
|
||||
}
|
||||
|
||||
void Linker::Execute()
|
||||
|
||||
@@ -206,8 +206,8 @@ bool Elf::isElfFile() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_elf_header->e_type != ET_SCE_DYNEXEC && m_elf_header->e_type != ET_SCE_DYNAMIC) {
|
||||
printf("ERROR:e_type expected 0xFE10 OR 0xFE18 is (%04x)\n", m_elf_header->e_type);
|
||||
if (m_elf_header->e_type != ET_SCE_DYNEXEC&& m_elf_header->e_type != ET_SCE_DYNAMIC&& m_elf_header->e_type != ET_SCE_EXEC) {
|
||||
printf("ERROR:e_type expected 0xFE10 OR 0xFE18 OR 0xfe00 is (%04x)\n", m_elf_header->e_type);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
80
src/Core/PS4/Stubs.cpp
Normal file
80
src/Core/PS4/Stubs.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
#include "Stubs.h"
|
||||
|
||||
#include "Util/aerolib.h"
|
||||
|
||||
#include "Util/log.h"
|
||||
|
||||
// Helper to provide stub implementations for missing functions
|
||||
//
|
||||
// This works by pre-compiling generic stub functions ("slots"), and then
|
||||
// on lookup, setting up the nid_entry they are matched with
|
||||
//
|
||||
// If it runs out of stubs with name information, it will return
|
||||
// a default implemetnation without function name details
|
||||
|
||||
// Up to 512, larger values lead to more resolve stub slots
|
||||
// and to longer compile / CI times
|
||||
//
|
||||
// Must match STUBS_LIST define
|
||||
#define MAX_STUBS 128
|
||||
|
||||
u64 UnresolvedStub() {
|
||||
LOG_ERROR("Unresolved Stub: called, returning zero to {}\n", __builtin_return_address(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u64 UnknownStub() {
|
||||
LOG_ERROR("Stub: Unknown (nid: Unknown) called, returning zero to {}\n", __builtin_return_address(0));
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static aerolib::nid_entry* stub_nids[MAX_STUBS];
|
||||
static std::string stub_nids_unknown[MAX_STUBS];
|
||||
|
||||
template <int stub_index>
|
||||
static u64 CommonStub() {
|
||||
auto entry = stub_nids[stub_index];
|
||||
if (entry) {
|
||||
LOG_ERROR("Stub: {} (nid: {}) called, returning zero to {}\n", entry->name, entry->nid, __builtin_return_address(0));
|
||||
} else {
|
||||
LOG_ERROR("Stub: Unknown (nid: {}) called, returning zero to {}\n", stub_nids_unknown[stub_index], __builtin_return_address(0));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static u32 UsedStubEntries;
|
||||
|
||||
#define XREP_1(x) \
|
||||
&CommonStub<x>,
|
||||
|
||||
#define XREP_2(x) XREP_1(x) XREP_1(x + 1)
|
||||
#define XREP_4(x) XREP_2(x) XREP_2(x + 2)
|
||||
#define XREP_8(x) XREP_4(x) XREP_4(x + 4)
|
||||
#define XREP_16(x) XREP_8(x) XREP_8(x + 8)
|
||||
#define XREP_32(x) XREP_16(x) XREP_16(x + 16)
|
||||
#define XREP_64(x) XREP_32(x) XREP_32(x + 32)
|
||||
#define XREP_128(x) XREP_64(x) XREP_64(x + 64)
|
||||
#define XREP_256(x) XREP_128(x) XREP_128(x + 128)
|
||||
#define XREP_512(x) XREP_256(x) XREP_256(x + 256)
|
||||
|
||||
#define STUBS_LIST XREP_128(0)
|
||||
|
||||
static u64 (*stub_handlers[MAX_STUBS])() = {
|
||||
STUBS_LIST
|
||||
};
|
||||
|
||||
u64 GetStub(const char* nid) {
|
||||
if (UsedStubEntries >= MAX_STUBS) {
|
||||
return (u64)&UnknownStub;
|
||||
}
|
||||
|
||||
auto entry = aerolib::find_by_nid(nid);
|
||||
if (!entry) {
|
||||
stub_nids_unknown[UsedStubEntries] = nid;
|
||||
} else {
|
||||
stub_nids[UsedStubEntries] = entry;
|
||||
}
|
||||
|
||||
return (u64)stub_handlers[UsedStubEntries++];
|
||||
}
|
||||
4
src/Core/PS4/Stubs.h
Normal file
4
src/Core/PS4/Stubs.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#include "types.h"
|
||||
|
||||
u64 UnresolvedStub();
|
||||
u64 GetStub(const char *nid);
|
||||
38
src/Core/PS4/Util/aerolib.cpp
Normal file
38
src/Core/PS4/Util/aerolib.cpp
Normal file
@@ -0,0 +1,38 @@
|
||||
#include "aerolib.h"
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "Util/log.h"
|
||||
|
||||
namespace aerolib {
|
||||
|
||||
// Use a direct table here + binary search as contents are static
|
||||
nid_entry nids[] = {
|
||||
#define STUB(nid, name) \
|
||||
{ nid, #name },
|
||||
#include "aerolib.inl"
|
||||
#undef STUB
|
||||
};
|
||||
|
||||
nid_entry* find_by_nid(const char* nid) {
|
||||
s64 l = 0;
|
||||
s64 r = sizeof(nids) / sizeof(nids[0]) - 1;
|
||||
|
||||
while (l <= r) {
|
||||
size_t m = l + (r - l) / 2;
|
||||
|
||||
int cmp = strcmp(nids[m].nid, nid);
|
||||
|
||||
if (cmp == 0)
|
||||
return &nids[m];
|
||||
else if (cmp < 0)
|
||||
l = m + 1;
|
||||
else
|
||||
r = m - 1;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
} // namespace aerolib
|
||||
File diff suppressed because it is too large
Load Diff
11225
src/Core/PS4/Util/aerolib.inl
Normal file
11225
src/Core/PS4/Util/aerolib.inl
Normal file
File diff suppressed because it is too large
Load Diff
63
src/Emulator/HLE/Libraries/LibC/libc.cpp
Normal file
63
src/Emulator/HLE/Libraries/LibC/libc.cpp
Normal file
@@ -0,0 +1,63 @@
|
||||
#include "libc.h"
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC {
|
||||
|
||||
PS4_SYSV_ABI int printf(VA_ARGS) {
|
||||
VA_CTX(ctx);
|
||||
return printf_ctx(&ctx);
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI vsnprintf(char* s, size_t n, const char* format, VaList* arg) { return vsnprintf_ctx(s, n, format, arg); }
|
||||
|
||||
PS4_SYSV_ABI void exit(int code) { std::exit(code); }
|
||||
|
||||
PS4_SYSV_ABI int atexit(void (*func)()) {
|
||||
int rt = std::atexit(func);
|
||||
if (rt != 0) {
|
||||
BREAKPOINT();
|
||||
}
|
||||
return rt;
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n) { return std::memcmp(s1, s2, n); }
|
||||
|
||||
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n) { return std::memcpy(dest, src, n); }
|
||||
|
||||
void* PS4_SYSV_ABI memset(void* s, int c, size_t n) { return std::memset(s, c, n); }
|
||||
|
||||
void* PS4_SYSV_ABI malloc(size_t size) { return std::malloc(size); }
|
||||
|
||||
void PS4_SYSV_ABI free(void* ptr) { std::free(ptr); }
|
||||
|
||||
int PS4_SYSV_ABI strcmp(const char* str1, const char* str2) { return std::strcmp(str1, str2); }
|
||||
|
||||
size_t PS4_SYSV_ABI strlen(const char* str) { return std::strlen(str); }
|
||||
|
||||
char* PS4_SYSV_ABI strncpy(char* dest, const char* src, size_t count) { return std::strncpy(dest, src, count); }
|
||||
|
||||
void* PS4_SYSV_ABI memmove(void* dest, const void* src, std::size_t count) { return std::memmove(dest, src, count); }
|
||||
|
||||
char* PS4_SYSV_ABI strcpy(char* dest, const char* src) { return std::strcpy(dest, src); }
|
||||
|
||||
char* PS4_SYSV_ABI strcat(char* dest, const char* src) { return std::strcat(dest, src); }
|
||||
|
||||
// math
|
||||
float PS4_SYSV_ABI atan2f(float y, float x) { return std::atan2f(y, x); }
|
||||
|
||||
float PS4_SYSV_ABI acosf(float num) { return std::acosf(num); }
|
||||
|
||||
float PS4_SYSV_ABI tanf(float num) { return std::tanf(num); }
|
||||
|
||||
float PS4_SYSV_ABI asinf(float num) { return std::asinf(num); }
|
||||
|
||||
double PS4_SYSV_ABI pow(double base, double exponent) { return std::pow(base, exponent); }
|
||||
|
||||
double PS4_SYSV_ABI _Sin(double x) { return std::sin(x); }
|
||||
|
||||
}; // namespace Emulator::HLE::Libraries::LibC
|
||||
32
src/Emulator/HLE/Libraries/LibC/libc.h
Normal file
32
src/Emulator/HLE/Libraries/LibC/libc.h
Normal file
@@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include <types.h>
|
||||
|
||||
#include "printf.h"
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC {
|
||||
|
||||
// HLE functions
|
||||
PS4_SYSV_ABI int printf(VA_ARGS);
|
||||
int PS4_SYSV_ABI vsnprintf(char* s, size_t n, const char* format, VaList* arg);
|
||||
PS4_SYSV_ABI void exit(int code);
|
||||
PS4_SYSV_ABI int atexit(void (*func)());
|
||||
int PS4_SYSV_ABI memcmp(const void* s1, const void* s2, size_t n);
|
||||
void* PS4_SYSV_ABI memcpy(void* dest, const void* src, size_t n);
|
||||
void* PS4_SYSV_ABI memset(void* s, int c, size_t n);
|
||||
void* PS4_SYSV_ABI malloc(size_t size);
|
||||
void PS4_SYSV_ABI free(void* ptr);
|
||||
int PS4_SYSV_ABI strcmp(const char* str1, const char* str2);
|
||||
size_t PS4_SYSV_ABI strlen(const char* str);
|
||||
char* PS4_SYSV_ABI strncpy(char* dest, const char* src, size_t count);
|
||||
void* PS4_SYSV_ABI memmove(void* dest, const void* src, std::size_t count);
|
||||
char* PS4_SYSV_ABI strcpy(char* destination, const char* source);
|
||||
char* PS4_SYSV_ABI strcat(char* dest, const char* src);
|
||||
float PS4_SYSV_ABI atan2f(float y, float x);
|
||||
float PS4_SYSV_ABI acosf(float num);
|
||||
float PS4_SYSV_ABI tanf(float num);
|
||||
float PS4_SYSV_ABI asinf(float num);
|
||||
double PS4_SYSV_ABI pow(double base, double exponent);
|
||||
double PS4_SYSV_ABI _Sin(double x);
|
||||
|
||||
} // namespace Emulator::HLE::Libraries::LibC
|
||||
147
src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp
Normal file
147
src/Emulator/HLE/Libraries/LibC/libc_cxa.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
#include "libc_cxa.h"
|
||||
#include "debug.h"
|
||||
#include "Util/log.h"
|
||||
|
||||
// adapted from https://opensource.apple.com/source/libcppabi/libcppabi-14/src/cxa_guard.cxx.auto.html
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC::Cxa {
|
||||
constexpr bool log_file_cxa = true; // disable it to disable logging
|
||||
|
||||
// This file implements the __cxa_guard_* functions as defined at:
|
||||
// http://www.codesourcery.com/public/cxx-abi/abi.html
|
||||
//
|
||||
// The goal of these functions is to support thread-safe, one-time
|
||||
// initialization of function scope variables. The compiler will generate
|
||||
// code like the following:
|
||||
//
|
||||
// if ( obj_guard.first_byte == 0 ) {
|
||||
// if ( __cxa_guard_acquire(&obj_guard) ) {
|
||||
// try {
|
||||
// ... initialize the object ...;
|
||||
// }
|
||||
// catch (...) {
|
||||
// __cxa_guard_abort(&obj_guard);
|
||||
// throw;
|
||||
// }
|
||||
// ... queue object destructor with __cxa_atexit() ...;
|
||||
// __cxa_guard_release(&obj_guard);
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// Notes:
|
||||
// ojb_guard is a 64-bytes in size and statically initialized to zero.
|
||||
//
|
||||
// Section 6.7 of the C++ Spec says "If control re-enters the declaration
|
||||
// recursively while the object is being initialized, the behavior is
|
||||
// undefined". This implementation calls abort().
|
||||
//
|
||||
|
||||
// Note don't use function local statics to avoid use of cxa functions...
|
||||
static pthread_mutex_t __guard_mutex;
|
||||
static pthread_once_t __once_control = PTHREAD_ONCE_INIT;
|
||||
|
||||
static void makeRecusiveMutex() {
|
||||
pthread_mutexattr_t recursiveMutexAttr;
|
||||
pthread_mutexattr_init(&recursiveMutexAttr);
|
||||
pthread_mutexattr_settype(&recursiveMutexAttr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&__guard_mutex, &recursiveMutexAttr);
|
||||
}
|
||||
|
||||
__attribute__((noinline)) static pthread_mutex_t* guard_mutex() {
|
||||
pthread_once(&__once_control, &makeRecusiveMutex);
|
||||
return &__guard_mutex;
|
||||
}
|
||||
|
||||
// helper functions for getting/setting flags in guard_object
|
||||
static bool initializerHasRun(u64* guard_object) { return (*((u08*)guard_object) != 0); }
|
||||
|
||||
static void setInitializerHasRun(u64* guard_object) { *((u08*)guard_object) = 1; }
|
||||
|
||||
static bool inUse(u64* guard_object) { return (((u08*)guard_object)[1] != 0); }
|
||||
|
||||
static void setInUse(u64* guard_object) { ((u08*)guard_object)[1] = 1; }
|
||||
|
||||
static void setNotInUse(u64* guard_object) { ((u08*)guard_object)[1] = 0; }
|
||||
|
||||
//
|
||||
// Returns 1 if the caller needs to run the initializer and then either
|
||||
// call __cxa_guard_release() or __cxa_guard_abort(). If zero is returned,
|
||||
// then the initializer has already been run. This function blocks
|
||||
// if another thread is currently running the initializer. This function
|
||||
// aborts if called again on the same guard object without an intervening
|
||||
// call to __cxa_guard_release() or __cxa_guard_abort().
|
||||
//
|
||||
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object) {
|
||||
// Double check that the initializer has not already been run
|
||||
if (initializerHasRun(guard_object)) return 0;
|
||||
|
||||
// We now need to acquire a lock that allows only one thread
|
||||
// to run the initializer. If a different thread calls
|
||||
// __cxa_guard_acquire() with the same guard object, we want
|
||||
// that thread to block until this thread is done running the
|
||||
// initializer and calls __cxa_guard_release(). But if the same
|
||||
// thread calls __cxa_guard_acquire() with the same guard object,
|
||||
// we want to abort.
|
||||
// To implement this we have one global pthread recursive mutex
|
||||
// shared by all guard objects, but only one at a time.
|
||||
|
||||
int result = ::pthread_mutex_lock(guard_mutex());
|
||||
if (result != 0) {
|
||||
LOG_TRACE_IF(log_file_cxa, "__cxa_guard_acquire(): pthread_mutex_lock failed with {}\n",result);
|
||||
}
|
||||
// At this point all other threads will block in __cxa_guard_acquire()
|
||||
|
||||
// Check if another thread has completed initializer run
|
||||
if (initializerHasRun(guard_object)) {
|
||||
int result = ::pthread_mutex_unlock(guard_mutex());
|
||||
if (result != 0) {
|
||||
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// The pthread mutex is recursive to allow other lazy initialized
|
||||
// function locals to be evaluated during evaluation of this one.
|
||||
// But if the same thread can call __cxa_guard_acquire() on the
|
||||
// *same* guard object again, we call abort();
|
||||
if (inUse(guard_object)) {
|
||||
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): initializer for function local static variable called enclosing function\n");
|
||||
}
|
||||
|
||||
// mark this guard object as being in use
|
||||
setInUse(guard_object);
|
||||
|
||||
// return non-zero to tell caller to run initializer
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// Sets the first byte of the guard_object to a non-zero value.
|
||||
// Releases any locks acquired by __cxa_guard_acquire().
|
||||
//
|
||||
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object) {
|
||||
// first mark initalizer as having been run, so
|
||||
// other threads won't try to re-run it.
|
||||
setInitializerHasRun(guard_object);
|
||||
|
||||
// release global mutex
|
||||
int result = ::pthread_mutex_unlock(guard_mutex());
|
||||
if (result != 0) {
|
||||
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_acquire(): pthread_mutex_unlock failed with {}\n",result);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Releases any locks acquired by __cxa_guard_acquire().
|
||||
//
|
||||
void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object) {
|
||||
int result = ::pthread_mutex_unlock(guard_mutex());
|
||||
if (result != 0) {
|
||||
LOG_TRACE_IF(log_file_cxa,"__cxa_guard_abort(): pthread_mutex_unlock failed with {}\n",result);
|
||||
}
|
||||
|
||||
// now reset state, so possible to try to initialize again
|
||||
setNotInUse(guard_object);
|
||||
}
|
||||
|
||||
} // namespace Emulator::HLE::Libraries::LibC::Cxa
|
||||
11
src/Emulator/HLE/Libraries/LibC/libc_cxa.h
Normal file
11
src/Emulator/HLE/Libraries/LibC/libc_cxa.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#define _TIMESPEC_DEFINED
|
||||
|
||||
#include <pthread.h>
|
||||
#include <types.h>
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC::Cxa {
|
||||
int PS4_SYSV_ABI __cxa_guard_acquire(u64* guard_object);
|
||||
void PS4_SYSV_ABI __cxa_guard_release(u64* guard_object);
|
||||
void PS4_SYSV_ABI __cxa_guard_abort(u64* guard_object);
|
||||
} // namespace Emulator::HLE::Libraries::LibC::Cxa
|
||||
702
src/Emulator/HLE/Libraries/LibC/printf.h
Normal file
702
src/Emulator/HLE/Libraries/LibC/printf.h
Normal file
@@ -0,0 +1,702 @@
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// \author (c) Marco Paland (info@paland.com)
|
||||
// 2014-2018, PALANDesign Hannover, Germany
|
||||
//
|
||||
// \license The MIT License (MIT)
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in
|
||||
// all copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
// THE SOFTWARE.
|
||||
//
|
||||
// \brief Tiny printf, sprintf and snprintf implementation, optimized for speed on
|
||||
// embedded systems with a very limited resources.
|
||||
// Use this instead of bloated standard/newlib printf.
|
||||
// These routines are thread safe and reentrant!
|
||||
//
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// Vita3K emulator project
|
||||
// Copyright (C) 2023 Vita3K team
|
||||
//
|
||||
// This program is free software; you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 2 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License along
|
||||
// with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
// copied from Vita3k project at 6/10/2023 (latest update 30/06/2023)
|
||||
// modifications for adapting va_args parameters
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdbool>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include "va_ctx.h"
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC {
|
||||
// ntoa conversion buffer size, this must be big enough to hold
|
||||
// one converted numeric number including padded zeros (dynamically created on stack)
|
||||
// 32 byte is a good default
|
||||
#define PRINTF_NTOA_BUFFER_SIZE 32U
|
||||
|
||||
// ftoa conversion buffer size, this must be big enough to hold
|
||||
// one converted float number including padded zeros (dynamically created on stack)
|
||||
// 32 byte is a good default
|
||||
#define PRINTF_FTOA_BUFFER_SIZE 32U
|
||||
|
||||
// define this to support floating point (%f)
|
||||
#define PRINTF_SUPPORT_FLOAT
|
||||
|
||||
// define this to support long long types (%llu or %p)
|
||||
#define PRINTF_SUPPORT_LONG_LONG
|
||||
|
||||
// define this to support the ptrdiff_t type (%t)
|
||||
// ptrdiff_t is normally defined in <stddef.h> as long or long long type
|
||||
#define PRINTF_SUPPORT_PTRDIFF_T
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// internal flag definitions
|
||||
#define FLAGS_ZEROPAD (1U << 0U)
|
||||
#define FLAGS_LEFT (1U << 1U)
|
||||
#define FLAGS_PLUS (1U << 2U)
|
||||
#define FLAGS_SPACE (1U << 3U)
|
||||
#define FLAGS_HASH (1U << 4U)
|
||||
#define FLAGS_UPPERCASE (1U << 5U)
|
||||
#define FLAGS_CHAR (1U << 6U)
|
||||
#define FLAGS_SHORT (1U << 7U)
|
||||
#define FLAGS_LONG (1U << 8U)
|
||||
#define FLAGS_LONG_LONG (1U << 9U)
|
||||
#define FLAGS_PRECISION (1U << 10U)
|
||||
#define FLAGS_WIDTH (1U << 11U)
|
||||
|
||||
// output function type
|
||||
typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
|
||||
|
||||
// wrapper (used as buffer) for output function type
|
||||
typedef struct {
|
||||
void (*fct)(char character, void* arg);
|
||||
void* arg;
|
||||
} out_fct_wrap_type;
|
||||
|
||||
// internal buffer output
|
||||
static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen) {
|
||||
if (idx < maxlen) {
|
||||
((char*)buffer)[idx] = character;
|
||||
}
|
||||
}
|
||||
|
||||
// internal null output
|
||||
static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen) {
|
||||
(void)character;
|
||||
(void)buffer;
|
||||
(void)idx;
|
||||
(void)maxlen;
|
||||
}
|
||||
|
||||
// internal output function wrapper
|
||||
static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen) {
|
||||
(void)idx;
|
||||
(void)maxlen;
|
||||
// buffer is the output fct pointer
|
||||
((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
|
||||
}
|
||||
|
||||
// internal strlen
|
||||
// \return The length of the string (excluding the terminating 0)
|
||||
static inline unsigned int _strlen(const char* str) {
|
||||
const char* s;
|
||||
for (s = str; *s; ++s)
|
||||
;
|
||||
return (unsigned int)(s - str);
|
||||
}
|
||||
|
||||
// internal test if char is a digit (0-9)
|
||||
// \return true if char is a digit
|
||||
static inline bool _is_digit(char ch) { return (ch >= '0') && (ch <= '9'); }
|
||||
|
||||
// internal ASCII string to unsigned int conversion
|
||||
static inline unsigned int _atoi(const char** str) {
|
||||
unsigned int i = 0U;
|
||||
while (_is_digit(**str)) {
|
||||
i = i * 10U + (unsigned int)(*((*str)++) - '0');
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
// internal itoa format
|
||||
static inline size_t _ntoa_format(out_fct_type out, char* buffer, size_t idx, size_t maxlen, char* buf, size_t len, bool negative, unsigned int base,
|
||||
unsigned int prec, unsigned int width, unsigned int flags) {
|
||||
const size_t start_idx = idx;
|
||||
|
||||
// pad leading zeros
|
||||
while (!(flags & FLAGS_LEFT) && (len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
|
||||
// handle hash
|
||||
if (flags & FLAGS_HASH) {
|
||||
if (((len == prec) || (len == width)) && (len > 0U)) {
|
||||
len--;
|
||||
if ((base == 16U) && (len > 0U)) {
|
||||
len--;
|
||||
}
|
||||
}
|
||||
if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'x';
|
||||
}
|
||||
if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = 'X';
|
||||
}
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
}
|
||||
|
||||
// handle sign
|
||||
if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
|
||||
len--;
|
||||
}
|
||||
if (len < PRINTF_NTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
} else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
} else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// pad spaces up to given width
|
||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for (size_t i = len; i < width; i++) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
for (size_t i = 0U; i < len; i++) {
|
||||
out(buf[len - i - 1U], buffer, idx++, maxlen);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx - start_idx < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
|
||||
// internal itoa for 'long' type
|
||||
static inline size_t _ntoa_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long value, bool negative, unsigned long base,
|
||||
unsigned int prec, unsigned int width, unsigned int flags) {
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
|
||||
// internal itoa for 'long long' type
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
static inline size_t _ntoa_long_long(out_fct_type out, char* buffer, size_t idx, size_t maxlen, unsigned long long value, bool negative,
|
||||
unsigned long long base, unsigned int prec, unsigned int width, unsigned int flags) {
|
||||
char buf[PRINTF_NTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
|
||||
// write if precision != 0 and value is != 0
|
||||
if (!(flags & FLAGS_PRECISION) || value) {
|
||||
do {
|
||||
const char digit = (char)(value % base);
|
||||
buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
|
||||
value /= base;
|
||||
} while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
|
||||
}
|
||||
|
||||
return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
|
||||
}
|
||||
#endif // PRINTF_SUPPORT_LONG_LONG
|
||||
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
static inline size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width,
|
||||
unsigned int flags) {
|
||||
char buf[PRINTF_FTOA_BUFFER_SIZE];
|
||||
size_t len = 0U;
|
||||
double diff = 0.0;
|
||||
|
||||
// if input is larger than thres_max, revert to exponential
|
||||
const double thres_max = (double)0x7FFFFFFF;
|
||||
|
||||
// powers of 10
|
||||
static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
|
||||
|
||||
// test for negative
|
||||
bool negative = false;
|
||||
if (value < 0) {
|
||||
negative = true;
|
||||
value = 0 - value;
|
||||
}
|
||||
|
||||
// set default precision to 6, if not set explicitly
|
||||
if (!(flags & FLAGS_PRECISION)) {
|
||||
prec = 6U;
|
||||
}
|
||||
// limit precision to 9, cause a prec >= 10 can lead to overflow errors
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
|
||||
buf[len++] = '0';
|
||||
prec--;
|
||||
}
|
||||
|
||||
int whole = (int)value;
|
||||
double tmp = (value - whole) * pow10[prec];
|
||||
unsigned long frac = (unsigned long)tmp;
|
||||
diff = tmp - frac;
|
||||
|
||||
if (diff > 0.5) {
|
||||
++frac;
|
||||
// handle rollover, e.g. case 0.99 with prec 1 is 1.0
|
||||
if (frac >= pow10[prec]) {
|
||||
frac = 0;
|
||||
++whole;
|
||||
}
|
||||
} else if ((diff == 0.5) && ((frac == 0U) || (frac & 1U))) {
|
||||
// if halfway, round up if odd, OR if last digit is 0
|
||||
++frac;
|
||||
}
|
||||
|
||||
// TBD: for very large numbers switch back to native sprintf for exponentials. Anyone want to write code to replace this?
|
||||
// Normal printf behavior is to print EVERY whole number digit which can be 100s of characters overflowing your buffers == bad
|
||||
if (value > thres_max) {
|
||||
return 0U;
|
||||
}
|
||||
|
||||
if (prec == 0U) {
|
||||
diff = value - (double)whole;
|
||||
if (diff > 0.5) {
|
||||
// greater than 0.5, round up, e.g. 1.6 -> 2
|
||||
++whole;
|
||||
} else if ((diff == 0.5) && (whole & 1)) {
|
||||
// exactly 0.5 and ODD, then round up
|
||||
// 1.5 -> 2, but 2.5 -> 2
|
||||
++whole;
|
||||
}
|
||||
} else {
|
||||
unsigned int count = prec;
|
||||
// now do fractional part, as an unsigned number
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
--count;
|
||||
buf[len++] = (char)(48U + (frac % 10U));
|
||||
if (!(frac /= 10U)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// add extra 0s
|
||||
while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
// add decimal
|
||||
buf[len++] = '.';
|
||||
}
|
||||
}
|
||||
|
||||
// do whole part, number is reversed
|
||||
while (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
buf[len++] = (char)(48 + (whole % 10));
|
||||
if (!(whole /= 10)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// pad leading zeros
|
||||
while (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
|
||||
buf[len++] = '0';
|
||||
}
|
||||
|
||||
// handle sign
|
||||
if ((len == width) && (negative || (flags & FLAGS_PLUS) || (flags & FLAGS_SPACE))) {
|
||||
len--;
|
||||
}
|
||||
if (len < PRINTF_FTOA_BUFFER_SIZE) {
|
||||
if (negative) {
|
||||
buf[len++] = '-';
|
||||
} else if (flags & FLAGS_PLUS) {
|
||||
buf[len++] = '+'; // ignore the space if the '+' exists
|
||||
} else if (flags & FLAGS_SPACE) {
|
||||
buf[len++] = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
// pad spaces up to given width
|
||||
if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
|
||||
for (size_t i = len; i < width; i++) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
// reverse string
|
||||
for (size_t i = 0U; i < len; i++) {
|
||||
out(buf[len - i - 1U], buffer, idx++, maxlen);
|
||||
}
|
||||
|
||||
// append pad spaces up to given width
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (idx < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
|
||||
return idx;
|
||||
}
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
|
||||
// internal vsnprintf
|
||||
static inline int _vsnprintf(out_fct_type out, char* buffer, const char* format, VaList* va_list) {
|
||||
unsigned int flags, width, precision, n;
|
||||
size_t idx = 0U;
|
||||
auto maxlen = static_cast<size_t>(-1);
|
||||
|
||||
if (!buffer) {
|
||||
// use null output function
|
||||
out = _out_null;
|
||||
}
|
||||
|
||||
while (*format) {
|
||||
// format specifier? %[flags][width][.precision][length]
|
||||
if (*format != '%') {
|
||||
// no
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
continue;
|
||||
} else {
|
||||
// yes, evaluate it
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate flags
|
||||
flags = 0U;
|
||||
do {
|
||||
switch (*format) {
|
||||
case '0':
|
||||
flags |= FLAGS_ZEROPAD;
|
||||
format++;
|
||||
n = 1U;
|
||||
break;
|
||||
case '-':
|
||||
flags |= FLAGS_LEFT;
|
||||
format++;
|
||||
n = 1U;
|
||||
break;
|
||||
case '+':
|
||||
flags |= FLAGS_PLUS;
|
||||
format++;
|
||||
n = 1U;
|
||||
break;
|
||||
case ' ':
|
||||
flags |= FLAGS_SPACE;
|
||||
format++;
|
||||
n = 1U;
|
||||
break;
|
||||
case '#':
|
||||
flags |= FLAGS_HASH;
|
||||
format++;
|
||||
n = 1U;
|
||||
break;
|
||||
default: n = 0U; break;
|
||||
}
|
||||
} while (n);
|
||||
|
||||
// evaluate width field
|
||||
width = 0U;
|
||||
if (_is_digit(*format)) {
|
||||
width = _atoi(&format);
|
||||
} else if (*format == '*') {
|
||||
const int w = vaArgInteger(va_list); // const int w = va.next<int>(cpu, mem);
|
||||
|
||||
if (w < 0) {
|
||||
flags |= FLAGS_LEFT; // reverse padding
|
||||
width = (unsigned int)-w;
|
||||
} else {
|
||||
width = (unsigned int)w;
|
||||
}
|
||||
format++;
|
||||
}
|
||||
|
||||
// evaluate precision field
|
||||
precision = 0U;
|
||||
if (*format == '.') {
|
||||
flags |= FLAGS_PRECISION;
|
||||
format++;
|
||||
if (_is_digit(*format)) {
|
||||
precision = _atoi(&format);
|
||||
} else if (*format == '*') {
|
||||
precision = vaArgInteger(va_list); // precision = (unsigned int)va.next<int>(cpu, mem);
|
||||
format++;
|
||||
}
|
||||
}
|
||||
|
||||
// evaluate length field
|
||||
switch (*format) {
|
||||
case 'l':
|
||||
flags |= FLAGS_LONG;
|
||||
format++;
|
||||
if (*format == 'l') {
|
||||
flags |= FLAGS_LONG_LONG;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
case 'h':
|
||||
flags |= FLAGS_SHORT;
|
||||
format++;
|
||||
if (*format == 'h') {
|
||||
flags |= FLAGS_CHAR;
|
||||
format++;
|
||||
}
|
||||
break;
|
||||
#if defined(PRINTF_SUPPORT_PTRDIFF_T)
|
||||
case 't':
|
||||
flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
#endif
|
||||
case 'j':
|
||||
flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
case 'z':
|
||||
flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
|
||||
format++;
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
// evaluate specifier
|
||||
switch (*format) {
|
||||
case 'd':
|
||||
case 'i':
|
||||
case 'u':
|
||||
case 'x':
|
||||
case 'X':
|
||||
case 'o':
|
||||
case 'b': {
|
||||
// set the base
|
||||
unsigned int base;
|
||||
if (*format == 'x' || *format == 'X') {
|
||||
base = 16U;
|
||||
} else if (*format == 'o') {
|
||||
base = 8U;
|
||||
} else if (*format == 'b') {
|
||||
base = 2U;
|
||||
flags &= ~FLAGS_HASH; // no hash for bin format
|
||||
} else {
|
||||
base = 10U;
|
||||
flags &= ~FLAGS_HASH; // no hash for dec format
|
||||
}
|
||||
// uppercase
|
||||
if (*format == 'X') {
|
||||
flags |= FLAGS_UPPERCASE;
|
||||
}
|
||||
|
||||
// no plus or space flag for u, x, X, o, b
|
||||
if ((*format != 'i') && (*format != 'd')) {
|
||||
flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
|
||||
}
|
||||
|
||||
// convert the integer
|
||||
if ((*format == 'i') || (*format == 'd')) {
|
||||
// signed
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
auto value = vaArgLongLong(va_list); // const long long value = va.next<long long>(cpu, mem);
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base,
|
||||
precision, width, flags);
|
||||
#endif
|
||||
} else if (flags & FLAGS_LONG) {
|
||||
auto value = vaArgLong(va_list); // const long value = va.next<long>(cpu, mem);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width,
|
||||
flags);
|
||||
} else {
|
||||
// const int value = (flags & FLAGS_CHAR) ? (char)va.next<int>(cpu, mem) : (flags & FLAGS_SHORT) ? (short
|
||||
// int)va.next<int>(cpu, mem): va.next<int>(cpu, mem);
|
||||
const int value = (flags & FLAGS_CHAR) ? static_cast<char>(vaArgInteger(va_list))
|
||||
: (flags & FLAGS_SHORT) ? static_cast<int16_t>(vaArgInteger(va_list))
|
||||
: vaArgInteger(va_list);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width,
|
||||
flags);
|
||||
}
|
||||
} else {
|
||||
// unsigned
|
||||
if (flags & FLAGS_LONG_LONG) {
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
// idx = _ntoa_long_long(out, buffer, idx, maxlen, va.next<unsigned long long>(cpu, mem), false, base, precision, width,
|
||||
// flags);
|
||||
idx =
|
||||
_ntoa_long_long(out, buffer, idx, maxlen, static_cast<u64>(vaArgLongLong(va_list)), false, base, precision, width, flags);
|
||||
#endif
|
||||
} else if (flags & FLAGS_LONG) {
|
||||
// idx = _ntoa_long(out, buffer, idx, maxlen, va.next<unsigned long>(cpu, mem), false, base, precision, width, flags);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, static_cast<u32>(vaArgLong(va_list)), false, base, precision, width, flags);
|
||||
} else {
|
||||
// const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va.next<unsigned int>(cpu, mem) : (flags & FLAGS_SHORT) ?
|
||||
// (unsigned short int)va.next<unsigned int>(cpu, mem) : va.next<unsigned int>(cpu, mem);
|
||||
const unsigned int value = (flags & FLAGS_CHAR) ? static_cast<u08>(vaArgInteger(va_list))
|
||||
: (flags & FLAGS_SHORT) ? static_cast<u16>(vaArgInteger(va_list))
|
||||
: static_cast<u32>(vaArgInteger(va_list));
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
#if defined(PRINTF_SUPPORT_FLOAT)
|
||||
case 'f':
|
||||
case 'F':
|
||||
// idx = _ftoa(out, buffer, idx, maxlen, va.next<double>(cpu, mem), precision, width, flags);
|
||||
idx = _ftoa(out, buffer, idx, maxlen, vaArgDouble(va_list), precision, width, flags);
|
||||
format++;
|
||||
break;
|
||||
#endif // PRINTF_SUPPORT_FLOAT
|
||||
case 'c': {
|
||||
unsigned int l = 1U;
|
||||
// pre padding
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// char output
|
||||
// out((char)va.next<int>(cpu, mem), buffer, idx++, maxlen);
|
||||
out(static_cast<char>(vaArgInteger(va_list)), buffer, idx++, maxlen);
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 's': {
|
||||
const char* p = vaArgPtr<const char>(va_list); // const char *p = va.next<Ptr<char>>(cpu, mem).get(mem);
|
||||
p = p != nullptr ? p : "(null)";
|
||||
unsigned int l = _strlen(p);
|
||||
// pre padding
|
||||
if (flags & FLAGS_PRECISION) {
|
||||
l = (l < precision ? l : precision);
|
||||
}
|
||||
if (!(flags & FLAGS_LEFT)) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
// string output
|
||||
while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
|
||||
out(*(p++), buffer, idx++, maxlen);
|
||||
}
|
||||
// post padding
|
||||
if (flags & FLAGS_LEFT) {
|
||||
while (l++ < width) {
|
||||
out(' ', buffer, idx++, maxlen);
|
||||
}
|
||||
}
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case 'p': {
|
||||
width = sizeof(void*) * 2U;
|
||||
flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
|
||||
if (is_ll) {
|
||||
// idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va.next<Ptr<void>>(cpu, mem).address(), false, 16U, precision,
|
||||
// width, flags);
|
||||
idx = _ntoa_long_long(out, buffer, idx, maxlen, reinterpret_cast<uintptr_t>(vaArgPtr<void>(va_list)), false, 16U, precision,
|
||||
width, flags);
|
||||
} else {
|
||||
#endif
|
||||
// idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va.next<Ptr<void>>(cpu, mem).address()), false, 16U,
|
||||
// precision, width, flags);
|
||||
idx = _ntoa_long(out, buffer, idx, maxlen, static_cast<uint32_t>(reinterpret_cast<uintptr_t>(vaArgPtr<void>(va_list))), false,
|
||||
16U, precision, width, flags);
|
||||
#if defined(PRINTF_SUPPORT_LONG_LONG)
|
||||
}
|
||||
#endif
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
|
||||
case '%':
|
||||
out('%', buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
|
||||
default:
|
||||
out(*format, buffer, idx++, maxlen);
|
||||
format++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// termination
|
||||
out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
|
||||
|
||||
// return written chars without terminating \0
|
||||
return (int)idx;
|
||||
}
|
||||
|
||||
static int printf_ctx(VaCtx* ctx) {
|
||||
const char* format = vaArgPtr<const char>(&ctx->va_list);
|
||||
char buffer[256];
|
||||
int result = _vsnprintf(_out_buffer, buffer, format, &ctx->va_list);
|
||||
printf("%s", buffer);
|
||||
return result;
|
||||
}
|
||||
|
||||
static int vsnprintf_ctx(char* s, size_t n, const char* format, VaList* arg) {
|
||||
char buffer[n];
|
||||
int result = _vsnprintf(_out_buffer, buffer, format, arg);
|
||||
std::strcpy(s, buffer);
|
||||
return result;
|
||||
}
|
||||
} // namespace Emulator::HLE::Libraries::LibC
|
||||
106
src/Emulator/HLE/Libraries/LibC/va_ctx.h
Normal file
106
src/Emulator/HLE/Libraries/LibC/va_ctx.h
Normal file
@@ -0,0 +1,106 @@
|
||||
#include <types.h>
|
||||
#include <xmmintrin.h>
|
||||
|
||||
#define VA_ARGS \
|
||||
uint64_t rdi, uint64_t rsi, uint64_t rdx, uint64_t rcx, uint64_t r8, uint64_t r9, uint64_t overflow_arg_area, __m128 xmm0, __m128 xmm1, \
|
||||
__m128 xmm2, __m128 xmm3, __m128 xmm4, __m128 xmm5, __m128 xmm6, __m128 xmm7, ...
|
||||
|
||||
#define VA_CTX(ctx) \
|
||||
alignas(16) VaCtx ctx; \
|
||||
(ctx).reg_save_area.gp[0] = rdi; \
|
||||
(ctx).reg_save_area.gp[1] = rsi; \
|
||||
(ctx).reg_save_area.gp[2] = rdx; \
|
||||
(ctx).reg_save_area.gp[3] = rcx; \
|
||||
(ctx).reg_save_area.gp[4] = r8; \
|
||||
(ctx).reg_save_area.gp[5] = r9; \
|
||||
(ctx).reg_save_area.fp[0] = xmm0; \
|
||||
(ctx).reg_save_area.fp[1] = xmm1; \
|
||||
(ctx).reg_save_area.fp[2] = xmm2; \
|
||||
(ctx).reg_save_area.fp[3] = xmm3; \
|
||||
(ctx).reg_save_area.fp[4] = xmm4; \
|
||||
(ctx).reg_save_area.fp[5] = xmm5; \
|
||||
(ctx).reg_save_area.fp[6] = xmm6; \
|
||||
(ctx).reg_save_area.fp[7] = xmm7; \
|
||||
(ctx).va_list.reg_save_area = &(ctx).reg_save_area; \
|
||||
(ctx).va_list.gp_offset = offsetof(VaRegSave, gp); \
|
||||
(ctx).va_list.fp_offset = offsetof(VaRegSave, fp); \
|
||||
(ctx).va_list.overflow_arg_area = &overflow_arg_area;
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibC {
|
||||
|
||||
// https://stackoverflow.com/questions/4958384/what-is-the-format-of-the-x86-64-va-list-structure
|
||||
|
||||
struct VaList {
|
||||
u32 gp_offset;
|
||||
u32 fp_offset;
|
||||
void* overflow_arg_area;
|
||||
void* reg_save_area;
|
||||
};
|
||||
|
||||
struct VaRegSave {
|
||||
u64 gp[6];
|
||||
__m128 fp[8];
|
||||
};
|
||||
|
||||
struct VaCtx {
|
||||
VaRegSave reg_save_area;
|
||||
VaList va_list;
|
||||
};
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T vaArgRegSaveAreaGp(VaList* l) {
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<u08*>(l->reg_save_area) + l->gp_offset);
|
||||
l->gp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
template <class T, u64 Align, u64 Size>
|
||||
T vaArgOverflowArgArea(VaList* l) {
|
||||
auto ptr = ((reinterpret_cast<u64>(l->overflow_arg_area) + (Align - 1)) & ~(Align - 1));
|
||||
auto* addr = reinterpret_cast<T*>(ptr);
|
||||
l->overflow_arg_area = reinterpret_cast<void*>(ptr + Size);
|
||||
return *addr;
|
||||
}
|
||||
|
||||
template <class T, uint32_t Size>
|
||||
T vaArgRegSaveAreaFp(VaList* l) {
|
||||
auto* addr = reinterpret_cast<T*>(static_cast<u08*>(l->reg_save_area) + l->fp_offset);
|
||||
l->fp_offset += Size;
|
||||
return *addr;
|
||||
}
|
||||
|
||||
inline int vaArgInteger(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<int, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<int, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline long long vaArgLongLong(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<long long, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<long long, 1, 8>(l);
|
||||
}
|
||||
inline long vaArgLong(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<long, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<long, 1, 8>(l);
|
||||
}
|
||||
|
||||
inline double vaArgDouble(VaList* l) {
|
||||
if (l->fp_offset <= 160) {
|
||||
return vaArgRegSaveAreaFp<double, 16>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<double, 1, 8>(l);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
T* vaArgPtr(VaList* l) {
|
||||
if (l->gp_offset <= 40) {
|
||||
return vaArgRegSaveAreaGp<T*, 8>(l);
|
||||
}
|
||||
return vaArgOverflowArgArea<T*, 1, 8>(l);
|
||||
}
|
||||
|
||||
} // namespace Emulator::HLE::Libraries::LibC
|
||||
@@ -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);
|
||||
}
|
||||
55
src/Emulator/HLE/Libraries/LibPad/pad.cpp
Normal file
55
src/Emulator/HLE/Libraries/LibPad/pad.cpp
Normal file
@@ -0,0 +1,55 @@
|
||||
#include "pad.h"
|
||||
|
||||
#include <Core/PS4/HLE/ErrorCodes.h>
|
||||
#include <Core/PS4/HLE/Libs.h>
|
||||
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include "Emulator/Host/controller.h"
|
||||
#include <debug.h>
|
||||
#include <Util/log.h>
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibPad {
|
||||
|
||||
constexpr bool log_file_pad = true; // disable it to disable logging
|
||||
|
||||
int PS4_SYSV_ABI scePadInit() { return SCE_OK; }
|
||||
|
||||
int PS4_SYSV_ABI scePadOpen(Emulator::HLE::Libraries::LibUserService::SceUserServiceUserId userId, s32 type, s32 index,
|
||||
const ScePadOpenParam* pParam) {
|
||||
return 1; // dummy
|
||||
}
|
||||
|
||||
int PS4_SYSV_ABI scePadReadState(int32_t handle, ScePadData* pData) {
|
||||
auto* controller = singleton<Emulator::Host::Controller::GameController>::instance();
|
||||
|
||||
int connectedCount = 0;
|
||||
bool isConnected = false;
|
||||
Emulator::Host::Controller::State state;
|
||||
|
||||
controller->readState(&state, &isConnected, &connectedCount);
|
||||
pData->buttons = state.buttonsState;
|
||||
pData->leftStick.x = 128; // dummy
|
||||
pData->leftStick.y = 128; // dummy
|
||||
pData->rightStick.x = 0; // dummy
|
||||
pData->rightStick.y = 0; // dummy
|
||||
pData->analogButtons.r2 = 0;//dummy
|
||||
pData->analogButtons.l2 = 0;//dummy
|
||||
pData->orientation.x = 0;
|
||||
pData->orientation.y = 0;
|
||||
pData->orientation.z = 0;
|
||||
pData->orientation.w = 0;
|
||||
|
||||
pData->connected = true; // isConnected; //TODO fix me proper
|
||||
pData->connectedCount = 1;//connectedCount;
|
||||
pData->deviceUniqueDataLen = 0;
|
||||
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
void libPad_Register(SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("hv1luiJrqQM", "libScePad", 1, "libScePad", 1, 1, scePadInit);
|
||||
LIB_FUNCTION("xk0AcarP3V4", "libScePad", 1, "libScePad", 1, 1, scePadOpen);
|
||||
LIB_FUNCTION("YndgXqQVV7c", "libScePad", 1, "libScePad", 1, 1, scePadReadState);
|
||||
}
|
||||
|
||||
} // namespace Emulator::HLE::Libraries::LibPad
|
||||
98
src/Emulator/HLE/Libraries/LibPad/pad.h
Normal file
98
src/Emulator/HLE/Libraries/LibPad/pad.h
Normal file
@@ -0,0 +1,98 @@
|
||||
#pragma once
|
||||
#include <Emulator/HLE/Libraries/LibUserService/user_service.h>
|
||||
#include <types.h>
|
||||
|
||||
#include "Core/PS4/Loader/SymbolsResolver.h"
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibPad {
|
||||
|
||||
typedef enum : u32 {
|
||||
SCE_PAD_BUTTON_L3 = 0x00000002,
|
||||
SCE_PAD_BUTTON_R3 = 0x00000004,
|
||||
SCE_PAD_BUTTON_OPTIONS = 0x00000008,
|
||||
SCE_PAD_BUTTON_UP = 0x00000010,
|
||||
SCE_PAD_BUTTON_RIGHT = 0x00000020,
|
||||
SCE_PAD_BUTTON_DOWN = 0x00000040,
|
||||
SCE_PAD_BUTTON_LEFT = 0x00000080,
|
||||
SCE_PAD_BUTTON_L2 = 0x00000100,
|
||||
SCE_PAD_BUTTON_R2 = 0x00000200,
|
||||
SCE_PAD_BUTTON_L1 = 0x00000400,
|
||||
SCE_PAD_BUTTON_R1 = 0x00000800,
|
||||
SCE_PAD_BUTTON_TRIANGLE = 0x00001000,
|
||||
SCE_PAD_BUTTON_CIRCLE = 0x00002000,
|
||||
SCE_PAD_BUTTON_CROSS = 0x00004000,
|
||||
SCE_PAD_BUTTON_SQUARE = 0x00008000,
|
||||
SCE_PAD_BUTTON_TOUCH_PAD = 0x00100000,
|
||||
SCE_PAD_BUTTON_INTERCEPTED = 0x80000000,
|
||||
} ScePadButton;
|
||||
|
||||
struct ScePadOpenParam {
|
||||
u08 reserve[8];
|
||||
};
|
||||
|
||||
struct ScePadAnalogStick {
|
||||
u08 x;
|
||||
u08 y;
|
||||
};
|
||||
struct ScePadAnalogButtons {
|
||||
u08 l2;
|
||||
u08 r2;
|
||||
u08 padding[2];
|
||||
};
|
||||
|
||||
struct SceFQuaternion {
|
||||
float x, y, z, w;
|
||||
};
|
||||
|
||||
struct SceFVector3 {
|
||||
float x, y, z;
|
||||
};
|
||||
|
||||
struct ScePadTouch {
|
||||
u16 x;
|
||||
u16 y;
|
||||
u08 id;
|
||||
u08 reserve[3];
|
||||
};
|
||||
|
||||
constexpr int SCE_PAD_MAX_TOUCH_NUM = 2;
|
||||
|
||||
typedef struct ScePadTouchData {
|
||||
u08 touchNum;
|
||||
u08 reserve[3];
|
||||
u32 reserve1;
|
||||
ScePadTouch touch[SCE_PAD_MAX_TOUCH_NUM];
|
||||
} ScePadTouchData;
|
||||
|
||||
struct ScePadExtensionUnitData {
|
||||
u32 extensionUnitId;
|
||||
u08 reserve[1];
|
||||
u08 dataLength;
|
||||
u08 data[10];
|
||||
};
|
||||
|
||||
struct ScePadData {
|
||||
u32 buttons;
|
||||
ScePadAnalogStick leftStick;
|
||||
ScePadAnalogStick rightStick;
|
||||
ScePadAnalogButtons analogButtons;
|
||||
SceFQuaternion orientation;
|
||||
SceFVector3 acceleration;
|
||||
SceFVector3 angularVelocity;
|
||||
ScePadTouchData touchData;
|
||||
bool connected;
|
||||
u64 timestamp;
|
||||
ScePadExtensionUnitData extensionUnitData;
|
||||
uint8_t connectedCount;
|
||||
uint8_t reserve[2];
|
||||
uint8_t deviceUniqueDataLen;
|
||||
uint8_t deviceUniqueData[12];
|
||||
};
|
||||
// hle functions
|
||||
int PS4_SYSV_ABI scePadInit();
|
||||
int PS4_SYSV_ABI scePadOpen(Emulator::HLE::Libraries::LibUserService::SceUserServiceUserId userId, s32 type, s32 index,
|
||||
const ScePadOpenParam* pParam);
|
||||
int PS4_SYSV_ABI scePadReadState(int32_t handle, ScePadData* pData);
|
||||
|
||||
void libPad_Register(SymbolsResolver* sym);
|
||||
}; // namespace Emulator::HLE::Libraries::LibPad
|
||||
@@ -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
|
||||
11
src/Emulator/HLE/Libraries/LibSystemService/system_service.h
Normal file
11
src/Emulator/HLE/Libraries/LibSystemService/system_service.h
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
#include "Core/PS4/Loader/SymbolsResolver.h"
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibSystemService {
|
||||
|
||||
//HLE functions
|
||||
s32 PS4_SYSV_ABI sceSystemServiceHideSplashScreen();
|
||||
|
||||
void libSystemService_Register(SymbolsResolver* sym);
|
||||
|
||||
}; // namespace Emulator::HLE::Libraries::LibUserService
|
||||
26
src/Emulator/HLE/Libraries/LibUserService/user_service.cpp
Normal file
26
src/Emulator/HLE/Libraries/LibUserService/user_service.cpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#include "user_service.h"
|
||||
|
||||
#include <Core/PS4/HLE/ErrorCodes.h>
|
||||
#include <Core/PS4/HLE/Libs.h>
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibUserService {
|
||||
|
||||
s32 PS4_SYSV_ABI sceUserServiceInitialize(const SceUserServiceInitializeParams* initParams) {
|
||||
// dummy
|
||||
return SCE_OK;
|
||||
}
|
||||
|
||||
s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(SceUserServiceLoginUserIdList* userIdList) {
|
||||
// dummy
|
||||
userIdList->user_id[0] = 1;
|
||||
userIdList->user_id[1] = -1;
|
||||
userIdList->user_id[2] = -1;
|
||||
userIdList->user_id[3] = -1;
|
||||
|
||||
return SCE_OK;
|
||||
}
|
||||
void libUserService_Register(SymbolsResolver* sym) {
|
||||
LIB_FUNCTION("j3YMu1MVNNo", "libSceUserService", 1, "libSceUserService", 1, 1, sceUserServiceInitialize);
|
||||
LIB_FUNCTION("fPhymKNvK-A", "libSceUserService", 1, "libSceUserService", 1, 1, sceUserServiceGetLoginUserIdList);
|
||||
}
|
||||
}; // namespace Emulator::HLE::Libraries::LibUserService
|
||||
20
src/Emulator/HLE/Libraries/LibUserService/user_service.h
Normal file
20
src/Emulator/HLE/Libraries/LibUserService/user_service.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
#include "Core/PS4/Loader/SymbolsResolver.h"
|
||||
|
||||
namespace Emulator::HLE::Libraries::LibUserService {
|
||||
|
||||
using SceUserServiceUserId = s32;
|
||||
|
||||
struct SceUserServiceInitializeParams {
|
||||
s32 priority;
|
||||
};
|
||||
|
||||
struct SceUserServiceLoginUserIdList {
|
||||
int user_id[4];
|
||||
};
|
||||
|
||||
s32 PS4_SYSV_ABI sceUserServiceInitialize(const SceUserServiceInitializeParams* initParams);
|
||||
s32 PS4_SYSV_ABI sceUserServiceGetLoginUserIdList(SceUserServiceLoginUserIdList* userIdList);
|
||||
|
||||
void libUserService_Register(SymbolsResolver* sym);
|
||||
}; // namespace Emulator::HLE::Libraries::LibUserService
|
||||
51
src/Emulator/Host/controller.cpp
Normal file
51
src/Emulator/Host/controller.cpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#include "controller.h"
|
||||
|
||||
namespace Emulator::Host::Controller {
|
||||
GameController::GameController() { m_states_num = 0;
|
||||
m_last_state = State();
|
||||
}
|
||||
void GameController::readState(State* state, bool* isConnected, int* connectedCount) {
|
||||
Lib::LockMutexGuard 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) {
|
||||
Lib::LockMutexGuard lock(m_mutex);
|
||||
auto state = getLastState();
|
||||
if (isPressed) {
|
||||
state.buttonsState |= button;
|
||||
} else {
|
||||
state.buttonsState &= ~button;
|
||||
}
|
||||
|
||||
addState(state);
|
||||
}
|
||||
|
||||
} // namespace Emulator::Host::Controller
|
||||
33
src/Emulator/Host/controller.h
Normal file
33
src/Emulator/Host/controller.h
Normal file
@@ -0,0 +1,33 @@
|
||||
#pragma once
|
||||
#include <types.h>
|
||||
#include "Lib/Threads.h"
|
||||
|
||||
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:
|
||||
Lib::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
|
||||
24
src/Emulator/Util/singleton.h
Normal file
24
src/Emulator/Util/singleton.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <new>
|
||||
|
||||
template <class T>
|
||||
class singleton {
|
||||
public:
|
||||
static T* instance() {
|
||||
if (!m_instance) {
|
||||
m_instance = static_cast<T*>(std::malloc(sizeof(T)));
|
||||
new (m_instance) T;
|
||||
}
|
||||
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
protected:
|
||||
singleton();
|
||||
~singleton();
|
||||
|
||||
private:
|
||||
static inline T* m_instance = nullptr;
|
||||
};
|
||||
@@ -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,25 +1,27 @@
|
||||
#include "emulator.h"
|
||||
|
||||
#include <Core/PS4/HLE/Graphics/graphics_render.h>
|
||||
#include <Util/Singleton.h>
|
||||
#include <Emulator/Host/controller.h>
|
||||
#include "Emulator/Util/singleton.h"
|
||||
#include <vulkan_util.h>
|
||||
|
||||
#include "Core/PS4/HLE/Graphics/video_out.h"
|
||||
#include "Emulator/HLE/Libraries/LibPad/pad.h"
|
||||
#include "version.h"
|
||||
|
||||
namespace Emulator {
|
||||
namespace Emu {
|
||||
|
||||
bool m_emu_needs_exit = false;
|
||||
|
||||
void emuInit(u32 width, u32 height) {
|
||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||
|
||||
window_ctx->m_graphic_ctx.screen_width = width;
|
||||
window_ctx->m_graphic_ctx.screen_height = height;
|
||||
}
|
||||
|
||||
void checkAndWaitForGraphicsInit() {
|
||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
||||
|
||||
while (!window_ctx->m_is_graphic_initialized) {
|
||||
@@ -48,7 +50,7 @@ static void CreateSdlWindow(WindowCtx* ctx) {
|
||||
SDL_SetWindowResizable(ctx->m_window, SDL_FALSE); // we don't support resizable atm
|
||||
}
|
||||
void emuRun() {
|
||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||
window_ctx->m_mutex.LockMutex();
|
||||
{
|
||||
// init window and wait until init finishes
|
||||
@@ -82,7 +84,7 @@ void emuRun() {
|
||||
case SDL_EVENT_DID_ENTER_FOREGROUND: break;
|
||||
|
||||
case SDL_EVENT_KEY_DOWN:
|
||||
case SDL_EVENT_KEY_UP: break;
|
||||
case SDL_EVENT_KEY_UP: keyboardEvent(&event); break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -96,14 +98,14 @@ void emuRun() {
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return &window_ctx->m_graphic_ctx;
|
||||
}
|
||||
|
||||
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image) {
|
||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||
if (window_ctx->is_window_hidden) {
|
||||
SDL_ShowWindow(window_ctx->m_window);
|
||||
window_ctx->is_window_hidden = false;
|
||||
@@ -198,4 +200,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
|
||||
@@ -5,7 +5,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace Emulator {
|
||||
namespace Emu {
|
||||
|
||||
struct VulkanExt {
|
||||
bool enable_validation_layers = false;
|
||||
@@ -81,4 +81,5 @@ void emuRun();
|
||||
void checkAndWaitForGraphicsInit();
|
||||
HLE::Libs::Graphics::GraphicCtx* getGraphicCtx();
|
||||
void DrawBuffer(HLE::Libs::Graphics::VideoOutVulkanImage* image);
|
||||
void keyboardEvent(SDL_Event* event);
|
||||
} // namespace Emulator
|
||||
51
src/main.cpp
51
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.
|
||||
// Read online: https://github.com/ocornut/imgui/tree/master/docs
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include <stdio.h>
|
||||
#include <SDL3/SDL.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "imgui.h"
|
||||
#include "imgui_impl_opengl3.h"
|
||||
#include "imgui_impl_sdl3.h"
|
||||
#if defined(IMGUI_IMPL_OPENGL_ES2)
|
||||
#include <SDL3/SDL_opengles2.h>
|
||||
#else
|
||||
#include <SDL3/SDL_opengl.h>
|
||||
#endif
|
||||
|
||||
#include <Util/log.h>
|
||||
|
||||
#include "GUI/ElfViewer.h"
|
||||
#include "spdlog/spdlog.h"
|
||||
#include "types.h"
|
||||
#include "GUI/ElfViewer.h"
|
||||
#include <Util/log.h>
|
||||
|
||||
// This example can also compile and run with Emscripten! See 'Makefile.emscripten' for details.
|
||||
#ifdef __EMSCRIPTEN__
|
||||
#include "../libs/emscripten/emscripten_mainloop_stub.h"
|
||||
#endif
|
||||
#include "Core/PS4/Linker.h"
|
||||
#include "Util/Singleton.h"
|
||||
#include <stdio.h>
|
||||
#include <inttypes.h>
|
||||
#include <Zydis/Zydis.h>
|
||||
#include "Core/PS4/HLE/Libs.h"
|
||||
#include "Lib/Threads.h"
|
||||
#include <emulator.h>
|
||||
#include "discord.h"
|
||||
#include <Util/config.h>
|
||||
#include <Core/PS4/HLE/Graphics/video_out.h>
|
||||
#include <Util/config.h>
|
||||
#include <Zydis/Zydis.h>
|
||||
#include <emulator.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "Core/PS4/HLE/Libs.h"
|
||||
#include "Core/PS4/Linker.h"
|
||||
#include "Lib/Threads.h"
|
||||
#include "Emulator/Util\singleton.h"
|
||||
#include "discord.h"
|
||||
|
||||
// Main code
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
int main(int argc, char* argv[]) {
|
||||
if (argc == 1) {
|
||||
printf("Usage: %s <elf or eboot.bin path>\n", argv[0]);
|
||||
return -1;
|
||||
@@ -46,18 +48,18 @@ int main(int argc, char* argv[])
|
||||
logging::init(true); // init logging
|
||||
auto width = Config::getScreenWidth();
|
||||
auto height = Config::getScreenHeight();
|
||||
Emulator::emuInit(width, height);
|
||||
Emu::emuInit(width, height);
|
||||
HLE::Libs::Graphics::VideoOut::videoOutInit(width, height);
|
||||
Lib::InitThreads();
|
||||
|
||||
const char* const path = argv[1]; // argument 1 is the path of self file to boot
|
||||
|
||||
auto* linker = Singleton<Linker>::Instance();
|
||||
auto* linker = singleton<Linker>::instance();
|
||||
HLE::Libs::Init_HLE_Libs(linker->getHLESymbols());
|
||||
auto *module =linker->LoadModule(path);//load main executable
|
||||
auto* module = linker->LoadModule(path); // load main executable
|
||||
Lib::Thread mainthread(
|
||||
[](void*) {
|
||||
auto* linker = Singleton<Linker>::Instance();
|
||||
auto* linker = singleton<Linker>::instance();
|
||||
linker->Execute();
|
||||
},
|
||||
nullptr);
|
||||
@@ -65,12 +67,9 @@ int main(int argc, char* argv[])
|
||||
Discord::RPC discordRPC;
|
||||
discordRPC.init();
|
||||
discordRPC.update(Discord::RPCStatus::Idling, "");
|
||||
Emulator::emuRun();
|
||||
Emu::emuRun();
|
||||
mainthread.JoinThread();
|
||||
|
||||
|
||||
|
||||
|
||||
#if 0
|
||||
// Setup SDL
|
||||
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMEPAD) != 0)
|
||||
|
||||
19
src/types.h
19
src/types.h
@@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
|
||||
using s08 = char;
|
||||
using s16 = short;
|
||||
using s32 = int;
|
||||
using s64 = long long;
|
||||
using s08 = std::int8_t;
|
||||
using s16 = std::int16_t;
|
||||
using s32 = std::int32_t;
|
||||
using s64 = std::int64_t;
|
||||
|
||||
using u08 = unsigned char;
|
||||
using u16 = unsigned short;
|
||||
using u32 = unsigned int;
|
||||
using u64 = unsigned long long;
|
||||
using u08 = std::uint8_t;
|
||||
using u16 = std::uint16_t;
|
||||
using u32 = std::uint32_t;
|
||||
using u64 = std::uint64_t;
|
||||
|
||||
using f32 = float;
|
||||
using f64 = double;
|
||||
@@ -19,4 +20,4 @@ using f64 = double;
|
||||
// UDLs for memory size values
|
||||
constexpr u64 operator""_KB(u64 x) { return 1024ULL * x; }
|
||||
constexpr u64 operator""_MB(u64 x) { return 1024_KB * x; }
|
||||
constexpr u64 operator""_GB(u64 x) { return 1024_MB * x; }
|
||||
constexpr u64 operator""_GB(u64 x) { return 1024_MB * x; }
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
#include <string_view>
|
||||
|
||||
namespace Emulator {
|
||||
constexpr char VERSION[] = "0.0.1";
|
||||
constexpr char VERSION[] = "0.0.2";
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#include <Core/PS4/GPU/gpu_memory.h>
|
||||
#include <SDL_vulkan.h>
|
||||
#include <Util/Singleton.h>
|
||||
#include <Emulator/Util/singleton.h>
|
||||
#include <Util/log.h>
|
||||
#include <debug.h>
|
||||
#include <vulkan/vk_enum_string_helper.h>
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
constexpr bool log_file_vulkanutil = true; // disable it to disable logging
|
||||
|
||||
void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
|
||||
Emulator::VulkanExt ext;
|
||||
void Graphics::Vulkan::vulkanCreate(Emu::WindowCtx* ctx) {
|
||||
Emu::VulkanExt ext;
|
||||
vulkanGetInstanceExtensions(&ext);
|
||||
|
||||
VkApplicationInfo app_info{};
|
||||
@@ -53,8 +53,8 @@ void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
|
||||
std::vector<const char*> device_extensions = {VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DEPTH_CLIP_ENABLE_EXTENSION_NAME,
|
||||
VK_EXT_COLOR_WRITE_ENABLE_EXTENSION_NAME, "VK_KHR_maintenance1"};
|
||||
|
||||
ctx->m_surface_capabilities = new Emulator::VulkanSurfaceCapabilities{};
|
||||
Emulator::VulkanQueues queues;
|
||||
ctx->m_surface_capabilities = new Emu::VulkanSurfaceCapabilities{};
|
||||
Emu::VulkanQueues queues;
|
||||
|
||||
vulkanFindCompatiblePhysicalDevice(ctx->m_graphic_ctx.m_instance, ctx->m_surface, device_extensions, ctx->m_surface_capabilities,
|
||||
&ctx->m_graphic_ctx.m_physical_device, &queues);
|
||||
@@ -79,11 +79,11 @@ void Graphics::Vulkan::vulkanCreate(Emulator::WindowCtx* ctx) {
|
||||
ctx->swapchain = vulkanCreateSwapchain(&ctx->m_graphic_ctx, 2);
|
||||
}
|
||||
|
||||
Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
|
||||
auto* window_ctx = Singleton<Emulator::WindowCtx>::Instance();
|
||||
Emu::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count) {
|
||||
auto* window_ctx = singleton<Emu::WindowCtx>::instance();
|
||||
Lib::LockMutexGuard lock(window_ctx->m_mutex);
|
||||
|
||||
auto* s = new Emulator::VulkanSwapchain;
|
||||
auto* s = new Emu::VulkanSwapchain;
|
||||
|
||||
VkExtent2D extent{};
|
||||
extent.width = clamp(ctx->screen_width, window_ctx->m_surface_capabilities->capabilities.minImageExtent.width,
|
||||
@@ -183,8 +183,8 @@ Emulator::VulkanSwapchain* Graphics::Vulkan::vulkanCreateSwapchain(HLE::Libs::Gr
|
||||
return s;
|
||||
}
|
||||
|
||||
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues) {
|
||||
auto get_queue = [ctx](int id, const Emulator::VulkanQueueInfo& info, bool with_mutex = false) {
|
||||
void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues) {
|
||||
auto get_queue = [ctx](int id, const Emu::VulkanQueueInfo& info, bool with_mutex = false) {
|
||||
ctx->queues[id].family = info.family;
|
||||
ctx->queues[id].index = info.index;
|
||||
vkGetDeviceQueue(ctx->m_device, ctx->queues[id].family, ctx->queues[id].index, &ctx->queues[id].vk_queue);
|
||||
@@ -203,8 +203,8 @@ void Graphics::Vulkan::vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx,
|
||||
}
|
||||
}
|
||||
|
||||
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
|
||||
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
|
||||
VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
|
||||
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions) {
|
||||
std::vector<VkDeviceQueueCreateInfo> queue_create_info(queues.family_count);
|
||||
std::vector<std::vector<float>> queue_priority(queues.family_count);
|
||||
uint32_t queue_create_info_num = 0;
|
||||
@@ -247,7 +247,7 @@ VkDevice Graphics::Vulkan::vulkanCreateDevice(VkPhysicalDevice physical_device,
|
||||
|
||||
return device;
|
||||
}
|
||||
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
|
||||
void Graphics::Vulkan::vulkanGetInstanceExtensions(Emu::VulkanExt* ext) {
|
||||
u32 required_extensions_count = 0;
|
||||
u32 available_extensions_count = 0;
|
||||
u32 available_layers_count = 0;
|
||||
@@ -283,8 +283,8 @@ void Graphics::Vulkan::vulkanGetInstanceExtensions(Emulator::VulkanExt* ext) {
|
||||
|
||||
void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface,
|
||||
const std::vector<const char*>& device_extensions,
|
||||
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||
Emulator::VulkanQueues* out_queues) {
|
||||
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||
Emu::VulkanQueues* out_queues) {
|
||||
u32 count_devices = 0;
|
||||
vkEnumeratePhysicalDevices(instance, &count_devices, nullptr);
|
||||
|
||||
@@ -292,7 +292,7 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
|
||||
vkEnumeratePhysicalDevices(instance, &count_devices, devices.data());
|
||||
|
||||
VkPhysicalDevice found_best_device = nullptr;
|
||||
Emulator::VulkanQueues found_best_queues;
|
||||
Emu::VulkanQueues found_best_queues;
|
||||
|
||||
for (const auto& device : devices) {
|
||||
VkPhysicalDeviceProperties device_properties{};
|
||||
@@ -316,8 +316,8 @@ void Graphics::Vulkan::vulkanFindCompatiblePhysicalDevice(VkInstance instance, V
|
||||
*out_queues = found_best_queues;
|
||||
}
|
||||
|
||||
Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
Emulator::VulkanQueues qs;
|
||||
Emu::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface) {
|
||||
Emu::VulkanQueues qs;
|
||||
|
||||
u32 queue_family_count = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_family_count, nullptr);
|
||||
@@ -334,7 +334,7 @@ Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice devic
|
||||
LOG_INFO_IF(log_file_vulkanutil, "queue family: {}, count = {}, present = {}\n", string_VkQueueFlags(f.queueFlags).c_str(), f.queueCount,
|
||||
(presentation_supported == VK_TRUE ? "true" : "false"));
|
||||
for (uint32_t i = 0; i < f.queueCount; i++) {
|
||||
Emulator::VulkanQueueInfo info;
|
||||
Emu::VulkanQueueInfo info;
|
||||
info.family = family;
|
||||
info.index = i;
|
||||
info.is_graphics = (f.queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0;
|
||||
@@ -401,7 +401,7 @@ Emulator::VulkanQueues Graphics::Vulkan::vulkanFindQueues(VkPhysicalDevice devic
|
||||
}
|
||||
|
||||
void Graphics::Vulkan::vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface,
|
||||
Emulator::VulkanSurfaceCapabilities* surfaceCap) {
|
||||
Emu::VulkanSurfaceCapabilities* surfaceCap) {
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physical_device, surface, &surfaceCap->capabilities);
|
||||
|
||||
uint32_t formats_count = 0;
|
||||
@@ -488,7 +488,7 @@ static void set_image_layout(VkCommandBuffer buffer, HLE::Libs::Graphics::Vulkan
|
||||
}
|
||||
|
||||
void Graphics::Vulkan::vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image,
|
||||
Emulator::VulkanSwapchain* dst_swapchain) {
|
||||
Emu::VulkanSwapchain* dst_swapchain) {
|
||||
auto* vk_buffer = buffer->getPool()->buffers[buffer->getIndex()];
|
||||
|
||||
HLE::Libs::Graphics::VulkanImage swapchain_image(HLE::Libs::Graphics::VulkanImageType::Unknown);
|
||||
|
||||
@@ -26,18 +26,18 @@ const T& clamp(const T& x, const T& min, const T& max) {
|
||||
return x;
|
||||
}
|
||||
|
||||
void vulkanCreate(Emulator::WindowCtx* ctx);
|
||||
void vulkanGetInstanceExtensions(Emulator::VulkanExt* ext);
|
||||
void vulkanCreate(Emu::WindowCtx* ctx);
|
||||
void vulkanGetInstanceExtensions(Emu::VulkanExt* ext);
|
||||
void vulkanFindCompatiblePhysicalDevice(VkInstance instance, VkSurfaceKHR surface, const std::vector<const char*>& device_extensions,
|
||||
Emulator::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||
Emulator::VulkanQueues* out_queues);
|
||||
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emulator::VulkanExt* r,
|
||||
const Emulator::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
|
||||
Emulator::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emulator::VulkanSurfaceCapabilities* surfaceCap);
|
||||
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emulator::VulkanQueues& queues);
|
||||
Emulator::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
|
||||
void vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image, Emulator::VulkanSwapchain* dst_swapchain);
|
||||
Emu::VulkanSurfaceCapabilities* out_capabilities, VkPhysicalDevice* out_device,
|
||||
Emu::VulkanQueues* out_queues);
|
||||
VkDevice vulkanCreateDevice(VkPhysicalDevice physical_device, VkSurfaceKHR surface, const Emu::VulkanExt* r,
|
||||
const Emu::VulkanQueues& queues, const std::vector<const char*>& device_extensions);
|
||||
Emu::VulkanQueues vulkanFindQueues(VkPhysicalDevice device, VkSurfaceKHR surface);
|
||||
void vulkanGetSurfaceCapabilities(VkPhysicalDevice physical_device, VkSurfaceKHR surface, Emu::VulkanSurfaceCapabilities* surfaceCap);
|
||||
void vulkanCreateQueues(HLE::Libs::Graphics::GraphicCtx* ctx, const Emu::VulkanQueues& queues);
|
||||
Emu::VulkanSwapchain* vulkanCreateSwapchain(HLE::Libs::Graphics::GraphicCtx* ctx, u32 image_count);
|
||||
void vulkanBlitImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanImage* src_image, Emu::VulkanSwapchain* dst_swapchain);
|
||||
void vulkanFillImage(HLE::Libs::Graphics::GraphicCtx* ctx, HLE::Libs::Graphics::VulkanImage* dst_image, const void* src_data, u64 size, u32 src_pitch,
|
||||
u64 dst_layout);
|
||||
void vulkanBufferToImage(GPU::CommandBuffer* buffer, HLE::Libs::Graphics::VulkanBuffer* src_buffer, u32 src_pitch,
|
||||
|
||||
201
third-party/result/LICENSE
vendored
Normal file
201
third-party/result/LICENSE
vendored
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "{}"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright {2016} {Mathieu Stefani}
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
132
third-party/result/README.md
vendored
Normal file
132
third-party/result/README.md
vendored
Normal file
@@ -0,0 +1,132 @@
|
||||
# Result
|
||||
This is an adaption of [https://github.com/oktal/result](https://github.com/oktal/result). Make sure to support the original library!
|
||||
|
||||
## Overview
|
||||
|
||||
`Result<T, E>` is a template type that can be used to return and propage errors. It can be used to replace
|
||||
exceptions in context where they are not allowed or too slow to be used. `Result<T, E>` is an algebraic data
|
||||
type of `Ok(T)` that represents success and `Err(E)` representing an error.
|
||||
|
||||
Design of this class has been mainly inspired by Rust's [std::result](https://doc.rust-lang.org/std/result/)
|
||||
|
||||
```
|
||||
|
||||
struct Request {
|
||||
};
|
||||
|
||||
struct Error {
|
||||
|
||||
enum class Kind {
|
||||
Timeout,
|
||||
Invalid,
|
||||
TooLong
|
||||
}
|
||||
|
||||
Error(Kind kind, std::string text);
|
||||
|
||||
Kind kind;
|
||||
std::string text;
|
||||
};
|
||||
|
||||
Result<Request, Error> parseRequest(const std::string& payload) {
|
||||
if (payload.size() > 512) return Err(Error(Kind::TooLong, "Request exceeded maximum allowed size (512 bytes)"));
|
||||
|
||||
Request request;
|
||||
return Ok(request);
|
||||
}
|
||||
|
||||
std::string payload = receivePayload();
|
||||
auto request = parseRequest(payload).expect("Failed to parse request");
|
||||
```
|
||||
|
||||
To return a successfull `Result`, use the `Ok()` function. To return an error one, use the `Err()` function.
|
||||
|
||||
## Extract and unwrap
|
||||
|
||||
To extract the value from a `Result<T, E>` type, you can use the `expect()` function that will yield the value
|
||||
of an `Ok(T)` or terminate the program with an error message passed as a parameter.
|
||||
|
||||
```
|
||||
Result<uint32_t, uint32_t> r1 = Ok(3u);
|
||||
|
||||
auto val = r1.expect("Failed to retrieve the value");
|
||||
assert(val == 3);
|
||||
```
|
||||
|
||||
`unwrap()` can also be used to extract the value of a `Result`, yielding the value of an `Ok(T)` value or terminating
|
||||
the program otherwise:
|
||||
|
||||
```
|
||||
Result<uint32_t, uint32_t> r1 = Ok(3u);
|
||||
|
||||
auto val = r1.unwrap();
|
||||
assert(val == 3);
|
||||
```
|
||||
|
||||
Instead a terminating the program, `unwrapOr` can be used to return a default value for an `Err(E)` Result:
|
||||
|
||||
```
|
||||
Result<uint32_t, uint32_t> r1 = Err(9u);
|
||||
|
||||
auto val = r1.unwrapOr(0);
|
||||
assert(val == 0);
|
||||
```
|
||||
|
||||
## Map and bind
|
||||
|
||||
To transform (or map) a `Result<T, E>` to a `Result<U, E>`, `Result` provides a `map` member function.
|
||||
`map` will apply a function to a contained `Ok(T)` value and will return the result of the transformation,
|
||||
and will leave an `Err(E)` untouched:
|
||||
|
||||
```
|
||||
std::string stringify(int val) { return std::to_string(val); }
|
||||
|
||||
Result<uint32_t, uint32_t> r1 = Ok(2u);
|
||||
auto r2 = r1.map(stringify); // Maps a Result<uint32_t, uint32_t> to Result<std::string, uint32_t>
|
||||
|
||||
assert(r2.unwrap(), "2");
|
||||
```
|
||||
|
||||
Note that `map` should return a simple value and not a `Result<U, E>`. A function returning nothing (`void`)
|
||||
applied to a `Result<T, E>` will yield a `Result<void, E>`.
|
||||
|
||||
To map a function to a contained `Err(E)` value, use the `mapError` function.
|
||||
|
||||
To *bind* a `Result<T, E>` to a `Result<U, E>`, you can use the `andThen` member function:
|
||||
|
||||
```
|
||||
Result<uint32_t, uint32_t> square(uint32_t val) { return Ok(val * val); }
|
||||
|
||||
Result<uint32_t, uint32_t> r1 = Ok(3u);
|
||||
auto r2 = r1.andThen(square);
|
||||
|
||||
assert(r2.unwrap(), 9);
|
||||
```
|
||||
|
||||
Use `orElse` to apply a function to a contained `Err(E)` value:
|
||||
|
||||
```
|
||||
Result<uint32_t, uint32_t> identity(uint32_t val) { return Ok(val); }
|
||||
|
||||
Result<uint32_t, uint32_t> r1 = Err(3u);
|
||||
assert(r1.andThen(identity).orElse(square).unwrap(), 9);
|
||||
```
|
||||
|
||||
## The TRY macro
|
||||
|
||||
Like Rust, a `TRY` macro is also provided that comes in handy when writing code that calls a lot of functions returning a `Result`.
|
||||
|
||||
the `TRY` macro will simply call its argument and short-cirtcuit the function returning an `Err(E)` if the operation returned an error `Result`:
|
||||
|
||||
```
|
||||
Result<void, IoError> copy(int srcFd, const char* dstFile) {
|
||||
|
||||
auto fd = TRY(open(dstFile));
|
||||
auto data = TRY(read(srcFd));
|
||||
TRY(write(fd, data));
|
||||
|
||||
return Ok();
|
||||
}
|
||||
```
|
||||
|
||||
Note that this macro uses a special extension called *compound statement* only supported by gcc and clang
|
||||
910
third-party/result/include/result.hpp
vendored
Normal file
910
third-party/result/include/result.hpp
vendored
Normal file
@@ -0,0 +1,910 @@
|
||||
/*
|
||||
Mathieu Stefani, 03 mai 2016
|
||||
|
||||
This header provides a Result type that can be used to replace exceptions in code
|
||||
that has to handle error.
|
||||
|
||||
Result<T, E> can be used to return and propagate an error to the caller. Result<T, E> is an algebraic
|
||||
data type that can either Ok(T) to represent success or Err(E) to represent an error.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <type_traits>
|
||||
|
||||
namespace types {
|
||||
template<typename T>
|
||||
struct Ok {
|
||||
Ok(const T& val) : val(val) { }
|
||||
Ok(T&& val) : val(std::move(val)) { }
|
||||
|
||||
T val;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct Ok<void> { };
|
||||
|
||||
template<typename E>
|
||||
struct Err {
|
||||
Err(const E& val) : val(val) { }
|
||||
Err(E&& val) : val(std::move(val)) { }
|
||||
|
||||
E val;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename T, typename CleanT = typename std::decay<T>::type>
|
||||
types::Ok<CleanT> Ok(T&& val) {
|
||||
return types::Ok<CleanT>(std::forward<T>(val));
|
||||
}
|
||||
|
||||
inline types::Ok<void> Ok() {
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
template<typename E, typename CleanE = typename std::decay<E>::type>
|
||||
types::Err<CleanE> Err(E&& val) {
|
||||
return types::Err<CleanE>(std::forward<E>(val));
|
||||
}
|
||||
|
||||
namespace Rust {
|
||||
template<typename T, typename E> struct Result;
|
||||
}
|
||||
|
||||
namespace details {
|
||||
|
||||
template<typename ...> struct void_t { typedef void type; };
|
||||
|
||||
namespace impl {
|
||||
template<typename Func> struct result_of;
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct result_of<Ret (Cls::*)(Args...)> : public result_of<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct result_of<Ret (Args...)> {
|
||||
typedef Ret type;
|
||||
};
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
struct result_of : public impl::result_of<decltype(&Func::operator())> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct result_of<Ret (Cls::*) (Args...) const> {
|
||||
typedef Ret type;
|
||||
};
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct result_of<Ret (*)(Args...)> {
|
||||
typedef Ret type;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
struct ResultOkType { typedef typename std::decay<R>::type type; };
|
||||
|
||||
template<typename T, typename E>
|
||||
struct ResultOkType<Rust::Result<T, E>> {
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
template<typename R>
|
||||
struct ResultErrType { typedef R type; };
|
||||
|
||||
template<typename T, typename E>
|
||||
struct ResultErrType<Rust::Result<T, E>> {
|
||||
typedef typename std::remove_reference<E>::type type;
|
||||
};
|
||||
|
||||
template<typename R> struct IsResult : public std::false_type { };
|
||||
template<typename T, typename E>
|
||||
struct IsResult<Rust::Result<T, E>> : public std::true_type { };
|
||||
|
||||
namespace ok {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T> struct Map;
|
||||
|
||||
template<typename Ret, typename Cls, typename Arg>
|
||||
struct Map<Ret (Cls::*)(Arg) const> : public Map<Ret (Arg)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename Arg>
|
||||
struct Map<Ret (Cls::*)(Arg)> : public Map<Ret (Arg)> { };
|
||||
|
||||
// General implementation
|
||||
template<typename Ret, typename Arg>
|
||||
struct Map<Ret (Arg)> {
|
||||
|
||||
static_assert(!IsResult<Ret>::value,
|
||||
"Can not map a callback returning a Result, use andThen instead");
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
|
||||
static_assert(
|
||||
std::is_same<T, Arg>::value ||
|
||||
std::is_convertible<T, Arg>::value,
|
||||
"Incompatible types detected");
|
||||
|
||||
if (result.isOk()) {
|
||||
auto res = func(result.storage().template get<T>());
|
||||
return types::Ok<Ret>(std::move(res));
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for callback returning void
|
||||
template<typename Arg>
|
||||
struct Map<void (Arg)> {
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
|
||||
if (result.isOk()) {
|
||||
func(result.storage().template get<T>());
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for a void Result
|
||||
template<typename Ret>
|
||||
struct Map<Ret (void)> {
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<Ret, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(std::is_same<T, void>::value,
|
||||
"Can not map a void callback on a non-void Result");
|
||||
|
||||
if (result.isOk()) {
|
||||
auto ret = func();
|
||||
return types::Ok<Ret>(std::move(ret));
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for callback returning void on a void Result
|
||||
template<>
|
||||
struct Map<void (void)> {
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<void, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(std::is_same<T, void>::value,
|
||||
"Can not map a void callback on a non-void Result");
|
||||
|
||||
if (result.isOk()) {
|
||||
func();
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
// General specialization for a callback returning a Result
|
||||
template<typename U, typename E, typename Arg>
|
||||
struct Map<Rust::Result<U, E> (Arg)> {
|
||||
|
||||
template<typename T, typename Func>
|
||||
static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(
|
||||
std::is_same<T, Arg>::value ||
|
||||
std::is_convertible<T, Arg>::value,
|
||||
"Incompatible types detected");
|
||||
|
||||
if (result.isOk()) {
|
||||
auto res = func(result.storage().template get<T>());
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization for a void callback returning a Result
|
||||
template<typename U, typename E>
|
||||
struct Map<Rust::Result<U, E> (void)> {
|
||||
|
||||
template<typename T, typename Func>
|
||||
static Rust::Result<U, E> map(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result");
|
||||
|
||||
if (result.isOk()) {
|
||||
auto res = func();
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Err<E>(result.storage().template get<E>());
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Map<Ret (*) (Args...)> : public impl::Map<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Map<Ret (Cls::*) (Args...)> : public impl::Map<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Map<Ret (Cls::*) (Args...) const> : public impl::Map<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Map<std::function<Ret (Args...)>> : public impl::Map<Ret (Args...)> { };
|
||||
|
||||
} // namespace ok
|
||||
|
||||
|
||||
namespace err {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename T> struct Map;
|
||||
|
||||
template<typename Ret, typename Cls, typename Arg>
|
||||
struct Map<Ret (Cls::*)(Arg) const> {
|
||||
|
||||
static_assert(!IsResult<Ret>::value,
|
||||
"Can not map a callback returning a Result, use orElse instead");
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<T, Ret> map(const Rust::Result<T, E>& result, Func func) {
|
||||
if (result.isErr()) {
|
||||
auto res = func(result.storage().template get<E>());
|
||||
return types::Err<Ret>(res);
|
||||
}
|
||||
|
||||
return types::Ok<T>(result.storage().template get<T>());
|
||||
}
|
||||
|
||||
template<typename E, typename Func>
|
||||
static Rust::Result<void, Ret> map(const Rust::Result<void, E>& result, Func func) {
|
||||
if (result.isErr()) {
|
||||
auto res = func(result.storage().template get<E>());
|
||||
return types::Err<Ret>(res);
|
||||
}
|
||||
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<typename Func> struct Map : public impl::Map<decltype(&Func::operator())> { };
|
||||
|
||||
} // namespace err;
|
||||
|
||||
namespace And {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename Func> struct Then;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Then<Ret (*)(Args...)> : public Then<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Then<Ret (Cls::*)(Args...)> : public Then<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Then<Ret (Cls::*)(Args...) const> : public Then<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Arg>
|
||||
struct Then<Ret (Arg)> {
|
||||
static_assert(std::is_same<Ret, void>::value,
|
||||
"then() should not return anything, use map() instead");
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
|
||||
if (result.isOk()) {
|
||||
func(result.storage().template get<T>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Ret>
|
||||
struct Then<Ret (void)> {
|
||||
static_assert(std::is_same<Ret, void>::value,
|
||||
"then() should not return anything, use map() instead");
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(std::is_same<T, void>::value, "Can not call a void-callback on a non-void Result");
|
||||
|
||||
if (result.isOk()) {
|
||||
func();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<typename Func>
|
||||
struct Then : public impl::Then<decltype(&Func::operator())> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Then<Ret (*) (Args...)> : public impl::Then<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Then<Ret (Cls::*)(Args...)> : public impl::Then<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Then<Ret (Cls::*)(Args...) const> : public impl::Then<Ret (Args...)> { };
|
||||
|
||||
} // namespace And
|
||||
|
||||
namespace Or {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename Func> struct Else;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Else<Ret (*)(Args...)> : public Else<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Else<Ret (Cls::*)(Args...)> : public Else<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Else<Ret (Cls::*)(Args...) const> : public Else<Ret (Args...)> { };
|
||||
|
||||
template<typename T, typename F, typename Arg>
|
||||
struct Else<Rust::Result<T, F> (Arg)> {
|
||||
|
||||
template<typename E, typename Func>
|
||||
static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(
|
||||
std::is_same<E, Arg>::value ||
|
||||
std::is_convertible<E, Arg>::value,
|
||||
"Incompatible types detected");
|
||||
|
||||
if (result.isErr()) {
|
||||
auto res = func(result.storage().template get<E>());
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Ok<T>(result.storage().template get<T>());
|
||||
}
|
||||
|
||||
template<typename E, typename Func>
|
||||
static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) {
|
||||
if (result.isErr()) {
|
||||
auto res = func(result.storage().template get<E>());
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
template<typename T, typename F>
|
||||
struct Else<Rust::Result<T, F> (void)> {
|
||||
|
||||
template<typename E, typename Func>
|
||||
static Rust::Result<T, F> orElse(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(std::is_same<T, void>::value,
|
||||
"Can not call a void-callback on a non-void Result");
|
||||
|
||||
if (result.isErr()) {
|
||||
auto res = func();
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Ok<T>(result.storage().template get<T>());
|
||||
}
|
||||
|
||||
template<typename E, typename Func>
|
||||
static Rust::Result<void, F> orElse(const Rust::Result<void, E>& result, Func func) {
|
||||
if (result.isErr()) {
|
||||
auto res = func();
|
||||
return res;
|
||||
}
|
||||
|
||||
return types::Ok<void>();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<typename Func>
|
||||
struct Else : public impl::Else<decltype(&Func::operator())> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Else<Ret (*) (Args...)> : public impl::Else<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Else<Ret (Cls::*)(Args...)> : public impl::Else<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Else<Ret (Cls::*)(Args...) const> : public impl::Else<Ret (Args...)> { };
|
||||
|
||||
} // namespace Or
|
||||
|
||||
namespace Other {
|
||||
|
||||
namespace impl {
|
||||
|
||||
template<typename Func> struct Wise;
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Wise<Ret (*)(Args...)> : public Wise<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Wise<Ret (Cls::*)(Args...)> : public Wise<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Wise<Ret (Cls::*)(Args...) const> : public Wise<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Arg>
|
||||
struct Wise<Ret (Arg)> {
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
static Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) {
|
||||
static_assert(
|
||||
std::is_same<E, Arg>::value ||
|
||||
std::is_convertible<E, Arg>::value,
|
||||
"Incompatible types detected");
|
||||
|
||||
static_assert(std::is_same<Ret, void>::value,
|
||||
"callback should not return anything, use mapError() for that");
|
||||
|
||||
if (result.isErr()) {
|
||||
func(result.storage().template get<E>());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
} // namespace impl
|
||||
|
||||
template<typename Func>
|
||||
struct Wise : public impl::Wise<decltype(&Func::operator())> { };
|
||||
|
||||
template<typename Ret, typename... Args>
|
||||
struct Wise<Ret (*) (Args...)> : public impl::Wise<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Wise<Ret (Cls::*)(Args...)> : public impl::Wise<Ret (Args...)> { };
|
||||
|
||||
template<typename Ret, typename Cls, typename... Args>
|
||||
struct Wise<Ret (Cls::*)(Args...) const> : public impl::Wise<Ret (Args...)> { };
|
||||
|
||||
} // namespace Other
|
||||
|
||||
template<typename T, typename E, typename Func,
|
||||
typename Ret =
|
||||
Rust::Result<
|
||||
typename details::ResultOkType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type,
|
||||
E>
|
||||
>
|
||||
Ret map(const Rust::Result<T, E>& result, Func func) {
|
||||
return ok::Map<Func>::map(result, func);
|
||||
}
|
||||
|
||||
template<typename T, typename E, typename Func,
|
||||
typename Ret =
|
||||
Rust::Result<T,
|
||||
typename details::ResultErrType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type
|
||||
>
|
||||
>
|
||||
Ret mapError(const Rust::Result<T, E>& result, Func func) {
|
||||
return err::Map<Func>::map(result, func);
|
||||
}
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
Rust::Result<T, E> then(const Rust::Result<T, E>& result, Func func) {
|
||||
return And::Then<Func>::then(result, func);
|
||||
}
|
||||
|
||||
template<typename T, typename E, typename Func>
|
||||
Rust::Result<T, E> otherwise(const Rust::Result<T, E>& result, Func func) {
|
||||
return Other::Wise<Func>::otherwise(result, func);
|
||||
}
|
||||
|
||||
template<typename T, typename E, typename Func,
|
||||
typename Ret =
|
||||
Rust::Result<T,
|
||||
typename details::ResultErrType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type
|
||||
>
|
||||
>
|
||||
Ret orElse(const Rust::Result<T, E>& result, Func func) {
|
||||
return Or::Else<Func>::orElse(result, func);
|
||||
}
|
||||
|
||||
struct ok_tag { };
|
||||
struct err_tag { };
|
||||
|
||||
template<typename T, typename E>
|
||||
struct Storage {
|
||||
static constexpr size_t Size = sizeof(T) > sizeof(E) ? sizeof(T) : sizeof(E);
|
||||
static constexpr size_t Align = sizeof(T) > sizeof(E) ? alignof(T) : alignof(E);
|
||||
|
||||
typedef typename std::aligned_storage<Size, Align>::type type;
|
||||
|
||||
Storage()
|
||||
: initialized_(false)
|
||||
{ }
|
||||
|
||||
void construct(types::Ok<T> ok)
|
||||
{
|
||||
new (&storage_) T(ok.val);
|
||||
initialized_ = true;
|
||||
}
|
||||
void construct(types::Err<E> err)
|
||||
{
|
||||
new (&storage_) E(err.val);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void rawConstruct(U&& val) {
|
||||
typedef typename std::decay<U>::type CleanU;
|
||||
|
||||
new (&storage_) CleanU(std::forward<U>(val));
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
const U& get() const {
|
||||
return *reinterpret_cast<const U *>(&storage_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U& get() {
|
||||
return *reinterpret_cast<U *>(&storage_);
|
||||
}
|
||||
|
||||
void destroy(ok_tag) {
|
||||
if (initialized_) {
|
||||
get<T>().~T();
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
void destroy(err_tag) {
|
||||
if (initialized_) {
|
||||
get<E>().~E();
|
||||
initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
type storage_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
template<typename E>
|
||||
struct Storage<void, E> {
|
||||
typedef typename std::aligned_storage<sizeof(E), alignof(E)>::type type;
|
||||
|
||||
void construct(types::Ok<void>)
|
||||
{
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void construct(types::Err<E> err)
|
||||
{
|
||||
new (&storage_) E(err.val);
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
void rawConstruct(U&& val) {
|
||||
typedef typename std::decay<U>::type CleanU;
|
||||
|
||||
new (&storage_) CleanU(std::forward<U>(val));
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void destroy(ok_tag) { initialized_ = false; }
|
||||
void destroy(err_tag) {
|
||||
if (initialized_) {
|
||||
get<E>().~E(); initialized_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
const U& get() const {
|
||||
return *reinterpret_cast<const U *>(&storage_);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
U& get() {
|
||||
return *reinterpret_cast<U *>(&storage_);
|
||||
}
|
||||
|
||||
type storage_;
|
||||
bool initialized_;
|
||||
};
|
||||
|
||||
template<typename T, typename E>
|
||||
struct Constructor {
|
||||
|
||||
static void move(Storage<T, E>&& src, Storage<T, E>& dst, ok_tag) {
|
||||
dst.rawConstruct(std::move(src.template get<T>()));
|
||||
src.destroy(ok_tag());
|
||||
}
|
||||
|
||||
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, ok_tag) {
|
||||
dst.rawConstruct(src.template get<T>());
|
||||
}
|
||||
|
||||
static void move(Storage<T, E>&& src, Storage<T, E>& dst, err_tag) {
|
||||
dst.rawConstruct(std::move(src.template get<E>()));
|
||||
src.destroy(err_tag());
|
||||
}
|
||||
|
||||
static void copy(const Storage<T, E>& src, Storage<T, E>& dst, err_tag) {
|
||||
dst.rawConstruct(src.template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
template<typename E>
|
||||
struct Constructor<void, E> {
|
||||
static void move(Storage<void, E>&& src, Storage<void, E>& dst, ok_tag) {
|
||||
}
|
||||
|
||||
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, ok_tag) {
|
||||
}
|
||||
|
||||
static void move(Storage<void, E>&& src, Storage<void, E>& dst, err_tag) {
|
||||
dst.rawConstruct(std::move(src.template get<E>()));
|
||||
src.destroy(err_tag());
|
||||
}
|
||||
|
||||
static void copy(const Storage<void, E>& src, Storage<void, E>& dst, err_tag) {
|
||||
dst.rawConstruct(src.template get<E>());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace details
|
||||
|
||||
namespace rpog {
|
||||
|
||||
template<typename T, typename = void> struct EqualityComparable : std::false_type { };
|
||||
|
||||
template<typename T>
|
||||
struct EqualityComparable<T,
|
||||
typename std::enable_if<
|
||||
true,
|
||||
typename details::void_t<decltype(std::declval<T>() == std::declval<T>())>::type
|
||||
>::type
|
||||
> : std::true_type
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
} // namespace rpog
|
||||
|
||||
namespace Rust {
|
||||
template<typename T, typename E>
|
||||
struct Result {
|
||||
|
||||
static_assert(!std::is_same<E, void>::value, "void error type is not allowed");
|
||||
|
||||
typedef details::Storage<T, E> storage_type;
|
||||
|
||||
Result(types::Ok<T> ok)
|
||||
: ok_(true)
|
||||
{
|
||||
storage_.construct(std::move(ok));
|
||||
}
|
||||
|
||||
Result(types::Err<E> err)
|
||||
: ok_(false)
|
||||
{
|
||||
storage_.construct(std::move(err));
|
||||
}
|
||||
|
||||
Result(Result&& other) {
|
||||
if (other.isOk()) {
|
||||
details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::ok_tag());
|
||||
ok_ = true;
|
||||
} else {
|
||||
details::Constructor<T, E>::move(std::move(other.storage_), storage_, details::err_tag());
|
||||
ok_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
Result(const Result& other) {
|
||||
if (other.isOk()) {
|
||||
details::Constructor<T, E>::copy(other.storage_, storage_, details::ok_tag());
|
||||
ok_ = true;
|
||||
} else {
|
||||
details::Constructor<T, E>::copy(other.storage_, storage_, details::err_tag());
|
||||
ok_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
~Result() {
|
||||
if (ok_)
|
||||
storage_.destroy(details::ok_tag());
|
||||
else
|
||||
storage_.destroy(details::err_tag());
|
||||
}
|
||||
|
||||
bool isOk() const {
|
||||
return ok_;
|
||||
}
|
||||
|
||||
bool isErr() const {
|
||||
return !ok_;
|
||||
}
|
||||
|
||||
T expect(const char* str) const {
|
||||
if (!isOk()) {
|
||||
std::fprintf(stderr, "%s\n", str);
|
||||
std::terminate();
|
||||
}
|
||||
return expect_impl(std::is_same<T, void>());
|
||||
}
|
||||
|
||||
template<typename Func,
|
||||
typename Ret =
|
||||
Result<
|
||||
typename details::ResultOkType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type,
|
||||
E>
|
||||
>
|
||||
Ret map(Func func) const {
|
||||
return details::map(*this, func);
|
||||
}
|
||||
|
||||
template<typename Func,
|
||||
typename Ret =
|
||||
Result<T,
|
||||
typename details::ResultErrType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type
|
||||
>
|
||||
>
|
||||
Ret mapError(Func func) const {
|
||||
return details::mapError(*this, func);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
Result<T, E> then(Func func) const {
|
||||
return details::then(*this, func);
|
||||
}
|
||||
|
||||
template<typename Func>
|
||||
Result<T, E> otherwise(Func func) const {
|
||||
return details::otherwise(*this, func);
|
||||
}
|
||||
|
||||
template<typename Func,
|
||||
typename Ret =
|
||||
Result<T,
|
||||
typename details::ResultErrType<
|
||||
typename details::result_of<Func>::type
|
||||
>::type
|
||||
>
|
||||
>
|
||||
Ret orElse(Func func) const {
|
||||
return details::orElse(*this, func);
|
||||
}
|
||||
|
||||
storage_type& storage() {
|
||||
return storage_;
|
||||
}
|
||||
|
||||
const storage_type& storage() const {
|
||||
return storage_;
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<
|
||||
!std::is_same<U, void>::value,
|
||||
U
|
||||
>::type
|
||||
unwrapOr(const U& defaultValue) const {
|
||||
if (isOk()) {
|
||||
return storage().template get<U>();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
template<typename U = T>
|
||||
typename std::enable_if<
|
||||
!std::is_same<U, void>::value,
|
||||
U
|
||||
>::type
|
||||
unwrap() const {
|
||||
if (isOk()) {
|
||||
return storage().template get<U>();
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "Attempting to unwrap an error Result\n");
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
E unwrapErr() const {
|
||||
if (isErr()) {
|
||||
return storage().template get<E>();
|
||||
}
|
||||
|
||||
std::fprintf(stderr, "Attempting to unwrapErr an ok Result\n");
|
||||
std::terminate();
|
||||
}
|
||||
|
||||
private:
|
||||
T expect_impl(std::true_type) const { }
|
||||
T expect_impl(std::false_type) const { return storage_.template get<T>(); }
|
||||
|
||||
bool ok_;
|
||||
storage_type storage_;
|
||||
};
|
||||
|
||||
template<typename T, typename E>
|
||||
bool operator==(const Rust::Result<T, E>& lhs, const Rust::Result<T, E>& rhs) {
|
||||
static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable");
|
||||
static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable");
|
||||
|
||||
if (lhs.isOk() && rhs.isOk()) {
|
||||
return lhs.storage().template get<T>() == rhs.storage().template get<T>();
|
||||
}
|
||||
if (lhs.isErr() && rhs.isErr()) {
|
||||
return lhs.storage().template get<E>() == rhs.storage().template get<E>();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename T, typename E>
|
||||
bool operator==(const Rust::Result<T, E>& lhs, types::Ok<T> ok) {
|
||||
static_assert(rpog::EqualityComparable<T>::value, "T must be EqualityComparable for Result to be comparable");
|
||||
|
||||
if (!lhs.isOk()) return false;
|
||||
|
||||
return lhs.storage().template get<T>() == ok.val;
|
||||
}
|
||||
|
||||
template<typename E>
|
||||
bool operator==(const Rust::Result<void, E>& lhs, types::Ok<void>) {
|
||||
return lhs.isOk();
|
||||
}
|
||||
|
||||
template<typename T, typename E>
|
||||
bool operator==(const Rust::Result<T, E>& lhs, types::Err<E> err) {
|
||||
static_assert(rpog::EqualityComparable<E>::value, "E must be EqualityComparable for Result to be comparable");
|
||||
if (!lhs.isErr()) return false;
|
||||
|
||||
return lhs.storage().template get<E>() == err.val;
|
||||
}
|
||||
} // end namespace Rust
|
||||
|
||||
#define TRY(...) \
|
||||
({ \
|
||||
auto res = __VA_ARGS__; \
|
||||
if (!res.isOk()) { \
|
||||
typedef details::ResultErrType<decltype(res)>::type E; \
|
||||
return types::Err<E>(res.storage().get<E>()); \
|
||||
} \
|
||||
typedef details::ResultOkType<decltype(res)>::type T; \
|
||||
res.storage().get<T>(); \
|
||||
})
|
||||
Reference in New Issue
Block a user