This commit is contained in:
SysRay 2024-06-03 17:16:19 +02:00
parent 38806e48f2
commit b181559a19
126 changed files with 18290 additions and 12 deletions

View File

@ -19,7 +19,6 @@
"cmake.sourceDirectory": "${workspaceFolder}",
"cmake.buildDirectory": "${workspaceFolder}/_build/_Release",
"cmake.configureArgs": [
// "-DBUILD_CORE=1",
"-DISDEBUG=1"
],
"cmake.configureSettings": {

View File

@ -9,7 +9,7 @@ set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN 1)
set(PSOFF_LIB_VERSION v.0.3)
set(PSOFF_RENDER_VERSION v.0.5)
set(PSOFF_RENDER_VERSION v.0.6-nightly_03.06.2024)
set(ProjectName psOff_${CMAKE_BUILD_TYPE})
project(${ProjectName} VERSION 0.0.1)
@ -89,11 +89,11 @@ if(CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
)
endif()
# set(TEST_BENCH OFF CACHE BOOL "Enable testing")
set(TEST_BENCH OFF CACHE BOOL "Enable testing")
# if(TEST_BENCH)
# add_subdirectory(tests)
# endif()
if(TEST_BENCH)
add_subdirectory(tests)
endif()
# include before link_libraries
add_subdirectory(tools/logging)
@ -113,12 +113,7 @@ add_compile_definitions(IMAGE_BASE=${IMAGE_BASE})
add_subdirectory(modules)
add_subdirectory(utility)
add_subdirectory(tools/gamereport)
if(BUILD_CORE)
add_subdirectory(core)
else()
add_custom_target(core)
endif()
add_subdirectory(core)
# #- Projects

85
core/CMakeLists.txt Normal file
View File

@ -0,0 +1,85 @@
# Place before add_subdir -> define for all childs
add_compile_definitions(
BOOST_ALL_NO_LIB
)
set(DST_INCLUDE_DIR "${CMAKE_BINARY_DIR}/core/export/include/core/")
# Add directories
add_subdirectory(kernel)
add_subdirectory(videoout)
add_subdirectory(initParams)
add_subdirectory(timer)
add_subdirectory(systemContent)
add_subdirectory(networking)
add_subdirectory(hotkeys)
add_subdirectory(trophies)
add_subdirectory(fileManager)
add_subdirectory(memory)
add_subdirectory(dmem)
add_subdirectory(unwinding)
add_subdirectory(runtime)
# Build
add_library(core SHARED
$<TARGET_OBJECTS:kernel>
$<TARGET_OBJECTS:initParams>
$<TARGET_OBJECTS:timer>
$<TARGET_OBJECTS:systemContent>
$<TARGET_OBJECTS:networking>
$<TARGET_OBJECTS:hotkeys>
$<TARGET_OBJECTS:trophies>
$<TARGET_OBJECTS:videoout>
$<TARGET_OBJECTS:fileManager>
$<TARGET_OBJECTS:memory>
$<TARGET_OBJECTS:dmem>
$<TARGET_OBJECTS:unwinding>
$<TARGET_OBJECTS:runtime>
)
add_dependencies(core logging third_party config_emu psoff_render)
target_link_libraries(core PRIVATE
libboost_url
libboost_thread
libboost_chrono
libboost_program_options
libboost_filesystem
sdl2
psoff_render.lib
OptickCore
psOff_utility
config_emu.lib
onecore.lib
IPHLPAPI.lib
Dbghelp.lib
wepoll.lib
Ws2_32.lib
libcrypto.lib
libssl.lib
pugixml.lib
ntdll.dll
imgui
VulkanMemoryAllocator
${Vulkan_LIBRARIES}
)
install(FILES $<TARGET_PDB_FILE:core> DESTINATION debug OPTIONAL)
install(TARGETS core LIBRARY DESTINATION .)
# Manage symbols
if(NOT ISDEBUG)
add_custom_command(
TARGET core POST_BUILD
COMMAND $<$<CONFIG:release>:${CMAKE_STRIP}>
ARGS --strip-all $<TARGET_FILE:core>
)
endif()
set_target_properties(core
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/core/export/lib"
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/core/export/bin"
PDB_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/"
)
file(COPY imports/exports/pm4_custom.h DESTINATION ${DST_INCLUDE_DIR}/imports/exports)

14
core/dmem/CMakeLists.txt Normal file
View File

@ -0,0 +1,14 @@
add_library(dmem OBJECT
memoryManager.cpp
types/directmem.cpp
types/flexiblemem.cpp
)
add_dependencies(dmem third_party psOff_utility)
target_include_directories(dmem PRIVATE
${Vulkan_INCLUDE_DIRS}
)
file(COPY memoryManager.h imemory.h DESTINATION ${DST_INCLUDE_DIR}/dmem)

40
core/dmem/imemory.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "utility/utility.h"
struct SceKernelVirtualQueryInfo;
struct SceKernelDirectMemoryQueryInfo;
class IMemoryType {
CLASS_NO_COPY(IMemoryType);
CLASS_NO_MOVE(IMemoryType);
protected:
IMemoryType() {}
public:
virtual ~IMemoryType() = default;
/**
* @brief Only for init
*
*/
virtual void setTotalSize(uint64_t totalSize) = 0;
virtual int alloc(size_t len, size_t alignment, int memoryType, uint64_t* outAddr) = 0;
virtual int free(off_t start, size_t len) = 0;
virtual int map(uint64_t vaddr, off_t directMemoryOffset, size_t len, int prot, int flags, size_t alignment, uint64_t* outAddr) = 0;
virtual int unmap(uint64_t vaddr, uint64_t size) = 0;
virtual int reserve(uint64_t start, size_t len, size_t alignment, int flags, uint64_t* outAddr) = 0;
virtual uint64_t size() const = 0;
virtual int getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) const = 0;
virtual int32_t virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const = 0;
virtual int32_t directQuery(uint64_t offset, SceKernelDirectMemoryQueryInfo* info) const = 0;
virtual void deinit() = 0;
};

124
core/dmem/memoryManager.cpp Normal file
View File

@ -0,0 +1,124 @@
#define __APICALL_EXTERN
#include "memoryManager.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "modules/libkernel/dmem.h"
#include "types/memory.h"
#include <boost/thread.hpp>
#include <map>
LOG_DEFINE_MODULE(MemoryManager);
namespace {
struct MappingData {
MappingType type;
uint64_t size;
};
} // namespace
class MemoryManager: public IMemoryManager {
std::unique_ptr<IMemoryType> m_directMemory;
std::unique_ptr<IMemoryType> m_flexibleMemory;
mutable boost::mutex m_mutexInt;
std::map<uint64_t, MappingData> m_mappings;
std::map<uint64_t, uint64_t> m_stackAddrMap;
public:
MemoryManager() {
m_directMemory = createDirectMemory(this);
m_flexibleMemory = createFlexibleMemory(this);
}
~MemoryManager() = default;
// ### Interface
void registerMapping(uint64_t vaddr, uint64_t size, MappingType type) final;
MappingType unregisterMapping(uint64_t vaddr) final;
void registerStack(uint64_t addr, uint64_t size) final;
void unregisterStack(uint64_t addr) final;
int32_t virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const final;
IMemoryType* directMemory() final { return m_directMemory.get(); }
IMemoryType* flexibleMemory() final { return m_flexibleMemory.get(); }
};
IMemoryManager* accessMemoryManager() {
static MemoryManager inst;
return &inst;
}
void MemoryManager::registerMapping(uint64_t vaddr, uint64_t size, MappingType type) {
boost::unique_lock lock(m_mutexInt);
m_mappings.emplace(std::make_pair(vaddr, MappingData {.type = type, .size = size}));
}
MappingType MemoryManager::unregisterMapping(uint64_t vaddr) {
boost::unique_lock lock(m_mutexInt);
if (auto it = m_mappings.find((uint64_t)vaddr); it != m_mappings.end()) {
auto type = it->second.type;
m_mappings.erase(it);
return type;
}
return MappingType::None;
}
void MemoryManager::registerStack(uint64_t addr, uint64_t size) {
boost::unique_lock lock(m_mutexInt);
m_stackAddrMap[addr] = size;
}
void MemoryManager::unregisterStack(uint64_t addr) {
boost::unique_lock lock(m_mutexInt);
m_stackAddrMap.erase(addr);
}
int32_t MemoryManager::virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const {
LOG_USE_MODULE(MemoryManager);
boost::unique_lock lock(m_mutexInt);
auto itItem = m_mappings.lower_bound(addr);
if (itItem == m_mappings.end() && addr > (itItem->first + itItem->second.size)) return getErr(ErrCode::_EACCES); // End reached
if (itItem == m_mappings.end() || (itItem != m_mappings.begin() && itItem->first != addr)) --itItem; // Get the correct item
int res = getErr(ErrCode::_EACCES);
switch (itItem->second.type) {
case MappingType::Direct: {
res = m_directMemory->virtualQuery(itItem->first, info);
} break;
case MappingType::Flexible: {
res = m_flexibleMemory->virtualQuery(itItem->first, info);
} break;
case MappingType::Fixed: {
} break;
case MappingType::File: {
} break;
default: break;
}
if (res == Ok) {
LOG_TRACE(L"Query OK: addr:0x%08llx - start:0x%08llx end:0x%08llx prot:%d type:%d", itItem->first, info->start, info->end, info->protection,
info->memoryType);
} else {
LOG_TRACE(L"Query Error: addr:0x%08llx", addr);
}
return res;
};

55
core/dmem/memoryManager.h Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#include "imemory.h"
#include "utility/utility.h"
#include <stdint.h>
enum class MappingType { None, File, Flexible, Fixed, Direct };
struct SceKernelVirtualQueryInfo;
class IMemoryManager {
CLASS_NO_COPY(IMemoryManager);
CLASS_NO_MOVE(IMemoryManager);
public:
IMemoryManager() = default;
virtual ~IMemoryManager() = default;
/**
* @brief Register mapped memory
*
* @param vaddr
* @param type
*/
virtual void registerMapping(uint64_t vaddr, uint64_t size, MappingType type) = 0;
/**
* @brief Unregisters mapping and returns the type of the mapping
*
* @param vaddr
* @return None: Mapping didn't exist
*/
virtual MappingType unregisterMapping(uint64_t vaddr) = 0;
virtual void registerStack(uint64_t addr, uint64_t size) = 0;
virtual void unregisterStack(uint64_t addr) = 0;
virtual int32_t virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const = 0;
virtual IMemoryType* directMemory() = 0;
virtual IMemoryType* flexibleMemory() = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IMemoryManager* accessMemoryManager();
#undef __APICALL

View File

@ -0,0 +1,545 @@
#include "../memoryManager.h"
#include "core/kernel/filesystem.h"
#include "core/runtime/procParam.h"
#include "core/runtime/runtimeLinker.h"
#include "core/videoout/videoout.h"
#include "logging.h"
#include "memory.h"
#include "modules/libkernel/codes.h"
#include "modules/libkernel/dmem.h"
#include "utility/utility.h"
#include <boost/thread.hpp>
#include <map>
#include <vk_mem_alloc.h>
#include <vulkan/vk_enum_string_helper.h>
#include <windows.h>
#undef min
LOG_DEFINE_MODULE(DirectMemory);
namespace {
constexpr uint64_t DIRECTMEM_START = 0x100000000u;
enum class MemoryState {
Free,
Reserved,
Commited,
};
struct MemoryMappingDirect {
uint64_t addr = 0;
uint64_t heapAddr = 0; // addr for MemoryInfo
uint64_t size = 0;
uint64_t alignment = 0;
VmaVirtualAllocation vmaAlloc;
};
struct MemoryInfo {
uint64_t addr = 0;
void* allocAddr = nullptr;
uint64_t size = 0;
uint64_t alignment = 0;
int memoryType = 0;
int prot = 0;
VmaVirtualAllocation vmaAlloc;
VmaVirtualBlock vmaBlock = nullptr;
MemoryState state = MemoryState::Free;
};
bool checkIsGPU(int protection) {
return (protection & 0xf0) > 0;
}
DWORD convProtection(int prot) {
switch (prot & 0xf) {
case 0: return PAGE_NOACCESS;
case 1: return PAGE_READONLY;
case 2:
case 3: return PAGE_READWRITE;
case 4: return PAGE_EXECUTE;
case 5: return PAGE_EXECUTE_READ;
case 6:
case 7: return PAGE_EXECUTE_READWRITE;
}
if (checkIsGPU(prot)) {
return PAGE_READWRITE; // special case: cpu read/writes gpumemory on host memory
}
return PAGE_NOACCESS;
}
} // namespace
class DirectMemory: public IMemoryType {
mutable boost::mutex m_mutexInt;
IMemoryManager* m_parent;
std::map<uint64_t, MemoryInfo> m_objects;
std::map<uint64_t, MemoryMappingDirect> m_mappings;
uint64_t m_allocSize = 0;
uint64_t m_usedSize = 0;
uint64_t m_sdkVersion = 0;
VmaVirtualBlock m_virtualDeviceMemory;
uint64_t getSDKVersion() {
if (m_sdkVersion == 0) {
auto* procParam = (ProcParam*)accessRuntimeLinker().mainModuleInfo().procParamAddr;
m_sdkVersion = procParam->header.sdkVersion;
}
return m_sdkVersion;
}
int retNoMem() {
if (m_sdkVersion < 0x3500000) return getErr(ErrCode::_EINVAL);
return getErr(ErrCode::_ENOMEM);
}
MemoryInfo* getMemoryInfo(uint64_t len, uint64_t alignment, int prot, VmaVirtualAllocation& outAlloc, uint64_t& outAddr);
public:
DirectMemory(IMemoryManager* parent): m_parent(parent) {
LOG_USE_MODULE(DirectMemory);
VmaVirtualBlockCreateInfo blockCreateInfo = {
.size = SCE_KERNEL_MAIN_DMEM_SIZE,
};
if (auto result = vmaCreateVirtualBlock(&blockCreateInfo, &m_virtualDeviceMemory); result != VK_SUCCESS) {
LOG_CRIT(L"vmaCreateVirtualBlock err:%S", string_VkResult(result));
}
}
virtual ~DirectMemory() { deinit(); }
// ### Interface
void setTotalSize(uint64_t totalSize) final;
int alloc(size_t len, size_t alignment, int memoryType, uint64_t* outAddr) final;
int free(off_t start, size_t len) final;
int map(uint64_t vaddr, off_t directMemoryOffset, size_t len, int prot, int flags, size_t alignment, uint64_t* outAddr) final;
int unmap(uint64_t vaddr, uint64_t size) final;
virtual int reserve(uint64_t start, size_t len, size_t alignment, int flags, uint64_t* outAddr) final;
uint64_t size() const final;
int getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) const final;
int32_t virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const final;
int32_t directQuery(uint64_t offset, SceKernelDirectMemoryQueryInfo* info) const final;
void deinit() final;
};
std::unique_ptr<IMemoryType> createDirectMemory(IMemoryManager* parent) {
return std::make_unique<DirectMemory>(parent);
}
void DirectMemory::setTotalSize(uint64_t totalSize) {
LOG_USE_MODULE(DirectMemory);
vmaDestroyVirtualBlock(m_virtualDeviceMemory);
VmaVirtualBlockCreateInfo blockCreateInfo = {
.size = totalSize,
};
if (auto result = vmaCreateVirtualBlock(&blockCreateInfo, &m_virtualDeviceMemory); result != VK_SUCCESS) {
LOG_CRIT(L"vmaCreateVirtualBlock err:%S", string_VkResult(result));
}
}
MemoryInfo* DirectMemory::getMemoryInfo(uint64_t len, uint64_t alignment, int prot, VmaVirtualAllocation& outAlloc, uint64_t& outAddr) {
if (m_objects.empty()) return nullptr;
VmaVirtualAllocationCreateInfo allocCreateInfo = {
.size = len,
.alignment = alignment,
};
for (auto& item: m_objects) {
if (item.second.state == MemoryState::Reserved) continue;
if (item.second.state == MemoryState::Commited && prot != item.second.prot) continue;
if (auto result = vmaVirtualAllocate(item.second.vmaBlock, &allocCreateInfo, &outAlloc, &outAddr); result == VK_SUCCESS) {
return &item.second;
}
}
return nullptr;
}
int DirectMemory::alloc(size_t len, size_t alignment, int memoryType, uint64_t* outAddr) {
LOG_USE_MODULE(DirectMemory);
if ((alignment > 0 && !util::isPowerOfTwo(alignment)) || (len % SCE_KERNEL_PAGE_SIZE != 0) || (alignment % SCE_KERNEL_PAGE_SIZE != 0))
return getErr(ErrCode::_EINVAL);
boost::unique_lock lock(m_mutexInt);
VmaVirtualAllocationCreateInfo allocCreateInfo = {
.size = len,
.alignment = alignment,
};
// Get block from device memory
VmaVirtualAllocation alloc;
if (auto result = vmaVirtualAllocate(m_virtualDeviceMemory, &allocCreateInfo, &alloc, outAddr); result != VK_SUCCESS) {
LOG_ERR(L"Alloc Error| len:0x%08llx alignment:0x%08llx memorytype:%d err:%d", len, alignment, memoryType, GetLastError());
return getErr(ErrCode::_ENOMEM);
}
// - check devicememory
*outAddr += DIRECTMEM_START; // For debugging info
// Device has space -> Create allocation
VmaVirtualBlockCreateInfo blockCreateInfo = {
.size = len,
};
VmaVirtualBlock block;
if (auto result = vmaCreateVirtualBlock(&blockCreateInfo, &block); result != VK_SUCCESS) {
LOG_ERR(L"Alloc Error| len:0x%08llx alignment:0x%08llx memorytype:%d err:%d", len, alignment, memoryType, GetLastError());
return getErr(ErrCode::_ENOMEM);
}
// -
m_allocSize += len;
m_objects.emplace(std::make_pair(
*outAddr, MemoryInfo {.addr = *outAddr, .size = len, .alignment = alignment, .memoryType = memoryType, .vmaAlloc = alloc, .vmaBlock = block}));
m_parent->registerMapping(*outAddr, len, MappingType::Direct);
LOG_DEBUG(L"-> Allocated: len:0x%08llx alignment:0x%08llx memorytype:%d -> 0x%08llx", len, alignment, memoryType, *outAddr);
return Ok;
}
int DirectMemory::free(off_t start, size_t len) {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
uint64_t addr = DIRECTMEM_START + start;
m_allocSize -= len;
// Find the object
if (m_objects.empty()) return Ok;
auto itHeap = m_objects.lower_bound(addr);
if (itHeap == m_objects.end() || (itHeap != m_objects.begin() && itHeap->first != addr)) --itHeap; // Get the correct item
if (!(itHeap->first <= addr && (itHeap->first + itHeap->second.size >= addr))) return Ok; // Can't be found
//-
LOG_DEBUG(L"free| addr:0x%08llx len:0x%08llx heap -> addr:0x%08llx len:0x%08llx", addr, len, itHeap->first, itHeap->second.size);
// special: Check reserve if full reservation or commits
if (itHeap->second.state == MemoryState::Reserved) {
// todo
VirtualFree((void*)itHeap->second.addr, itHeap->second.size, 0);
return Ok;
}
if (len != 0 && (itHeap->first != addr || itHeap->second.size != len)) {
LOG_ERR(L"free Error| start:0x%08llx len:0x%08llx != start:0x%08llx len:0x%08llx", addr, len, itHeap->first, itHeap->second.size);
}
if (checkIsGPU(itHeap->second.prot)) {
// todo
} else {
if (itHeap->second.allocAddr != 0) VirtualFree(itHeap->second.allocAddr, itHeap->second.size, 0);
}
{
std::list<uint64_t> dump;
for (auto& item: m_mappings) {
if (item.second.heapAddr == itHeap->first) {
m_usedSize -= item.second.size;
LOG_TRACE(L"Missing unmap for addr:0x%08llx len:0x%08llx, force unmap", item.second.addr, item.second.size);
vmaVirtualFree(itHeap->second.vmaBlock, item.second.vmaAlloc);
m_parent->unregisterMapping(item.first);
dump.push_back(item.first);
}
for (auto item: dump) {
m_mappings.erase(item);
}
}
}
vmaDestroyVirtualBlock(itHeap->second.vmaBlock);
vmaVirtualFree(m_virtualDeviceMemory, itHeap->second.vmaAlloc);
m_objects.erase(itHeap);
return Ok;
}
int DirectMemory::map(uint64_t vaddr, off_t offset, size_t len, int prot, int flags, size_t alignment, uint64_t* outAddr) {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
if (flags & (int)filesystem::SceMapMode::VOID_) {
LOG_CRIT(L"todo void map");
}
uint64_t desVaddr = 0;
if (flags & (int)filesystem::SceMapMode::FIXED) {
desVaddr = vaddr;
}
VmaVirtualAllocation alloc = nullptr;
// search for free space
uint64_t fakeAddrOffset = 0;
MemoryInfo* info = nullptr;
if (flags & (int)filesystem::SceMapMode::FIXED) {
for (auto& item: m_objects) {
if (item.second.state == MemoryState::Reserved && item.first <= vaddr && item.second.size >= len) {
info = &item.second;
desVaddr = info->addr;
}
}
}
if (info == nullptr) info = getMemoryInfo(len, alignment, prot, alloc, fakeAddrOffset);
if (info == nullptr) return retNoMem();
// -
// Check if Commit needed
if (info->state == MemoryState::Free || info->state == MemoryState::Reserved) {
MEM_ADDRESS_REQUIREMENTS addressReqs = {0};
MEM_EXTENDED_PARAMETER extendedParams = {0};
addressReqs.Alignment = 0; // 64 KB alignment
addressReqs.LowestStartingAddress = (PVOID)DIRECTMEM_START;
addressReqs.HighestEndingAddress = (PVOID)0;
extendedParams.Type = MemExtendedParameterAddressRequirements;
extendedParams.Pointer = &addressReqs;
uint32_t flags = MEM_COMMIT;
if (info->state != MemoryState::Reserved) {
flags |= MEM_RESERVE | MEM_WRITE_WATCH;
}
void* ptr = VirtualAlloc2(NULL, (void*)desVaddr, info->size, flags, convProtection(prot), desVaddr != 0 ? 0 : &extendedParams, desVaddr != 0 ? 0 : 1);
if (ptr == 0) {
auto const err = GetLastError();
LOG_ERR(L"Commit Error| addr:0x%08llx len:0x%08llx err:%d", info->addr, info->size, GetLastError());
return getErr(ErrCode::_EINVAL);
}
info->allocAddr = ptr;
info->state = MemoryState::Commited;
info->prot = prot;
LOG_DEBUG(L"Commit| addr:0x%08llx len:0x%08llx prot:%d ->", info->addr, info->size, prot, ptr);
}
// - commit
*outAddr = (uint64_t)info->allocAddr + fakeAddrOffset;
m_mappings.emplace(
std::make_pair(*outAddr, MemoryMappingDirect {.addr = *outAddr, .heapAddr = info->addr, .size = len, .alignment = alignment, .vmaAlloc = alloc}));
m_usedSize += len;
LOG_DEBUG(L"-> Map: start:0x%08llx(0x%x) len:0x%08llx alignment:0x%08llx prot:%d -> 0x%08llx", vaddr, offset, len, alignment, prot, *outAddr);
if (checkIsGPU(prot)) {
if (!accessVideoOut().notify_allocHeap(*outAddr, len, prot)) {
return getErr(ErrCode::_EINVAL);
}
}
return Ok;
}
int DirectMemory::reserve(uint64_t start, size_t len, size_t alignment, int flags, uint64_t* outAddr) {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
MEM_ADDRESS_REQUIREMENTS addressReqs = {0};
MEM_EXTENDED_PARAMETER extendedParams = {0};
addressReqs.Alignment = alignment;
addressReqs.LowestStartingAddress = (PVOID)0x100000000;
addressReqs.HighestEndingAddress = (PVOID)0;
extendedParams.Type = MemExtendedParameterAddressRequirements;
extendedParams.Pointer = &addressReqs;
*outAddr = (uint64_t)VirtualAlloc2(NULL, 0, len, MEM_RESERVE | MEM_WRITE_WATCH, PAGE_NOACCESS, &extendedParams, 1);
if (*outAddr == 0) {
auto const err = GetLastError();
LOG_ERR(L"Reserve Error| addr:0x%08llx len:0x%08llx align:0x%08llx flags:0x%x err:%d", start, len, alignment, flags, GetLastError());
return getErr(ErrCode::_EINVAL);
}
LOG_DEBUG(L"-> Reserve: start:0x%08llx len:0x%08llx alignment:0x%08llx flags:0x%x -> 0x%08llx", start, len, alignment, flags, *outAddr);
m_objects.emplace(
std::make_pair(*outAddr, MemoryInfo {.addr = *outAddr, .size = len, .alignment = alignment, .memoryType = 0, .state = MemoryState::Reserved}));
m_parent->registerMapping(*outAddr, len, MappingType::Direct);
return Ok;
}
int DirectMemory::unmap(uint64_t vaddr, uint64_t size) {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
// Find the object
if (m_mappings.empty()) return Ok;
auto itItem = m_mappings.lower_bound(vaddr);
if (itItem == m_mappings.end() || (itItem != m_mappings.begin() && itItem->first != vaddr)) --itItem; // Get the correct item
if (!(itItem->first <= vaddr && (itItem->first + itItem->second.size >= vaddr))) {
LOG_ERR(L"Couldn't find mapping for 0x%08llx 0x%08llx", vaddr, size);
return -1;
}
//-
if (auto it = m_objects.find(itItem->second.heapAddr); it != m_objects.end()) {
vmaVirtualFree(it->second.vmaBlock, itItem->second.vmaAlloc);
} else {
LOG_ERR(L"Couldn't find Heap for 0x%08llx", itItem->second.heapAddr);
}
LOG_DEBUG(L"unmap| 0x%08llx 0x%08llx", vaddr, size);
m_usedSize -= itItem->second.size;
m_mappings.erase(itItem);
return Ok;
}
uint64_t DirectMemory::size() const {
return SCE_KERNEL_MAIN_DMEM_SIZE;
}
int DirectMemory::getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) const {
LOG_USE_MODULE(DirectMemory);
LOG_DEBUG(L"availableSize: start:0x%lx end:0x%lx alignment:0x%08llx", start, end, alignment);
auto itItem = m_objects.lower_bound(DIRECTMEM_START + start);
if (m_objects.empty() || itItem == m_objects.end()) {
*startOut = start;
*sizeOut = std::min(SCE_KERNEL_MAIN_DMEM_SIZE, (uint64_t)end - start);
return Ok;
}
// *startOut = start;
// *sizeOut = (itItem->second.addr - DIRECTMEM_START) - start;
if (itItem->second.addr + itItem->second.size >= DIRECTMEM_START + end) {
*startOut = end;
*sizeOut = 0;
return Ok;
}
*startOut = start;
*sizeOut = 0;
auto itEnd = m_objects.lower_bound(DIRECTMEM_START + end);
for (; itItem != itEnd; ++itItem) {
*startOut = (itItem->second.addr + itItem->second.size) - DIRECTMEM_START;
}
if (*startOut > end)
*sizeOut = *startOut - end;
else
*sizeOut = end - *startOut;
return Ok;
}
void DirectMemory::deinit() {
for (auto& item: m_objects) {
if (checkIsGPU(item.second.prot)) {
// done by gpuMemoryManager
} else {
// memory::free(item.first);
}
}
m_objects.clear();
}
int32_t DirectMemory::virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
if (m_objects.empty()) return getErr(ErrCode::_EACCES);
auto itItem = m_objects.lower_bound(addr);
if (itItem == m_objects.end() && addr > (itItem->first + itItem->second.size)) return getErr(ErrCode::_EACCES); // End reached
if (itItem == m_objects.end() || (itItem != m_objects.begin() && itItem->first != addr)) --itItem; // Get the correct item
info->protection = itItem->second.prot;
info->memoryType = itItem->second.memoryType;
info->isFlexibleMemory = false;
info->isDirectMemory = true;
info->isPooledMemory = false;
// info->isStack = false; // done by parent
auto itMapping = m_mappings.lower_bound(itItem->first);
if (itMapping == m_mappings.end() || (itMapping != m_mappings.begin() && itMapping->first != itItem->first)) --itMapping; // Get the correct item
if (!(itMapping->first <= itItem->first && (itMapping->first + itMapping->second.size > itItem->first))) {
if (itItem->second.state == MemoryState::Reserved) {
info->start = (void*)itItem->first;
info->end = (void*)(itItem->first + itItem->second.size);
info->offset = 0;
info->isCommitted = false;
return Ok;
}
return getErr(ErrCode::_EACCES);
}
info->start = (void*)itMapping->first;
info->end = (void*)(itMapping->first + itMapping->second.size);
info->offset = 0;
info->isCommitted = true;
return Ok;
}
int32_t DirectMemory::directQuery(uint64_t offset, SceKernelDirectMemoryQueryInfo* info) const {
LOG_USE_MODULE(DirectMemory);
boost::unique_lock lock(m_mutexInt);
for (auto& item: m_objects) {
auto off = item.second.addr - DIRECTMEM_START;
if (offset >= off && offset < off + item.second.size) {
info->start = item.second.addr;
info->end = item.second.addr + item.second.size;
info->memoryType = item.second.memoryType;
LOG_DEBUG(L"Query OK: offset:0x%08llx -> start:0x%08llx end:0x%08llx type:%d", offset, info->start, info->end, info->memoryType);
return Ok;
}
}
LOG_DEBUG(L"Query Error: offset:0x%08llx", offset);
return getErr(ErrCode::_EACCES);
}

View File

@ -0,0 +1,267 @@
#include "../memoryManager.h"
#include "core/kernel/filesystem.h"
#include "core/runtime/procParam.h"
#include "core/runtime/runtimeLinker.h"
#include "core/videoout/videoout.h"
#include "logging.h"
#include "memory.h"
#include "modules/libkernel/codes.h"
#include "modules/libkernel/dmem.h"
#include "utility/utility.h"
#include <boost/thread.hpp>
#include <map>
#include <vk_mem_alloc.h>
#include <vulkan/vk_enum_string_helper.h>
#include <windows.h>
#undef min
LOG_DEFINE_MODULE(FlexibleMemory);
namespace {
constexpr uint64_t DIRECTMEM_START = 0x100000000u;
enum class MemoryState {
Free,
Reserved,
Commited,
};
struct MemoryMapping {
uint64_t addr = 0;
uint64_t size = 0;
int prot = 0;
};
bool checkIsGPU(int protection) {
return (protection & 0xf0) > 0;
}
DWORD convProtection(int prot) {
switch (prot & 0xf) {
case 0: return PAGE_NOACCESS;
case 1: return PAGE_READONLY;
case 2:
case 3: return PAGE_READWRITE;
case 4: return PAGE_EXECUTE;
case 5: return PAGE_EXECUTE_READ;
case 6:
case 7: return PAGE_EXECUTE_READWRITE;
}
if (checkIsGPU(prot)) {
return PAGE_READWRITE; // special case: cpu read/writes gpumemory on host memory
}
return PAGE_NOACCESS;
}
} // namespace
class FlexibleMemory: public IMemoryType {
mutable boost::mutex m_mutexInt;
IMemoryManager* m_parent;
std::map<uint64_t, MemoryMapping> m_mappings;
uint64_t m_configuresSize = 448 * 1024 * 1024;
uint64_t m_usedSize = 0;
public:
FlexibleMemory(IMemoryManager* parent): m_parent(parent) {}
virtual ~FlexibleMemory() { deinit(); }
// ### Interface
void setTotalSize(uint64_t totalSize) final;
int alloc(size_t len, size_t alignment, int memoryType, uint64_t* outAddr) final;
int free(off_t start, size_t len) final;
int map(uint64_t vaddr, off_t directMemoryOffset, size_t len, int prot, int flags, size_t alignment, uint64_t* outAddr) final;
int unmap(uint64_t vaddr, uint64_t size) final;
virtual int reserve(uint64_t start, size_t len, size_t alignment, int flags, uint64_t* outAddr) final;
uint64_t size() const final;
int getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) const final;
int32_t virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const final;
int32_t directQuery(uint64_t offset, SceKernelDirectMemoryQueryInfo* info) const final;
void deinit() final;
};
std::unique_ptr<IMemoryType> createFlexibleMemory(IMemoryManager* parent) {
return std::make_unique<FlexibleMemory>(parent);
}
void FlexibleMemory::setTotalSize(uint64_t totalSize) {
LOG_USE_MODULE(FlexibleMemory);
m_configuresSize = totalSize;
}
int FlexibleMemory::alloc(size_t len, size_t alignment, int memoryType, uint64_t* outAddr) {
LOG_USE_MODULE(FlexibleMemory);
LOG_CRIT(L"NOT IMPLEMENTED");
return Ok;
}
int FlexibleMemory::free(off_t start, size_t len) {
LOG_USE_MODULE(FlexibleMemory);
LOG_CRIT(L"NOT IMPLEMENTED");
return Ok;
}
int FlexibleMemory::map(uint64_t vaddr, off_t offset, size_t len, int prot, int flags, size_t alignment, uint64_t* outAddr) {
LOG_USE_MODULE(FlexibleMemory);
boost::unique_lock lock(m_mutexInt);
if (flags & (int)filesystem::SceMapMode::VOID_) {
LOG_CRIT(L"todo void map");
}
uint64_t desVaddr = 0;
if (flags & (int)filesystem::SceMapMode::FIXED) {
desVaddr = vaddr;
}
if (m_configuresSize <= (m_usedSize + len)) return getErr(ErrCode::_ENOMEM);
// Commit
{
MEM_ADDRESS_REQUIREMENTS addressReqs = {0};
MEM_EXTENDED_PARAMETER extendedParams = {0};
addressReqs.Alignment = 0; // 64 KB alignment
addressReqs.LowestStartingAddress = (PVOID)DIRECTMEM_START;
addressReqs.HighestEndingAddress = (PVOID)0;
extendedParams.Type = MemExtendedParameterAddressRequirements;
extendedParams.Pointer = &addressReqs;
*outAddr = (uint64_t)VirtualAlloc2(NULL, (void*)desVaddr, len, MEM_RESERVE | MEM_COMMIT | MEM_WRITE_WATCH, convProtection(prot),
desVaddr != 0 ? 0 : &extendedParams, desVaddr != 0 ? 0 : 1);
if (*outAddr == 0) {
auto const err = GetLastError();
LOG_ERR(L"Commit Error| addr:0x%08llx len:0x%08llx err:%d", desVaddr, len, GetLastError());
return getErr(ErrCode::_EINVAL);
}
}
// - commit
m_mappings.emplace(std::make_pair(*outAddr, MemoryMapping {.addr = *outAddr, .size = len, .prot = prot}));
m_parent->registerMapping(*outAddr, len, MappingType::Flexible);
m_usedSize += len;
LOG_DEBUG(L"-> Map: start:0x%08llx(0x%x) len:0x%08llx alignment:0x%08llx prot:%d -> 0x%08llx", vaddr, offset, len, alignment, prot, *outAddr);
if (checkIsGPU(prot)) {
if (!accessVideoOut().notify_allocHeap(*outAddr, len, prot)) {
return getErr(ErrCode::_EINVAL);
}
}
return Ok;
}
int FlexibleMemory::reserve(uint64_t start, size_t len, size_t alignment, int flags, uint64_t* outAddr) {
LOG_USE_MODULE(FlexibleMemory);
LOG_CRIT(L"NOT IMPLEMENTED");
return Ok;
}
int FlexibleMemory::unmap(uint64_t vaddr, uint64_t size) {
LOG_USE_MODULE(FlexibleMemory);
boost::unique_lock lock(m_mutexInt);
// Find the object
if (m_mappings.empty()) return getErr(ErrCode::_EINVAL);
auto itItem = m_mappings.lower_bound(vaddr);
if (itItem == m_mappings.end() || (itItem != m_mappings.begin() && itItem->first != vaddr)) --itItem; // Get the correct item
if (!(itItem->first <= vaddr && (itItem->first + itItem->second.size >= vaddr))) {
LOG_ERR(L"Couldn't find mapping for 0x%08llx 0x%08llx", vaddr, size);
return -1;
}
//-
if (checkIsGPU(itItem->second.prot)) {
// todo
} else {
if (itItem->second.addr != 0) VirtualFree((void*)itItem->second.addr, itItem->second.size, 0);
}
LOG_DEBUG(L"unmap| 0x%08llx 0x%08llx", vaddr, size);
m_usedSize -= itItem->second.size;
m_mappings.erase(itItem);
return Ok;
}
uint64_t FlexibleMemory::size() const {
return m_configuresSize;
}
int FlexibleMemory::getAvailableSize(uint32_t start, uint32_t end, size_t alignment, uint32_t* startOut, size_t* sizeOut) const {
LOG_USE_MODULE(FlexibleMemory);
*sizeOut = m_configuresSize - m_usedSize;
LOG_DEBUG(L"availableSize: 0x%08llx ", *sizeOut);
return Ok;
}
void FlexibleMemory::deinit() {
for (auto& item: m_mappings) {
if (checkIsGPU(item.second.prot)) {
// done by gpuMemoryManager
} else {
// memory::free(item.first);
}
}
m_mappings.clear();
}
int32_t FlexibleMemory::virtualQuery(uint64_t addr, SceKernelVirtualQueryInfo* info) const {
boost::unique_lock lock(m_mutexInt);
auto itMapping = m_mappings.lower_bound(addr);
if (itMapping == m_mappings.end() || (itMapping != m_mappings.begin() && itMapping->first != addr)) --itMapping; // Get the correct item
if (!(itMapping->first <= addr && (itMapping->first + itMapping->second.size >= addr))) {
return getErr(ErrCode::_EACCES);
}
info->protection = itMapping->second.prot;
info->memoryType = 3;
info->isFlexibleMemory = true;
info->isDirectMemory = false;
info->isPooledMemory = false;
// info->isStack = false; // done by parent
info->start = (void*)itMapping->first;
info->end = (void*)(itMapping->first + itMapping->second.size);
info->offset = 0;
info->isCommitted = true;
return Ok;
}
int32_t FlexibleMemory::directQuery(uint64_t offset, SceKernelDirectMemoryQueryInfo* info) const {
LOG_USE_MODULE(FlexibleMemory);
LOG_CRIT(L"NOT IMPLEMENTED");
return Ok;
}

7
core/dmem/types/memory.h Normal file
View File

@ -0,0 +1,7 @@
#pragma once
#include "../imemory.h"
class IMemoryManager;
std::unique_ptr<IMemoryType> createDirectMemory(IMemoryManager* parent);
std::unique_ptr<IMemoryType> createFlexibleMemory(IMemoryManager* parent);

View File

@ -0,0 +1,15 @@
add_library(fileManager OBJECT
fileManager.cpp
types/type_in.cpp
types/type_out.cpp
types/type_zero.cpp
types/type_null.cpp
types/type_file.cpp
types/type_random.cpp
types/type_rng.cpp
types/type_lib.cpp
)
add_dependencies(fileManager third_party psOff_utility)
file(COPY fileManager.h ifile.h DESTINATION ${DST_INCLUDE_DIR}/fileManager)

View File

@ -0,0 +1,273 @@
#define __APICALL_EXTERN
#include "fileManager.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "magic_enum/magic_enum.hpp"
#include "types/type_in.h"
#include "types/type_out.h"
#include "utility/utility.h"
#include <algorithm>
#include <fstream>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <utility>
LOG_DEFINE_MODULE(FileManager);
struct UniData {
CLASS_NO_COPY(UniData);
enum class Type { File, Dir };
Type const m_type;
std::filesystem::path const m_path;
UniData(Type type, std::filesystem::path const& path): m_type(type), m_path(path) {}
virtual ~UniData() = default;
};
struct FileData: public UniData {
std::unique_ptr<IFile> const m_file;
FileData(std::unique_ptr<IFile>&& file, std::filesystem::path const& path): UniData(UniData::Type::File, path), m_file(std::move(file)) {}
virtual ~FileData() { m_file->sync(); }
};
struct DirData: public UniData {
std::unique_ptr<std::filesystem::directory_iterator> const m_file;
uint32_t count = 0;
DirData(std::unique_ptr<std::filesystem::directory_iterator>&& dirIt, std::filesystem::path const& path)
: UniData(UniData::Type::Dir, path), m_file(std::move(dirIt)) {}
virtual ~DirData() = default;
};
namespace {
int insertItem(std::vector<std::unique_ptr<UniData>>& container, UniData* item) {
int index = -1;
for (size_t n = 0; n < container.size(); ++n) {
if (!container[n]) {
container[n] = std::unique_ptr<UniData>(item);
index = n;
break;
}
}
if (index < 0) {
index = container.size();
container.push_back(std::unique_ptr<UniData>(item));
}
return index;
}
} // namespace
class FileManager: public IFileManager {
std::vector<std::unique_ptr<UniData>> m_openFiles;
std::mutex m_mutext_int;
/// First: mountpoint Second: RootDir
std::unordered_map<MountType, std::unordered_map<std::string, std::filesystem::path>> m_mountPointList;
std::filesystem::path m_dirGameFiles;
std::unordered_map<std::string, std::filesystem::path> m_mappedPaths;
bool m_updateSearch = false;
public:
FileManager() { init(); };
virtual ~FileManager() = default;
void init() {
// Order of the first three files is important, do not change it!
assert(addFile(createType_in(), "/dev/stdin") == 0);
assert(addFile(createType_out(SCE_TYPEOUT_ERROR), "/dev/stdout") == 1);
assert(addFile(createType_out(SCE_TYPEOUT_DEBUG), "/dev/stderr") == 2);
}
void addMountPoint(std::string_view mountPoint, std::filesystem::path const& root, MountType type) final {
LOG_USE_MODULE(FileManager);
std::unique_lock const lock(m_mutext_int);
m_mountPointList[type][std::string(mountPoint)] = root;
// Create root dir
std::filesystem::create_directories(root);
LOG_INFO(L"+ MountPoint: %S->%s", mountPoint.data(), root.c_str());
}
std::filesystem::path getMountPoint(MountType type, std::string mountPoint) final {
std::unique_lock const lock(m_mutext_int);
if (auto it = m_mountPointList[type].find(mountPoint); it != m_mountPointList[type].end()) return it->second;
return {};
}
void clearMountPoint(MountType type, std::string mountPoint) final {
LOG_USE_MODULE(FileManager);
std::unique_lock const lock(m_mutext_int);
LOG_INFO(L"- MountPoint for %S", magic_enum::enum_name(type).data());
m_mountPointList[type].erase(mountPoint);
}
std::optional<std::filesystem::path> getMappedPath(std::string_view path) final {
LOG_USE_MODULE(FileManager);
std::unique_lock const lock(m_mutext_int);
// Check Cache
{
auto it = m_mappedPaths.find(path.data());
if (it != m_mappedPaths.end()) {
return it->second;
}
}
//-
// Special case: Mounted Gamefiles
if (path[0] != '/') {
auto const mapped = m_dirGameFiles / path;
LOG_INFO(L"Gamefiles mapped:%s", mapped.c_str());
return mapped;
}
// -
uint8_t offset = (path[0] == '/' && path[1] == '/') ? 1 : 0; // Path can start with //app0 ?
for (auto const& mountType: m_mountPointList) {
for (auto const& item: mountType.second) {
auto const& mountPoint = item.first;
auto const& rootDir = item.second;
if (path.size() >= mountPoint.size() && std::mismatch(mountPoint.begin(), mountPoint.end(), path.begin() + offset).first == mountPoint.end()) {
if (path[mountPoint.size() + offset] == '/') ++offset; // path.substr() should return relative path
if (mountType.first == MountType::App && m_updateSearch) {
auto const& updateDir = m_mountPointList[MountType::Update].begin()->second;
auto const mapped = updateDir / path.substr(mountPoint.size() + offset);
if (std::filesystem::exists(mapped)) {
LOG_DEBUG(L"mapped: %s root:%s source:%S", mapped.c_str(), updateDir.c_str(), path.data());
m_mappedPaths[path.data()] = mapped;
return mapped;
}
}
auto const mapped = rootDir / path.substr(mountPoint.size() + offset);
LOG_DEBUG(L"mapped: %s root:%s source:%S", mapped.c_str(), rootDir.c_str(), path.data());
m_mappedPaths[path.data()] = mapped;
return mapped;
}
}
}
LOG_WARN(L"Unknown map:%S", path.data());
return {};
}
void setGameFilesDir(std::filesystem::path const& path) final {
std::unique_lock const lock(m_mutext_int);
m_dirGameFiles = path;
}
std::filesystem::path const& getGameFilesDir() const final { return m_dirGameFiles; }
int addFile(std::unique_ptr<IFile>&& file, std::filesystem::path const& path) final {
std::unique_lock const lock(m_mutext_int);
return insertItem(m_openFiles, std::make_unique<FileData>(std::move(file), path).release());
}
int addDirIterator(std::unique_ptr<std::filesystem::directory_iterator>&& it, std::filesystem::path const& path) final {
std::unique_lock const lock(m_mutext_int);
return insertItem(m_openFiles, std::make_unique<DirData>(std::move(it), path).release());
}
void remove(int handle) final {
std::unique_lock const lock(m_mutext_int);
m_openFiles[handle].reset();
}
IFile* accessFile(int handle) final {
std::unique_lock const lock(m_mutext_int);
if (handle < m_openFiles.size() && m_openFiles[handle] && m_openFiles[handle]->m_type == UniData::Type::File) {
return static_cast<FileData*>(m_openFiles[handle].get())->m_file.get();
}
return nullptr;
}
std::filesystem::path getPath(int handle) final {
std::unique_lock const lock(m_mutext_int);
if (handle < m_openFiles.size() && m_openFiles[handle]) {
return m_openFiles[handle]->m_path;
}
return {};
}
int getDents(int handle, char* buf, int nbytes, int64_t* basep) final {
LOG_USE_MODULE(FileManager);
std::unique_lock const lock(m_mutext_int);
auto& oFile = m_openFiles[handle];
if (oFile->m_type != UniData::Type::Dir) return -1;
auto dir = static_cast<DirData*>(oFile.get());
auto endDir = std::filesystem::directory_iterator();
if ((*dir->m_file) == endDir) return 0;
#pragma pack(push, 1)
struct DataStruct {
uint32_t fileno;
uint16_t reclen;
uint8_t type;
uint8_t namlen;
char name[256];
};
#pragma pack(pop, 1)
auto count = dir->count;
int n = 0;
while (n < nbytes - sizeof(DataStruct)) {
auto item = (DataStruct*)(buf + n);
auto const filename = (*dir->m_file)->path().filename().string();
if (sizeof(DataStruct) + std::min(filename.size(), 255llu) >= nbytes) break;
item->fileno = count;
item->type = ((*dir->m_file)->is_regular_file() ? 8 : 4);
item->namlen = filename.copy(item->name, 255);
item->name[item->namlen] = '\0';
n += sizeof(DataStruct);
item->reclen = sizeof(DataStruct);
LOG_DEBUG(L"KernelGetdirentries[%d]: %S %u offset:%u count:%u", handle, item->name, item->type, n, count);
std::error_code err;
(*dir->m_file).increment(err);
count = ++dir->count;
if ((*dir->m_file) == endDir || err) break;
};
if (basep != nullptr) {
*basep = count;
}
return n;
}
void enableUpdateSearch() final { m_updateSearch = true; }
};
IFileManager& accessFileManager() {
static FileManager inst;
return inst;
}

View File

@ -0,0 +1,133 @@
#pragma once
#include "ifile.h"
#include "utility/utility.h"
#include <filesystem>
#include <fstream>
#include <memory>
#include <optional>
#include <string>
enum class MountType { App, Update, Save, Temp, Data };
constexpr int FILE_DESCRIPTOR_MIN = 3;
constexpr std::string_view MOUNT_POINT_TEMP = "temp";
constexpr std::string_view MOUNT_POINT_DATA = "data";
constexpr std::string_view MOUNT_POINT_APP = "/app0/";
constexpr std::string_view MOUNT_POINT_UPDATE = "/app1/";
constexpr std::string_view SAVE_DATA_POINT = "/savedata";
class IFileManager {
CLASS_NO_COPY(IFileManager);
CLASS_NO_MOVE(IFileManager);
protected:
IFileManager() = default;
virtual ~IFileManager() = default;
public:
/**
* @brief Get the mapped path. (Resolves linux paths, mounts etc.)
*
* @param path
* @return std::optional<std::filesystem::path> on success, the mapped path
*/
virtual std::optional<std::filesystem::path> getMappedPath(std::string_view path) = 0;
/**
* @brief Adds a fake mount point. getMappedPath() uses those to resolve to the correct path.
* Just replaces both strings
*
* @param mountPoint e.g. /savedata
* @param root actual path e.g. C:/savedir
* @param type Used to group the mounts. normaly only one mount per type is used. (savedata uses multiple ...)
*/
virtual void addMountPoint(std::string_view mountPoint, std::filesystem::path const& root, MountType type) = 0;
/**
* @brief Get the actual path for a mount point
*
* @param type
* @param mountPoint e.g. /savedata
* @return std::filesystem::path empty on error
*/
virtual std::filesystem::path getMountPoint(MountType type, std::string mountPoint) = 0;
/**
* @brief Removes the mountpoint
*
* @param type
* @param mountPoint
*/
virtual void clearMountPoint(MountType type, std::string mountPoint) = 0;
/**
* @brief Sets the path where all games files should be located (savegames etc).
* Used by main to setup the emulator
* @param path
*/
virtual void setGameFilesDir(std::filesystem::path const& path) = 0;
/**
* @brief Get the Game Files Dir
*
* @return std::filesystem::path const&
*/
virtual std::filesystem::path const& getGameFilesDir() const = 0;
/**
* @brief Add a open file.
*
* @param file
* @param path the mapped path to the file/folder
* @return int handle of the fstream. used by getFile() etc.
*/
virtual int addFile(std::unique_ptr<IFile>&& file, std::filesystem::path const& path) = 0;
/**
* @brief Add a directory_iterator
*
* @param it
* @param path the mapped path to the folder
* @return int handle to the directory_iterator
*/
virtual int addDirIterator(std::unique_ptr<std::filesystem::directory_iterator>&& it, std::filesystem::path const& path) = 0;
/**
* @brief remove the file/folder with the associated handle.
* Only the internal mapping is removed!
*
* @param handle
*/
virtual void remove(int handle) = 0;
/**
* @brief Get access to the File
*
* @param handle
* @return std::fstream*
*/
virtual IFile* accessFile(int handle) = 0;
virtual int getDents(int handle, char* buf, int nbytes, int64_t* basep) = 0;
/**
* @brief Get the mapped path of a open file/folder
*
* @param handle
* @return std::filesystem::path
*/
virtual std::filesystem::path getPath(int handle) = 0;
virtual void enableUpdateSearch() = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IFileManager& accessFileManager();
#undef __APICALL

87
core/fileManager/ifile.h Normal file
View File

@ -0,0 +1,87 @@
#pragma once
#include "modules_include/common.h"
#include "utility/utility.h"
#include <stdint.h>
enum class FileType {
File,
Device,
Library,
};
enum class SceWhence : int {
beg,
cur,
end,
};
class IFile {
CLASS_NO_COPY(IFile);
CLASS_NO_MOVE(IFile);
FileType const m_type;
protected:
int32_t m_errCode = 0;
IFile(FileType type): m_type(type) {}
public:
virtual ~IFile() = default;
auto getType() const { return m_type; }
auto getErr() const { return m_errCode; }
void clearError() { m_errCode = 0; }
/**
* @brief read n bytes from file to (uint8_t)buf[nbytes]
*
* @param buf
* @param nbytes
* @return size_t number of bytes read, 0:empty
*/
virtual size_t read(void* buf, size_t nbytes) = 0;
/**
* @brief write n bytes from (uint8_t)buf[nbytes] to file
*
* @param buf
* @param nbytes
* @return size_t number of bytes written
*/
virtual size_t write(void* buf, size_t nbytes) = 0;
virtual void sync() = 0;
/**
* @brief Set the read/write position
*
* @param offset
* @param whence
* @return int64_t
*/
virtual int64_t lseek(int64_t offset, SceWhence whence) = 0;
/**
* @brief Manipulates the underlying device parameters of special files.
*
* @param handle
* @param request
* @return int error code, 0:no error
*/
virtual int ioctl(int request, SceVariadicList argp) = 0;
/**
* @brief Performs one of the operations on the open file descriptor.
*
* @param handle
* @param cmd
* @return int error code, 0:no error
*/
virtual int fcntl(int cmd, SceVariadicList argp) = 0;
virtual void* getNative() = 0;
};

View File

@ -0,0 +1,8 @@
## FileManager
Manages all Linux files (mounts etc.), converts paths to Host paths and is the owner of opened files.
<div align="center">
![](../../out/docs/uml/modules/fileManager.svg)
</div>

View File

@ -0,0 +1,157 @@
#include "type_file.h"
#include "logging.h"
#include <filesystem>
#include <fstream>
#include <windows.h>
LOG_DEFINE_MODULE(File);
namespace {
typedef HANDLE Handle_t;
std::pair<Handle_t, int32_t> open(std::filesystem::path path, filesystem::SceOpen mode) {
LOG_USE_MODULE(File);
DWORD shareMode = 0;
DWORD access = 0;
// Set acces and shareMode;
switch (mode.mode) {
case filesystem::SceOpenMode::RDONLY: {
access = GENERIC_READ;
if (!mode.exlock) shareMode = FILE_SHARE_READ;
} break;
case filesystem::SceOpenMode::WRONLY: {
access = GENERIC_WRITE;
if (!mode.exlock) shareMode = FILE_SHARE_WRITE;
} break;
case filesystem::SceOpenMode::RDWR: {
access = GENERIC_READ | GENERIC_WRITE;
if (!mode.exlock) shareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
} break;
}
// -
DWORD flags = OPEN_EXISTING;
if (mode.trunc) flags = CREATE_ALWAYS;
// Create new if it doesn't exist
else if (mode.create) {
flags = CREATE_NEW;
}
auto pathString = path.wstring();
std::replace(pathString.begin(), pathString.end(), L'/', L'\\');
HANDLE file = CreateFileW(pathString.c_str(), access, shareMode, 0, flags, FILE_ATTRIBUTE_NORMAL, 0);
if (file == INVALID_HANDLE_VALUE) {
auto const lastErr = GetLastError();
LOG_WARN(L"open file: %s flags:0x%x access:0x%x shareMode:0x%x mode:0x%llx(%u) error:0x%x ", pathString.c_str(), flags, access, shareMode,
*(uint32_t*)(&mode), mode.mode, lastErr);
if (lastErr == 0x57) return {nullptr, (int)ErrCode::_EINVAL};
if (lastErr == 0x20) return {nullptr, (int)ErrCode::_EALREADY};
return {nullptr, Ok};
}
LOG_DEBUG(L"[%lld] opened file: %s flags:0x%x access:0x%x shareMode:0x%x mode:0x%x(%u)", file, pathString.c_str(), flags, access, shareMode,
*(uint32_t*)(&mode), mode.mode);
return {file, Ok};
}
} // namespace
class TypeFile: public IFile {
Handle_t m_file = INVALID_HANDLE_VALUE;
filesystem::SceOpen const m_mode;
bool m_isAppend = false;
public:
TypeFile(std::filesystem::path path, filesystem::SceOpen mode): IFile(FileType::File), m_mode(mode) {
auto [file, err] = open(path, mode);
m_file = file;
m_errCode = err;
m_isAppend = mode.append;
}
virtual ~TypeFile() {
if (m_file != INVALID_HANDLE_VALUE) {
CloseHandle(m_file);
sync();
}
}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
virtual void* getNative() final { return m_file; }
};
std::unique_ptr<IFile> createType_file(std::filesystem::path path, filesystem::SceOpen mode) {
return std::make_unique<TypeFile>(path, mode);
}
size_t TypeFile::read(void* buf, size_t nbytes) {
DWORD numRead = 0;
if (!ReadFile(m_file, buf, nbytes, &numRead, nullptr)) {
auto const lastError = GetLastError();
if (lastError != ERROR_IO_PENDING) {
LOG_USE_MODULE(File);
LOG_WARN(L"Read error: 0x%x", GetLastError());
m_errCode = (int)ErrCode::_EIO;
return 0;
}
}
return numRead;
}
size_t TypeFile::write(void* buf, size_t nbytes) {
// The file is opened in append mode. Before each write(2),
// the file offset is positioned at the end of the file
if (m_isAppend) {
lseek(0, SceWhence::end);
}
// -
DWORD numWrite = 0;
if (!WriteFile(m_file, buf, nbytes, &numWrite, nullptr)) {
LOG_USE_MODULE(File);
LOG_WARN(L"Write error: 0x%x", GetLastError());
m_errCode = (int)ErrCode::_EIO;
return 0;
}
return numWrite;
}
void TypeFile::sync() {
FlushFileBuffers(m_file);
}
int TypeFile::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeFile::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeFile::lseek(int64_t offset, SceWhence whence) {
static int _whence[] = {
FILE_BEGIN,
FILE_CURRENT,
FILE_END,
};
LONG upperHalf = offset >> 32;
DWORD lowerHalf = SetFilePointer(m_file, (uint32_t)offset, &upperHalf, _whence[(int)whence]);
return ((uint64_t)upperHalf << 32) | lowerHalf;
}

View File

@ -0,0 +1,9 @@
#pragma once
#include "../ifile.h"
#include "core/kernel/filesystem.h"
#include <filesystem>
#include <memory>
std::unique_ptr<IFile> createType_file(std::filesystem::path path, filesystem::SceOpen mode);

View File

@ -0,0 +1,48 @@
#include "type_in.h"
#include "modules_include/common.h"
class TypeIn: public IFile {
public:
TypeIn(): IFile(FileType::Device) {}
virtual ~TypeIn() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_in() {
return std::make_unique<TypeIn>();
}
size_t TypeIn::read(void* buf, size_t nbytes) {
if (nbytes == 0) return 0;
printf("Emulator awaits your input: ");
return std::fread(buf, 1, nbytes, stdin);
}
size_t TypeIn::write(void* buf, size_t nbytes) {
return nbytes;
}
void TypeIn::sync() {}
int TypeIn::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeIn::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeIn::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "../ifile.h"
#include <memory>
std::unique_ptr<IFile> createType_in();

View File

@ -0,0 +1,55 @@
#include "type_lib.h"
#include "core/runtime/runtimeLinker.h"
#include "logging.h"
LOG_DEFINE_MODULE(IODevice_LIB);
class TypeLib: public IFile {
Program* m_prog;
public:
TypeLib(Program* prog): IFile(FileType::Library), m_prog(prog) {}
~TypeLib() { accessRuntimeLinker().stopModule(m_prog->id); }
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_lib(Program* prog) {
return std::make_unique<TypeLib>(prog);
}
size_t TypeLib::read(void* buf, size_t nbytes) {
LOG_USE_MODULE(IODevice_LIB);
LOG_CRIT(L"Program(%p)->read(%p, %llu)", m_prog->id, buf, nbytes);
return 0;
}
size_t TypeLib::write(void* buf, size_t nbytes) {
LOG_USE_MODULE(IODevice_LIB);
LOG_CRIT(L"Program(%p)->write(%p, %llu)", m_prog->id, buf, nbytes);
return 0;
}
void TypeLib::sync() {}
int TypeLib::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeLib::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeLib::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include "../ifile.h"
struct Program;
std::unique_ptr<IFile> createType_lib(Program* prog);

View File

@ -0,0 +1,46 @@
#include "type_null.h"
#include "modules_include/common.h"
class TypeNull: public IFile {
public:
TypeNull(): IFile(FileType::Device) {}
virtual ~TypeNull() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_null() {
return std::make_unique<TypeNull>();
}
size_t TypeNull::read(void* buf, size_t nbytes) {
return -1;
}
size_t TypeNull::write(void* buf, size_t nbytes) {
return nbytes;
}
void TypeNull::sync() {}
int TypeNull::ioctl(int request, SceVariadicList argp) {
return -1;
}
int TypeNull::fcntl(int cmd, SceVariadicList argp) {
return -1;
}
int64_t TypeNull::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "../ifile.h"
#include <memory>
std::unique_ptr<IFile> createType_null();

View File

@ -0,0 +1,70 @@
#include "type_out.h"
#include "logging.h"
LOG_DEFINE_MODULE(IODevice_OUT);
class TypeOut: public IFile {
const SceFileOutChannel m_channel = SCE_TYPEOUT_ERROR;
public:
TypeOut(SceFileOutChannel ch): IFile(FileType::Device), m_channel(ch) {}
virtual ~TypeOut() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_out(SceFileOutChannel ch) {
return std::make_unique<TypeOut>(ch);
}
size_t TypeOut::read(void* buf, size_t nbytes) {
return 0;
}
size_t TypeOut::write(void* buf, size_t nbytes) {
LOG_USE_MODULE(IODevice_OUT);
std::string str((const char*)buf, nbytes);
while (str.back() == '\n')
str.pop_back();
if (str.length() == 0) return nbytes;
switch (m_channel) {
case SCE_TYPEOUT_ERROR: {
printf("Console:%s\n", str.c_str());
LOG_DEBUG(L"%S", str.c_str());
} break;
case SCE_TYPEOUT_DEBUG: LOG_DEBUG(L"%S", str.c_str()); break;
default: LOG_CRIT(L"Unknown channel: %d", (uint32_t)m_channel); break;
}
return nbytes;
}
void TypeOut::sync() {}
int TypeOut::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeOut::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeOut::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,12 @@
#pragma once
#include "../ifile.h"
#include <memory>
enum SceFileOutChannel {
SCE_TYPEOUT_ERROR,
SCE_TYPEOUT_DEBUG,
};
std::unique_ptr<IFile> createType_out(SceFileOutChannel ch);

View File

@ -0,0 +1,50 @@
#include "type_random.h"
class TypeRandom: public IFile {
public:
TypeRandom(): IFile(FileType::Device) {}
virtual ~TypeRandom() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_random() {
std::srand(std::time(nullptr));
return std::make_unique<TypeRandom>();
}
size_t TypeRandom::read(void* buf, size_t nbytes) {
auto cbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++)
cbuf[i] = std::rand() & 0xFF;
return nbytes;
}
size_t TypeRandom::write(void* buf, size_t nbytes) {
return 0;
}
void TypeRandom::sync() {}
int TypeRandom::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeRandom::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeRandom::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "../ifile.h"
#include <memory>
std::unique_ptr<IFile> createType_random();

View File

@ -0,0 +1,63 @@
#include "logging.h"
#include "type_random.h"
LOG_DEFINE_MODULE(IODevice_RNG);
class TypeRNG: public IFile {
public:
TypeRNG(): IFile(FileType::Device) {}
virtual ~TypeRNG() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_rng() {
std::srand(std::time(nullptr));
return std::make_unique<TypeRNG>();
}
size_t TypeRNG::read(void* buf, size_t nbytes) {
auto cbuf = static_cast<char*>(buf);
for (size_t i = 0; i < nbytes; i++)
cbuf[i] = std::rand() & 0xFF;
return nbytes;
}
size_t TypeRNG::write(void* buf, size_t nbytes) {
return 0;
}
void TypeRNG::sync() {}
int TypeRNG::ioctl(int request, SceVariadicList argp) {
LOG_USE_MODULE(IODevice_RNG);
switch (request) {
// These are unknown for now
case 0x40445301: break;
case 0x40445302: break;
default: LOG_ERR(L"Unknown ioctl request to /dev/rng: %d", request); return -1;
}
return 0;
}
int TypeRNG::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeRNG::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "../ifile.h"
#include <memory>
std::unique_ptr<IFile> createType_rng();

View File

@ -0,0 +1,48 @@
#include "type_zero.h"
class TypeZero: public IFile {
public:
TypeZero(): IFile(FileType::Device) {}
virtual ~TypeZero() {}
// ### Interface
size_t read(void* buf, size_t nbytes) final;
size_t write(void* buf, size_t nbytes) final;
void sync() final;
int ioctl(int request, SceVariadicList argp) final;
int fcntl(int cmd, SceVariadicList argp) final;
int64_t lseek(int64_t offset, SceWhence whence) final;
void* getNative() final { return nullptr; }
};
std::unique_ptr<IFile> createType_zero() {
return std::make_unique<TypeZero>();
}
size_t TypeZero::read(void* buf, size_t nbytes) {
for (size_t i = 0; i < nbytes; i++) {
((char*)buf)[i] = '\0';
}
return nbytes;
}
size_t TypeZero::write(void* buf, size_t nbytes) {
return nbytes;
}
void TypeZero::sync() {}
int TypeZero::ioctl(int request, SceVariadicList argp) {
return 0;
}
int TypeZero::fcntl(int cmd, SceVariadicList argp) {
return 0;
}
int64_t TypeZero::lseek(int64_t offset, SceWhence whence) {
return -1;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "../ifile.h"
#include <memory>
std::unique_ptr<IFile> createType_zero();

View File

@ -0,0 +1,9 @@
add_library(hotkeys OBJECT
hotkeys.cpp
)
add_dependencies(hotkeys third_party config_emu)
target_compile_definitions(hotkeys PUBLIC WIN32_LEAN_AND_MEAN)
file(COPY hotkeys.h DESTINATION ${DST_INCLUDE_DIR}/hotkeys)

149
core/hotkeys/hotkeys.cpp Normal file
View File

@ -0,0 +1,149 @@
#define __APICALL_EXTERN
#include "hotkeys.h"
#undef __APICALL_EXTERN
#include "config_emu.h"
#include <unordered_map>
class Hotkeys: public IHotkeys {
private:
struct HkBacks {
HkCommand cmd;
HkFunc func;
};
struct KBind {
SDL_Keycode kc;
uint16_t mod;
};
std::vector<HkBacks> m_cbacks = {};
std::array<KBind, (size_t)HkCommand::COMMANDS_MAX> m_binds = {{SDLK_UNKNOWN, 0}};
uint32_t m_state = 0;
HkCommand lookupCommand(SDL_Keycode kc, uint16_t mod) {
for (uint32_t c = uint32_t(HkCommand::UNKNOWN); c < uint32_t(HkCommand::COMMANDS_MAX); ++c) {
auto& bind = m_binds[(uint32_t)c];
if (kc == bind.kc && mod == bind.mod) return HkCommand(c);
}
return HkCommand::UNKNOWN;
}
public:
Hotkeys() {
static std::unordered_map<std::string, HkCommand> map = {
{"gamereport.send", HkCommand::GAMEREPORT_SEND_REPORT},
{"overlay.open", HkCommand::OVERLAY_MENU},
{"controller.l3", HkCommand::CONTROLLER_L3},
{"controller.r3", HkCommand::CONTROLLER_R3},
{"controller.options", HkCommand::CONTROLLER_OPTIONS},
{"controller.dpad_up", HkCommand::CONTROLLER_DPAD_UP},
{"controller.dpad_right", HkCommand::CONTROLLER_DPAD_RIGHT},
{"controller.dpad_down", HkCommand::CONTROLLER_DPAD_DOWN},
{"controller.dpad_left", HkCommand::CONTROLLER_DPAD_LEFT},
{"controller.l2", HkCommand::CONTROLLER_L2},
{"controller.r2", HkCommand::CONTROLLER_R2},
{"controller.l1", HkCommand::CONTROLLER_L1},
{"controller.r1", HkCommand::CONTROLLER_R1},
{"controller.triangle", HkCommand::CONTROLLER_TRIANGLE},
{"controller.circle", HkCommand::CONTROLLER_CIRCLE},
{"controller.cross", HkCommand::CONTROLLER_CROSS},
{"controller.square", HkCommand::CONTROLLER_SQUARE},
{"controller.touchpad", HkCommand::CONTROLLER_TOUCH_PAD},
{"controller.lx+", HkCommand::CONTROLLER_LX_UP},
{"controller.lx-", HkCommand::CONTROLLER_LX_DOWN},
{"controller.ly+", HkCommand::CONTROLLER_LY_UP},
{"controller.ly-", HkCommand::CONTROLLER_LY_DOWN},
{"controller.rx+", HkCommand::CONTROLLER_RX_UP},
{"controller.rx-", HkCommand::CONTROLLER_RX_DOWN},
{"controller.ry+", HkCommand::CONTROLLER_RY_UP},
{"controller.ry-", HkCommand::CONTROLLER_RY_DOWN},
};
auto readBind = [](std::string& str, KBind* kb) -> bool {
if (str.length() == 0) return false;
auto delim = str.find_last_of("||");
if (delim != std::string::npos) {
auto mod = str.substr(0, delim - 1);
auto key = str.substr(delim + 1);
kb->kc = SDL_GetScancodeFromName(key.c_str());
switch (SDL_GetScancodeFromName(mod.c_str())) {
case SDL_SCANCODE_LALT: kb->mod = KMOD_LALT; return true;
case SDL_SCANCODE_LCTRL: kb->mod = KMOD_LCTRL; return true;
case SDL_SCANCODE_LSHIFT: kb->mod = KMOD_LSHIFT; return true;
case SDL_SCANCODE_RALT: kb->mod = KMOD_RALT; return true;
case SDL_SCANCODE_RCTRL: kb->mod = KMOD_RCTRL; return true;
case SDL_SCANCODE_RSHIFT: kb->mod = KMOD_RSHIFT; return true;
default: return false;
}
}
if ((kb->kc = SDL_GetScancodeFromName(str.c_str())) != SDL_SCANCODE_UNKNOWN) {
kb->mod = 0x0000;
return true;
}
return false;
};
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::CONTROLS);
for (auto& [bname, sdlkeys]: (*jData)["keybinds"].items()) {
auto it = map.find(bname.c_str());
if (it == map.end()) continue;
std::string temp;
sdlkeys.get_to(temp);
auto& bind = m_binds[(uint32_t)it->second];
if (!readBind(temp, &bind)) {
printf("Missing key binding for \"%s\"!\n", bname.c_str());
bind = {0, 0};
continue;
}
for (uint32_t c = uint32_t(HkCommand::UNKNOWN); c < uint32_t(HkCommand::COMMANDS_MAX); ++c) {
auto& tbind = m_binds[(uint32_t)c];
if (bind.kc == SDL_SCANCODE_UNKNOWN || tbind.kc == SDL_SCANCODE_UNKNOWN) continue;
if (tbind.kc == bind.kc && tbind.mod == bind.mod && (uint32_t)it->second != c) {
printf("Key conflict found! Please rebind your \"%s\" key.\n", bname.c_str());
bind = {0, 0};
continue;
}
}
}
}
void registerCallback(HkCommand cmd, HkFunc func) final { m_cbacks.emplace_back(cmd, func); }
bool isPressed(HkCommand cmd) final { return (m_state & (1ull << (uint8_t)cmd)) != 0; }
int32_t isPressedEx(HkCommand cmd1, HkCommand cmd2, int32_t ifcmd1, int32_t ifcmd2, int32_t def) final {
return isPressed(cmd1) ? ifcmd1 : (isPressed(cmd2) ? ifcmd2 : def);
}
void update(const SDL_KeyboardEvent* event) final {
bool pressed = event->type == SDL_KEYDOWN;
HkCommand cmd = lookupCommand(event->keysym.scancode, event->keysym.mod & 0x03C3);
if (cmd == HkCommand::UNKNOWN) return;
uint32_t bit = (1ull << (uint8_t)cmd);
if (pressed && ((m_state & bit) == 0)) {
for (auto& hkc: m_cbacks)
if (hkc.cmd == cmd) hkc.func(cmd);
m_state |= bit;
} else if (!pressed && ((m_state & bit) != 0)) {
m_state &= ~bit;
}
}
};
IHotkeys& accessHotkeys() {
static Hotkeys ih;
return ih;
}

72
core/hotkeys/hotkeys.h Normal file
View File

@ -0,0 +1,72 @@
#pragma once
#include "utility/utility.h"
#include <SDL2/SDL.h>
#include <functional>
enum class HkCommand {
UNKNOWN = 0,
// System commands
GAMEREPORT_SEND_REPORT,
OVERLAY_MENU,
// Gamepad emulation
CONTROLLER_L3,
CONTROLLER_R3,
CONTROLLER_OPTIONS,
CONTROLLER_DPAD_UP,
CONTROLLER_DPAD_RIGHT,
CONTROLLER_DPAD_DOWN,
CONTROLLER_DPAD_LEFT,
CONTROLLER_L2,
CONTROLLER_R2,
CONTROLLER_L1,
CONTROLLER_R1,
CONTROLLER_TRIANGLE,
CONTROLLER_CIRCLE,
CONTROLLER_CROSS,
CONTROLLER_SQUARE,
CONTROLLER_TOUCH_PAD,
CONTROLLER_LX_UP,
CONTROLLER_LX_DOWN,
CONTROLLER_LY_UP,
CONTROLLER_LY_DOWN,
CONTROLLER_RX_UP,
CONTROLLER_RX_DOWN,
CONTROLLER_RY_UP,
CONTROLLER_RY_DOWN,
/**
* Warning! If this value ever exceeds 32 you should increase
* the size of `m_state` variable in `class Hotkeys` aswell!
*/
COMMANDS_MAX,
};
typedef std::function<void(HkCommand)> HkFunc;
class IHotkeys {
CLASS_NO_COPY(IHotkeys);
CLASS_NO_MOVE(IHotkeys);
public:
IHotkeys() = default;
virtual ~IHotkeys() = default;
virtual void registerCallback(HkCommand cmd, HkFunc func) = 0;
virtual bool isPressed(HkCommand cmd) = 0;
virtual int32_t isPressedEx(HkCommand cmd1, HkCommand cmd2, int32_t ifcmd1, int32_t ifcmd2, int32_t def) = 0;
virtual void update(const SDL_KeyboardEvent* event) = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IHotkeys& accessHotkeys();
#undef __APICALL

View File

@ -0,0 +1,35 @@
#pragma once
#include <stdint.h>
namespace Pm4 {
#define PM4_GET(u, r, f) (((u) >> Pm4::r##_##f##_SHIFT) & Pm4::r##_##f##_MASK)
enum class Custom : uint8_t {
R_VS,
R_PS,
R_DRAW_INDEX,
R_DRAW_INDEX_AUTO,
R_DRAW_INDEX_OFFSET,
R_DRAW_RESET,
R_WAIT_FLIP_DONE,
R_CS,
R_DISPATCH_DIRECT,
R_DISPATCH_RESET,
R_DISPATCH_WAIT_MEM,
R_PUSH_MARKER,
R_POP_MARKER,
R_VS_EMBEDDED,
R_PS_EMBEDDED,
R_VS_UPDATE,
R_PS_UPDATE,
R_VGT_CONTROL,
R_NUM_COMMANDS, // used for array size
};
constexpr uint32_t create(uint16_t len, Custom baseIndex) {
return 0x60000000u | (((len - 2u) & 0x3fffu) << 8u) | (uint8_t)baseIndex;
}
} // namespace Pm4

View File

@ -0,0 +1,3 @@
## Exports
Exports currently provided by the emulator itself.

View File

@ -0,0 +1,7 @@
add_library(initParams OBJECT
initParams.cpp
)
add_dependencies(initParams third_party)
file(COPY initParams.h DESTINATION ${DST_INCLUDE_DIR}/initParams)

View File

@ -0,0 +1,101 @@
#define __APICALL_INITPARAMS_EXTERN
#include "initParams.h"
#undef __APICALL_INITPARAMS_EXTERN
#include <boost/program_options.hpp>
#include <iostream>
#include <memory>
class InitParams: public IInitParams {
boost::program_options::variables_map m_vm;
public:
InitParams() = default;
bool init(int argc, char** argv) final;
bool isDebug() final;
std::string getApplicationPath() final;
std::string getApplicationRoot() final;
std::string getUpdateRoot() final;
bool enableValidation() final;
bool enableBrightness() final;
bool useVSYNC() final;
bool try4K() final;
virtual ~InitParams() = default;
};
bool InitParams::init(int argc, char** argv) {
namespace po = boost::program_options;
po::options_description desc("Allowed options");
desc.add_options()
// clang-format off
("help,h", "Help")
("d", "Wait for debugger")
("vkValidation", "Enable vulkan validation layers")
("bright", "use srgb display format (brightness)")
("4k", "try 4K display mode if game supports it")
("vsync", po::value<bool>()->default_value(true), "Enable vertical synchronization")
("file", po::value<std::string>(), "fullpath to applications binary")
("root", po::value<std::string>(), "Applications root")
("update", po::value<std::string>(), "update files folder")
// clang-format on
;
try {
po::store(po::parse_command_line(argc, argv, desc), m_vm);
po::notify(m_vm);
} catch (...) {
return false;
}
if (m_vm.count("help")) {
std::cout << desc << '\n';
return false;
}
if (m_vm.count("file") == 0) {
std::cout << "--file missing\n";
return false;
}
return true;
}
IInitParams* accessInitParams() {
static InitParams obj;
return &obj;
}
bool InitParams::isDebug() {
return m_vm.count("d");
}
std::string InitParams::getApplicationPath() {
return m_vm.count("file") ? m_vm["file"].as<std::string>() : std::string();
}
std::string InitParams::getApplicationRoot() {
return m_vm.count("root") ? m_vm["root"].as<std::string>() : std::string();
}
std::string InitParams::getUpdateRoot() {
return m_vm.count("update") ? m_vm["update"].as<std::string>() : std::string();
}
bool InitParams::enableValidation() {
return m_vm.count("vkValidation");
}
bool InitParams::enableBrightness() {
return m_vm.count("bright");
}
bool InitParams::useVSYNC() {
return m_vm["vsync"].as<bool>();
}
bool InitParams::try4K() {
return m_vm.count("4k");
}

View File

@ -0,0 +1,37 @@
#pragma once
#include "utility/utility.h"
#include <string>
class IInitParams {
CLASS_NO_COPY(IInitParams);
CLASS_NO_MOVE(IInitParams);
public:
IInitParams() = default;
virtual bool init(int argc, char** argv) = 0;
virtual bool isDebug() = 0;
virtual std::string getApplicationPath() = 0;
virtual std::string getApplicationRoot() = 0;
virtual std::string getUpdateRoot() = 0;
virtual bool enableValidation() = 0;
virtual bool enableBrightness() = 0;
virtual bool useVSYNC() = 0;
virtual bool try4K() = 0;
virtual ~IInitParams() = default;
};
#if defined(__APICALL_INITPARAMS_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IInitParams* accessInitParams();
#undef __APICALL

View File

@ -0,0 +1,12 @@
add_library(kernel OBJECT
eventqueue.cpp
eventflag.cpp
errors.cpp
filesystem.cpp
pthread.cpp
semaphore_fifo.cpp
)
add_dependencies(kernel third_party)
file(COPY errors.h eventflag.h eventqueue_types.h eventqueue.h filesystem.h pthread.h pthread_types.h eventqueue.h semaphore.h DESTINATION ${DST_INCLUDE_DIR}/kernel)

13
core/kernel/errors.cpp Normal file
View File

@ -0,0 +1,13 @@
#define __APICALL_EXTERN
#include "errors.h"
#undef __APICALL_EXTERN
int* getError_pthread() {
thread_local int g_errno = 0;
return &g_errno;
}
void setError_pthread(int error) {
// todo tracing stacktrace?
*getError_pthread() = error;
}

33
core/kernel/errors.h Normal file
View File

@ -0,0 +1,33 @@
#pragma once
#include "modules_include/common.h"
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL int* getError_pthread();
__APICALL void setError_pthread(int);
/**
* @brief Takes Kernel errors from gerErr(), converts and saves it
*
* @param result from gerErr()
* @return int32_t
*/
static int32_t POSIX_CALL(int32_t result) {
if (result >= 0) return result;
int res = result - (int32_t)0x80020000;
setError_pthread(res);
return res;
}
static int32_t POSIX_SET(ErrCode error) {
setError_pthread((int32_t)error);
return -1;
}
#undef __APICALL

177
core/kernel/eventflag.cpp Normal file
View File

@ -0,0 +1,177 @@
#define __APICALL_EXTERN
#include "eventflag.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "modules_include/common.h"
#include <boost/thread/condition.hpp>
#include <memory>
LOG_DEFINE_MODULE(KernelEventFlag);
namespace Kernel::EventFlag {
class KernelEventFlag: public IKernelEventFlag {
CLASS_NO_COPY(KernelEventFlag);
public:
KernelEventFlag(std::string const& name, bool single, bool /*fifo*/, uint64_t bits): m_singleThread(single), m_bits(bits), m_name(name) {};
virtual ~KernelEventFlag();
void set(uint64_t bits) final;
void clear(uint64_t bits) final;
void cancel(uint64_t bits, int* num_waiting_threads) final;
int wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) final;
int poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result) final {
uint32_t micros = 0;
return wait(bits, wait_mode, clear_mode, result, &micros);
}
private:
enum class Status { Set, Canceled, Deleted };
void setStatus(Status status) {
m_status = status;
if (status == Status::Set) m_cond_var_status.notify_all();
}
boost::mutex m_mutex_cond;
boost::condition_variable m_cond_var;
boost::condition_variable m_cond_var_status;
Status m_status = Status::Set;
int m_waitingThreads = 0;
bool m_singleThread = false;
std::string m_name;
uint64_t m_bits = 0;
};
KernelEventFlag::~KernelEventFlag() {
boost::unique_lock lock(m_mutex_cond);
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
m_status = Status::Deleted;
m_cond_var.notify_all();
m_cond_var_status.wait(lock, [this] { return m_waitingThreads == 0; });
}
void KernelEventFlag::set(uint64_t bits) {
boost::unique_lock lock(m_mutex_cond);
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
m_bits |= bits;
m_cond_var.notify_all();
}
void KernelEventFlag::clear(uint64_t bits) {
boost::unique_lock lock(m_mutex_cond);
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
m_bits &= bits;
}
void KernelEventFlag::cancel(uint64_t bits, int* num_waiting_threads) {
boost::unique_lock lock(m_mutex_cond);
m_cond_var_status.wait(lock, [this] { return m_status == Status::Set; });
if (num_waiting_threads != nullptr) {
*num_waiting_threads = m_waitingThreads;
}
setStatus(Status::Canceled);
m_bits = bits;
m_cond_var.notify_all();
m_cond_var_status.wait(lock, [this] { return m_waitingThreads == 0; });
setStatus(Status::Set);
}
int KernelEventFlag::wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) {
LOG_USE_MODULE(KernelEventFlag);
boost::unique_lock lock(m_mutex_cond);
if (m_singleThread && m_waitingThreads > 0) {
return getErr(ErrCode::_EPERM);
}
uint32_t micros = 0;
bool infinitely = true;
if (ptr_micros != nullptr) {
micros = *ptr_micros;
infinitely = false;
}
auto const start = boost::chrono::system_clock::now();
++m_waitingThreads;
auto waitFunc = [this, wait_mode, bits] {
return (m_status == Status::Canceled || m_status == Status::Deleted || (wait_mode == WaitMode::And && (m_bits & bits) == bits) ||
(wait_mode == WaitMode::Or && (m_bits & bits) != 0));
};
if (infinitely) {
m_cond_var.wait(lock, waitFunc);
} else {
if (!m_cond_var.wait_for(lock, boost::chrono::microseconds(micros), waitFunc)) {
if (result != nullptr) {
*result = m_bits;
}
*ptr_micros = 0;
--m_waitingThreads;
return getErr(ErrCode::_ETIMEDOUT);
}
}
--m_waitingThreads;
m_cond_var_status.notify_all();
if (result != nullptr) {
*result = m_bits;
}
auto elapsed = boost::chrono::duration_cast<boost::chrono::microseconds>(boost::chrono::system_clock::now() - start).count();
if (result != nullptr) {
*result = m_bits;
}
if (ptr_micros != nullptr) {
*ptr_micros = (elapsed >= micros ? 0 : micros - elapsed);
// LOG_TRACE(L"wait %llu us -> %llu us", micros, elapsed);
}
// Aborted
if (m_status == Status::Canceled) {
return getErr(ErrCode::_ECANCELED);
} else if (m_status == Status::Deleted) {
return getErr(ErrCode::_EINTR);
}
// -
if (clear_mode == ClearMode::All) {
m_bits = 0;
} else if (clear_mode == ClearMode::Bits) {
m_bits &= ~bits;
}
return Ok;
}
IKernelEventFlag_t createEventFlag(std::string const& name, bool single, bool fifo, uint64_t bits) {
return std::make_unique<KernelEventFlag>(std::string(name), single, fifo, bits).release();
}
int deleteEventFlag(IKernelEventFlag_t ef) {
LOG_USE_MODULE(KernelEventFlag);
if (ef == nullptr) {
return getErr(ErrCode::_ESRCH);
}
LOG_INFO(L"Deleted ptr:0x%08llx", (uint64_t)ef);
delete ef;
return Ok;
}
} // namespace Kernel::EventFlag

44
core/kernel/eventflag.h Normal file
View File

@ -0,0 +1,44 @@
#pragma once
#include "utility/utility.h"
namespace Kernel::EventFlag {
enum class ClearMode { None, All, Bits };
enum class WaitMode { And, Or };
class IKernelEventFlag {
CLASS_NO_COPY(IKernelEventFlag);
CLASS_NO_MOVE(IKernelEventFlag);
protected:
IKernelEventFlag() = default;
public:
virtual ~IKernelEventFlag() = default;
virtual void set(uint64_t bits) = 0;
virtual void clear(uint64_t bits) = 0;
virtual void cancel(uint64_t bits, int* num_waiting_threads) = 0;
virtual int wait(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result, uint32_t* ptr_micros) = 0;
virtual int poll(uint64_t bits, WaitMode wait_mode, ClearMode clear_mode, uint64_t* result) = 0;
};
using IKernelEventFlag_t = IKernelEventFlag*;
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IKernelEventFlag_t createEventFlag(std::string const& name, bool single, bool fifo, uint64_t bits);
__APICALL int deleteEventFlag(IKernelEventFlag_t ef);
#undef __APICALL
} // namespace Kernel::EventFlag

298
core/kernel/eventqueue.cpp Normal file
View File

@ -0,0 +1,298 @@
#define __APICALL_EXTERN
#include "eventqueue.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "modules_include/common.h"
#include <algorithm>
#include <boost/chrono.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <chrono>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
namespace Kernel {
namespace EventQueue {
LOG_DEFINE_MODULE(EventQueue);
class KernelEqueue: public IKernelEqueue {
public:
KernelEqueue() = default;
virtual ~KernelEqueue();
[[nodiscard]] std::string const& getName() const { return m_name; }
void setName(std::string&& name) { m_name.swap(name); }
int waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros);
int pollEvents(KernelEvent_t ev, int num) {
std::unique_lock lock(m_mutex_cond);
auto ret = getTriggeredEvents(ev, num);
return ret;
}
int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) final;
int addUserEvent(int ident) final;
int addUserEventEdge(int ident) final;
int triggerUserEvent(uintptr_t ident, void* udata) final;
int addEvent(const KernelEqueueEvent& event) final;
int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) final;
int deleteEvent(uintptr_t ident, int16_t filter) final;
private:
int getTriggeredEvents(KernelEvent_t ev, int num);
std::list<KernelEqueueEvent> m_events; // We dont ecpext many events ~5
boost::mutex m_mutex_cond;
std::mutex m_mutex_int;
boost::condition_variable m_cond_var;
std::string m_name;
bool m_closed = false;
};
KernelEqueue::~KernelEqueue() {
std::unique_lock const lock(m_mutex_cond);
/*
for (auto& ev: m_events) {
if (ev.filter.delete_event_func != nullptr) {
ev.filter.delete_event_func(this, &ev);
}
}*/
m_closed = true;
m_cond_var.notify_all();
}
int KernelEqueue::waitForEvents(KernelEvent_t ev, int num, SceKernelUseconds const* micros) {
LOG_USE_MODULE(EventQueue);
// LOG_TRACE(L"->waitForEvents: ident:0x%08llx num:%d", ev->ident, num);
boost::unique_lock lock(m_mutex_cond);
auto hasEvents = [&] {
for (auto& ev: m_events) {
if (ev.triggered) {
return true;
}
}
return false;
};
if (micros != nullptr) {
m_cond_var.wait_for(lock, boost::chrono::microseconds(*micros), [&] { return hasEvents() | m_closed; });
} else {
m_cond_var.wait(lock, [&] { return hasEvents() | m_closed; });
}
// LOG_TRACE(L"<-waitForEvents: ident:0x%08llx ret:%d", ev->ident, ret);
return getTriggeredEvents(ev, num);
}
int KernelEqueue::getTriggeredEvents(KernelEvent_t eventList, int num) {
LOG_USE_MODULE(EventQueue);
int countTriggered = 0;
for (auto& ev: m_events) {
if (ev.triggered) {
eventList[countTriggered++] = ev.event;
if ((((uint32_t)ev.event.flags & (uint32_t)EventFlags::EV_CLEAR) != 0) && ev.filter.reset_func != nullptr) {
ev.filter.reset_func(&ev);
}
LOG_TRACE(L"Event Triggered: ident:0x%08llx, filter:%d", (uint64_t)ev.event.ident, ev.event.filter);
if (countTriggered >= num) {
break;
}
}
}
return countTriggered;
}
void event_trigger_func(Kernel::EventQueue::KernelEqueueEvent* event, void* trigger_data) {
event->triggered = true;
event->event.fflags++;
event->event.udata = trigger_data;
}
void event_reset_func(Kernel::EventQueue::KernelEqueueEvent* event) {
event->triggered = false;
event->event.fflags = 0;
event->event.udata = 0;
}
void event_delete_func(Kernel::EventQueue::IKernelEqueue_t eq, Kernel::EventQueue::KernelEqueueEvent* event) {}
int createEqueue(IKernelEqueue_t* eq, const char* name) {
LOG_USE_MODULE(EventQueue);
if (eq == nullptr || name == nullptr) {
return getErr(ErrCode::_EINVAL);
}
auto item = std::make_unique<KernelEqueue>();
item->setName(std::string(name));
LOG_INFO(L"+EventQueue (%S)", item->getName().c_str());
*eq = item.release();
return Ok;
}
int deleteEqueue(IKernelEqueue_t eq) {
LOG_USE_MODULE(EventQueue);
if (eq == nullptr) {
return getErr(ErrCode::_EBADF);
}
LOG_INFO(L"-EventQueue (%S)", ((KernelEqueue*)eq)->getName().c_str());
delete (eq);
return Ok;
}
int KernelEqueue::wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* time) {
if (ev == nullptr) {
return getErr(ErrCode::_EFAULT);
}
if (num < 1) {
return getErr(ErrCode::_EINVAL);
}
if (time == nullptr) {
*out = waitForEvents(ev, num, nullptr);
if (*out == 0) {
return getErr(ErrCode::_ETIMEDOUT);
}
} else {
*out = waitForEvents(ev, num, time);
if (*out == 0 && *time > 0) {
return getErr(ErrCode::_ETIMEDOUT);
}
}
return Ok;
}
int KernelEqueue::addUserEvent(int ident) {
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
.event =
{
.ident = ident,
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
.flags = Kernel::EventQueue::EventFlags::EV_NONE,
.fflags = 0,
.data = 0,
.udata = 0,
},
.filter {
.data = nullptr,
.trigger_func = event_trigger_func,
.reset_func = event_reset_func,
.delete_event_func = event_delete_func,
}});
}
int KernelEqueue::addUserEventEdge(int ident) {
return addEvent(Kernel::EventQueue::KernelEqueueEvent {.triggered = false,
.event =
{
.ident = ident,
.filter = Kernel::EventQueue::KERNEL_EVFILT_USER,
.flags = Kernel::EventQueue::EventFlags::EV_CLEAR,
.fflags = 0,
.data = 0,
.udata = 0,
},
.filter {
.data = nullptr,
.trigger_func = event_trigger_func,
.reset_func = event_reset_func,
.delete_event_func = event_delete_func,
}});
}
int KernelEqueue::triggerUserEvent(uintptr_t ident, void* udata) {
LOG_USE_MODULE(EventQueue);
LOG_TRACE(L"triggerUserEvent: ident:0x%08llx", (uint64_t)ident);
return triggerEvent(ident, Kernel::EventQueue::KERNEL_EVFILT_USER, udata);
}
int KernelEqueue::addEvent(const KernelEqueueEvent& event) {
LOG_USE_MODULE(EventQueue);
LOG_INFO(L"(%S) Add Event: ident:0x%08llx, filter:%d", m_name.c_str(), (uint64_t)event.event.ident, event.event.filter);
std::unique_lock lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(),
[=](KernelEqueueEvent const& ev) { return ev.event.ident == event.event.ident && ev.event.filter == event.event.filter; });
if (it != m_events.end()) {
*it = event;
} else {
m_events.push_back(event);
}
if (event.triggered) {
lock.unlock();
m_cond_var.notify_all();
}
return Ok;
}
int KernelEqueue::triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) {
LOG_USE_MODULE(EventQueue);
LOG_TRACE(L"triggerEvent: ident:0x%08llx, filter:%d", (uint64_t)ident, filter);
std::unique_lock lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
if (it != m_events.end()) {
if (it->filter.trigger_func != nullptr) {
it->filter.trigger_func(&(*it), trigger_data);
} else {
it->triggered = true;
it->event.fflags++;
}
lock.unlock();
m_cond_var.notify_all();
return Ok;
}
return getErr(ErrCode::_ENOENT);
}
int KernelEqueue::deleteEvent(uintptr_t ident, int16_t filter) {
LOG_USE_MODULE(EventQueue);
LOG_INFO(L"deleteEvent: ident:0x%08llx, filter:%llu", (uint64_t)ident, filter);
std::unique_lock const lock(m_mutex_cond);
auto it = std::find_if(m_events.begin(), m_events.end(), [=](KernelEqueueEvent const& ev) { return ev.event.ident == ident && ev.event.filter == filter; });
if (it != m_events.end()) {
if (it->filter.delete_event_func != nullptr) {
it->filter.delete_event_func(this, &(*it));
}
m_events.erase(it);
return Ok;
}
return getErr(ErrCode::_ENOENT);
}
} // namespace EventQueue
} // namespace Kernel

41
core/kernel/eventqueue.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "eventqueue_types.h"
#include "modules_include/common.h"
#include "utility/utility.h"
namespace Kernel::EventQueue {
class IKernelEqueue {
CLASS_NO_COPY(IKernelEqueue);
CLASS_NO_MOVE(IKernelEqueue);
protected:
IKernelEqueue() = default;
public:
virtual ~IKernelEqueue() = default;
virtual int wait(KernelEvent_t ev, int num, int* out, const SceKernelUseconds* timo) = 0;
virtual int addUserEvent(int ident) = 0;
virtual int addUserEventEdge(int ident) = 0;
virtual int triggerUserEvent(uintptr_t ident, void* udata) = 0;
virtual int addEvent(const KernelEqueueEvent& event) = 0;
virtual int triggerEvent(uintptr_t ident, int16_t filter, void* trigger_data) = 0;
virtual int deleteEvent(uintptr_t ident, int16_t filter) = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL int createEqueue(IKernelEqueue_t* eq, const char* name);
__APICALL int deleteEqueue(IKernelEqueue_t eq);
#undef __APICALL
} // namespace Kernel::EventQueue

View File

@ -0,0 +1,55 @@
#pragma once
#include <stdint.h>
namespace Kernel::EventQueue {
constexpr int16_t KERNEL_EVFILT_TIMER = -7;
constexpr int16_t KERNEL_EVFILT_READ = -1;
constexpr int16_t KERNEL_EVFILT_WRITE = -2;
constexpr int16_t KERNEL_EVFILT_USER = -11;
constexpr int16_t KERNEL_EVFILT_FILE = -4;
constexpr int16_t KERNEL_EVFILT_GRAPHICS = -14;
constexpr int16_t KERNEL_EVFILT_VIDEO_OUT = -13;
constexpr int16_t KERNEL_EVFILT_HRTIMER = -15;
enum class EventFlags : uint32_t {
EV_NONE = 0,
EV_ONESHOT = 0x10, // only report one occurrence
EV_CLEAR = 0x20, // clear event state after reporting
EV_RECEIPT = 0x40, // force EV_ERROR on success, data=0
EV_DISPATCH = 0x80, // disable event after reporting
EV_SYSFLAGS = 0xF000, // reserved by system
EV_FLAG1 = 0x2000, // filter-specific flag
};
struct KernelEvent {
int ident = 0;
int16_t filter = 0;
EventFlags flags = EventFlags::EV_NONE;
uint32_t fflags = 0;
intptr_t data = 0;
void* udata = nullptr;
};
struct KernelEqueueEvent;
class IKernelEqueue;
using IKernelEqueue_t = IKernelEqueue*;
using KernelEvent_t = KernelEvent*;
using trigger_func_t = void (*)(KernelEqueueEvent* event, void* trigger_data);
using reset_func_t = void (*)(KernelEqueueEvent* event);
using delete_func_t = void (*)(IKernelEqueue_t eq, KernelEqueueEvent* event);
struct KernelFilter {
void* data = nullptr;
trigger_func_t trigger_func = nullptr;
reset_func_t reset_func = nullptr;
delete_func_t delete_event_func = nullptr;
};
struct KernelEqueueEvent {
bool triggered = false;
KernelEvent event;
KernelFilter filter;
};
} // namespace Kernel::EventQueue

743
core/kernel/filesystem.cpp Normal file
View File

@ -0,0 +1,743 @@
#define __APICALL_EXTERN
#include "filesystem.h"
#undef __APICALL_EXTERN
#include "core/dmem/memoryManager.h"
#include "core/fileManager/fileManager.h"
#include "core/fileManager/types/type_file.h"
#include "core/fileManager/types/type_null.h"
#include "core/fileManager/types/type_random.h"
#include "core/fileManager/types/type_rng.h"
#include "core/fileManager/types/type_zero.h"
#include "core/memory/memory.h"
#include "logging.h"
#include <assert.h>
#include <unordered_map>
#include <windows.h>
LOG_DEFINE_MODULE(filesystem);
namespace {
std::pair<uint32_t, uint32_t> convProtection(int prot) {
switch (prot & 0xf) {
case 0: return {PAGE_NOACCESS, 0};
case 1: return {PAGE_READONLY, FILE_MAP_READ};
case 2:
case 3: return {PAGE_READWRITE, FILE_MAP_ALL_ACCESS};
case 4: return {PAGE_EXECUTE, FILE_MAP_ALL_ACCESS | FILE_MAP_EXECUTE};
case 5: return {PAGE_EXECUTE_READ, FILE_MAP_EXECUTE | FILE_MAP_READ};
case 6:
case 7: return {PAGE_EXECUTE_READWRITE, FILE_MAP_ALL_ACCESS};
}
return {PAGE_NOACCESS, 0};
}
std::unique_ptr<IFile> createType_dev(std::filesystem::path path, std::ios_base::openmode mode) {
LOG_USE_MODULE(filesystem);
if (path == "/dev/random" || path == "/dev/urandom") {
return createType_random();
} else if (path == "/dev/rng") {
return createType_rng();
} else if (path == "/dev/zero") {
return createType_zero();
} else if (path == "/dev/null") {
return createType_null();
} else { // todo: other devices
LOG_CRIT(L"%s: unknown device!", path.c_str());
}
return {};
}
int open_dev(const char* path, filesystem::SceOpen flags, filesystem::SceKernelMode kernelMode) {
LOG_USE_MODULE(filesystem);
if (auto str = std::string_view(path); str.starts_with("/dev/std")) {
if (str.ends_with("stdin")) return 0;
if (str.ends_with("stdout")) return 1;
if (str.ends_with("stderr")) return 2;
return getErr(ErrCode::_ENOENT);
}
std::ios_base::openmode mode = std::ios::binary;
switch (flags.mode) {
case filesystem::SceOpenMode::RDONLY: mode |= std::ios::in; break;
case filesystem::SceOpenMode::WRONLY: mode |= std::ios::out; break;
case filesystem::SceOpenMode::RDWR: mode |= std::ios::out | std::ios::in; break;
}
auto file = createType_dev(path, mode);
int const handle = accessFileManager().addFile(std::move(file), path);
LOG_INFO(L"OpenFile[%d]: %S mode:0x%x(0x%x)", handle, path, mode, kernelMode);
return handle;
}
} // namespace
namespace filesystem {
int mmap(void* addr, size_t len, int prot, SceMap flags, int fd, int64_t offset, void** res) {
LOG_USE_MODULE(filesystem);
// Mapping cases
if (flags.mode == SceMapMode::FIXED) {
LOG_ERR(L"todo: Mmap fixed 0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld", addr, len, prot, flags, fd, offset);
return getErr(ErrCode::_EINVAL);
}
if (flags.mode == SceMapMode::VOID_) {
// reserve memory
LOG_ERR(L"todo: Mmap void 0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld", addr, len, prot, flags, fd, offset);
return getErr(ErrCode::_EINVAL);
}
if (flags.type == SceMapType::ANON) {
int result = accessMemoryManager()->flexibleMemory()->map((uint64_t)addr, 0, len, prot, (int&)flags, 0, (uint64_t*)res); // registers mapping (flexible)
LOG_DEBUG(L"Mmap anon addr:0x%08llx len:0x%08llx prot:%d flags:%d fd:%d offset:%lld -> out:0x%08llx", addr, len, prot, flags, fd, offset, *res);
return result;
}
// -
// Do file mapping
if (fd < FILE_DESCRIPTOR_MIN) {
LOG_DEBUG(L"Mmap error addr:0x%08llx len:0x%08llx prot:%d flags:%08llx fd:%d offset:%lld -> out:0x%08llx", addr, len, prot, flags, fd, offset, *res);
return getErr(ErrCode::_EPERM);
}
auto const [fileProt, viewProt] = convProtection(prot);
auto file = accessFileManager().accessFile(fd);
if (file->getNative() == nullptr) {
LOG_ERR(L"Mmap fd:%d no nativeHandle", fd);
return getErr(ErrCode::_EMFILE);
}
uint64_t mappingLength = (fileProt == PAGE_READONLY) || (fileProt == PAGE_EXECUTE_READ) ? 0 : len;
auto lenSplit = std::bit_cast<std::array<uint32_t, 2>>(mappingLength);
HANDLE hFileMapping = CreateFileMapping(file->getNative(),
NULL, // default security
fileProt,
lenSplit[1], // maximum object size (high-order DWORD)
lenSplit[0], // maximum object size (low-order DWORD)
NULL); // name of mapping object
*res = nullptr;
if (hFileMapping == NULL) {
LOG_ERR(L"Mmap CreateFileMapping == NULL for| 0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld err:%04x", addr, mappingLength, prot, flags, fd,
offset, GetLastError());
} else {
auto offsetSplit = std::bit_cast<std::array<uint32_t, 2>>(offset);
*res = MapViewOfFile(hFileMapping, // handle to file mapping object
viewProt, // read/write permission
offsetSplit[1], // high offset
offsetSplit[0], // low offset
mappingLength); // number of bytes to map
if (*res == NULL) {
LOG_ERR(L"Mmap MapViewOfFile == NULL for| 0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld err:%04x", addr, mappingLength, prot, flags, fd,
offset, GetLastError());
} else {
LOG_DEBUG(L"Mmap addr:0x%08llx len:0x%08llx prot:%d flags:%0lx fd:%d offset:%lld -> out:0x%08llx", addr, mappingLength, prot, flags, fd, offset, *res);
accessMemoryManager()->registerMapping((uint64_t)*res, len, MappingType::File);
}
}
CloseHandle(hFileMapping); // is kept open internally until mapView is unmapped
if (*res == nullptr) {
return getErr(ErrCode::_EACCES);
}
return Ok;
}
int munmap(void* addr, size_t len) {
LOG_USE_MODULE(filesystem);
// unregister and then delete
auto type = accessMemoryManager()->unregisterMapping((uint64_t)addr);
switch (type) {
case MappingType::File: {
return UnmapViewOfFile(addr) != 0 ? Ok : -1;
} break;
case MappingType::Direct: {
return accessMemoryManager()->directMemory()->unmap((uint64_t)addr, len);
} break;
case MappingType::Flexible: {
return accessMemoryManager()->flexibleMemory()->unmap((uint64_t)addr, len);
} break;
case MappingType::Fixed: {
LOG_ERR(L"tod munmap Fixed 0x%08llx 0x%08llx", addr, len);
} break;
case MappingType::None: {
LOG_ERR(L"munmap unkown 0x%08llx 0x%08llx type:%d", addr, len, type);
} break;
}
return -1;
}
size_t read(int handle, void* buf, size_t nbytes) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
return getErr(ErrCode::_EBADF);
}
auto const count = file->read((char*)buf, nbytes);
LOG_TRACE(L"KernelRead[%d]: 0x%08llx:%llu read(%lld)", handle, (uint64_t)buf, nbytes, count);
return count;
}
int64_t write(int handle, const void* buf, size_t nbytes) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"KernelWrite[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
return getErr(ErrCode::_EBADF);
}
size_t const count = file->write((char*)buf, nbytes);
LOG_TRACE(L"KernelWrite[%d]: 0x%08llx:%llu count:%llu", handle, (uint64_t)buf, nbytes, count);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return count;
}
int ioctl(int handle, int request, SceVariadicList argp) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"KernelIoctl[%d] file==nullptr: request:0x%08llx argp:0x%08llx", handle, request, (uint64_t)argp);
return getErr(ErrCode::_EBADF);
}
int const ret = file->ioctl(request, argp);
LOG_TRACE(L"KernelIoctl[%d] request:0x%08llx argp:0x%08llx, ret:%d", handle, request, (uint64_t)argp, ret);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return ret;
}
int open(const char* path, SceOpen flags, SceKernelMode kernelMode) {
LOG_USE_MODULE(filesystem);
if (path == nullptr) {
return getErr(ErrCode::_EINVAL);
}
if (std::string_view(path).starts_with("/dev/")) {
return open_dev(path, flags, kernelMode);
}
auto mapped = accessFileManager().getMappedPath(path);
if (!mapped) {
return getErr(ErrCode::_EACCES);
}
auto const mappedPath = mapped.value();
// handle excl (file already exists)
if (flags.excl && flags.create && std::filesystem::exists(mappedPath)) {
return getErr(ErrCode::_EEXIST);
}
// -
bool isDir = std::filesystem::is_directory(mappedPath);
if (flags.directory || isDir) {
if (!isDir) {
LOG_WARN(L"Directory doesn't exist: %s", mappedPath.c_str());
return getErr(ErrCode::_ENOTDIR);
}
if (!std::filesystem::exists(mappedPath.parent_path())) std::filesystem::create_directories(mappedPath);
auto dirIt = std::make_unique<std::filesystem::directory_iterator>(mappedPath);
int const handle = accessFileManager().addDirIterator(std::move(dirIt), mappedPath);
LOG_INFO(L"OpenDir [%d]: %S (%s)", handle, path, mappedPath.c_str());
return handle;
} else {
// error if read only input file does not exist
if (flags.mode == SceOpenMode::RDONLY && !std::filesystem::exists(mappedPath)) {
LOG_WARN(L"File doesn't exist: %s mode:0x%x", mappedPath.c_str(), flags.mode);
return getErr(ErrCode::_ENOENT);
}
auto file = createType_file(mappedPath, flags);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
int const handle = accessFileManager().addFile(std::move(file), mappedPath);
LOG_INFO(L"OpenFile[%d]: %s mode:0x%x(0x%x)", handle, mappedPath.c_str(), *(int*)&flags, kernelMode);
return handle;
}
}
int close(int handle) {
LOG_USE_MODULE(filesystem);
LOG_INFO(L"Closed[%d]", handle);
if (handle < FILE_DESCRIPTOR_MIN) {
return getErr(ErrCode::_EPERM);
}
accessFileManager().remove(handle);
return Ok;
}
int unlink(const char* path) {
LOG_USE_MODULE(filesystem);
if (path == nullptr) {
return getErr(ErrCode::_EINVAL);
}
auto const _mapped = accessFileManager().getMappedPath(path);
if (!_mapped) {
return getErr(ErrCode::_EACCES);
}
auto const mapped = _mapped.value();
if (std::filesystem::is_directory(mapped)) {
return getErr(ErrCode::_EPERM);
}
if (std::filesystem::remove(mapped)) {
LOG_INFO(L"Deleted: %S", path);
return Ok;
}
return getErr(ErrCode::_ENOENT);
}
int chmod(const char* path, SceKernelMode mode) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"TODO %S", __FUNCTION__);
return Ok;
}
int checkReachability(const char* path) {
auto mapped = accessFileManager().getMappedPath(path);
if (!mapped) {
return getErr(ErrCode::_EACCES);
}
auto const mappedPath = mapped.value();
if (std::filesystem::exists(mappedPath.parent_path())) return Ok;
return getErr(ErrCode::_EACCES);
}
void sync(void) {
// todo: sync all open files?
}
int fsync(int handle) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"KernelFsync[%d]", handle);
return getErr(ErrCode::_EBADF);
}
file->sync();
return Ok;
}
int fdatasync(int fd) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S %d", __FUNCTION__, fd);
return Ok;
}
int fcntl(int handle, int cmd, SceVariadicList argp) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"KernelFcntl[%d] file==nullptr: cmd:0x%08lx argp:0x%08llx", handle, cmd, (uint64_t)argp);
return getErr(ErrCode::_EBADF);
}
int const ret = file->fcntl(cmd, argp);
LOG_TRACE(L"KernelFcntl[%d] request:0x%08llx argp:0x%08lx, ret:%d", handle, cmd, (uint64_t)argp, ret);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return ret;
}
size_t readv(int handle, const SceKernelIovec* iov, int iovcnt) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
return getErr(ErrCode::_EBADF);
}
// todo: move it to IFile?
size_t count = 0;
for (int n = 0; n < iovcnt; ++n) {
auto* item = &iov[n];
if (item->iov_base == nullptr || item->iov_len == 0) continue;
auto const countTemp = file->read((char*)item->iov_base, item->iov_len);
LOG_TRACE(L"KernelRead[%d]: 0x%08llx:%llu read(%lld)", handle, (uint64_t)item->iov_base, item->iov_len, countTemp);
count += countTemp;
if (auto err = file->getErr(); err != Ok) {
LOG_TRACE(L"file end");
break;
}
}
return count;
}
size_t writev(int handle, const SceKernelIovec* iov, int iovcnt) {
LOG_USE_MODULE(filesystem);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
return getErr(ErrCode::_EBADF);
}
// todo: move it to IFile?
size_t count = 0;
for (int n = 0; n < iovcnt; n++) {
auto* item = &iov[n];
if (item->iov_base == nullptr || item->iov_len == 0) continue;
auto const countTemp = file->write(item->iov_base, item->iov_len);
LOG_TRACE(L"KernelWrite[%d]: 0x%08llx:%llu write(%lld)", handle, (uint64_t)item->iov_base, item->iov_len, countTemp);
count += countTemp;
if (auto err = file->getErr(); err != Ok) {
LOG_TRACE(L"file end");
break;
}
}
return count;
}
int fchmod(int fd, SceKernelMode mode) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
int rename(const char* from, const char* to) {
auto mapped1 = accessFileManager().getMappedPath(from);
if (!mapped1) {
return getErr(ErrCode::_EACCES);
}
auto mapped2 = accessFileManager().getMappedPath(to);
if (!mapped2) {
return getErr(ErrCode::_EACCES);
}
try {
std::filesystem::rename(*mapped1, *mapped2);
} catch (...) {
return getErr(ErrCode::_ENFILE);
}
return Ok;
}
int mkdir(const char* path, SceKernelMode mode) {
auto _mapped = accessFileManager().getMappedPath(path);
if (!_mapped) {
return getErr(ErrCode::_EACCES);
}
auto const mapped = _mapped.value();
if (!std::filesystem::create_directory(mapped)) {
return getErr(ErrCode::_EIO);
}
return Ok;
}
int rmdir(const char* path) {
auto mapped = accessFileManager().getMappedPath(path);
if (!mapped) {
return getErr(ErrCode::_EACCES);
}
std::filesystem::remove_all(*mapped);
return Ok;
}
int utimes(const char* path, const SceKernelTimeval* times) {
return Ok;
}
int stat(const char* path, SceKernelStat* sb) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"KernelStat: %S", path);
memset(sb, 0, sizeof(SceKernelStat));
auto _mapped = accessFileManager().getMappedPath(path);
if (!_mapped) {
return getErr(ErrCode::_EACCES);
}
auto const mapped = _mapped.value();
if (!std::filesystem::exists(mapped)) {
return getErr(ErrCode::_ENOENT);
}
bool const isDir = std::filesystem::is_directory(mapped);
sb->mode = 0000777u | (isDir ? 0040000u : 0100000u);
if (isDir) {
sb->size = 0;
sb->blksize = 512;
sb->blocks = 0;
} else {
sb->size = (int64_t)std::filesystem::file_size(mapped);
sb->blksize = 512;
sb->blocks = (sb->size + 511) / 512;
auto const lastW = std::filesystem::last_write_time(mapped);
auto const time = std::chrono::time_point_cast<std::chrono::nanoseconds>(lastW).time_since_epoch().count();
ns2timespec(&sb->aTime, time);
ns2timespec(&sb->mTime, time);
}
sb->cTime = sb->aTime;
sb->birthtime = sb->mTime;
return Ok;
}
int fstat(int fd, SceKernelStat* sb) {
auto mapped = accessFileManager().getPath(fd);
if (mapped.empty()) {
return getErr(ErrCode::_EACCES);
}
return stat(mapped.string().c_str(), sb);
}
int futimes(int fd, const SceKernelTimeval* times) {
return Ok;
}
int getdirentries(int fd, char* buf, int nbytes, long* basep) {
if (fd < FILE_DESCRIPTOR_MIN) {
return getErr(ErrCode::_EPERM);
}
if (buf == nullptr) {
return getErr(ErrCode::_EFAULT);
}
auto count = accessFileManager().getDents(fd, buf, nbytes, (int64_t*)basep);
if (count < 0) {
return getErr(ErrCode::_EINVAL);
}
return count;
}
int getdents(int fd, char* buf, int nbytes) {
return getdirentries(fd, buf, nbytes, nullptr);
}
size_t preadv(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"preadv [%d]", handle);
if (offset < 0) {
return getErr(ErrCode::_EINVAL);
}
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"preadv[%d] file==nullptr", handle);
return getErr(ErrCode::_EBADF);
}
file->clearError();
file->lseek(offset, SceWhence::beg);
auto const count = readv(handle, iov, iovcnt);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return count;
}
size_t pwritev(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"pwritev [%d]", handle);
if (offset < 0) {
return getErr(ErrCode::_EINVAL);
}
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"pwritev[%d] file==nullptr", handle);
return getErr(ErrCode::_EBADF);
}
file->clearError();
file->lseek(offset, SceWhence::beg);
auto const count = writev(handle, iov, iovcnt);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return count;
}
size_t pread(int handle, void* buf, size_t nbytes, int64_t offset) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"pread [%d]", handle);
if (buf == nullptr) {
return getErr(ErrCode::_EFAULT);
}
if (offset < 0) {
return getErr(ErrCode::_EINVAL);
}
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"pread[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
return getErr(ErrCode::_EBADF);
}
file->clearError();
file->lseek(offset, SceWhence::beg);
auto const count = file->read((char*)buf, nbytes);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
LOG_TRACE(L"pread[%d]: 0x%08llx:%llu read(%lld) offset:0x%08llx", handle, (uint64_t)buf, nbytes, count, offset);
return count;
}
size_t pwrite(int handle, const void* buf, size_t nbytes, int64_t offset) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"pwrite[%d]: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
auto file = accessFileManager().accessFile(handle);
if (file == nullptr) {
LOG_ERR(L"write[%d] file==nullptr: 0x%08llx:%llu", handle, (uint64_t)buf, nbytes);
return getErr(ErrCode::_EBADF);
}
file->clearError();
file->lseek(offset, SceWhence::beg);
auto const count = file->write((char*)buf, nbytes);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return count;
}
int64_t lseek(int handle, int64_t offset, int whence) {
LOG_USE_MODULE(filesystem);
LOG_TRACE(L"lseek [%d] 0x%08llx %d", handle, offset, whence);
if (whence > 2) return getErr(ErrCode::_EINVAL);
auto file = accessFileManager().accessFile(handle);
file->clearError();
auto const pos = file->lseek(offset, (SceWhence)whence);
if (auto err = file->getErr(); err != Ok) {
return getErr((ErrCode)err);
}
return pos;
}
int truncate(const char* path, int64_t length) {
auto mapped = accessFileManager().getMappedPath(path);
if (!mapped) {
return getErr(ErrCode::_EACCES);
}
std::filesystem::resize_file(*mapped, length);
return Ok;
}
int ftruncate(int fd, int64_t length) {
auto mapped = accessFileManager().getPath(fd);
if (mapped.empty()) {
return getErr(ErrCode::_EACCES);
}
return truncate(mapped.string().c_str(), length);
}
int setCompressionAttribute(int fd, int flag) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
int lwfsSetAttribute(int fd, int flags) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
int lwfsAllocateBlock(int fd, int64_t size) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
int lwfsTrimBlock(int fd, int64_t size) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"todo %S", __FUNCTION__);
return Ok;
}
int64_t lwfsLseek(int fd, int64_t offset, int whence) {
LOG_USE_MODULE(filesystem);
LOG_ERR(L"seek [%d]", fd);
return Ok;
}
size_t lwfsWrite(int fd, const void* buf, size_t nbytes) {
return Ok;
}
} // namespace filesystem

138
core/kernel/filesystem.h Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include "modules_include/common.h"
#include <stdint.h>
namespace filesystem {
enum class SceMapMode : uint16_t {
SHARED = 0x0001,
PRIVATE = 0x0002,
FIXED = 0x0010,
NO_OVERWRITE = 0x0080,
VOID_ = 0x0100,
};
enum class SceMapType : uint16_t {
FILE = 0x0,
ANON = 0x1,
SYSTEM = 0x2,
ALLAVAILABLE = 0x4,
};
struct SceMap {
SceMapMode mode : 12;
SceMapType type : 4;
};
enum class SceOpenMode : uint32_t {
RDONLY = 0,
WRONLY = 1,
RDWR = 2,
};
struct SceOpen {
SceOpenMode mode : 2;
uint32_t nonblock : 1;
uint32_t append : 1;
uint32_t shlock : 1;
uint32_t exlock : 1;
uint32_t async : 1;
uint32_t fsync : 1;
uint32_t nofollow : 1;
uint32_t create : 1;
uint32_t trunc : 1;
uint32_t excl : 1;
uint32_t dsync : 1;
uint32_t dummy0 : 1;
uint32_t fhaslock : 1;
uint32_t noctty : 1;
uint32_t direct : 1;
uint32_t directory : 1;
uint32_t exec : 1;
uint32_t tty_init : 1;
uint32_t cloexec : 1;
};
struct SceKernelStat {
uint32_t dev;
uint32_t ino;
uint16_t mode;
uint16_t nlink;
uint32_t uid;
uint32_t gid;
uint32_t rdev;
SceKernelTimespec aTime;
SceKernelTimespec mTime;
SceKernelTimespec cTime;
int64_t size;
int64_t blocks;
uint32_t blksize;
uint32_t flags;
uint32_t gen;
int32_t lspare;
SceKernelTimespec birthtime;
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(SceKernelTimespec)));
unsigned int: (8 / 2) * (16 - static_cast<int>(sizeof(SceKernelTimespec)));
};
struct Sce_iovec {
void* iov_base;
size_t iov_len;
};
typedef uint16_t SceKernelMode; // todo needed?
typedef struct Sce_iovec SceKernelIovec;
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL int mmap(void* addr, size_t len, int prot, SceMap flags, int fd, int64_t offset, void** res);
__APICALL int munmap(void* address, size_t len);
__APICALL size_t read(int handle, void* buf, size_t nbytes);
__APICALL int ioctl(int handle, int request, SceVariadicList argp);
__APICALL int64_t write(int handle, const void* buf, size_t nbytes);
__APICALL int open(const char* path, SceOpen flags, SceKernelMode kernelMode);
__APICALL int close(int handle);
__APICALL int unlink(const char* path);
__APICALL int chmod(const char* path, SceKernelMode mode);
__APICALL int checkReachability(const char* path);
__APICALL void sync(void);
__APICALL int fsync(int handle);
__APICALL int fdatasync(int fd);
__APICALL int fcntl(int fd, int cmd, SceVariadicList argp);
__APICALL size_t readv(int handle, const SceKernelIovec* iov, int iovcnt);
__APICALL size_t writev(int handle, const SceKernelIovec* iov, int iovcnt);
__APICALL int fchmod(int fd, SceKernelMode mode);
__APICALL int rename(const char* from, const char* to);
__APICALL int mkdir(const char* path, SceKernelMode mode);
__APICALL int rmdir(const char* path);
__APICALL int utimes(const char* path, const SceKernelTimeval* times);
__APICALL int stat(const char* path, SceKernelStat* sb);
__APICALL int fstat(int fd, SceKernelStat* sb);
__APICALL int futimes(int fd, const SceKernelTimeval* times);
__APICALL int getdirentries(int fd, char* buf, int nbytes, long* basep);
__APICALL int getdents(int fd, char* buf, int nbytes);
__APICALL size_t preadv(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset);
__APICALL size_t pwritev(int handle, const SceKernelIovec* iov, int iovcnt, int64_t offset);
__APICALL size_t pread(int handle, void* buf, size_t nbytes, int64_t offset);
__APICALL size_t pwrite(int handle, const void* buf, size_t nbytes, int64_t offset);
__APICALL int64_t lseek(int handle, int64_t offset, int whence);
__APICALL int truncate(const char* path, int64_t length);
__APICALL int ftruncate(int fd, int64_t length);
__APICALL int setCompressionAttribute(int fd, int flag);
__APICALL int lwfsSetAttribute(int fd, int flags);
__APICALL int lwfsAllocateBlock(int fd, int64_t size);
__APICALL int lwfsTrimBlock(int fd, int64_t size);
__APICALL int64_t lwfsLseek(int fd, int64_t offset, int whence);
__APICALL size_t lwfsWrite(int fd, const void* buf, size_t nbytes);
#undef __APICALL
}; // namespace filesystem

1296
core/kernel/pthread.cpp Normal file

File diff suppressed because it is too large Load Diff

195
core/kernel/pthread.h Normal file
View File

@ -0,0 +1,195 @@
#pragma once
#include "pthread_types.h"
#include "utility/utility.h"
#include <modules_include/common.h>
#include <stdint.h>
struct PthreadAttrPrivate;
struct PthreadBarrierPrivate;
struct PthreadBarrierattrPrivate;
struct PthreadCondattrPrivate;
struct PthreadCondPrivate;
struct PthreadMutexPrivate;
struct PthreadMutexattrPrivate;
struct PthreadRwlockPrivate;
struct PthreadRwlockattrPrivate;
struct PthreadPrivate;
struct PthreadOnce;
using ScePthread_obj = uint8_t*;
using ScePthreadAttr = PthreadAttrPrivate*;
using ScePthreadBarrier = PthreadBarrierPrivate*;
using ScePthreadBarrierattr = PthreadBarrierattrPrivate*;
using ScePthreadCondattr = PthreadCondattrPrivate*;
using ScePthreadCond = PthreadCondPrivate*;
using ScePthreadMutex = PthreadMutexPrivate*;
using ScePthreadMutexattr = PthreadMutexattrPrivate*;
using ScePthreadRwlock = PthreadRwlockPrivate*;
using ScePthreadRwlockattr = PthreadRwlockattrPrivate*;
using ScePthread = PthreadPrivate*;
using ScePthreadOnce = PthreadOnce*;
using ScePthreadKey = int;
#if defined(__APICALL_PTHREAD_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
struct DTVKey;
namespace pthread {
__APICALL uint8_t* getTLSStaticBlock(ScePthread_obj obj);
__APICALL uint64_t* getDTV(ScePthread_obj obj);
__APICALL int getThreadId();
__APICALL int getThreadId(ScePthread_obj obj);
__APICALL ScePthread_obj& getSelf();
__APICALL int attrDestroy(ScePthreadAttr* attr);
__APICALL int attrGetstack(const ScePthreadAttr* attr, void** stackAddr, size_t* stackSize);
__APICALL int attrGetstacksize(const ScePthreadAttr* attr, size_t* stackSize);
__APICALL int attrGetguardsize(const ScePthreadAttr* attr, size_t* guardSize);
__APICALL int attrGetstackaddr(const ScePthreadAttr* attr, void** stackAddr);
__APICALL int attrGetdetachstate(const ScePthreadAttr* attr, SceDetachState* state);
__APICALL int attrSetstacksize(ScePthreadAttr* attr, size_t stackSize);
__APICALL int attrSetguardsize(ScePthreadAttr* attr, size_t guardSize);
__APICALL int attrSetstack(ScePthreadAttr* attr, void* addr, size_t size);
__APICALL int attrSetstackaddr(ScePthreadAttr* attr, void* addr);
__APICALL int attrSetdetachstate(ScePthreadAttr* attr, SceDetachState state);
__APICALL int barrierDestroy(ScePthreadBarrier* barrier);
__APICALL int barrierInit(ScePthreadBarrier* barrier, const ScePthreadBarrierattr* attr, unsigned count, const char* name);
__APICALL int barrierWait(ScePthreadBarrier* barrier);
__APICALL int barrierattrDestroy(ScePthreadBarrierattr* barrier);
__APICALL int barrierattrInit(ScePthreadBarrierattr* barrier);
__APICALL int barrierattrGetpshared(ScePthreadBarrierattr* barrier, int* pshared);
__APICALL int barrierattrSetpshared(ScePthreadBarrierattr* barrier, int pshared);
__APICALL int condattrDestroy(ScePthreadCondattr* attr);
__APICALL int condattrInit(ScePthreadCondattr* attr);
__APICALL int condInit(ScePthreadCond* cond, const ScePthreadCondattr* attr, const char* name);
__APICALL int condBroadcast(ScePthreadCond* cond);
__APICALL int condDestroy(ScePthreadCond* cond);
__APICALL int condSignal(ScePthreadCond* cond);
__APICALL int condSignalto(ScePthreadCond* cond, ScePthread_obj obj);
__APICALL int condTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, SceKernelUseconds usec);
__APICALL int condTimedwait(ScePthreadCond* cond, ScePthreadMutex* mutex, const SceKernelTimespec* t);
__APICALL int condWait(ScePthreadCond* cond, ScePthreadMutex* mutex);
__APICALL int detach(ScePthread_obj obj);
__APICALL int equal(ScePthread_obj thread1, ScePthread_obj thread2);
__APICALL int getcpuclockid(ScePthread_obj thread, int* clock);
__APICALL int join(ScePthread_obj obj, void** value);
__APICALL int keyDelete(ScePthreadKey key);
__APICALL int keyCreate(ScePthreadKey* key, pthread_key_destructor_func_t destructor);
__APICALL int mutexattrInit(ScePthreadMutexattr* attr);
__APICALL int mutexattrDestroy(ScePthreadMutexattr* attr);
__APICALL int mutexattrGettype(ScePthreadMutexattr* attr, int* type);
__APICALL int mutexattrSettype(ScePthreadMutexattr* attr, int type);
__APICALL int mutexDestroy(ScePthreadMutex* mutex);
__APICALL int mutexInit(ScePthreadMutex* mutex, const ScePthreadMutexattr* attr, const char* name);
__APICALL int mutexLock(ScePthreadMutex* mutex);
__APICALL int mutexTrylock(ScePthreadMutex* mutex);
__APICALL int mutexTimedlock(ScePthreadMutex* mutex, SceKernelUseconds usec);
__APICALL int mutexTimedlock(ScePthreadMutex* mutex, const SceKernelTimespec* t);
__APICALL int mutexUnlock(ScePthreadMutex* mutex);
__APICALL int rwlockDestroy(ScePthreadRwlock* rwlock);
__APICALL int rwlockInit(ScePthreadRwlock* rwlock, const ScePthreadRwlockattr* attr, const char* name);
__APICALL int rwlockRdlock(ScePthreadRwlock* rwlock);
__APICALL int rwlockTimedrdlock(ScePthreadRwlock* rwlock, SceKernelUseconds usec);
__APICALL int rwlockTimedrdlock(ScePthreadRwlock* rwlock, const SceKernelTimespec* t);
__APICALL int rwlockTimedwrlock(ScePthreadRwlock* rwlock, SceKernelUseconds usec);
__APICALL int rwlockTimedwrlock(ScePthreadRwlock* rwlock, const SceKernelTimespec* t);
__APICALL int rwlockTryrdlock(ScePthreadRwlock* rwlock);
__APICALL int rwlockTrywrlock(ScePthreadRwlock* rwlock);
__APICALL int rwlockUnlock(ScePthreadRwlock* rwlock);
__APICALL int rwlockWrlock(ScePthreadRwlock* rwlock);
__APICALL int rwlockattrDestroy(ScePthreadRwlockattr* attr);
__APICALL int rwlockattrSettype(ScePthreadRwlockattr* attr, int type);
__APICALL int rwlockattrInit(ScePthreadRwlockattr* attr);
__APICALL int rwlockattrGettype(ScePthreadRwlockattr* attr, int* type);
__APICALL int setspecific(ScePthreadKey key, const void* value);
__APICALL void* getspecific(ScePthreadKey key);
__APICALL int cancel(ScePthread_obj obj);
__APICALL int setcancelstate(int state, int* oldState);
__APICALL int setcanceltype(int type, int* oldType);
__APICALL void testCancel(void);
__APICALL int getprio(ScePthread_obj obj, int* prio);
__APICALL int setprio(ScePthread_obj obj, int prio);
__APICALL void yield(void);
__APICALL int mutexattrGetprioceiling(ScePthreadMutexattr* attr, int* prio);
__APICALL int mutexattrSetprioceiling(ScePthreadMutexattr* attr, int prio);
__APICALL int mutexGetprioceiling(ScePthreadMutex* mutex, int* prio);
__APICALL int mutexSetprioceiling(ScePthreadMutex* mutex, int prio, int* oldPrio);
__APICALL int mutexattrGetprotocol(ScePthreadMutexattr* attr, int* protocol);
__APICALL int mutexattrSetprotocol(ScePthreadMutexattr* attr, int protocol);
__APICALL int mutexattrGetpshared(ScePthreadMutexattr* attr, int* pshared);
__APICALL int mutexattrSetpshared(ScePthreadMutexattr* attr, int pshared);
__APICALL int attrGetinheritsched(const ScePthreadAttr* attr, SceInheritShed* inheritSched);
__APICALL int attrGetschedparam(const ScePthreadAttr* attr, SceSchedParam* param);
__APICALL int attrGetschedpolicy(const ScePthreadAttr* attr, SceShedPolicy* policy);
__APICALL int attrSetinheritsched(ScePthreadAttr* attr, SceInheritShed inheritSched);
__APICALL int attrSetschedparam(ScePthreadAttr* attr, const SceSchedParam* param);
__APICALL int attrSetschedpolicy(ScePthreadAttr* attr, SceShedPolicy policy);
__APICALL int getschedparam(ScePthread_obj obj, int* policy, SceSchedParam* param);
__APICALL int setschedparam(ScePthread_obj obj, int policy, const SceSchedParam* param);
__APICALL int attrGet(ScePthread_obj obj, ScePthreadAttr* attr);
__APICALL int attrGetaffinity(const ScePthreadAttr* attr, SceKernelCpumask* mask);
__APICALL int attrSetaffinity(ScePthreadAttr* attr, const SceKernelCpumask mask);
__APICALL int getaffinity(ScePthread_obj obj, SceKernelCpumask* mask);
__APICALL int setaffinity(ScePthread_obj obj, const SceKernelCpumask mask);
__APICALL int getthreadid(void);
__APICALL int once(ScePthreadOnce once_control, pthread_once_init init_routine);
__APICALL int rename(ScePthread_obj obj, const char* name);
__APICALL int getName(ScePthread_obj obj, char* name);
__APICALL void setThreadDtors(thread_dtors_func_t dtors);
__APICALL int attrInit(ScePthreadAttr* attr);
__APICALL void exit(void* value);
__APICALL void raise(ScePthread_obj obj, void* callback, int signo);
__APICALL int create(ScePthread_obj* obj, const ScePthreadAttr* attr, pthread_entry_func_t entry, void* arg, const char* name);
__APICALL int attrSetscope(ScePthreadAttr* attr, int flag);
__APICALL int attrGetscope(ScePthreadAttr* attr, int* flag);
__APICALL void cxa_finalize(void* /*p*/);
__APICALL void cleanup_push(thread_clean_func_t func, void* arg);
__APICALL void cleanup_pop(int execute);
} // namespace pthread
#undef __APICALL

View File

@ -0,0 +1,169 @@
#pragma once
#include "core/unwinding/unwind.h"
#include "modules_include/common.h"
#include "pthread_types.h"
#include "utility/utility.h"
#include <boost/thread/recursive_mutex.hpp>
#include <boost/thread/thread.hpp>
constexpr size_t DEFAULT_STACKSIZE = 16 * 1024 * 1024;
constexpr uint64_t XSAVE_CHK_GUARD = 0xDeadBeef5533CCAAu;
constexpr int KEYS_MAX = 256;
constexpr int DESTRUCTOR_ITERATIONS = 4;
constexpr size_t DTV_MAX_KEYS = 256;
struct PthreadAttrPrivate {
private:
size_t stackSize = DEFAULT_STACKSIZE;
void* stackAddr = 0; /// if != 0 use the custum stack
SceSchedParam shedParam = {.sched_priority = 700};
SceKernelCpumask affinity = 0x7f;
size_t guardSize = 0x1000;
SceShedPolicy policy = SceShedPolicy::FIFO;
int scope = 0;
SceDetachState detachState = SceDetachState::JOINABLE;
SceInheritShed inheritShed = SceInheritShed::INHERIT;
public:
PthreadAttrPrivate() = default;
auto getStackAddr() const noexcept { return stackAddr; }
void setStackAddr(decltype(PthreadAttrPrivate::stackAddr) param) noexcept { stackAddr = param; }
auto getStackSize() const noexcept { return stackSize; }
void setStackSize(decltype(PthreadAttrPrivate::stackSize) param) noexcept { stackSize = param; }
auto getGuardSize() const noexcept { return guardSize; }
void setGuardSize(decltype(PthreadAttrPrivate::guardSize) param) noexcept { guardSize = param; }
auto getAffinity() const noexcept { return affinity; }
void setAffinity(decltype(PthreadAttrPrivate::affinity) param) noexcept { affinity = param; }
auto getPolicy() const noexcept { return policy; }
void setPolicy(decltype(PthreadAttrPrivate::policy) param) noexcept { policy = param; }
auto getScope() const noexcept { return scope; }
void setScope(decltype(PthreadAttrPrivate::scope) param) noexcept { scope = param; }
auto getShedParam() const noexcept { return shedParam; }
void setShedParam(decltype(PthreadAttrPrivate::shedParam) param) noexcept { shedParam = param; }
auto getDetachState() const noexcept { return detachState; }
void setDetachState(decltype(PthreadAttrPrivate::detachState) param) noexcept { detachState = param; }
auto getInheritShed() const noexcept { return inheritShed; }
void setInheritShed(decltype(PthreadAttrPrivate::inheritShed) param) noexcept { inheritShed = param; }
};
struct PthreadMutexPrivate {
uint64_t initialized = 1;
uint8_t reserved[256]; // type is bigger on ps than here
boost::recursive_mutex p;
std::string name;
size_t id;
int prioCeiling;
SceMutexType type = SceMutexType::DEFAULT;
~PthreadMutexPrivate() {}
};
struct PthreadMutexattrPrivate {
uint64_t initialized = 1;
uint8_t reserved[60]; // type is bigger on ps than here
SceMutexShared pshared = SceMutexShared::PRIVATE;
SceMutexType type = SceMutexType::DEFAULT;
SceMutexProtocol pprotocol = SceMutexProtocol::PRIO_NONE;
int prioCeiling;
};
struct DTVKey {
bool used = false;
pthread_key_destructor_func_t destructor = nullptr;
};
constexpr size_t DTV_SIZE = DTV_MAX_KEYS;
struct PthreadPrivate {
uint64_t dtv[DTV_SIZE];
std::string name;
boost::thread p;
PthreadAttrPrivate attr = {};
pthread_entry_func_t entry = nullptr;
void* arg = nullptr;
int unique_id = 0;
std::atomic_bool started = false;
int policy = 0;
std::vector<std::pair<thread_clean_func_t, void*>> cleanupFuncs;
bool detached = false; // fake detach
unwinding_jmp_buf _unwinding;
~PthreadPrivate() = default;
};
struct PthreadRwlockPrivate {
uint64_t initialized = 1;
uint8_t reserved[256]; // type is bigger on ps than here
boost::shared_mutex p;
std::string name;
size_t id;
bool isWrite = false;
~PthreadRwlockPrivate() {}
};
struct PthreadBarrierPrivate {};
struct PthreadBarrierattrPrivate {
int pshared;
};
struct PthreadCondattrPrivate {
uint64_t initialized = 1;
uint8_t reserved[64]; // type is bigger on ps than here
};
struct PthreadRwlockattrPrivate {
uint64_t initialized = 1;
uint8_t reserved[64]; // type is bigger on ps than here
int type;
};
struct PthreadCondPrivate {
uint64_t initialized = 1;
uint8_t reserved[64]; // type is bigger on ps than here
boost::condition_variable p;
std::string name;
~PthreadCondPrivate() {}
};
struct PthreadOnce {
int32_t isInit; // 0: no, 1: yes
int32_t _align;
PthreadMutexPrivate* mutex;
};

View File

@ -0,0 +1,51 @@
#pragma once
#include "utility/utility.h"
// clang-format off
using pthread_entry_func_t = SYSV_ABI void* (*)(void*);
using pthread_key_destructor_func_t = SYSV_ABI void (*)(void*);
using thread_dtors_func_t = SYSV_ABI void (*)();
using thread_clean_func_t = SYSV_ABI void (*)(void*);
using pthread_once_init = SYSV_ABI void (*)();
// clang-format on
struct SceSchedParam {
int sched_priority;
};
enum class SceMutexProtocol { PRIO_NONE, PRIO_INHERIT, PRIO_PROTECT };
enum class SceMutexType { DEFAULT, ERRORCHECK, RECURSIVE, NORMAL, ADAPTIVE_NP };
enum class SceMutexShared { PRIVATE, SHARED };
enum class SceDetachState {
JOINABLE,
DETACHED,
};
enum class SceShedPolicy {
OTHER,
FIFO,
RR,
};
enum class SceInheritShed {
EXPLICIT,
INHERIT = 4,
};
enum class SceCancelState {
ENABLE,
DISABLE,
};
enum class SceCancelType {
DEFERRED,
ASYNC = 4,
};
struct SceCleanInfo {
SceCleanInfo* prev;
thread_clean_func_t func;
void* arg;
int onHeap;
};

40
core/kernel/semaphore.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
#include "utility/utility.h"
#include <memory>
class ISemaphore {
CLASS_NO_COPY(ISemaphore);
CLASS_NO_MOVE(ISemaphore);
protected:
ISemaphore() = default;
public:
virtual ~ISemaphore() = default;
virtual int cancel(int setCount, int* numCanceled) = 0;
virtual int signal(int signalCount) = 0;
virtual int wait(int needcount, uint32_t* pMicros) = 0;
virtual int try_wait(int needcount, uint32_t* pMicros) = 0;
virtual int poll(int needCount) = 0;
virtual std::string_view const getName() const = 0;
virtual size_t getId() const = 0;
virtual size_t getSignalCounter() const = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL std::unique_ptr<ISemaphore> createSemaphore_fifo(const char* name, int initCount, int maxCount);
__APICALL std::unique_ptr<ISemaphore> createSemaphore_prio(const char* name, int initCount, int maxCount);
#undef __APICALL

View File

@ -0,0 +1,311 @@
#define __APICALL_EXTERN
#include "semaphore.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "modules_include/common.h"
#include "pthread.h"
#include <boost/chrono.hpp>
#include <boost/thread/condition.hpp>
#include <boost/thread/mutex.hpp>
#include <map>
#include <queue>
LOG_DEFINE_MODULE(Semaphore);
namespace {
enum class SemState { idle, signaled, waiting, canceled };
struct SemData {
size_t index = 0;
uint32_t needs = 0;
SemState state = SemState::idle;
boost::condition_variable m_condVar;
SemData* parent = nullptr;
SemData* child = nullptr;
};
} // namespace
class Semaphore: public ISemaphore {
boost::mutex m_mutexInt;
boost::condition_variable m_condState;
size_t const m_id = [] {
static size_t count = 0;
return count++;
}();
std::string const m_name;
int const m_maxCount;
uint32_t m_signalValue = 0;
bool m_isStop = false;
std::map<size_t, std::shared_ptr<SemData>> m_semData; // todo use pthread specific
SemData* m_curBack = nullptr;
SemData* m_curStart = nullptr;
size_t m_numWaiters = 0;
size_t m_countWaits = 0;
public:
Semaphore(const std::string& name, int initCount, int maxCount): m_name(name), m_maxCount(maxCount) { m_signalValue = initCount; };
virtual ~Semaphore() {
m_isStop = true;
boost::unique_lock lock(m_mutexInt);
while (m_curStart != nullptr) {
m_curStart->m_condVar.notify_one();
m_curStart = m_curStart->child;
}
m_condState.wait(lock, [this] { return m_numWaiters == 0; });
}
// ### Interface
std::string_view const getName() const final { return m_name; }
size_t getSignalCounter() const final { return m_signalValue; }
size_t getId() const final { return m_id; }
int cancel(int setCount, int* numCanceled) final;
int signal(int signalCount) final;
int wait(int needcount, uint32_t* pMicros) final;
int try_wait(int needcount, uint32_t* pMicros) final;
int poll(int needCount) final;
private:
int wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock);
int poll_internal(int needCount, boost::unique_lock<boost::mutex>& lock);
};
std::unique_ptr<ISemaphore> createSemaphore_fifo(const char* name, int initCount, int maxCount) {
return std::make_unique<Semaphore>(name == nullptr ? "" : name, initCount, maxCount);
}
int Semaphore::cancel(int setCount, int* numCanceled) {
LOG_USE_MODULE(Semaphore);
if (setCount <= 0 || setCount >= m_maxCount) return getErr(ErrCode::_EINVAL);
boost::unique_lock lock(m_mutexInt);
*numCanceled = 0;
auto start = m_curStart;
while (start != nullptr) {
if (start->needs > setCount) break;
++*numCanceled;
setCount -= start->needs;
start->state = SemState::canceled;
start = start->child; // next
}
m_signalValue = 0;
// Notify back
if (*numCanceled > 0) {
lock.unlock();
start->m_condVar.notify_one();
}
// -
return Ok;
}
int Semaphore::signal(int signalCount) {
LOG_USE_MODULE(Semaphore);
boost::unique_lock lock(m_mutexInt);
m_signalValue += signalCount;
// Notify back
if (m_curStart != nullptr && m_curStart->needs <= m_signalValue) {
m_curStart->state = SemState::signaled;
LOG_TRACE(L"KernelSema(%llu) name:%S notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
auto& cond = m_curStart->m_condVar;
lock.unlock();
cond.notify_one(); // race condition if m_curStart is used
} else {
LOG_TRACE(L"KernelSema(%llu) name:%S signal| count:%d", m_id, m_name.c_str(), m_signalValue);
}
// -
return Ok;
}
int Semaphore::wait_internal(int needCount, uint32_t* pMicros, boost::unique_lock<boost::mutex>& lock) {
LOG_USE_MODULE(Semaphore);
if (poll_internal(needCount, lock) == Ok) {
return Ok;
}
auto itThread = m_semData.find(pthread::getThreadId());
if (itThread == m_semData.end()) {
itThread = m_semData.emplace(std::make_pair(pthread::getThreadId(), std::make_shared<SemData>())).first;
}
++m_numWaiters;
auto ownData = itThread->second;
ownData->index = ++m_countWaits;
ownData->needs = needCount;
// enque in list
if (m_curBack != nullptr) {
m_curBack->child = ownData.get();
}
if (m_curStart == nullptr) {
m_curStart = ownData.get();
}
ownData->parent = m_curBack;
m_curBack = ownData.get();
// - list
int ret = Ok;
ownData->state = SemState::waiting;
LOG_TRACE(L"-> KernelSema(%llu) name:%S wait| count:%d needs:%d index:%llu state:%d", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index,
ownData->state);
if (pMicros == nullptr) {
ownData->m_condVar.wait(lock, [this, ownData] { return m_isStop || ownData->state != SemState::waiting; });
} else {
std::chrono::time_point<std::chrono::system_clock> startTime = std::chrono::system_clock::now();
if (!ownData->m_condVar.wait_for(lock, boost::chrono::microseconds(*pMicros),
[this, ownData] { return m_isStop || ownData->state != SemState::waiting; })) {
// timeout
ret = getErr(ErrCode::_ETIMEDOUT);
// remove from list
if (ownData->parent != nullptr) {
ownData->parent->child = ownData->child;
}
if (ownData->child != nullptr) {
ownData->child->parent = ownData->parent;
}
LOG_TRACE(L"<- KernelSema(%llu) name:%S timeout| count:%d needs:%d index:%llu", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index);
// Special: first in list -> notify next
if (ownData->parent == nullptr) {
m_curStart = ownData->child;
if (m_curStart != nullptr && (m_isStop || m_curStart->needs <= m_signalValue)) {
m_curStart->state = SemState::signaled;
LOG_TRACE(L"KernelSema(%llu) name:%S timeout notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
auto& cond = m_curStart->m_condVar;
lock.unlock();
cond.notify_one(); // race condition if m_curStart is used
}
}
// - special
// Reset ownData
ownData->child = nullptr;
ownData->parent = nullptr;
ownData->state = SemState::idle;
*pMicros = 0;
if (--m_numWaiters == 0 && m_isStop) {
lock.unlock();
m_condState.notify_one();
}
return ret;
}
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::system_clock::now() - startTime).count();
if (pMicros != nullptr) {
*pMicros = (elapsed >= *pMicros ? 0 : *pMicros - elapsed);
}
}
if (m_isStop || ownData->state == SemState::canceled) {
ret = getErr(ErrCode::_ECANCELED);
}
if (ret == Ok) {
m_signalValue -= ownData->needs;
}
// Set list start/end
if (ownData->child == nullptr) {
// Reached end -> reset back
m_curBack = nullptr;
m_countWaits = 0;
}
m_curStart = ownData->child;
// -
LOG_TRACE(L"<- KernelSema(%llu) name:%S wait| count:%d needs:%d index:%llu state:%d", m_id, m_name.c_str(), m_signalValue, needCount, ownData->index,
ownData->state);
// Reset ownData
ownData->child = nullptr;
ownData->parent = nullptr;
ownData->state = SemState::idle;
// -
// notify next
if (m_curStart != nullptr && (m_isStop || m_curStart->needs <= m_signalValue)) {
m_curStart->state = SemState::signaled;
LOG_TRACE(L"KernelSema(%llu) name:%S notify| count:%d index:%llu", m_id, m_name.c_str(), m_signalValue, m_curStart->index);
auto& cond = m_curStart->m_condVar;
lock.unlock();
cond.notify_one(); // race condition if m_curStart is used
}
//
if (--m_numWaiters == 0 && m_isStop) {
lock.unlock();
m_condState.notify_one();
}
return ret;
}
int Semaphore::wait(int needcount, uint32_t* pMicros) {
boost::unique_lock lock(m_mutexInt);
return wait_internal(needcount, pMicros, lock);
}
int Semaphore::try_wait(int needcount, uint32_t* pMicros) {
boost::unique_lock lock(m_mutexInt);
if (m_curBack != nullptr) {
return getErr(ErrCode::_EBUSY);
}
return wait_internal(needcount, pMicros, lock);
}
int Semaphore::poll_internal(int needCount, boost::unique_lock<boost::mutex>& lock) {
if (m_curBack == 0 && (needCount <= m_signalValue)) {
m_signalValue -= needCount;
return Ok;
}
return getErr(ErrCode::_EAGAIN); // Waiters in queue, todo need enqueue?
}
int Semaphore::poll(int needCount) {
boost::unique_lock lock(m_mutexInt);
return poll_internal(needCount, lock);
}

View File

@ -0,0 +1,7 @@
add_library(memory OBJECT
memory.cpp
)
add_dependencies(memory third_party psOff_utility)
file(COPY memory.h DESTINATION ${DST_INCLUDE_DIR}/memory)

339
core/memory/memory.cpp Normal file
View File

@ -0,0 +1,339 @@
#define __APICALL_EXTERN
#include "memory.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "utility/utility.h"
#include <mutex>
#include <windows.h>
LOG_DEFINE_MODULE(memory);
namespace {
bool checkIsGPU(int protection) {
return (protection & 0xf0) > 0;
}
DWORD convProtection(int prot) {
switch (prot & 0xf) {
case 0: return PAGE_NOACCESS;
case 1: return PAGE_READONLY;
case 2:
case 3: return PAGE_READWRITE;
case 4: return PAGE_EXECUTE;
case 5: return PAGE_EXECUTE_READ;
case 6:
case 7: return PAGE_EXECUTE_READWRITE;
}
if (checkIsGPU(prot)) {
return PAGE_READWRITE; // special case: cpu read/writes gpumemory on host memory
}
return PAGE_NOACCESS;
}
int convProtection(DWORD prot) {
switch (prot) {
case PAGE_NOACCESS: return 0;
case PAGE_READONLY: return SceProtRead;
case PAGE_READWRITE: return SceProtRead | SceProtWrite;
case PAGE_EXECUTE: return SceProtExecute;
case PAGE_EXECUTE_READ: return SceProtExecute | SceProtRead;
case PAGE_EXECUTE_READWRITE: return SceProtRead | SceProtWrite | SceProtExecute;
}
return 0;
}
using VirtualAlloc2_func_t = /*WINBASEAPI*/ PVOID WINAPI (*)(HANDLE, PVOID, SIZE_T, ULONG, ULONG, MEM_EXTENDED_PARAMETER*, ULONG);
VirtualAlloc2_func_t getVirtualAlloc2() {
static auto funcVirtualAlloc2 = [] {
LOG_USE_MODULE(memory);
HMODULE h = GetModuleHandle("KernelBase");
if (h != nullptr) {
auto const addr = reinterpret_cast<VirtualAlloc2_func_t>(GetProcAddress(h, "VirtualAlloc2"));
if (addr == nullptr) {
LOG_CRIT(L"virtual_alloc2 == nullptr");
}
return addr;
}
LOG_CRIT(L"KernelBase not found");
return (VirtualAlloc2_func_t) nullptr;
}();
return funcVirtualAlloc2;
}
} // namespace
namespace memory {
int getpagesize(void) {
return util::getPageSize();
}
uint64_t getTotalSystemMemory() {
MEMORYSTATUSEX status;
status.dwLength = sizeof(status);
GlobalMemoryStatusEx(&status);
return status.ullTotalPhys;
}
uintptr_t reserve(uint64_t start, uint64_t size, uint64_t alignment, bool isGpu) {
LOG_USE_MODULE(memory);
MEM_ADDRESS_REQUIREMENTS requirements {
.LowestStartingAddress = (PVOID)start,
.HighestEndingAddress = (PVOID)0,
.Alignment = alignment,
};
MEM_EXTENDED_PARAMETER param {
.Type = MemExtendedParameterAddressRequirements,
.Pointer = &requirements,
};
auto ptr = (uintptr_t)getVirtualAlloc2()(GetCurrentProcess(), nullptr, size, isGpu ? MEM_RESERVE | MEM_WRITE_WATCH : MEM_RESERVE, PAGE_NOACCESS, &param, 1);
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
if (err != ERROR_INVALID_PARAMETER) {
LOG_ERR(L"reserve failed err:0x%08x| start:0x%08llx size:%llu alignment:%llu isGpu:%d", err, start, size, alignment, isGpu);
}
return 0;
}
return ptr;
}
uint64_t commit(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment, int prot) {
LOG_USE_MODULE(memory);
auto ptr = (uintptr_t)VirtualAlloc((LPVOID)(baseAddr + offset), size, MEM_COMMIT, convProtection(prot));
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
LOG_ERR(L"commit failed err:0x%0x| base:0x%08llx offset:0x%08llx size:%llu alignment:%llu prot:%d", err, baseAddr, offset, size, alignment, prot);
}
return ptr;
}
uint64_t allocGPUMemory(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment) {
LOG_USE_MODULE(memory);
MEMORY_BASIC_INFORMATION im;
VirtualQuery((LPVOID)baseAddr, &im, sizeof(im));
VirtualFree(im.AllocationBase, 0, MEM_RELEASE);
auto ptrBase = reserve((uint64_t)im.AllocationBase, im.RegionSize, alignment, true);
if (ptrBase == 0) {
auto err = static_cast<uint32_t>(GetLastError());
LOG_ERR(L"allocGPUMemory failed err:0x%0x| addr:0x%08llx size:%llu", err, baseAddr, im.RegionSize);
return 0;
}
auto ptr = (uintptr_t)VirtualAlloc((LPVOID)(baseAddr + offset), size, MEM_COMMIT | MEM_WRITE_WATCH, PAGE_READWRITE);
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
LOG_ERR(L"allocGPUMemory failed err:0x%0x| addr:0x%08llx size:%llu", err, baseAddr, size);
return 0;
}
LOG_DEBUG(L"allocGPUMemory| base:0x%08llx(0x%08llx) addr:0x%08llx", ptrBase, baseAddr, ptr);
return ptr;
}
int64_t queryAlloc(uintptr_t addr, uintptr_t* start, uintptr_t* end, int* prot) {
MEMORY_BASIC_INFORMATION im;
if (VirtualQuery((LPVOID)addr, &im, sizeof(im)) == 0) {
return -1;
}
if (start != nullptr) *start = (uintptr_t)im.BaseAddress;
if (end != nullptr) *end = *start + im.RegionSize;
if (prot != nullptr) {
*prot = convProtection(im.Protect);
}
return (int64_t)im.AllocationBase;
}
uint64_t allocAligned(uint64_t address, uint64_t size, int prot, uint64_t alignment) {
LOG_USE_MODULE(memory);
constexpr uint64_t USER_MIN = DIRECTMEM_START;
constexpr uint64_t USER_MAX = 0xFBFFFFFFFFu;
MEM_ADDRESS_REQUIREMENTS req2 {};
MEM_EXTENDED_PARAMETER param2 {};
req2.LowestStartingAddress = (address == 0 ? reinterpret_cast<PVOID>(USER_MIN) : reinterpret_cast<PVOID>(address));
req2.HighestEndingAddress = reinterpret_cast<PVOID>(USER_MAX);
req2.Alignment = alignment;
param2.Type = MemExtendedParameterAddressRequirements;
param2.Pointer = &req2;
static auto virtual_alloc2 = getVirtualAlloc2();
if (virtual_alloc2 == nullptr) {
LOG_CRIT(L"virtual_alloc2 == nullptr");
return 0;
}
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
if (checkIsGPU(prot)) flags |= MEM_WRITE_WATCH;
auto ptr = reinterpret_cast<uintptr_t>(virtual_alloc2(0, nullptr, size, flags, convProtection(prot), &param2, 1));
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
if (err != ERROR_INVALID_PARAMETER) {
LOG_ERR(L"VirtualAlloc2(alignment = 0x%08llx failed: 0x%04x", alignment, err);
} else {
return alloc(0, size, prot);
}
}
return ptr;
}
uint64_t alloc(uint64_t address, uint64_t size, int prot) {
LOG_USE_MODULE(memory);
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
if (checkIsGPU(prot)) flags |= MEM_WRITE_WATCH;
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, flags, convProtection(prot)));
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
if (err != ERROR_INVALID_ADDRESS) {
LOG_ERR(L"VirtualAlloc() failed @0x%08llx:0x%08llx protection:0x%x, err:0x%04x", address, size, prot, err);
} else {
return allocAligned(address, size, prot, 0);
}
}
return ptr;
}
bool allocFixed(uint64_t address, uint64_t size, int protection) {
LOG_USE_MODULE(memory);
DWORD flags = static_cast<DWORD>(MEM_COMMIT) | static_cast<DWORD>(MEM_RESERVE);
if (checkIsGPU(protection)) flags |= MEM_WRITE_WATCH;
auto ptr = reinterpret_cast<uintptr_t>(VirtualAlloc(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, flags, convProtection(protection)));
if (ptr == 0) {
auto err = static_cast<uint32_t>(GetLastError());
LOG_ERR(L"VirtualAlloc() failed: 0x%04x", err);
return false;
}
if (ptr != address) {
LOG_ERR(L"VirtualAlloc() failed: wrong address");
VirtualFree(reinterpret_cast<LPVOID>(ptr), 0, MEM_RELEASE);
return false;
}
return true;
}
bool free(uint64_t address) {
LOG_USE_MODULE(memory);
if (VirtualFree(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), 0, MEM_RELEASE) == 0) {
LOG_ERR(L"VirtualFree() failed: 0x%04x", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool protect(uint64_t address, uint64_t size, int protection, int* oldProt) {
LOG_USE_MODULE(memory);
DWORD oldProtection;
if (VirtualProtect(reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size, convProtection(protection), &oldProtection) == 0) {
LOG_ERR(L"VirtualProtect() failed addr:0x%08llx size:0x%08llx prot:%d err:0x%04x", address, size, protection, static_cast<uint32_t>(GetLastError()));
return false;
}
if (oldProt != nullptr) *oldProt = (protection & 0xF0) | convProtection(oldProtection);
return true;
}
int getProtection(uint64_t address) {
LOG_USE_MODULE(memory);
MEMORY_BASIC_INFORMATION mbi;
if (!VirtualQuery((const void*)address, &mbi, sizeof(mbi))) {
LOG_ERR(L"Failed to query memory");
return 0;
}
return convProtection(mbi.Protect);
}
void installHook_long(uintptr_t dst, uintptr_t src, _t_hook& pGateway, size_t lenOpCodes) {
std::array<uint8_t, 14> codeGateway = {
0xFF, 0x25, 0x00, 0x00, 0x00, 0x00, // jmp qword
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // addr
};
// setup jmp back code
*(uint64_t*)&codeGateway[6] = (uint64_t)(src + lenOpCodes);
LOG_USE_MODULE(memory);
{
// Interceptor, jmp to dst
std::array code = codeGateway;
*(uint64_t*)&code[6] = (uint64_t)(dst);
int oldMode;
protect(src, code.size(), SceProtExecute | SceProtRead | SceProtWrite, &oldMode);
memcpy(pGateway.data.data(), (uint8_t*)src, lenOpCodes); // save code -> add to gateway
memcpy((uint8_t*)src, code.data(), code.size());
if (::FlushInstructionCache(GetCurrentProcess(), (void*)src, code.size()) == 0) {
LOG_ERR(L"FlushInstructionCache() failed: 0x%04x", static_cast<uint32_t>(GetLastError()));
return;
}
protect(src, code.size(), oldMode);
// - interceptor
}
memcpy(pGateway.data.data() + lenOpCodes, (uint8_t*)codeGateway.data(), codeGateway.size()); // cpy to gateway
protect((uintptr_t)pGateway.data.data(), pGateway.data.size(), SceProtExecute | SceProtRead, nullptr);
}
int VirtualLock::check_mmaped(void* addr, size_t len) {
return 0;
}
VirtualLock& VirtualLock::instance() {
static VirtualLock instance;
return instance;
}
void VirtualLock::lock() {
MMLock.lock();
}
void VirtualLock::unlock() {
MMLock.unlock();
}
void MLOCK(VirtualLock& vLock) {
vLock.lock();
}
void MUNLOCK(VirtualLock& vLock) {
vLock.unlock();
}
int check_mmaped(void* addr, size_t len) {
LOG_USE_MODULE(memory);
VirtualLock& vLock = VirtualLock::instance();
MLOCK(vLock);
int result = vLock.check_mmaped(addr, len);
MUNLOCK(vLock);
return result;
}
} // namespace memory

60
core/memory/memory.h Normal file
View File

@ -0,0 +1,60 @@
#pragma once
#include <array>
#include <mutex>
#include <stdint.h>
constexpr int SceProtRead = 1;
constexpr int SceProtWrite = 2;
constexpr int SceProtExecute = 4;
constexpr uint64_t DIRECTMEM_START = 0x100000000u;
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
namespace memory {
constexpr inline bool isExecute(int prot) {
return (prot & 0x4) > 0;
}
struct _t_hook {
std::array<uint8_t, 14 + 8> data; // Should be enough for inserting the hook (min 14 max 14+8)
};
class VirtualLock {
public:
int check_mmaped(void* addr, size_t len);
static VirtualLock& instance();
void lock();
void unlock();
private:
std::mutex MMLock;
};
void MLOCK(VirtualLock& vLock);
void MUNLOCK(VirtualLock& vLock);
__APICALL int getpagesize(void);
__APICALL uint64_t getTotalSystemMemory();
__APICALL uintptr_t reserve(uint64_t start, uint64_t size, uint64_t alignment, bool isGpu);
__APICALL uint64_t commit(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment, int prot);
__APICALL uint64_t allocGPUMemory(uintptr_t baseAddr, uint64_t offset, uint64_t size, uint64_t alignment);
__APICALL int64_t queryAlloc(uintptr_t addr, uintptr_t* start, uintptr_t* end, int* prot);
__APICALL uint64_t alloc(uint64_t address, uint64_t size, int prot);
__APICALL uint64_t allocAligned(uint64_t address, uint64_t size, int prot, uint64_t alignment);
__APICALL bool allocFixed(uint64_t address, uint64_t size, int prot);
__APICALL bool free(uint64_t address);
__APICALL bool protect(uint64_t address, uint64_t size, int prot, int* oldMode = nullptr);
__APICALL int getProtection(uint64_t address);
__APICALL int check_mmaped(void* addr, size_t len);
__APICALL void installHook_long(uintptr_t dst, uintptr_t src, _t_hook& pGateway, size_t lenOpCodes);
} // namespace memory
#undef __APICALL

View File

@ -0,0 +1,17 @@
add_library(networking OBJECT
states/offline.cpp
states/online/netctl.cpp
states/online/resolver.cpp
states/online/socket.cpp
states/online/epoll.cpp
states/online/http.cpp
networking.cpp
)
add_dependencies(networking third_party psOff_utility config_emu)
target_compile_definitions(networking PUBLIC WIN32_LEAN_AND_MEAN _WINSOCK_DEPRECATED_NO_WARNINGS)
file(COPY networking.h DESTINATION ${DST_INCLUDE_DIR}/networking)

View File

@ -0,0 +1,57 @@
#define __APICALL_EXTERN
#include "networking.h"
#undef __APICALL_EXTERN
#include "config_emu.h"
#include "logging.h"
#include "states/offline.h"
#include "states/online.h"
LOG_DEFINE_MODULE(NetworkingCore);
static thread_local int32_t g_net_errno = 0;
INetworking::INetworking() {
LOG_USE_MODULE(NetworkingCore);
WSADATA data = {};
if (WSAStartup(MAKEWORD(2, 2), &data) == SOCKET_ERROR) LOG_CRIT(L"WSAStartup failed: %d", getLastError());
}
INetworking::~INetworking() {
LOG_USE_MODULE(NetworkingCore);
if (WSACleanup() == SOCKET_ERROR) LOG_CRIT(L"WSACleanup failed: %d", getLastError());
}
int32_t INetworking::getLastError() {
auto win_err = (uint32_t)GetLastError();
if (win_err == WSANOTINITIALISED) return Err::Net::ERROR_ENOTINIT;
return (0x80000000 | (0x041 << 16) | (0x0100 | (win_err - 10000)));
}
int32_t* INetworking::getErrnoPtr() {
return &g_net_errno;
}
class NetInitializer {
INetworking* m_itf;
public:
NetInitializer() {
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL);
bool state = false;
if (getJsonParam(jData, "netEnabled", state) && state == true) {
static OnlineNet ion;
m_itf = &ion;
} else {
static OfflineNet oon;
m_itf = &oon;
}
}
INetworking& getClass() { return (*m_itf); }
};
INetworking& accessNetworking() {
static NetInitializer ni;
return ni.getClass();
}

View File

@ -0,0 +1,165 @@
#pragma once
#include "modules/libSceHttp/httpsTypes.h"
#include "modules/libSceHttp/types.h"
#include "modules/libSceNet/resolverTypes.h"
#include "modules/libSceNet/socketTypes.h"
#include "modules/libSceNet/types.h"
#include "modules/libSceNetCtl/types.h"
#include "modules/libSceSsl/types.h"
#include "modules_include/common.h"
#include "utility/utility.h"
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
class INetworking {
CLASS_NO_COPY(INetworking);
CLASS_NO_MOVE(INetworking);
protected:
INetworking();
public:
~INetworking();
/**
* @brief returns current network error code
*
* @return int32_t
*/
__APICALL static int32_t getLastError();
/**
* @brief returns errno ptr
*
* @return int32_t*
*/
__APICALL static int32_t* getErrnoPtr();
/* SceNetCtl facility*/
/**
* @brief returns information about specified network parameter
*
* @param code
* @param info
* @return int32_t
*/
virtual int32_t netCtlGetInfo(int32_t code, SceNetCtlInfo* info) = 0;
virtual int32_t netCtlGetState(int32_t* state) = 0;
/* SceNet facility */
/* Resolver sub-facility */
virtual SceNetId resolverCreate(const char* name, int memid, int flags) = 0;
virtual int32_t resolverStartNtoa(SceNetId rid, const char* hostname, SceNetInAddr_t* addr, int timeout, int retries, int flags) = 0;
virtual int32_t resolverStartAton(SceNetId rid, const SceNetInAddr_t* addr, char* hostname, int len, int timeout, int retry, int flags) = 0;
virtual int32_t resolverStartNtoaMultipleRecords(SceNetId rid, const char* hostname, SceNetResolverInfo* info, int timeout, int retries, int flags) = 0;
virtual int32_t resolverGetError(SceNetId rid, int* result) = 0;
virtual int32_t resolverDestroy(SceNetId rid) = 0;
virtual int32_t resolverAbort(SceNetId rid, int flags) = 0;
/* Epoll sub-facility */
virtual SceNetId epollCreate(const char* name, int flags) = 0;
virtual int epollControl(SceNetId eid, int op, SceNetId id, SceNetEpollEvent* event) = 0;
virtual int epollWait(SceNetId eid, SceNetEpollEvent* events, int maxevents, int timeout) = 0;
virtual int epollDestroy(SceNetId eid) = 0;
virtual int epollAbort(SceNetId eid, int flags) = 0;
/* Socket sub-facility */
virtual SceNetId socketCreate(const char* name, int family, int type, int protocol) = 0;
virtual SceNetId socketAccept(SceNetId s, SceNetSockaddr* addr, SceNetSocklen_t* addrlen) = 0;
virtual int socketBind(SceNetId s, const SceNetSockaddr* addr, SceNetSocklen_t addrlen) = 0;
virtual int socketConnect(SceNetId s, const SceNetSockaddr* name, SceNetSocklen_t namelen) = 0;
virtual int socketGetpeername(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) = 0;
virtual int socketGetsockname(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) = 0;
virtual int socketGetsockopt(SceNetId s, int level, int optname, void* optval, SceNetSocklen_t* optlen) = 0;
virtual int socketListen(SceNetId s, int backlog) = 0;
virtual int socketRecv(SceNetId s, void* buf, size_t len, int flags) = 0;
virtual int socketRecvfrom(SceNetId s, void* buf, size_t len, int flags, SceNetSockaddr* from, SceNetSocklen_t* fromlen) = 0;
virtual int socketRecvmsg(SceNetId s, SceNetMsghdr* msg, int flags) = 0;
virtual int socketSend(SceNetId s, const void* msg, size_t len, int flags) = 0;
virtual int socketSendto(SceNetId s, const void* msg, size_t len, int flags, const SceNetSockaddr* to, SceNetSocklen_t tolen) = 0;
virtual int socketSendmsg(SceNetId s, const SceNetMsghdr* msg, int flags) = 0;
virtual int socketSetsockopt(SceNetId s, int level, int optname, const void* optval, SceNetSocklen_t optlen) = 0;
virtual int socketShutdown(SceNetId s, int how) = 0;
virtual int socketClose(SceNetId s) = 0;
virtual int socketAbort(SceNetId s, int flags) = 0;
/* HTTP facility */
/* HTTP1 facility */
virtual int httpInit(int libnetMemId, int libsslCtxId, size_t poolSize) = 0;
virtual int httpTerm(int libhttpCtxId) = 0;
virtual int httpGetMemoryPoolStats(int libhttpCtxId, SceHttpMemoryPoolStats* currentStat) = 0;
virtual int httpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer, int isAutoProxyConf) = 0;
virtual int httpDeleteTemplate(int tmplId) = 0;
virtual int httpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive) = 0;
virtual int httpCreateConnectionWithURL(int tmplId, const char* url, int isEnableKeepalive) = 0;
virtual int httpDeleteConnection(int connId) = 0;
virtual int httpCreateRequest(int connId, int method, const char* path, uint64_t contentLength) = 0;
virtual int httpCreateRequest2(int connId, const char* method, const char* path, uint64_t contentLength) = 0;
virtual int httpCreateRequestWithURL(int connId, int method, const char* url, uint64_t contentLength) = 0;
virtual int httpCreateRequestWithURL2(int connId, const char* method, const char* url, uint64_t contentLength) = 0;
virtual int httpDeleteRequest(int reqId) = 0;
virtual int httpSetRequestContentLength(int id, uint64_t contentLength) = 0;
virtual int httpSetChunkedTransferEnabled(int id, int isEnable) = 0;
virtual int httpSetInflateGZIPEnabled(int id, int isEnable) = 0;
virtual int httpSendRequest(int reqId, const void* postData, size_t size) = 0;
virtual int httpAbortRequest(int reqId) = 0;
virtual int httpGetResponseContentLength(int reqId, int* result, uint64_t* contentLength) = 0;
virtual int httpGetStatusCode(int reqId, int* statusCode) = 0;
virtual int httpGetAllResponseHeaders(int reqId, char** header, size_t* headerSize) = 0;
virtual int httpReadData(int reqId, void* data, size_t size) = 0;
virtual int httpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) = 0;
virtual int httpRemoveRequestHeader(int id, const char* name) = 0;
virtual int httpParseResponseHeader(const char* header, size_t headerLen, const char* fieldStr, const char** fieldValue, size_t* valueLen) = 0;
virtual int httpParseStatusLine(const char* statusLine, size_t lineLen, int* httpMajorVer, int* httpMinorVer, int* responseCode, const char** reasonPhrase,
size_t* phraseLen) = 0;
virtual int httpSetResponseHeaderMaxSize(int id, size_t headerSize) = 0;
virtual int httpSetAuthInfoCallback(int id, SceHttpAuthInfoCallback cbfunc, void* userArg) = 0;
virtual int httpSetAuthEnabled(int id, int isEnable) = 0;
virtual int httpGetAuthEnabled(int id, int* isEnable) = 0;
virtual int httpAuthCacheFlush(int libhttpCtxId) = 0;
virtual int httpSetRedirectCallback(int id, SceHttpRedirectCallback cbfunc, void* userArg) = 0;
virtual int httpSetAutoRedirect(int id, int isEnable) = 0;
virtual int httpGetAutoRedirect(int id, int* isEnable) = 0;
virtual int httpRedirectCacheFlush(int libhttpCtxId) = 0;
virtual int httpSetResolveTimeOut(int id, uint32_t usec) = 0;
virtual int httpSetResolveRetry(int id, int retry) = 0;
virtual int httpSetConnectTimeOut(int id, uint32_t usec) = 0;
virtual int httpSetSendTimeOut(int id, uint32_t usec) = 0;
virtual int httpSetRecvTimeOut(int id, uint32_t usec) = 0;
virtual int httpSetRequestStatusCallback(int id, SceHttpRequestStatusCallback cbfunc, void* userArg) = 0;
virtual int httpGetLastErrno(int reqId, int* errNum) = 0;
virtual int httpSetNonblock(int id, int isEnable) = 0;
virtual int httpGetNonblock(int id, int* isEnable) = 0;
virtual int httpTrySetNonblock(int id, int isEnable) = 0;
virtual int httpTryGetNonblock(int id, int* isEnable) = 0;
virtual int httpCreateEpoll(int libhttpCtxId, SceHttpEpollHandle* eh) = 0;
virtual int httpSetEpoll(int id, SceHttpEpollHandle eh, void* userArg) = 0;
virtual int httpUnsetEpoll(int id) = 0;
virtual int httpGetEpoll(int id, SceHttpEpollHandle* eh, void** userArg) = 0;
virtual int httpDestroyEpoll(int libhttpCtxId, SceHttpEpollHandle eh) = 0;
virtual int httpWaitRequest(SceHttpEpollHandle eh, SceHttpNBEvent* nbev, int maxevents, int timeout) = 0;
virtual int httpAbortWaitRequest(SceHttpEpollHandle eh) = 0;
// HTTPS
virtual int httpsLoadCert(int libhttpCtxId, int caCertNum, const SceSslData** caList, const SceSslData* cert, const SceSslData* privKey) = 0;
virtual int httpsUnloadCert(int libhttpCtxId) = 0;
virtual int httpsEnableOption(int id, uint32_t sslFlags) = 0;
virtual int httpsDisableOption(int id, uint32_t sslFlags) = 0;
virtual int httpsGetSslError(int id, int* errNum, uint32_t* detail) = 0;
virtual int httpsSetSslCallback(int id, SceHttpsCallback cbfunc, void* userArg) = 0;
virtual int httpsSetSslVersion(int id, SceSslVersion version) = 0;
};
__APICALL INetworking& accessNetworking();
#undef __APICALL

View File

@ -0,0 +1,416 @@
#include "offline.h"
#include "logging.h"
#include "modules/libSceNetCtl/codes.h"
LOG_DEFINE_MODULE(OfflineNetworkingCore);
int32_t OfflineNet::netCtlGetInfo(int32_t code, SceNetCtlInfo* info) {
LOG_USE_MODULE(OfflineNetworkingCore);
LOG_TRACE(L"netCtlGetInfo(%d, %p)", code, info);
switch (code) {
case 1: info->device = 0; break;
case 2: memset(info->ether_addr.data, 0, SCE_NET_ETHER_ADDR_LEN); break;
case 3: info->mtu = 0; break;
case 4: info->link = 0; break;
case 5: memset(info->bssid.data, 0, SCE_NET_ETHER_ADDR_LEN); break;
case 6: *info->ssid = '\0'; break;
case 7: info->wifi_security = 0; break;
case 8: info->rssi_dbm = 0; break;
case 9: info->rssi_percentage = 0; break;
case 10: info->channel = 0; break;
case 11: info->ip_config = 0; break;
case 12: *info->dhcp_hostname = '\0'; break;
case 13: *info->pppoe_auth_name = '\0'; break;
case 14: *info->ip_address = '\0'; break;
case 15: *info->netmask = '\0'; break;
case 16: *info->default_route = '\0'; break;
case 17: *info->primary_dns = '\0'; break;
case 18: *info->secondary_dns = '\0'; break;
case 19: info->http_proxy_config = 0; break;
case 20: *info->http_proxy_server = '\0'; break;
case 21: info->http_proxy_port = 0; break;
}
return Err::NetCtl::NOT_CONNECTED;
}
int32_t OfflineNet::netCtlGetState(int32_t* state) {
*state = 0; // Network disconnected
return Ok;
}
SceNetId OfflineNet::resolverCreate(const char* name, int memid, int flags) {
static SceNetId id = 0;
return ++id;
}
int32_t OfflineNet::resolverStartNtoa(SceNetId rid, const char* hostname, SceNetInAddr_t* addr, int timeout, int retries, int flags) {
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OfflineNet::resolverStartAton(SceNetId rid, const SceNetInAddr_t* addr, char* hostname, int len, int timeout, int retry, int flags) {
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OfflineNet::resolverStartNtoaMultipleRecords(SceNetId rid, const char* hostname, SceNetResolverInfo* info, int timeout, int retries, int flags) {
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OfflineNet::resolverGetError(SceNetId rid, int* result) {
*result = (int)ErrCode::_ETIMEDOUT;
return Ok;
}
int32_t OfflineNet::resolverDestroy(SceNetId rid) {
return Ok;
}
int32_t OfflineNet::resolverAbort(SceNetId rid, int flags) {
return Ok;
}
SceNetId OfflineNet::epollCreate(const char* name, int flags) {
static SceNetId id = 0;
return ++id;
}
int32_t OfflineNet::epollControl(SceNetId eid, int op, SceNetId id, SceNetEpollEvent* event) {
return Ok;
}
int32_t OfflineNet::epollWait(SceNetId eid, SceNetEpollEvent* events, int maxevents, int timeout) {
return Ok;
}
int32_t OfflineNet::epollDestroy(SceNetId eid) {
return Ok;
}
int32_t OfflineNet::epollAbort(SceNetId eid, int flags) {
return Ok;
}
SceNetId OfflineNet::socketCreate(const char* name, int family, int type, int protocol) {
*INetworking::getErrnoPtr() = NetErrNo::SCE_NET_EPROTONOSUPPORT;
return Err::Net::ERROR_EINVAL;
}
SceNetId OfflineNet::socketAccept(SceNetId s, SceNetSockaddr* addr, SceNetSocklen_t* addrlen) {
return 0;
}
int OfflineNet::socketBind(SceNetId s, const SceNetSockaddr* addr, SceNetSocklen_t addrlen) {
return Ok;
}
int OfflineNet::socketConnect(SceNetId s, const SceNetSockaddr* name, SceNetSocklen_t namelen) {
return Ok;
}
int OfflineNet::socketGetpeername(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) {
return Ok;
}
int OfflineNet::socketGetsockname(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) {
return Ok;
}
int OfflineNet::socketGetsockopt(SceNetId s, int level, int optname, void* optval, SceNetSocklen_t* optlen) {
return 0;
}
int OfflineNet::socketListen(SceNetId s, int backlog) {
return Ok;
}
int OfflineNet::socketRecv(SceNetId s, void* buf, size_t len, int flags) {
return 0;
}
int OfflineNet::socketRecvfrom(SceNetId s, void* buf, size_t len, int flags, SceNetSockaddr* from, SceNetSocklen_t* fromlen) {
return 0;
}
int OfflineNet::socketRecvmsg(SceNetId s, SceNetMsghdr* msg, int flags) {
return Ok;
}
int OfflineNet::socketSend(SceNetId s, const void* msg, size_t len, int flags) {
return 0;
}
int OfflineNet::socketSendto(SceNetId s, const void* msg, size_t len, int flags, const SceNetSockaddr* to, SceNetSocklen_t tolen) {
return 0;
}
int OfflineNet::socketSendmsg(SceNetId s, const SceNetMsghdr* msg, int flags) {
return Ok;
}
int OfflineNet::socketSetsockopt(SceNetId s, int level, int optname, const void* optval, SceNetSocklen_t optlen) {
return Ok;
}
int OfflineNet::socketShutdown(SceNetId s, int how) {
return Ok;
}
int OfflineNet::socketClose(SceNetId s) {
return Ok;
}
int OfflineNet::socketAbort(SceNetId s, int flags) {
return Ok;
}
int OfflineNet::httpInit(int libnetMemId, int libsslCtxId, size_t poolSize) {
return Ok;
}
int OfflineNet::httpTerm(int libhttpCtxId) {
return Ok;
}
int OfflineNet::httpGetMemoryPoolStats(int libhttpCtxId, SceHttpMemoryPoolStats* currentStat) {
return Ok;
}
int OfflineNet::httpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer, int isAutoProxyConf) {
static int id = 0;
return ++id;
}
int OfflineNet::httpDeleteTemplate(int tmplId) {
return Ok;
}
int OfflineNet::httpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive) {
static int id = 0;
return ++id;
}
int OfflineNet::httpCreateConnectionWithURL(int tmplId, const char* url, int isEnableKeepalive) {
static int id = 0;
return ++id;
}
int OfflineNet::httpDeleteConnection(int connId) {
return Ok;
}
int OfflineNet::httpCreateRequest(int connId, int method, const char* path, uint64_t contentLength) {
static int id = 0;
return ++id;
}
int OfflineNet::httpCreateRequest2(int connId, const char* method, const char* path, uint64_t contentLength) {
static int id = 0;
return ++id;
}
int OfflineNet::httpCreateRequestWithURL(int connId, int method, const char* url, uint64_t contentLength) {
static int id = 0;
return ++id;
}
int OfflineNet::httpCreateRequestWithURL2(int connId, const char* method, const char* url, uint64_t contentLength) {
static int id = 0;
return ++id;
}
int OfflineNet::httpDeleteRequest(int reqId) {
return Ok;
}
int OfflineNet::httpSetRequestContentLength(int id, uint64_t contentLength) {
return Ok;
}
int OfflineNet::httpSetChunkedTransferEnabled(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpSetInflateGZIPEnabled(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpSendRequest(int reqId, const void* postData, size_t size) {
return Ok;
}
int OfflineNet::httpAbortRequest(int reqId) {
return Ok;
}
int OfflineNet::httpGetResponseContentLength(int reqId, int* result, uint64_t* contentLength) {
return Ok;
}
int OfflineNet::httpGetStatusCode(int reqId, int* statusCode) {
return Ok;
}
int OfflineNet::httpGetAllResponseHeaders(int reqId, char** header, size_t* headerSize) {
return Ok;
}
int OfflineNet::httpReadData(int reqId, void* data, size_t size) {
return Ok;
}
int OfflineNet::httpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) {
return Ok;
}
int OfflineNet::httpRemoveRequestHeader(int id, const char* name) {
return Ok;
}
int OfflineNet::httpParseResponseHeader(const char* header, size_t headerLen, const char* fieldStr, const char** fieldValue, size_t* valueLen) {
return Ok;
}
int OfflineNet::httpParseStatusLine(const char* statusLine, size_t lineLen, int* httpMajorVer, int* httpMinorVer, int* responseCode, const char** reasonPhrase,
size_t* phraseLen) {
return Ok;
}
int OfflineNet::httpSetResponseHeaderMaxSize(int id, size_t headerSize) {
return Ok;
}
int OfflineNet::httpSetAuthInfoCallback(int id, SceHttpAuthInfoCallback cbfunc, void* userArg) {
return Ok;
}
int OfflineNet::httpSetAuthEnabled(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpGetAuthEnabled(int id, int* isEnable) {
return Ok;
}
int OfflineNet::httpAuthCacheFlush(int libhttpCtxId) {
return Ok;
}
int OfflineNet::httpSetRedirectCallback(int id, SceHttpRedirectCallback cbfunc, void* userArg) {
return Ok;
}
int OfflineNet::httpSetAutoRedirect(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpGetAutoRedirect(int id, int* isEnable) {
return Ok;
}
int OfflineNet::httpRedirectCacheFlush(int libhttpCtxId) {
return Ok;
}
int OfflineNet::httpSetResolveTimeOut(int id, uint32_t usec) {
return Ok;
}
int OfflineNet::httpSetResolveRetry(int id, int retry) {
return Ok;
}
int OfflineNet::httpSetConnectTimeOut(int id, uint32_t usec) {
return Ok;
}
int OfflineNet::httpSetSendTimeOut(int id, uint32_t usec) {
return Ok;
}
int OfflineNet::httpSetRecvTimeOut(int id, uint32_t usec) {
return Ok;
}
int OfflineNet::httpSetRequestStatusCallback(int id, SceHttpRequestStatusCallback cbfunc, void* userArg) {
return Ok;
}
int OfflineNet::httpGetLastErrno(int reqId, int* errNum) {
return Ok;
}
int OfflineNet::httpSetNonblock(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpGetNonblock(int id, int* isEnable) {
return Ok;
}
int OfflineNet::httpTrySetNonblock(int id, int isEnable) {
return Ok;
}
int OfflineNet::httpTryGetNonblock(int id, int* isEnable) {
return Ok;
}
int OfflineNet::httpCreateEpoll(int libhttpCtxId, SceHttpEpollHandle* eh) {
static int id = 0;
return ++id;
}
int OfflineNet::httpSetEpoll(int id, SceHttpEpollHandle eh, void* userArg) {
return Ok;
}
int OfflineNet::httpUnsetEpoll(int id) {
return Ok;
}
int OfflineNet::httpGetEpoll(int id, SceHttpEpollHandle* eh, void** userArg) {
return Ok;
}
int OfflineNet::httpDestroyEpoll(int libhttpCtxId, SceHttpEpollHandle eh) {
return Ok;
}
int OfflineNet::httpWaitRequest(SceHttpEpollHandle eh, SceHttpNBEvent* nbev, int maxevents, int timeout) {
return Ok;
}
int OfflineNet::httpAbortWaitRequest(SceHttpEpollHandle eh) {
return Ok;
}
// HTTPS
int OfflineNet::httpsLoadCert(int libhttpCtxId, int caCertNum, const SceSslData** caList, const SceSslData* cert, const SceSslData* privKey) {
return Ok;
}
int OfflineNet::httpsUnloadCert(int libhttpCtxId) {
return Ok;
}
int OfflineNet::httpsEnableOption(int id, uint32_t sslFlags) {
return Ok;
}
int OfflineNet::httpsDisableOption(int id, uint32_t sslFlags) {
return Ok;
}
int OfflineNet::httpsGetSslError(int id, int* errNum, uint32_t* detail) {
return Ok;
}
int OfflineNet::httpsSetSslCallback(int id, SceHttpsCallback cbfunc, void* userArg) {
return Ok;
}
int OfflineNet::httpsSetSslVersion(int id, SceSslVersion version) {
return Ok;
}

View File

@ -0,0 +1,122 @@
#pragma once
#include "../networking.h"
class OfflineNet: public INetworking {
public:
/* SceNetCtl facility*/
/**
* @brief gets information about network parameters
*
* @param code
* @param info
* @return int32_t
*/
int32_t netCtlGetInfo(int32_t code, SceNetCtlInfo* info) final;
int32_t netCtlGetState(int32_t* state) final;
/* SceNet facility */
/* Resolver sub-facility */
SceNetId resolverCreate(const char* name, int memid, int flags) final;
int32_t resolverStartNtoa(SceNetId rid, const char* hostname, SceNetInAddr_t* addr, int timeout, int retries, int flags) final;
int32_t resolverStartAton(SceNetId rid, const SceNetInAddr_t* addr, char* hostname, int len, int timeout, int retry, int flags) final;
int32_t resolverStartNtoaMultipleRecords(SceNetId rid, const char* hostname, SceNetResolverInfo* info, int timeout, int retries, int flags) final;
int32_t resolverGetError(SceNetId rid, int* result) final;
int32_t resolverDestroy(SceNetId rid) final;
int32_t resolverAbort(SceNetId rid, int flags) final;
/* Epoll sub-facility */
SceNetId epollCreate(const char* name, int flags) final;
int32_t epollControl(SceNetId eid, int op, SceNetId id, SceNetEpollEvent* event) final;
int32_t epollWait(SceNetId eid, SceNetEpollEvent* events, int maxevents, int timeout) final;
int32_t epollDestroy(SceNetId eid) final;
int32_t epollAbort(SceNetId eid, int flags) final;
/* Socket sub-facility */
SceNetId socketCreate(const char* name, int family, int type, int protocol) final;
SceNetId socketAccept(SceNetId s, SceNetSockaddr* addr, SceNetSocklen_t* addrlen) final;
int socketBind(SceNetId s, const SceNetSockaddr* addr, SceNetSocklen_t addrlen) final;
int socketConnect(SceNetId s, const SceNetSockaddr* name, SceNetSocklen_t namelen) final;
int socketGetpeername(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) final;
int socketGetsockname(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) final;
int socketGetsockopt(SceNetId s, int level, int optname, void* optval, SceNetSocklen_t* optlen) final;
int socketListen(SceNetId s, int backlog) final;
int socketRecv(SceNetId s, void* buf, size_t len, int flags) final;
int socketRecvfrom(SceNetId s, void* buf, size_t len, int flags, SceNetSockaddr* from, SceNetSocklen_t* fromlen) final;
int socketRecvmsg(SceNetId s, SceNetMsghdr* msg, int flags) final;
int socketSend(SceNetId s, const void* msg, size_t len, int flags) final;
int socketSendto(SceNetId s, const void* msg, size_t len, int flags, const SceNetSockaddr* to, SceNetSocklen_t tolen) final;
int socketSendmsg(SceNetId s, const SceNetMsghdr* msg, int flags) final;
int socketSetsockopt(SceNetId s, int level, int optname, const void* optval, SceNetSocklen_t optlen) final;
int socketShutdown(SceNetId s, int how) final;
int socketClose(SceNetId s) final;
int socketAbort(SceNetId s, int flags) final;
/* HTTP facility */
/* HTTP1 facility */
int httpInit(int libnetMemId, int libsslCtxId, size_t poolSize) final;
int httpTerm(int libhttpCtxId) final;
int httpGetMemoryPoolStats(int libhttpCtxId, SceHttpMemoryPoolStats* currentStat) final;
int httpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer, int isAutoProxyConf) final;
int httpDeleteTemplate(int tmplId) final;
int httpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive) final;
int httpCreateConnectionWithURL(int tmplId, const char* url, int isEnableKeepalive) final;
int httpDeleteConnection(int connId) final;
int httpCreateRequest(int connId, int method, const char* path, uint64_t contentLength) final;
int httpCreateRequest2(int connId, const char* method, const char* path, uint64_t contentLength) final;
int httpCreateRequestWithURL(int connId, int method, const char* url, uint64_t contentLength) final;
int httpCreateRequestWithURL2(int connId, const char* method, const char* url, uint64_t contentLength) final;
int httpDeleteRequest(int reqId) final;
int httpSetRequestContentLength(int id, uint64_t contentLength) final;
int httpSetChunkedTransferEnabled(int id, int isEnable) final;
int httpSetInflateGZIPEnabled(int id, int isEnable) final;
int httpSendRequest(int reqId, const void* postData, size_t size) final;
int httpAbortRequest(int reqId) final;
int httpGetResponseContentLength(int reqId, int* result, uint64_t* contentLength) final;
int httpGetStatusCode(int reqId, int* statusCode) final;
int httpGetAllResponseHeaders(int reqId, char** header, size_t* headerSize) final;
int httpReadData(int reqId, void* data, size_t size) final;
int httpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) final;
int httpRemoveRequestHeader(int id, const char* name) final;
int httpParseResponseHeader(const char* header, size_t headerLen, const char* fieldStr, const char** fieldValue, size_t* valueLen) final;
int httpParseStatusLine(const char* statusLine, size_t lineLen, int* httpMajorVer, int* httpMinorVer, int* responseCode, const char** reasonPhrase,
size_t* phraseLen) final;
int httpSetResponseHeaderMaxSize(int id, size_t headerSize) final;
int httpSetAuthInfoCallback(int id, SceHttpAuthInfoCallback cbfunc, void* userArg) final;
int httpSetAuthEnabled(int id, int isEnable) final;
int httpGetAuthEnabled(int id, int* isEnable) final;
int httpAuthCacheFlush(int libhttpCtxId) final;
int httpSetRedirectCallback(int id, SceHttpRedirectCallback cbfunc, void* userArg) final;
int httpSetAutoRedirect(int id, int isEnable) final;
int httpGetAutoRedirect(int id, int* isEnable) final;
int httpRedirectCacheFlush(int libhttpCtxId) final;
int httpSetResolveTimeOut(int id, uint32_t usec) final;
int httpSetResolveRetry(int id, int retry) final;
int httpSetConnectTimeOut(int id, uint32_t usec) final;
int httpSetSendTimeOut(int id, uint32_t usec) final;
int httpSetRecvTimeOut(int id, uint32_t usec) final;
int httpSetRequestStatusCallback(int id, SceHttpRequestStatusCallback cbfunc, void* userArg) final;
int httpGetLastErrno(int reqId, int* errNum) final;
int httpSetNonblock(int id, int isEnable) final;
int httpGetNonblock(int id, int* isEnable) final;
int httpTrySetNonblock(int id, int isEnable) final;
int httpTryGetNonblock(int id, int* isEnable) final;
int httpCreateEpoll(int libhttpCtxId, SceHttpEpollHandle* eh) final;
int httpSetEpoll(int id, SceHttpEpollHandle eh, void* userArg) final;
int httpUnsetEpoll(int id) final;
int httpGetEpoll(int id, SceHttpEpollHandle* eh, void** userArg) final;
int httpDestroyEpoll(int libhttpCtxId, SceHttpEpollHandle eh) final;
int httpWaitRequest(SceHttpEpollHandle eh, SceHttpNBEvent* nbev, int maxevents, int timeout) final;
int httpAbortWaitRequest(SceHttpEpollHandle eh) final;
// HTTPS
int httpsLoadCert(int libhttpCtxId, int caCertNum, const SceSslData** caList, const SceSslData* cert, const SceSslData* privKey) final;
int httpsUnloadCert(int libhttpCtxId) final;
int httpsEnableOption(int id, uint32_t sslFlags) final;
int httpsDisableOption(int id, uint32_t sslFlags) final;
int httpsGetSslError(int id, int* errNum, uint32_t* detail) final;
int httpsSetSslCallback(int id, SceHttpsCallback cbfunc, void* userArg) final;
int httpsSetSslVersion(int id, SceSslVersion version) final;
};

View File

@ -0,0 +1,126 @@
#pragma once
#include "../networking.h"
#include <WS2tcpip.h>
#include <WinSock2.h>
#include <Windows.h>
class OnlineNet: public INetworking {
public:
/* SceNetCtl facility*/
/**
* @brief gets information about network parameters
*
* @param code
* @param info
* @return int32_t
*/
int32_t netCtlGetInfo(int32_t code, SceNetCtlInfo* info) final;
int32_t netCtlGetState(int32_t* state) final;
/* SceNet facility */
/* Resolver sub-facility */
SceNetId resolverCreate(const char* name, int memid, int flags) final;
int32_t resolverStartNtoa(SceNetId rid, const char* hostname, SceNetInAddr_t* addr, int timeout, int retries, int flags) final;
int32_t resolverStartAton(SceNetId rid, const SceNetInAddr_t* addr, char* hostname, int len, int timeout, int retry, int flags) final;
int32_t resolverStartNtoaMultipleRecords(SceNetId rid, const char* hostname, SceNetResolverInfo* info, int timeout, int retries, int flags) final;
int32_t resolverGetError(SceNetId rid, int* result) final;
int32_t resolverDestroy(SceNetId rid) final;
int32_t resolverAbort(SceNetId rid, int flags) final;
/* Epoll sub-facility */
SceNetId epollCreate(const char* name, int flags) final;
int32_t epollControl(SceNetId eid, int op, SceNetId id, SceNetEpollEvent* event) final;
int32_t epollWait(SceNetId eid, SceNetEpollEvent* events, int maxevents, int timeout) final;
int32_t epollDestroy(SceNetId eid) final;
int32_t epollAbort(SceNetId eid, int flags) final;
/* Socket sub-facility */
SceNetId socketCreate(const char* name, int family, int type, int protocol) final;
SceNetId socketAccept(SceNetId s, SceNetSockaddr* addr, SceNetSocklen_t* addrlen) final;
int socketBind(SceNetId s, const SceNetSockaddr* addr, SceNetSocklen_t addrlen) final;
int socketConnect(SceNetId s, const SceNetSockaddr* name, SceNetSocklen_t namelen) final;
int socketGetpeername(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) final;
int socketGetsockname(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) final;
int socketGetsockopt(SceNetId s, int level, int optname, void* optval, SceNetSocklen_t* optlen) final;
int socketListen(SceNetId s, int backlog) final;
int socketRecv(SceNetId s, void* buf, size_t len, int flags) final;
int socketRecvfrom(SceNetId s, void* buf, size_t len, int flags, SceNetSockaddr* from, SceNetSocklen_t* fromlen) final;
int socketRecvmsg(SceNetId s, SceNetMsghdr* msg, int flags) final;
int socketSend(SceNetId s, const void* msg, size_t len, int flags) final;
int socketSendto(SceNetId s, const void* msg, size_t len, int flags, const SceNetSockaddr* to, SceNetSocklen_t tolen) final;
int socketSendmsg(SceNetId s, const SceNetMsghdr* msg, int flags) final;
int socketSetsockopt(SceNetId s, int level, int optname, const void* optval, SceNetSocklen_t optlen) final;
int socketShutdown(SceNetId s, int how) final;
int socketClose(SceNetId s) final;
int socketAbort(SceNetId s, int flags) final;
/* HTTP facility */
/* HTTP1 facility */
int httpInit(int libnetMemId, int libsslCtxId, size_t poolSize) final;
int httpTerm(int libhttpCtxId) final;
int httpGetMemoryPoolStats(int libhttpCtxId, SceHttpMemoryPoolStats* currentStat) final;
int httpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer, int isAutoProxyConf) final;
int httpDeleteTemplate(int tmplId) final;
int httpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive) final;
int httpCreateConnectionWithURL(int tmplId, const char* url, int isEnableKeepalive) final;
int httpDeleteConnection(int connId) final;
int httpCreateRequest(int connId, int method, const char* path, uint64_t contentLength) final;
int httpCreateRequest2(int connId, const char* method, const char* path, uint64_t contentLength) final;
int httpCreateRequestWithURL(int connId, int method, const char* url, uint64_t contentLength) final;
int httpCreateRequestWithURL2(int connId, const char* method, const char* url, uint64_t contentLength) final;
int httpDeleteRequest(int reqId) final;
int httpSetRequestContentLength(int id, uint64_t contentLength) final;
int httpSetChunkedTransferEnabled(int id, int isEnable) final;
int httpSetInflateGZIPEnabled(int id, int isEnable) final;
int httpSendRequest(int reqId, const void* postData, size_t size) final;
int httpAbortRequest(int reqId) final;
int httpGetResponseContentLength(int reqId, int* result, uint64_t* contentLength) final;
int httpGetStatusCode(int reqId, int* statusCode) final;
int httpGetAllResponseHeaders(int reqId, char** header, size_t* headerSize) final;
int httpReadData(int reqId, void* data, size_t size) final;
int httpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) final;
int httpRemoveRequestHeader(int id, const char* name) final;
int httpParseResponseHeader(const char* header, size_t headerLen, const char* fieldStr, const char** fieldValue, size_t* valueLen) final;
int httpParseStatusLine(const char* statusLine, size_t lineLen, int* httpMajorVer, int* httpMinorVer, int* responseCode, const char** reasonPhrase,
size_t* phraseLen) final;
int httpSetResponseHeaderMaxSize(int id, size_t headerSize) final;
int httpSetAuthInfoCallback(int id, SceHttpAuthInfoCallback cbfunc, void* userArg) final;
int httpSetAuthEnabled(int id, int isEnable) final;
int httpGetAuthEnabled(int id, int* isEnable) final;
int httpAuthCacheFlush(int libhttpCtxId) final;
int httpSetRedirectCallback(int id, SceHttpRedirectCallback cbfunc, void* userArg) final;
int httpSetAutoRedirect(int id, int isEnable) final;
int httpGetAutoRedirect(int id, int* isEnable) final;
int httpRedirectCacheFlush(int libhttpCtxId) final;
int httpSetResolveTimeOut(int id, uint32_t usec) final;
int httpSetResolveRetry(int id, int retry) final;
int httpSetConnectTimeOut(int id, uint32_t usec) final;
int httpSetSendTimeOut(int id, uint32_t usec) final;
int httpSetRecvTimeOut(int id, uint32_t usec) final;
int httpSetRequestStatusCallback(int id, SceHttpRequestStatusCallback cbfunc, void* userArg) final;
int httpGetLastErrno(int reqId, int* errNum) final;
int httpSetNonblock(int id, int isEnable) final;
int httpGetNonblock(int id, int* isEnable) final;
int httpTrySetNonblock(int id, int isEnable) final;
int httpTryGetNonblock(int id, int* isEnable) final;
int httpCreateEpoll(int libhttpCtxId, SceHttpEpollHandle* eh) final;
int httpSetEpoll(int id, SceHttpEpollHandle eh, void* userArg) final;
int httpUnsetEpoll(int id) final;
int httpGetEpoll(int id, SceHttpEpollHandle* eh, void** userArg) final;
int httpDestroyEpoll(int libhttpCtxId, SceHttpEpollHandle eh) final;
int httpWaitRequest(SceHttpEpollHandle eh, SceHttpNBEvent* nbev, int maxevents, int timeout) final;
int httpAbortWaitRequest(SceHttpEpollHandle eh) final;
// HTTPS
int httpsLoadCert(int libhttpCtxId, int caCertNum, const SceSslData** caList, const SceSslData* cert, const SceSslData* privKey) final;
int httpsUnloadCert(int libhttpCtxId) final;
int httpsEnableOption(int id, uint32_t sslFlags) final;
int httpsDisableOption(int id, uint32_t sslFlags) final;
int httpsGetSslError(int id, int* errNum, uint32_t* detail) final;
int httpsSetSslCallback(int id, SceHttpsCallback cbfunc, void* userArg) final;
int httpsSetSslVersion(int id, SceSslVersion version) final;
};

View File

@ -0,0 +1,97 @@
#include "../online.h"
#include "logging.h"
#include <WinSock2.h>
#include <unordered_map>
#include <wepoll.h>
LOG_DEFINE_MODULE(OnlineNetCore_epoll);
namespace {
constexpr int SCE_NET_EPOLLIN = 0x00000001;
constexpr int SCE_NET_EPOLLOUT = 0x00000002;
constexpr int SCE_NET_EPOLLERR = 0x00000008;
constexpr int SCE_NET_EPOLLHUP = 0x00000010;
constexpr int SCE_NET_EPOLLDESCID = 0x00010000;
constexpr int SCE_NET_EPOLL_CTL_ADD = 1;
constexpr int SCE_NET_EPOLL_CTL_MOD = 2;
constexpr int SCE_NET_EPOLL_CTL_DEL = 3;
constexpr int SCE_NET_EPOLL_ABORT_FLAG_PRESERVATION = 0x00000001;
#define EPOLLDESCID (1u << 14)
static inline uint32_t sce_map_epoll_events(uint32_t events) {
LOG_USE_MODULE(OnlineNetCore_epoll);
uint32_t _events = 0;
if (events & SCE_NET_EPOLLIN) _events |= EPOLLIN;
if (events & SCE_NET_EPOLLOUT) _events |= EPOLLOUT;
if (events & SCE_NET_EPOLLERR) _events |= EPOLLERR;
if (events & SCE_NET_EPOLLHUP) _events |= EPOLLHUP;
if (events & SCE_NET_EPOLLDESCID) _events |= EPOLLDESCID; // Not an actual event, todo
return _events;
}
// Probably the safest way, since sizeof(SceNetId) < sizeof(HANDLE)
// We cannot guarantee that this HANDLE value will not exceed int32_t
static std::unordered_map<SceNetId, HANDLE> map;
static HANDLE sce_map_id_get(SceNetId id) {
if (map.find(id) != map.end()) return map[id];
return INVALID_HANDLE_VALUE;
}
static HANDLE sce_map_id_del(SceNetId id) {
auto it = map.find(id);
if (it != map.end()) {
auto value = map[id];
map.erase(it);
return value;
}
return INVALID_HANDLE_VALUE;
}
} // namespace
SceNetId OnlineNet::epollCreate(const char* name, int flags) {
static SceNetId id = 0;
HANDLE ep;
if ((ep = epoll_create1(flags)) != NULL && ep != INVALID_HANDLE_VALUE) {
map[++id] = ep;
return id;
}
return INetworking::getLastError();
}
int OnlineNet::epollControl(SceNetId eid, int op, SceNetId id, SceNetEpollEvent* event) {
epoll_event ev {
.events = sce_map_epoll_events(event->events),
.data = {.ptr = event->data.ptr},
};
if (ev.events & EPOLLDESCID) return Ok; // todo handle all events
if (epoll_ctl(sce_map_id_get(eid), op, id, &ev) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::epollWait(SceNetId eid, SceNetEpollEvent* events, int maxevents, int timeout) {
epoll_event ev {
.events = sce_map_epoll_events(events->events),
.data = {.ptr = events->data.ptr},
};
if (ev.events & EPOLLDESCID) return Ok; // todo handle all events
if (epoll_wait(sce_map_id_get(eid), &ev, maxevents, timeout) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::epollDestroy(SceNetId eid) {
if (epoll_close(sce_map_id_del(eid)) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::epollAbort(SceNetId eid, int flags) {
return Ok;
}

View File

@ -0,0 +1,719 @@
#include "../online.h"
#include "logging.h"
#include <Windows.h>
#include <boost/asio.hpp>
#include <boost/url.hpp>
LOG_DEFINE_MODULE(OnlineNetCore_http);
using namespace boost::asio;
namespace {
constexpr size_t ARRAY_LENGTH = 128;
template <typename T, size_t size>
class HttpContainer {
T* m_data[size] = {nullptr};
public:
int lookupAvailableIdx(T* action) {
int i;
bool found = false;
for (i = 1; i < size; i++) {
if (!m_data[i]) {
m_data[i] = action;
break;
}
}
if (found == false) {
delete action;
return 0;
}
return i;
}
int testId(int id) {
if (id < 1 || id >= size || m_data[id] == nullptr) return Err::HTTP_BAD_ID;
return Ok;
}
void remove(int id) {
delete m_data[id];
m_data[id] = nullptr;
}
T* getId(int id) { return m_data[id]; }
};
static io_service svc;
static ip::tcp::resolver resolver(svc);
struct HttpClient {
int libnetMemId;
int libsslCtxId;
size_t poolSize;
uint32_t connectTimeout;
HttpClient(int libnetMemId, int libsslCtxId, size_t poolSize): libnetMemId(libnetMemId), libsslCtxId(libsslCtxId), poolSize(poolSize) {}
};
struct HttpTemplate {
HttpClient* parentClient;
const char* userAgent;
int httpVer;
int isAutoProxyConf;
HttpTemplate(HttpClient* parentClient, const char* userAgent, int httpVer, int isAutoProxyConf)
: parentClient(parentClient), userAgent(userAgent), httpVer(httpVer), isAutoProxyConf(isAutoProxyConf) {}
};
struct HttpConnection {
HttpTemplate* parentTemplate;
const char* serverName;
const char* scheme;
uint16_t port;
int isEnableKeepalive;
bool connected = false;
bool shouldFreeStrings;
ip::tcp::resolver::query* query = nullptr;
ip::tcp::resolver::iterator endpoint_iterator;
ip::tcp::socket* socket = nullptr;
HttpConnection(HttpTemplate* parentTemplate, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive, bool shouldFreeStrings)
: parentTemplate(parentTemplate),
serverName(serverName),
scheme(scheme),
port(port),
isEnableKeepalive(isEnableKeepalive),
shouldFreeStrings(shouldFreeStrings) {}
};
struct HttpResponse {
int statusCode;
uint32_t contentLength;
const char* body = nullptr;
};
struct HttpRequest {
HttpConnection* parentConnection;
int method;
const char* path;
uint64_t contentLength;
const void* postData = nullptr;
size_t size;
HttpResponse* lastResponse = nullptr;
bool shouldFreePath;
HttpRequest(HttpConnection* parentConnection, int method, const char* path, uint64_t contentLength, bool shouldFreePath)
: parentConnection(parentConnection), method(method), path(path), contentLength(contentLength), shouldFreePath(shouldFreePath) {}
};
struct HttpRequestParams {
HttpConnection* connection;
HttpRequest* request;
uint32_t connectTimeout;
const char* userAgent;
int httpVer;
int isAutoProxyConf;
const char* serverName;
const char* scheme;
uint16_t port;
int isEnableKeepalive;
int method;
const char* path;
uint64_t contentLength;
const void* postData; // will be assigned to nullptr
size_t size;
HttpRequestParams(HttpRequest* from) {
connection = from->parentConnection;
request = from;
connectTimeout = from->parentConnection->parentTemplate->parentClient->connectTimeout;
userAgent = from->parentConnection->parentTemplate->userAgent;
httpVer = from->parentConnection->parentTemplate->httpVer;
isAutoProxyConf = from->parentConnection->parentTemplate->isAutoProxyConf;
serverName = from->parentConnection->serverName;
scheme = from->parentConnection->scheme;
port = from->parentConnection->port;
isEnableKeepalive = from->parentConnection->isEnableKeepalive;
method = from->method;
path = from->path;
contentLength = from->contentLength;
postData = from->postData;
size = from->size;
}
};
static HttpContainer<HttpClient, ARRAY_LENGTH> g_client;
static HttpContainer<HttpTemplate, ARRAY_LENGTH> g_template;
static HttpContainer<HttpConnection, ARRAY_LENGTH> g_connection;
static HttpContainer<HttpRequest, ARRAY_LENGTH> g_request;
#define GET_LAST_RESPONSE \
HttpRequest* request = g_request.getId(reqId); \
HttpResponse* response = request->lastResponse; \
if (response == nullptr) return Err::HTTP_SEND_REQUIRED;
static void deleteResponse(HttpResponse* response) {
if (response->body != nullptr) {
delete response->body;
}
delete response;
}
static int httpMethodStringToInt(const char* method) {
if (::_stricmp(method, "get") == 0) {
return SCE_HTTP_GET;
} else if (::_stricmp(method, "post") == 0) {
return SCE_HTTP_POST;
}
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"unsupported http method: %S", method);
return -1;
}
static int32_t performHttpRequest(HttpRequestParams* request, HttpResponse* response) {
LOG_USE_MODULE(OnlineNetCore_http);
HttpConnection* connection = request->connection;
try {
if (!connection->connected) {
connection->query = new ip::tcp::resolver::query(request->serverName, std::to_string(request->port));
connection->endpoint_iterator = resolver.resolve(*connection->query);
connection->socket = new ip::tcp::socket(svc);
boost::asio::connect(*connection->socket, connection->endpoint_iterator);
connection->connected = true;
if (std::strcmp(connection->scheme, "https") == 0) {
LOG_TRACE(L"detected an attempt to connect via https, it's not supported yet");
return Err::HTTP_SSL_ERROR;
}
if (std::strcmp(connection->scheme, "http") != 0) {
LOG_TRACE(L"unknown scheme: %S://", connection->scheme);
return Err::HTTP_BAD_SCHEME;
}
}
if (request->request->lastResponse != nullptr) {
deleteResponse(request->request->lastResponse);
}
boost::asio::streambuf buffer;
std::ostream bufferStream(&buffer);
if (request->method == SCE_HTTP_GET) {
bufferStream << "GET ";
} else if (request->method == SCE_HTTP_POST) {
bufferStream << "POST ";
} else {
LOG_TRACE(L"unsupported request method code (%d), pretending we've failed to connect", request->method);
return Err::HTTP_FAILURE;
}
bufferStream << request->path;
if (request->httpVer == 1)
bufferStream << "HTTP/1.0";
else
bufferStream << "HTTP/1.1";
bufferStream << "\r\n";
if (request->httpVer != 1 /* means HTTP 1.1 */) {
bufferStream << "Host: " << request->serverName << "\r\n";
bufferStream << "User-Agent: " << request->userAgent << "\r\n";
bufferStream << "Accept: */*\r\n";
bufferStream << "Connection: " << (request->isEnableKeepalive ? "keep-alive" : "close") << "\r\n";
}
if (request->postData != nullptr) {
bufferStream << "Content-Length: " << request->contentLength << "\r\n\r\n";
for (size_t i = 0; i < request->size; i++) {
bufferStream << ((char*)request->postData)[i];
}
}
boost::asio::write(*connection->socket, buffer);
boost::asio::streambuf responseBuffer;
boost::asio::read_until(*connection->socket, responseBuffer, "\r\n");
std::istream responseBufferStream(&responseBuffer);
std::string httpVersion;
int statusCode;
std::string statusDescription;
responseBufferStream >> httpVersion;
responseBufferStream >> statusCode;
std::getline(responseBufferStream, statusDescription);
boost::asio::read_until(*connection->socket, responseBuffer, "\r\n\r\n");
bool foundContentLengthHeader = false;
uint32_t contentLength;
std::string header;
while (std::getline(responseBufferStream, header) && header != "\r") {
if (header.rfind("Content-Length: ", 0) == 0) { // todo: remove case-sensitive check
std::string contentLengthUnparsed = header.substr(16);
contentLength = std::stoi(contentLengthUnparsed);
foundContentLengthHeader = true;
break;
}
}
if (!foundContentLengthHeader) {
LOG_TRACE(L"failed to find \"Content-Length\" header in the response");
return Err::HTTP_FAILURE;
}
bool tooLow;
if ((tooLow = contentLength < 1) || contentLength > 1610612736) {
LOG_TRACE(L"bad content length: %S", (tooLow ? "less than 1 byte" : "more than 1.5 GiB"));
return Err::HTTP_FAILURE;
}
size_t currentIdx = 0;
char* body;
try {
body = new char[contentLength];
} catch (std::bad_alloc&) {
LOG_TRACE(L"failed to allocate %d bytes", contentLength);
return Err::HTTP_FAILURE;
}
response->statusCode = statusCode;
response->contentLength = contentLength;
response->body = body;
boost::system::error_code error;
while (boost::asio::read(*connection->socket, responseBuffer, boost::asio::transfer_at_least(1), error)) {
std::streamsize available = responseBuffer.in_avail();
char c;
for (std::streamsize i = 0; i < available; i++) {
responseBufferStream >> c;
body[currentIdx++] = c;
}
}
if (error != boost::asio::error::eof) throw boost::system::system_error(error);
return Ok;
} catch (const boost::exception& e) {
LOG_TRACE(L"caught a boost exception while performing an http request");
return Err::HTTP_FAILURE;
} catch (const std::exception& e) {
LOG_TRACE(L"caught an std::exception while performing an http request");
return Err::HTTP_FAILURE;
}
}
} // namespace
int OnlineNet::httpInit(int libnetMemId, int libsslCtxId, size_t poolSize) {
auto const handle = g_client.lookupAvailableIdx(new HttpClient(libnetMemId, libsslCtxId, poolSize));
if (handle == 0) return Err::HTTP_OUT_OF_MEMORY;
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"new http client (id: %d, memId: %d, sslCtxId: %d, poolSize: %d)", handle, libnetMemId, libsslCtxId, poolSize);
return handle;
}
int OnlineNet::httpTerm(int libhttpCtxId) {
if (auto ret = g_client.testId(libhttpCtxId)) return ret;
g_client.remove(libhttpCtxId);
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"http client removed (id: %d)", libhttpCtxId);
return Ok;
}
int OnlineNet::httpGetMemoryPoolStats(int libhttpCtxId, SceHttpMemoryPoolStats* currentStat) {
if (auto ret = g_client.testId(libhttpCtxId)) return ret;
currentStat->currentInuseSize = 16384; // todo (?)
currentStat->maxInuseSize = 131072;
currentStat->poolSize = g_client.getId(libhttpCtxId)->poolSize;
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"memory pool stats were requested (client id: %d)", libhttpCtxId);
return Ok;
}
int OnlineNet::httpCreateTemplate(int libhttpCtxId, const char* userAgent, int httpVer, int isAutoProxyConf) {
if (auto ret = g_client.testId(libhttpCtxId)) return ret;
HttpClient* client = g_client.getId(libhttpCtxId);
auto const handle = g_template.lookupAvailableIdx(new HttpTemplate(client, userAgent, httpVer, isAutoProxyConf));
if (handle == 0) return Err::HTTP_OUT_OF_MEMORY;
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"new http template (id: %d, client id: %d)", handle, libhttpCtxId);
return handle;
}
int OnlineNet::httpDeleteTemplate(int tmplId) {
if (auto ret = g_template.testId(tmplId)) return ret;
g_template.remove(tmplId);
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"http template removed (id: %d)", tmplId);
return Ok;
}
static int sceHttpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive, bool shouldFreeStrings) {
if (auto ret = g_template.testId(tmplId)) return ret;
HttpTemplate* httpTemplate = g_template.getId(tmplId);
auto const handle = g_connection.lookupAvailableIdx(new HttpConnection(httpTemplate, serverName, scheme, port, isEnableKeepalive, shouldFreeStrings));
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"new http connection (id: %d, template id: %d)", handle, tmplId);
return handle;
}
int OnlineNet::httpCreateConnection(int tmplId, const char* serverName, const char* scheme, uint16_t port, int isEnableKeepalive) {
return sceHttpCreateConnection(tmplId, serverName, scheme, port, isEnableKeepalive, false);
}
int OnlineNet::httpCreateConnectionWithURL(int tmplId, const char* url, int isEnableKeepalive) {
boost::urls::url link(url);
uint16_t port;
bool isNotHttp;
if ((isNotHttp = std::strcmp(link.scheme().data(), "http") != 0) && std::strcmp(link.scheme().data(), "https") != 0) {
return Err::HTTP_BAD_SCHEME;
}
if (link.has_port()) {
port = link.port_number();
} else {
if (isNotHttp)
port = 443;
else
port = 80;
}
int result = sceHttpCreateConnection(tmplId, link.host().data(), link.scheme().data(), port, isEnableKeepalive, true);
return result;
}
int OnlineNet::httpDeleteConnection(int connId) {
if (auto ret = g_connection.testId(connId)) return ret;
HttpConnection* connection = g_connection.getId(connId);
if (connection->query != nullptr) {
delete connection->query;
}
if (connection->socket != nullptr) {
connection->socket->close();
delete connection->socket;
}
if (connection->shouldFreeStrings) {
delete connection->scheme;
delete connection->serverName;
}
g_connection.remove(connId);
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"http connection removed (id: %d)", connId);
return Ok;
}
static int sceHttpCreateRequest(int connId, int method, const char* path, uint64_t contentLength, bool shouldFreePath) {
if (auto ret = g_connection.testId(connId)) return ret;
HttpConnection* httpConnection = g_connection.getId(connId);
auto handle = g_request.lookupAvailableIdx(new HttpRequest(httpConnection, method, path, contentLength, shouldFreePath));
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"new http request (id: %d, connection id: %d)", handle, connId);
return Ok;
}
int OnlineNet::httpCreateRequest(int connId, int method, const char* path, uint64_t contentLength) {
return sceHttpCreateRequest(connId, method, path, contentLength, false);
}
int OnlineNet::httpCreateRequest2(int connId, const char* method, const char* path, uint64_t contentLength) {
return httpCreateRequest(connId, httpMethodStringToInt(method), path, contentLength);
}
int OnlineNet::httpCreateRequestWithURL(int connId, int method, const char* url, uint64_t contentLength) {
boost::urls::url link(url);
if (link.has_password()) {
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"urls containing credentials are not supported");
return Err::HTTP_BAD_PARAM;
}
int result = sceHttpCreateRequest(connId, method, link.path().data(), contentLength, true);
return result;
}
int OnlineNet::httpCreateRequestWithURL2(int connId, const char* method, const char* url, uint64_t contentLength) {
return httpCreateRequestWithURL(connId, httpMethodStringToInt(method), url, contentLength);
}
int OnlineNet::httpDeleteRequest(int reqId) {
if (auto ret = g_request.testId(reqId)) return ret;
HttpRequest* request = g_request.getId(reqId);
if (request->lastResponse != nullptr) {
deleteResponse(request->lastResponse);
}
if (request->shouldFreePath) {
delete request->path;
}
g_request.remove(reqId);
LOG_USE_MODULE(OnlineNetCore_http);
LOG_TRACE(L"http request removed (id: %d)", reqId);
return Ok;
}
int OnlineNet::httpSetRequestContentLength(int id, uint64_t contentLength) {
int reqId = id; // (?)
if (auto ret = g_request.testId(reqId)) return ret;
g_request.getId(reqId)->contentLength = contentLength;
return Ok;
}
int OnlineNet::httpSetChunkedTransferEnabled(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpSetInflateGZIPEnabled(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpSendRequest(int reqId, const void* postData, size_t size) {
if (auto ret = g_request.testId(reqId)) return ret;
HttpRequest* request = g_request.getId(reqId);
request->postData = postData;
request->size = size;
HttpRequestParams* fullParams = new HttpRequestParams(request);
HttpResponse* response = new HttpResponse();
int32_t result = performHttpRequest(fullParams, response);
delete fullParams;
if (result == Ok) {
request->lastResponse = response;
} else {
deleteResponse(response);
}
return result;
}
int OnlineNet::httpAbortRequest(int reqId) {
return Ok;
}
int OnlineNet::httpGetResponseContentLength(int reqId, int* result, uint64_t* contentLength) {
if (auto ret = g_request.testId(reqId)) return ret;
GET_LAST_RESPONSE;
*result = 0; // Content-Length is guaranteed to exist, otherwise performHttpRequest would have failed
*contentLength = response->contentLength;
return Ok;
}
int OnlineNet::httpGetStatusCode(int reqId, int* statusCode) {
if (auto ret = g_request.testId(reqId)) return ret;
GET_LAST_RESPONSE;
*statusCode = response->statusCode;
return Ok;
}
int OnlineNet::httpGetAllResponseHeaders(int reqId, char** header, size_t* headerSize) {
*headerSize = 0;
return Ok;
}
int OnlineNet::httpReadData(int reqId, void* data, size_t size) {
if (auto ret = g_request.testId(reqId)) return ret;
GET_LAST_RESPONSE;
size_t finalSize = min(size, response->contentLength);
memcpy(data, response->body, finalSize);
return Ok;
}
int OnlineNet::httpAddRequestHeader(int id, const char* name, const char* value, uint32_t mode) {
return Ok;
}
int OnlineNet::httpRemoveRequestHeader(int id, const char* name) {
return Ok;
}
int OnlineNet::httpParseResponseHeader(const char* header, size_t headerLen, const char* fieldStr, const char** fieldValue, size_t* valueLen) {
return Ok;
}
int OnlineNet::httpParseStatusLine(const char* statusLine, size_t lineLen, int* httpMajorVer, int* httpMinorVer, int* responseCode, const char** reasonPhrase,
size_t* phraseLen) {
return Ok;
}
int OnlineNet::httpSetResponseHeaderMaxSize(int id, size_t headerSize) {
return Ok;
}
int OnlineNet::httpSetAuthInfoCallback(int id, SceHttpAuthInfoCallback cbfunc, void* userArg) {
return Ok;
}
int OnlineNet::httpSetAuthEnabled(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpGetAuthEnabled(int id, int* isEnable) {
return Ok;
}
int OnlineNet::httpAuthCacheFlush(int libhttpCtxId) {
return Ok;
}
int OnlineNet::httpSetRedirectCallback(int id, SceHttpRedirectCallback cbfunc, void* userArg) {
return Ok;
}
int OnlineNet::httpSetAutoRedirect(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpGetAutoRedirect(int id, int* isEnable) {
return Ok;
}
int OnlineNet::httpRedirectCacheFlush(int libhttpCtxId) {
return Ok;
}
int OnlineNet::httpSetResolveTimeOut(int id, uint32_t usec) {
return Ok;
}
int OnlineNet::httpSetResolveRetry(int id, int retry) {
return Ok;
}
int OnlineNet::httpSetConnectTimeOut(int id, uint32_t usec) {
return Ok;
}
int OnlineNet::httpSetSendTimeOut(int id, uint32_t usec) {
return Err::SEND_TIMEOUT;
}
int OnlineNet::httpSetRecvTimeOut(int id, uint32_t usec) {
return Ok;
}
int OnlineNet::httpSetRequestStatusCallback(int id, SceHttpRequestStatusCallback cbfunc, void* userArg) {
return Ok;
}
int OnlineNet::httpGetLastErrno(int reqId, int* errNum) {
return Ok;
}
int OnlineNet::httpSetNonblock(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpGetNonblock(int id, int* isEnable) {
return Ok;
}
int OnlineNet::httpTrySetNonblock(int id, int isEnable) {
return Ok;
}
int OnlineNet::httpTryGetNonblock(int id, int* isEnable) {
return Ok;
}
int OnlineNet::httpCreateEpoll(int libhttpCtxId, SceHttpEpollHandle* eh) {
return Ok;
}
int OnlineNet::httpSetEpoll(int id, SceHttpEpollHandle eh, void* userArg) {
return Ok;
}
int OnlineNet::httpUnsetEpoll(int id) {
return Ok;
}
int OnlineNet::httpGetEpoll(int id, SceHttpEpollHandle* eh, void** userArg) {
return Ok;
}
int OnlineNet::httpDestroyEpoll(int libhttpCtxId, SceHttpEpollHandle eh) {
return Ok;
}
int OnlineNet::httpWaitRequest(SceHttpEpollHandle eh, SceHttpNBEvent* nbev, int maxevents, int timeout) {
return Ok;
}
int OnlineNet::httpAbortWaitRequest(SceHttpEpollHandle eh) {
return Ok;
}
// HTTPS
int OnlineNet::httpsLoadCert(int libhttpCtxId, int caCertNum, const SceSslData** caList, const SceSslData* cert, const SceSslData* privKey) {
return Ok;
}
int OnlineNet::httpsUnloadCert(int libhttpCtxId) {
return Ok;
}
int OnlineNet::httpsEnableOption(int id, uint32_t sslFlags) {
return Ok;
}
int OnlineNet::httpsDisableOption(int id, uint32_t sslFlags) {
return Ok;
}
int OnlineNet::httpsGetSslError(int id, int* errNum, uint32_t* detail) {
return Ok;
}
int OnlineNet::httpsSetSslCallback(int id, SceHttpsCallback cbfunc, void* userArg) {
return Ok;
}
int OnlineNet::httpsSetSslVersion(int id, SceSslVersion version) {
return Ok;
}

View File

@ -0,0 +1,196 @@
#include "../online.h"
#include "config_emu.h"
#include "logging.h"
#include <iphlpapi.h>
LOG_DEFINE_MODULE(OnlineNetCore);
int32_t OnlineNet::netCtlGetInfo(int32_t code, SceNetCtlInfo* info) {
LOG_USE_MODULE(OnlineNetCore);
PIP_ADAPTER_INFO pAda = nullptr; // Adapters head
PIP_ADAPTER_INFO pCurrAda = nullptr; // Used adapter
ULONG uAdaBufLen;
PIP_ADAPTER_ADDRESSES pAddr = nullptr; // Addresses head
PIP_ADAPTER_ADDRESSES pCurrAddr = nullptr; // Used adapter addr
ULONG uAddrBufLen;
auto initAdapterInfo = [&pAda, &pCurrAda, &uAdaBufLen]() {
LOG_USE_MODULE(OnlineNetCore);
pAda = nullptr;
uAdaBufLen = 0;
if (GetAdaptersInfo(nullptr, &uAdaBufLen) == ERROR_BUFFER_OVERFLOW) { // Obtaining buffer length
pAda = (PIP_ADAPTER_INFO)malloc(uAdaBufLen);
} else {
LOG_ERR(L"Failed to obtain size needed for GetAdaptersInfo buffer!");
return Err::Net::ERROR_EFAULT;
}
if (auto ret = GetAdaptersInfo(pAda, &uAdaBufLen)) {
free(pAda);
pAda = nullptr;
LOG_ERR(L"GetAdaptersInfo failed: %x", ret);
return Err::Net::ERROR_EFAULT;
}
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::GENERAL);
std::string macAddr;
if (!getJsonParam(jData, "netMAC", macAddr)) {
macAddr = "00:00:00:00:00:00";
}
pCurrAda = pAda;
if (macAddr == "00:00:00:00:00:00") {
while (pCurrAda != nullptr) { // Find the first adapter with non-zero ip address
if (strcmp(pCurrAda->IpAddressList.IpAddress.String, "0.0.0.0") != 0) break;
pCurrAda = pCurrAda->Next;
}
} else {
uint8_t exMAC[6] = {0}; // Expected MAC address
if (sscanf_s(macAddr.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &exMAC[0], &exMAC[1], &exMAC[2], &exMAC[3], &exMAC[4], &exMAC[5]) == 6) {
while (pCurrAda != nullptr) { // Find the adapter with specified MAC address
if (pCurrAda->AddressLength == sizeof(exMAC) && memcmp(pCurrAda->Address, exMAC, sizeof(exMAC)) == 0) break;
pCurrAda = pCurrAda->Next;
}
} else {
LOG_ERR(L"initAdapterInfo: failed to parse netMAC value from config!");
}
}
if (pCurrAda != nullptr) return Ok;
LOG_ERR(L"initAdapterInfo: failed to find suitable network adapter!");
free(pAda);
pAda = pCurrAda = nullptr;
return Err::Net::ERROR_EFAULT;
};
auto initAdapterAddr = [&pAddr, &pCurrAddr, &uAddrBufLen, &pAda, &pCurrAda, initAdapterInfo]() {
LOG_USE_MODULE(OnlineNetCore);
pAddr = nullptr;
uAddrBufLen = 0;
if (GetAdaptersAddresses(AF_INET, 0, nullptr, nullptr, &uAddrBufLen) == ERROR_BUFFER_OVERFLOW) { // Obtaining buffer length
pAddr = (PIP_ADAPTER_ADDRESSES)malloc(uAddrBufLen);
} else {
LOG_ERR(L"Failed to obtain size needed for GetAdaptersAddresses buffer!");
return Err::Net::ERROR_EFAULT;
}
if (auto ret = GetAdaptersAddresses(AF_INET, 0, nullptr, pAddr, &uAddrBufLen)) {
free(pAddr);
pAddr = nullptr;
LOG_ERR(L"GetAdaptersAddresses failed: %d (%d)", ret, uAddrBufLen);
return Err::Net::ERROR_EFAULT;
}
if (auto ret = initAdapterInfo()) { // Trying to find suitable adapter first
free(pAddr);
pAddr = nullptr;
return ret;
}
pCurrAddr = pAddr;
while (pCurrAddr != nullptr) { // Adapter found, okay. Now we should match this adapter with its address
if (pCurrAda->AddressLength == pCurrAddr->PhysicalAddressLength && memcmp(pCurrAddr->PhysicalAddress, pCurrAda->Address, pCurrAda->AddressLength) == 0)
break;
pCurrAddr = pAddr->Next;
}
if (pCurrAddr != nullptr) return Ok;
LOG_ERR(L"initAdapterAddr: failed to find suitable network address!");
free(pAddr);
free(pAda);
pAddr = pCurrAddr = nullptr;
pAda = pCurrAda = nullptr;
return Ok;
};
switch (code) {
case 1: info->device = 0; break;
case 2: {
if (auto ret = initAdapterInfo()) return ret;
memcpy(info->ether_addr.data, pCurrAda->Address, pCurrAda->AddressLength);
} break;
case 3: {
if (auto ret = initAdapterAddr()) return ret;
info->mtu = (uint32_t)pCurrAddr->Mtu;
} break;
case 4: info->link = 1; break;
case 5:
case 6:
case 7: // Big and scary fallthrough
case 8:
case 9:
case 10: return Err::Net::ERROR_NOT_AVAIL; // All these above for WiFi
case 11: info->ip_config = 0; break;
case 12: {
DWORD size = sizeof(info->dhcp_hostname);
if (GetComputerNameA(info->dhcp_hostname, &size)) break; // todo: shouldn't be DNS suffix there?
info->dhcp_hostname[0] = '\0';
} break;
case 13: {
auto const count = std::string("ps4_pppoe").copy(info->pppoe_auth_name, 128); // Huh?
info->pppoe_auth_name[count] = '\0';
} break;
case 14: {
if (auto ret = initAdapterInfo()) return ret;
auto const count = std::string(pCurrAda->IpAddressList.IpAddress.String).copy(info->ip_address, 16);
info->ip_address[count] = '\0';
} break;
case 15: {
if (auto ret = initAdapterInfo()) return ret;
auto const count = std::string(pCurrAda->IpAddressList.IpMask.String).copy(info->netmask, 16);
info->netmask[count] = '\0';
} break;
case 16: {
if (auto ret = initAdapterInfo()) return ret;
auto const count = std::string(pCurrAda->GatewayList.IpAddress.String).copy(info->default_route, 16);
info->default_route[count] = '\0';
} break;
case 17: {
if (auto ret = initAdapterAddr()) return ret;
auto dns = pCurrAddr->FirstDnsServerAddress;
if (dns == nullptr) {
info->primary_dns[0] = '\0';
break;
}
auto addr = &pCurrAddr->FirstDnsServerAddress->Address;
DWORD pdnssz = sizeof(info->primary_dns);
if (WSAAddressToStringA(addr->lpSockaddr, addr->iSockaddrLength, nullptr, info->primary_dns, &pdnssz) == SOCKET_ERROR)
info->primary_dns[0] = '\0'; // todo return error?
} break;
case 18: {
if (auto ret = initAdapterAddr()) return ret;
auto dns = pCurrAddr->FirstDnsServerAddress;
if (dns == nullptr) {
info->secondary_dns[0] = '\0';
break;
}
if (dns->Next != nullptr) dns = dns->Next; // Use the secondary DNS if present
auto addr = &dns->Address;
DWORD pdnssz = sizeof(info->secondary_dns);
if (WSAAddressToStringA(addr->lpSockaddr, addr->iSockaddrLength, nullptr, info->secondary_dns, &pdnssz) == SOCKET_ERROR)
info->secondary_dns[0] = '\0'; // todo return error?
} break;
case 19: info->http_proxy_config = 0; break;
case 20: *info->http_proxy_server = '\0'; break;
case 21: info->http_proxy_port = 0; break;
default: LOG_CRIT(L"unknown code: %d", code);
}
if (pAda != nullptr) free(pAda);
if (pAddr != nullptr) free(pAddr);
return Ok;
}
int32_t OnlineNet::netCtlGetState(int32_t* state) {
*state = 3; // IP address obtained
return Ok;
}

View File

@ -0,0 +1,231 @@
#include "../online.h"
#include "config_emu.h"
#include "logging.h"
#include <Windows.h>
#include <boost/asio.hpp>
#include <unordered_map>
LOG_DEFINE_MODULE(OnlineNetCore_resolv);
using namespace boost::asio;
namespace {
static io_service svc;
struct Resolver {
char name[32];
ip::tcp::resolver res;
bool interrupted;
int internError;
Resolver(): res(svc), interrupted(false), name() {}
};
static Resolver* g_resolvers[128] = {nullptr};
class PreMap {
std::unordered_map<std::string, SceNetInAddr_t> m_mapAddrs;
public:
PreMap() {
LOG_USE_MODULE(OnlineNetCore_resolv);
auto [lock, jData] = accessConfig()->accessModule(ConfigModFlag::RESOLVE);
if ((*jData).is_object()) {
for (auto [key, value]: (*jData).items()) {
try {
SceNetInAddr_t addr;
if (value.is_number_integer()) {
value.get_to(addr);
} else {
std::string saddr;
value.get_to(saddr);
addr = ip::address_v4::from_string(saddr).to_ulong();
}
m_mapAddrs.insert(std::pair<std::string, SceNetInAddr_t>(key, addr));
} catch (json::exception& ex) {
LOG_ERR(L"Invalid resolve.json field: %S", key.c_str());
}
}
return;
}
LOG_ERR(L"Something wrong with your resolve.json!");
}
SceNetInAddr_t search(const std::string& hostname) {
auto it = m_mapAddrs.find(hostname);
if (it != m_mapAddrs.end()) {
return it->second;
}
return 0x00000000;
}
std::string_view const reverse_search(const SceNetInAddr_t addr) {
auto it = std::find_if(m_mapAddrs.begin(), m_mapAddrs.end(), [&addr](auto&& p) { return p.second == addr; });
if (it == m_mapAddrs.end()) return "";
return it->first;
}
};
PreMap& getPreMap() {
static PreMap imp;
return imp;
}
static inline int32_t testResolver(SceNetId rid) {
if (rid < 0 || rid > 127 || g_resolvers[rid] == nullptr) return Err::Net::ERROR_EINVAL;
return Ok;
}
} // namespace
SceNetId OnlineNet::resolverCreate(const char* name, int memid, int flags) {
LOG_USE_MODULE(OnlineNetCore_resolv);
if (flags != 0) {
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_EINVAL;
return -1;
}
for (int i = 0; i < 128; i++) {
if (g_resolvers[i] == nullptr) {
auto resolv = g_resolvers[i] = new Resolver();
LOG_TRACE(L"new resolver: %S,%d (memid: %d, flags: %d)", name, i, memid, flags);
if (auto namelen = ::strlen(name)) {
if (namelen > sizeof(Resolver::name) - 1) {
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_ENAMETOOLONG;
return -1;
}
::strncpy_s(resolv->name, name, namelen);
}
return i;
}
}
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_EBADF;
return -1;
}
int32_t OnlineNet::resolverStartNtoa(SceNetId rid, const char* hostname, SceNetInAddr_t* addr, int timeout, int retries, int flags) {
LOG_USE_MODULE(OnlineNetCore_resolv);
if (auto ret = testResolver(rid)) return ret;
auto resolver = g_resolvers[rid];
resolver->internError = 0;
{
std::string _hostname(hostname);
if (auto _raddr = getPreMap().search(_hostname)) {
*addr = _raddr;
return Ok;
}
}
ip::tcp::resolver::query query(ip::tcp::v4(), hostname, "0");
for (int cretr = 0; cretr < retries; ++cretr) {
if (resolver->interrupted) {
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_EINTR;
return Err::Net::ERROR_EFAULT;
}
try {
auto it = resolver->res.resolve(query);
*addr = (*it).endpoint().address().to_v4().to_uint();
return Ok;
} catch (boost::system::system_error& ex) {
LOG_ERR(L"%S: failed to resolve %S (%d/%d): %S", __FUNCTION__, hostname, cretr, retries, ex.what());
}
}
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_RESOLVER_ETIMEDOUT;
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OnlineNet::resolverStartAton(SceNetId rid, const SceNetInAddr_t* addr, char* hostname, int len, int timeout, int retry, int flags) {
LOG_USE_MODULE(OnlineNetCore_resolv);
if (auto ret = testResolver(rid)) return ret;
LOG_ERR(L"todo %S", __FUNCTION__);
auto resolver = g_resolvers[rid];
resolver->internError = 0;
{
auto _hn = getPreMap().reverse_search(*addr);
if (auto _hnlen = _hn.length()) {
if (len <= _hnlen) return Err::Net::ERROR_EINVAL;
_hn.copy(hostname, _hnlen + 1);
return Ok;
}
}
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_RESOLVER_ETIMEDOUT;
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OnlineNet::resolverStartNtoaMultipleRecords(SceNetId rid, const char* hostname, SceNetResolverInfo* info, int timeout, int retries, int flags) {
LOG_USE_MODULE(OnlineNetCore_resolv);
if (auto ret = testResolver(rid)) return ret;
auto resolver = g_resolvers[rid];
resolver->internError = 0;
{
std::string _hostname(hostname);
if (auto _raddr = getPreMap().search(_hostname)) {
info->addrs[0].af = SCE_NET_AF_INET;
info->addrs[0].un.addr = _raddr;
info->records = 1;
return Ok;
}
}
ip::tcp::resolver::query query(ip::tcp::v4(), hostname, "0");
info->records = 0;
for (int cretr = 0; cretr < retries; ++cretr) {
// todo: should set sce_net_errno
if (resolver->interrupted) {
resolver->internError = NetErrNo::SCE_NET_EINTR;
return Err::Net::ERROR_EFAULT;
}
try {
for (auto addr: resolver->res.resolve(query)) {
auto& iaddr = info->addrs[info->records++];
iaddr.af = SCE_NET_AF_INET;
iaddr.un.addr = addr.endpoint().address().to_v4().to_uint();
if (info->records == SCE_NET_RESOLVER_MULTIPLE_RECORDS_MAX) break;
}
info->records -= 1; // We should decrease value by 1 to get the actual records count
info->dns4records = info->records;
return Ok;
} catch (boost::system::system_error& ex) {
LOG_ERR(L"%S: failed to resolve %S (%d/%d): %S", __FUNCTION__, hostname, cretr, retries, ex.what());
}
}
// *sceNetErrnoLoc() = NetErrNo::SCE_NET_RESOLVER_ETIMEDOUT;
return getErr(ErrCode::_ETIMEDOUT);
}
int32_t OnlineNet::resolverGetError(SceNetId rid, int* result) {
LOG_USE_MODULE(OnlineNetCore_resolv);
if (auto ret = testResolver(rid)) return ret;
LOG_ERR(L"todo %S", __FUNCTION__);
*result = g_resolvers[rid]->internError;
return Ok;
}
int32_t OnlineNet::resolverDestroy(SceNetId rid) {
if (auto ret = testResolver(rid)) return ret;
delete g_resolvers[rid];
g_resolvers[rid] = nullptr;
return Ok;
}
int32_t OnlineNet::resolverAbort(SceNetId rid, int flags) {
if (auto ret = testResolver(rid)) return ret;
g_resolvers[rid]->interrupted = true;
return Ok;
}

View File

@ -0,0 +1,113 @@
#include "../online.h"
SceNetId OnlineNet::socketCreate(const char* name, int family, int type, int protocol) {
if (family != SCE_NET_AF_INET) {
*INetworking::getErrnoPtr() = NetErrNo::SCE_NET_EPROTONOSUPPORT;
return Err::Net::ERROR_EINVAL;
}
switch (type) {
case SCE_NET_SOCK_STREAM:
case SCE_NET_SOCK_DGRAM:
case SCE_NET_SOCK_RAW: return socket(AF_INET, type, protocol);
default: {
*INetworking::getErrnoPtr() = NetErrNo::SCE_NET_EPROTONOSUPPORT;
return Err::Net::ERROR_EPROTOTYPE;
}
}
}
SceNetId OnlineNet::socketAccept(SceNetId s, SceNetSockaddr* addr, SceNetSocklen_t* addrlen) {
int _addrlen = addrlen ? (int)*addrlen : sizeof(SceNetSockaddr);
SOCKET _ret = accept(s, (sockaddr*)addr, &_addrlen);
if (_ret == INVALID_SOCKET) return INetworking::getLastError();
*addrlen = (SceNetSocklen_t)_addrlen;
return (SceNetId)_ret;
}
int OnlineNet::socketBind(SceNetId s, const SceNetSockaddr* addr, SceNetSocklen_t addrlen) {
if (bind(s, (const sockaddr*)addr, addrlen) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketConnect(SceNetId s, const SceNetSockaddr* name, SceNetSocklen_t namelen) {
if (connect(s, (const sockaddr*)name, namelen) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketGetpeername(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) {
if (getpeername(s, (sockaddr*)name, (int*)namelen) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketGetsockname(SceNetId s, SceNetSockaddr* name, SceNetSocklen_t* namelen) {
if (getsockname(s, (sockaddr*)name, (int*)namelen) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketGetsockopt(SceNetId s, int level, int optname, void* optval, SceNetSocklen_t* optlen) {
int _optlen = optlen ? (int)*optlen : sizeof(SceNetSockaddr);
int _ret = getsockopt(s, level, optname, (char*)optval, &_optlen);
if (_ret == SOCKET_ERROR) return INetworking::getLastError();
*optlen = (SceNetSocklen_t)_optlen;
return _ret;
}
int OnlineNet::socketListen(SceNetId s, int backlog) {
if (listen(s, backlog) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketRecv(SceNetId s, void* buf, size_t len, int flags) {
int _ret = recv(s, (char*)buf, (int)len, flags);
if (_ret == SOCKET_ERROR) return INetworking::getLastError();
return _ret;
}
int OnlineNet::socketRecvfrom(SceNetId s, void* buf, size_t len, int flags, SceNetSockaddr* from, SceNetSocklen_t* fromlen) {
int _fromlen = fromlen ? (int)*fromlen : sizeof(SceNetSockaddr);
int _ret = recvfrom(s, (char*)buf, (int)len, flags, (sockaddr*)from, &_fromlen);
if (_ret == SOCKET_ERROR) return INetworking::getLastError();
*fromlen = (SceNetSocklen_t)_fromlen;
return _ret;
}
int OnlineNet::socketRecvmsg(SceNetId s, SceNetMsghdr* msg, int flags) {
return Ok;
}
int OnlineNet::socketSend(SceNetId s, const void* msg, size_t len, int flags) {
int _ret;
if ((_ret = send(s, (const char*)msg, (int)len, flags)) == SOCKET_ERROR) return INetworking::getLastError();
return _ret;
}
int OnlineNet::socketSendto(SceNetId s, const void* msg, size_t len, int flags, const SceNetSockaddr* to, SceNetSocklen_t tolen) {
int _ret;
if ((_ret = sendto(s, (const char*)msg, (int)len, flags, (const sockaddr*)to, (int)tolen)) == SOCKET_ERROR) return INetworking::getLastError();
return _ret;
}
int OnlineNet::socketSendmsg(SceNetId s, const SceNetMsghdr* msg, int flags) {
return Ok;
}
int OnlineNet::socketSetsockopt(SceNetId s, int level, int optname, const void* optval, SceNetSocklen_t optlen) {
if (setsockopt(s, level, optname, (char*)optval, (int)optlen) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketShutdown(SceNetId s, int how) {
if (shutdown(s, how) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketClose(SceNetId s) {
if (closesocket(s) == SOCKET_ERROR) return INetworking::getLastError();
return Ok;
}
int OnlineNet::socketAbort(SceNetId s, int flags) {
return Ok;
}

10
core/readme.md Normal file
View File

@ -0,0 +1,10 @@
# Overview
<div align="center">
![](../out/docs/uml/modules/coreDeps.svg)
</div>
All communication goes through the core library. The callbacks to the emulator are set during startup, before loading and setting up the target library.
Modules should only communicate unidirectional with the core library. Everything that can't be done locally inside modules, goes here.

View File

@ -0,0 +1,16 @@
add_library(runtime OBJECT
util/exceptionHandler.cpp
util/moduleLoader.cpp
util/virtualmemory.cpp
formats/elf64.cpp
exports/intern.cpp
runtimeLinker.cpp
)
add_dependencies(runtime third_party psOff_utility)
file(COPY runtimeLinker.h runtimeExport.h program.h procParam.h DESTINATION ${DST_INCLUDE_DIR}/runtime)
file(COPY exports/intern.h exports/macro.h DESTINATION ${DST_INCLUDE_DIR}/runtime/exports)
file(COPY formats/ISymbols.h formats/IFormat.h DESTINATION ${DST_INCLUDE_DIR}/runtime/formats)

View File

@ -0,0 +1,59 @@
#define __APICALL_EXTERN
#include "intern.h"
#undef __APICALL_EXTERN
#include "../runtimeLinker.h"
#include "macro.h"
#include "utility/utility.h"
namespace intern {
SYSV_ABI void module_start(size_t argc, const void* argp, int* pRes) {
// todo ?
}
void initIntercepts();
void init() {
LIB_DEFINE_START("intern", 0, "intern", 0, 0)
LIB_FUNC("module_start", module_start), LIB_DEFINE_END();
initIntercepts();
}
/*
void SYSV_ABI logfunc(const char* str, void* args) {
char sanitize[1024] = "gamelogger| ";
for (int i = 12; i < 1024; ++i) {
if (*str >= ' ' && *str <= '~')
sanitize[i] = *str++;
else {
sanitize[i] = '\n';
sanitize[i + 1] = '\0';
break;
}
}
vprintf(sanitize, (va_list)&args);
}
*/
void post() {
/*
// NieR: Automata v01.06 internal game logger interception example:
auto mp = accessRuntimeLinker().accessMainProg();
accessRuntimeLinker().interceptInternal(mp, 0xbe09e0, (uint64_t)&logfunc);
*/
}
void initIntercepts() {
/*Usage
// functions has to be of SYSV_ABI!
accessRuntimeLinker().interceptAdd((uintptr_t)int_Malloc, "Y7aJ1uydPMo", "libc", "libc")
// Calling original
auto const origFunction = accessRuntimeLinker().interceptGetAddr((uintptr_t)int_Malloc);
typedef SYSV_ABI void* (*fcnPtr)(void*, size_t);
void* ret = ((fcnPtr)origFunction)(ptr, size);
*/
}
} // namespace intern

View File

@ -0,0 +1,16 @@
#pragma once
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
namespace intern {
__APICALL void init();
__APICALL void post();
} // namespace intern
#undef __APICALL

View File

@ -0,0 +1,26 @@
#pragma once
#define LIB_DEFINE_START(lib, ver, mod, verMajor, verMinor) \
{ \
auto libInfo = std::make_unique<Symbols::SymbolExport>(lib, ver, mod, verMajor, verMinor); \
libInfo->symbolsMap = {
#define LIB_FUNC(sym, func) \
{ \
sym, { \
sym, #func, reinterpret_cast<uint64_t>(func), Symbols::SymbolType::Func \
} \
}
#define LIB_OBJECT(sym, obj) \
{ \
sym, { \
sym, #obj, reinterpret_cast<uint64_t>(obj), Symbols::SymbolType::Object \
} \
}
#define LIB_DEFINE_END() \
} \
; \
accessRuntimeLinker().addExport(std::move(libInfo)); \
}

View File

@ -0,0 +1,68 @@
#pragma once
#include "../program.h"
#include "ISymbols.h"
#include "utility/utility.h"
#include <filesystem>
#include <memory>
#include <string>
#include <utility>
class IRuntimeLinker;
struct LibraryId {
bool operator==(const LibraryId& other) const { return version == other.version && name == other.name; }
std::string id;
int version;
std::string_view name;
};
class IFormat: public Symbols::IResolve {
CLASS_NO_COPY(IFormat);
protected:
uint64_t m_baseVaddr; // Set with load2Mem
size_t m_fileSize;
std::filesystem::path const m_path; /// Fullpath
std::filesystem::path const m_filename; /// Just the filename
util::InterfaceState m_intState = util::InterfaceState::NotInit;
IFormat(std::filesystem::path&& path): m_path(std::move(path)), m_filename(m_path.filename()) {}
public:
virtual ~IFormat() = default;
virtual bool init() = 0;
virtual bool load2Mem(Program* prog) = 0;
virtual bool loadSegment(uint8_t* loadAddr, uint64_t fileOffset, uint64_t size) = 0;
virtual void getAllocInfo(uint64_t& baseSize, uint64_t& baseSizeAligned, uint64_t& sizeAlloc) = 0;
virtual bool containsAddr(uint64_t vaddr, uint64_t baseVaddr) = 0;
virtual void setupMissingRelocationHandler(Program* prog, void* relocateHandler, void* payload) = 0;
virtual Symbols::SymbolInfo getSymbolInfo(uint64_t const relIndex) const = 0;
virtual void relocate(Program const* prog, uint64_t invalidMemoryAddr, std::string_view libName) = 0;
virtual std::unordered_map<uint64_t, std::string_view> getDebugStrings() const = 0;
virtual std::string collectDebugInfos(std::unordered_map<uint64_t, std::string_view>& debugStrings) const = 0;
virtual void dtInit(uint64_t base, size_t argc = 0, const void* argp = nullptr) const = 0;
virtual void dtDeinit(uint64_t base) const = 0;
virtual std::vector<std::pair<uint64_t, uint64_t>> getExecSections() = 0;
auto getSizeFile() const { return m_fileSize; }
auto getFilename() const { return m_filename; }
auto getFilepath() const { return m_path; }
};

View File

@ -0,0 +1,93 @@
#pragma once
#include <string>
#include <unordered_map>
#include <vector>
struct LibraryId;
namespace Symbols {
enum class SymbolType {
Unknown,
Func,
Object,
TlsModule,
NoType,
};
enum class BindType { Unknown, Local, Global, Weak };
struct Info {
std::string_view libraryName; /// Fullname
int libraryVersion;
std::string_view modulName; /// Fullname
int moduleVersionMajor;
int moduleVersionMinor;
bool operator==(Info const& lhs) const {
return libraryName == lhs.libraryName && libraryVersion >= lhs.libraryVersion && modulName == lhs.modulName &&
moduleVersionMajor >= lhs.moduleVersionMajor && moduleVersionMinor >= lhs.moduleVersionMinor;
}
};
struct SymbolInfo: public Info {
std::string_view name;
SymbolType type;
};
struct Record {
std::string_view name = {};
uint64_t vaddr = 0;
};
struct RelocationInfo {
bool resolved = false;
BindType bind = BindType::Unknown;
SymbolType type = SymbolType::Unknown;
uint64_t value = 0;
uint64_t vaddr = 0;
uint64_t base_vaddr = 0;
std::string_view name;
bool bind_self = false;
};
class IResolve {
public:
virtual uintptr_t getAddress(std::string_view symName, std::string_view libName, std::string_view modName) const = 0;
virtual uintptr_t getAddress(std::string_view symName) const = 0;
virtual std::unordered_map<std::string_view, LibraryId> const& getExportedLibs() const = 0;
virtual std::unordered_map<std::string_view, LibraryId> const& getImportedLibs() const = 0;
};
struct SymbolExport {
struct Symbol {
std::string name;
std::string dbgName;
uint64_t vaddr;
SymbolType type;
};
std::string libraryName;
int libraryVersion;
std::string modulName;
int moduleVersionMajor;
int moduleVersionMinor;
std::unordered_map<std::string, Symbol> symbolsMap;
SymbolExport(std::string_view libraryName, int libraryVersion, std::string_view modulName, int moduleVersionMajor, int moduleVersionMinor)
: libraryName(libraryName),
libraryVersion(libraryVersion),
modulName(modulName),
moduleVersionMajor(moduleVersionMajor),
moduleVersionMinor(moduleVersionMinor) {}
};
struct SymbolIntercept {
uint64_t vaddr;
std::string_view name;
std::string_view libraryName;
std::string_view modulName;
};
} // namespace Symbols

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,295 @@
#pragma once
#include "IFormat.h"
#include <cstdint>
#include <memory>
#include <optional>
#include <unordered_map>
namespace elf64 {
#pragma pack(push, 1) // Structs match file data
// ### Custom Structs
struct CustomHeader {
uint8_t ident[12];
uint16_t size1;
uint16_t size2;
uint64_t sizeFile;
uint16_t numSegments;
uint16_t unknown;
uint32_t pad;
};
struct SegmentHeader {
uint64_t type;
uint64_t offset;
uint64_t sizeCom; /// compressed
uint64_t sizeDecom; /// decompressed
};
constexpr unsigned char EI_ABIVERSION = 8;
constexpr uint64_t ET_EXEC = 0xfe00; /// Exe
constexpr uint64_t ET_DYNEXEC = 0xfe10; /// Exe
constexpr uint64_t ET_DYNAMIC = 0xfe18; /// Lib
constexpr uint64_t EM_ARCH_X86_64 = 0x3e;
// ### Program Segment type
constexpr uint64_t PH_LOAD = 1;
constexpr uint64_t PH_DYNAMIC = 2;
constexpr uint64_t PH_TLS = 7;
constexpr uint64_t PH_OS_DYNLIBDATA = 0x61000000;
constexpr uint64_t PH_OS_PROCPARAM = 0x61000001;
constexpr uint64_t PH_OS_MODULEPARAM = 0x61000002;
constexpr uint64_t PH_OS_RELRO = 0x61000010;
constexpr uint64_t PH_GNU_EH_HEADER = 0x6474e550;
constexpr uint64_t SF_ORDR = 0x1; // ordered?
constexpr uint64_t SF_ENCR = 0x2; // encrypted
constexpr uint64_t SF_SIGN = 0x4; // signed
constexpr uint64_t SF_DFLG = 0x8; // deflated
constexpr uint64_t SF_BFLG = 0x800; // block segment
constexpr int64_t DT_DEBUG = 0x00000015;
constexpr int64_t DT_FINI = 0x0000000d;
constexpr int64_t DT_FINI_ARRAY = 0x0000001a;
constexpr int64_t DT_FINI_ARRAYSZ = 0x0000001c;
constexpr int64_t DT_FLAGS = 0x0000001e;
constexpr int64_t DT_INIT = 0x0000000c;
constexpr int64_t DT_INIT_ARRAY = 0x00000019;
constexpr int64_t DT_INIT_ARRAYSZ = 0x0000001b;
constexpr int64_t DT_NEEDED = 0x00000001;
constexpr int64_t DT_OS_EXPORT_LIB = 0x61000013;
constexpr int64_t DT_OS_EXPORT_LIB_1 = 0x61000047;
constexpr int64_t DT_OS_EXPORT_LIB_ATTR = 0x61000017;
constexpr int64_t DT_OS_FINGERPRINT = 0x61000007;
constexpr int64_t DT_OS_HASH = 0x61000025;
constexpr int64_t DT_OS_HASHSZ = 0x6100003d;
constexpr int64_t DT_OS_IMPORT_LIB = 0x61000015;
constexpr int64_t DT_OS_IMPORT_LIB_1 = 0x61000049;
constexpr int64_t DT_OS_IMPORT_LIB_ATTR = 0x61000019;
constexpr int64_t DT_OS_JMPREL = 0x61000029;
constexpr int64_t DT_OS_MODULE_ATTR = 0x61000011;
constexpr int64_t DT_OS_MODULE_INFO = 0x6100000d;
constexpr int64_t DT_OS_MODULE_INFO_1 = 0x61000043;
constexpr int64_t DT_OS_NEEDED_MODULE = 0x6100000f;
constexpr int64_t DT_OS_NEEDED_MODULE_1 = 0x61000045;
constexpr int64_t DT_OS_ORIGINAL_FILENAME = 0x61000009;
constexpr int64_t DT_OS_ORIGINAL_FILENAME_1 = 0x61000041;
constexpr int64_t DT_OS_PLTGOT = 0x61000027;
constexpr int64_t DT_OS_PLTREL = 0x6100002b;
constexpr int64_t DT_OS_PLTRELSZ = 0x6100002d;
constexpr int64_t DT_OS_RELA = 0x6100002f;
constexpr int64_t DT_OS_RELAENT = 0x61000033;
constexpr int64_t DT_OS_RELASZ = 0x61000031;
constexpr int64_t DT_OS_STRSZ = 0x61000037;
constexpr int64_t DT_OS_STRTAB = 0x61000035;
constexpr int64_t DT_OS_SYMENT = 0x6100003b;
constexpr int64_t DT_OS_SYMTAB = 0x61000039;
constexpr int64_t DT_OS_SYMTABSZ = 0x6100003f;
constexpr int64_t DT_PREINIT_ARRAY = 0x00000020;
constexpr int64_t DT_PREINIT_ARRAYSZ = 0x00000021;
constexpr int64_t DT_REL = 0x00000011;
constexpr int64_t DT_RELA = 0x00000007;
constexpr int64_t DT_SONAME = 0x0000000e;
constexpr int64_t DT_TEXTREL = 0x00000016;
constexpr int64_t DT_HASH = 0x00000004;
constexpr int64_t DT_STRTAB = 0x00000005;
constexpr int64_t DT_STRSZ = 0x0000000a;
constexpr int64_t DT_SYMTAB = 0x00000006;
constexpr int64_t DT_SYMENT = 0x0000000b;
constexpr int64_t DT_PLTGOT = 0x00000003;
constexpr int64_t DT_PLTREL = 0x00000014;
constexpr int64_t DT_JMPREL = 0x00000017;
constexpr int64_t DT_PLTRELSZ = 0x00000002;
constexpr int64_t DT_RELASZ = 0x00000008;
constexpr int64_t DT_RELAENT = 0x00000009;
constexpr int64_t DT_RELACOUNT = 0x6ffffff9;
constexpr uint8_t STB_LOCAL = 0;
constexpr uint8_t STB_GLOBAL = 1;
constexpr uint8_t STB_WEAK = 2;
constexpr uint8_t STT_NOTYPE = 0;
constexpr uint8_t STT_OBJECT = 1;
constexpr uint8_t STT_FUNC = 2;
constexpr uint32_t R_X86_64_64 = 1;
constexpr uint32_t R_X86_64_GLOB_DAT = 6;
constexpr uint32_t R_X86_64_JUMP_SLOT = 7;
constexpr uint32_t R_X86_64_RELATIVE = 8;
constexpr uint32_t R_X86_64_DTPMOD64 = 16;
constexpr uint32_t R_X86_64_TPOFF64 = 18;
// DWARF
constexpr uint8_t DW_EH_PE_omit = 0xff;
constexpr uint8_t DW_EH_PE_uleb128 = 0x01;
constexpr uint8_t DW_EH_PE_udata2 = 0x02;
constexpr uint8_t DW_EH_PE_udata4 = 0x03;
constexpr uint8_t DW_EH_PE_udata8 = 0x04;
constexpr uint8_t DW_EH_PE_sleb128 = 0x09;
constexpr uint8_t DW_EH_PE_sdata2 = 0x0A;
constexpr uint8_t DW_EH_PE_sdata4 = 0x0B;
constexpr uint8_t DW_EH_PE_sdata8 = 0x0C;
constexpr uint8_t DW_EH_PE_absptr = 0x00;
constexpr uint8_t DW_EH_PE_pcrel = 0x10;
constexpr uint8_t DW_EH_PE_datarel = 0x30;
// ### ELF Structs
struct ElfHeader {
unsigned char ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phOff;
uint64_t shOff;
uint32_t flags;
uint16_t sizeEh; // Elf header size
uint16_t sizePh; /// Program segment Header size
uint16_t numPh;
uint16_t sizeSh; /// Section Header size
uint16_t numSh;
uint16_t indexSh;
};
struct ProgramHeader {
uint32_t type;
uint32_t flags;
uint64_t offset;
uint64_t vaddr;
uint64_t paddr;
uint64_t sizeFile;
uint64_t sizeMem;
uint64_t align;
};
struct SectionHeader {
uint32_t name;
uint32_t type;
uint64_t flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t addrAlign;
uint64_t entrySize;
};
struct DynamicHeader {
uint64_t tag;
union {
uint64_t value;
uint64_t ptr;
} _un;
};
struct Elf64_Sym {
unsigned char getBind() const { return info >> 4u; }
unsigned char getType() const { return info & 0xfu; }
uint32_t name;
unsigned char info;
unsigned char other;
uint16_t shndx;
uint64_t value;
uint64_t size;
};
struct Elf64_Rela {
auto getSymbol() const { return (uint32_t)(info >> 32u); }
auto getType() const { return (uint32_t)(info & 0xffffffff); }
uint64_t offset;
uint64_t info;
int64_t addend;
};
#pragma pack(pop)
struct ModuleId {
bool operator==(const ModuleId& other) const { return versionMajor == other.versionMajor && versionMinor == other.versionMinor && name == other.name; }
std::string id;
int versionMajor;
int versionMinor;
std::string_view name;
};
class SymbolMap {
uint32_t const* const m_hashTable;
uint64_t const m_hashTableSize;
char const* const m_strTable;
uint64_t const m_strTableSize;
Elf64_Sym const* m_symbolTable;
uint64_t const m_symbolTableSizeTotal;
public:
SymbolMap(uint32_t const* hashTable, uint64_t hashTableSize, char const* strTable, uint64_t strTableSize, Elf64_Sym const* symbolTable,
uint64_t symbolTableSizeTotal)
: m_hashTable(hashTable),
m_hashTableSize(hashTableSize),
m_strTable(strTable),
m_strTableSize(strTableSize),
m_symbolTable(symbolTable),
m_symbolTableSizeTotal(symbolTableSizeTotal) {}
size_t size() const { return m_symbolTableSizeTotal; }
// e.g. symname = "LHuSmO3SLd8#libc#libc"
Elf64_Sym const* find(std::string_view symname) const;
// eg "Qoo175Ig+-k", "libc", "libc"
elf64::Elf64_Sym const* find(std::string_view symname, std::string_view libName, std::string_view modName) const;
Elf64_Sym const& getSymbol(size_t index) const { return m_symbolTable[index]; };
std::string_view getName(Elf64_Sym const& symbol) const { return m_strTable + symbol.name; };
};
struct DynamicInfo {
SymbolMap const symbolsMap;
DynamicInfo(SymbolMap&& symbolsMap): symbolsMap(std::move(symbolsMap)) {}
std::optional<uint64_t> vaddrInit;
std::optional<uint64_t> vaddrFini = 0;
std::optional<uint64_t> vaddrInitArray = 0;
std::optional<uint64_t> vaddrFinArray = 0;
std::optional<uint64_t> vaddrPreinitArray = 0;
uint64_t vaddrPltgot = 0;
uint64_t sizeInitArray = 0;
uint64_t sizeFiniArray = 0;
uint64_t sizePreinitArray = 0;
Elf64_Rela const* jmprelaTable = nullptr;
uint64_t jmprelaTableSize = 0;
Elf64_Rela const* relaTable = nullptr;
uint64_t relaTableTotalSize = 0;
uint64_t relaTableEntrySize = 0;
uint64_t relativeCount = 0;
uint64_t debug = 0;
uint64_t textrel = 0;
uint64_t flags = 0;
std::unordered_map<std::string_view, LibraryId> exportedLibs;
std::unordered_map<std::string_view, ModuleId> exportedMods;
std::unordered_map<std::string_view, LibraryId> importedLibs;
std::unordered_map<std::string_view, ModuleId> importedMods;
};
void dump(IFormat* format, std::fstream& outFile, std::fstream& mapFile);
} // namespace elf64

View File

@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
constexpr uint64_t SYSTEM_RESERVED = 0x800000000u;
constexpr uint64_t INVALID_OFFSET = 0x040000000u;
constexpr uint64_t INVALID_MEMORY = SYSTEM_RESERVED + INVALID_OFFSET;
constexpr size_t XSAVE_BUFFER_SIZE = 2688;

112
core/runtime/procParam.h Normal file
View File

@ -0,0 +1,112 @@
#pragma once
#include <stdint.h>
struct SceKernelMemParam {
uint64_t* sceKernelExtendedPageTable;
uint64_t* sceKernelFlexibleMemorySize;
uint8_t* sceKernelExtendedMemory1;
uint64_t const* sceKernelExtendedGpuPageTable;
uint8_t const* sceKernelExtendedMemory2;
uint64_t const* sceKernelExtendedCpuPageTable;
};
struct SceKernelFsParam {
uint64_t size;
uint64_t* sceKernelFsDupDent;
void* sceWorkspaceUpdateMode;
void* sceTraceAprNameMode;
};
struct SceMallocReplace {
uint64_t size = sizeof(SceMallocReplace);
uint64_t unknown1 = 1;
void* malloc_init = nullptr;
void* malloc_finalize = nullptr;
void* malloc = nullptr;
void* free = nullptr;
void* calloc = nullptr;
void* realloc = nullptr;
void* memalign = nullptr;
void* reallocalign = nullptr;
void* posix_memalign = nullptr;
void* malloc_stats = nullptr;
void* malloc_stats_fast = nullptr;
void* malloc_usable_size = nullptr;
};
struct SceLibcNewReplace {
uint64_t Size = sizeof(SceLibcNewReplace);
uint64_t Unknown1 = 0x2;
void* user_new = nullptr;
void* user_new_nothrow = nullptr;
void* user_new_array = nullptr;
void* user_new_array_nothrow = nullptr;
void* user_delete = nullptr;
void* user_delete_nothrow = nullptr;
void* user_delete_array = nullptr;
void* user_delete_array_nothrow = nullptr;
// optional
// void* user_delete_3= nullptr;
// void* user_delete_4= nullptr;
// void* user_delete_array_3= nullptr;
// void* user_delete_array_4= nullptr;
};
struct SceLibcParam0 {
uint64_t Size;
uint32_t entry_count;
};
struct SceLibcParam1: SceLibcParam0 {
uint32_t SceLibcInternalHeap; //(entry_count > 1)
uint32_t* sceLibcHeapSize;
uint32_t* sceLibcHeapDelayedAlloc;
uint32_t* sceLibcHeapExtendedAlloc;
uint32_t* sceLibcHeapInitialSize;
SceMallocReplace* _sceLibcMallocReplace;
SceLibcNewReplace* _sceLibcNewReplace;
};
struct SceLibcParam2: SceLibcParam1 {
uint64_t* sceLibcHeapHighAddressAlloc; //(entry_count > 2)
uint32_t* Need_sceLibc;
};
struct SceLibcParam3: SceLibcParam2 {
uint64_t* sceLibcHeapMemoryLock; //(entry_count > 4)
uint64_t* sceKernelInternalMemorySize;
uint64_t* _sceLibcMallocReplaceForTls;
};
struct SceLibcParam4: SceLibcParam3 {
uint64_t* sceLibcMaxSystemSize; //(entry_count > 7)
};
struct SceLibcParam5: SceLibcParam4 {
uint64_t* sceLibcHeapDebugFlags; //(entry_count > 8)
uint32_t* sceLibcStdThreadStackSize;
void* Unknown3;
uint32_t* sceKernelInternalMemoryDebugFlags;
uint32_t* sceLibcWorkerThreadNum;
uint32_t* sceLibcWorkerThreadPriority;
uint32_t* sceLibcThreadUnnamedObjects;
};
struct ProcParam {
struct {
uint64_t size; /// 0x50
uint32_t magic; /// "orbi" (0x4942524F)
uint32_t entryCount; /// >=1
uint64_t sdkVersion; /// 0x4508101
} header;
char const* sceProcessName;
char const* sceUserMainThreadName;
uint32_t const* sceUserMainThreadPriority;
uint32_t const* sceUserMainThreadStackSize;
SceLibcParam0* PSceLibcParam;
SceKernelMemParam* _sceKernelMemParam;
SceKernelFsParam* _sceKernelFsParam;
uint32_t* sceProcessPreloadEnabled;
uint64_t* unknown;
};

73
core/runtime/program.h Normal file
View File

@ -0,0 +1,73 @@
#pragma once
#include "runtimeExport.h"
#include <filesystem>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
struct ThreadLocalStorage {
uint32_t index;
uintptr_t vaddrImage = 0;
uint64_t sizeImage = 0;
uint64_t offset = 0;
uint32_t alignment = 0;
};
struct Program;
class IFormat;
struct RelocateHandlerPayload {
Program* prog;
IFormat* format;
};
struct Program {
RelocateHandlerPayload relocatePayload;
uint64_t baseVaddr = 0;
uint64_t entryOffAddr = 0;
bool started = false;
bool failGlobalUnresolved = true;
uint64_t procParamVaddr = 0;
ThreadLocalStorage tls;
SceKernelModuleInfoEx moduleInfoEx;
uint64_t pltVaddr = 0;
uint32_t pltNum = 0;
bool isMainProg = false;
std::filesystem::path const filename;
std::filesystem::path const path;
int32_t id;
uint64_t const baseSize;
uint64_t const baseSizeAligned;
uint64_t const desiredBaseAddr;
uint64_t const allocSize;
bool const useStaticTLS;
std::vector<uint8_t> trampolines_fs;
std::vector<CxaDestructor> cxa;
Program(std::filesystem::path path_, uint64_t baseSize_, uint64_t baseSizeAligned_, uint64_t desiredBaseAddr_, uint64_t allocSize_, bool useStaticTLS_)
: filename(path_.filename()),
path(path_),
id(-1),
baseSize(baseSize_),
baseSizeAligned(baseSizeAligned_),
desiredBaseAddr(desiredBaseAddr_),
allocSize(allocSize_),
useStaticTLS(useStaticTLS_) {}
~Program() {}
};

View File

@ -0,0 +1,146 @@
#pragma once
#include "utility/utility.h"
#include <filesystem>
#include <stdint.h>
using cxa_destructor_func_t = void (*)(void*);
struct CxaDestructor {
cxa_destructor_func_t destructor_func;
void* destructor_object;
};
#pragma pack(push, 1)
struct alignas(32) EntryParams {
int argc = 0;
uint32_t pad = 0;
const char* argv[3] = {0, 0, 0};
};
struct ModulInfo {
uint64_t seg0Addr;
uint64_t seg0Size;
uint64_t procParamAddr;
};
struct SceKernelModuleSegmentInfo {
uint64_t address;
uint32_t size;
int prot;
};
struct SceKernelModuleInfo {
uint64_t size = sizeof(SceKernelModuleInfo);
char name[255];
SceKernelModuleSegmentInfo segments[3];
uint32_t segment_count;
uint32_t ref_count;
uint8_t fingerprint[20];
};
struct SceKernelModuleInfoEx {
uint64_t size = sizeof(SceKernelModuleInfoEx);
char name[255];
int id;
uint32_t tls_index;
void* tls_init_addr;
uint32_t tls_init_size;
uint32_t tls_size;
uint32_t tls_offset;
uint32_t tls_align;
uint64_t init_proc_addr;
uint64_t fini_proc_addr;
uint64_t reserved1 = 0;
uint64_t reserved2 = 0;
void* eh_frame_hdr_addr;
void* eh_frame_addr;
uint32_t eh_frame_hdr_size;
uint32_t eh_frame_size;
SceKernelModuleSegmentInfo segments[3];
uint32_t segment_count;
uint32_t ref_count;
};
#pragma pack(pop)
class IRuntimeExport {
CLASS_NO_COPY(IRuntimeExport);
CLASS_NO_MOVE(IRuntimeExport);
protected:
IRuntimeExport() = default;
public:
virtual ~IRuntimeExport() = default;
virtual int loadStartModule(std::filesystem::path const& path, size_t args, const void* argp, int* pRes) = 0;
virtual void initTLS(uint8_t* obj) = 0;
virtual uint64_t getTLSStaticBlockSize() const = 0;
virtual EntryParams const* getEntryParams() const = 0;
virtual void cxa_add_atexit(CxaDestructor&&, int moduleId) = 0;
virtual void cxa_finalize(int moduleId) = 0;
virtual ModulInfo mainModuleInfo() const = 0;
virtual SceKernelModuleInfoEx const* getModuleInfoEx(uint64_t vaddr) const = 0;
virtual std::vector<int> getModules() const = 0;
/**
* @brief Get the symbols address
*
* @param symName nid
* @param libName
* @param modName
* @return void* address
*/
virtual void* getSymbol(std::string_view symName, std::string_view libName, std::string_view modName) const = 0;
/**
* @brief Searches the module for the symbol
*
* @param module moduleId
* @param symName
* @param isNid
* @return void*
*/
virtual void* getSymbol(int module, std::string_view symName, bool isNid) const = 0;
// ### THREAD LOCAL STORAGE
virtual void* getTLSAddr(uint32_t index, uint64_t offset) = 0;
virtual void setTLSKey(uint32_t key, const void* value) = 0;
virtual void* getTLSKey(uint32_t key) = 0;
virtual uint32_t createTLSKey(void* destructor) = 0;
virtual void deleteTLSKey(uint32_t key) = 0;
virtual void destroyTLSKeys(uint8_t* obj) = 0;
// - ### TLS
// ### Interception
/**
* @brief function (addr) is called instead of the library symbol
*
* @param addr function address
* @param name symbol name (nid)
* @param libraryName
* @param modulName
*/
virtual void interceptAdd(uintptr_t addr, std::string_view name, std::string_view libraryName, std::string_view modulName) = 0;
/**
* @brief Gets the original addr
*
* @param addr
* @return uintptr_t
*/
virtual uintptr_t interceptGetAddr(uintptr_t addr) const = 0;
// -
};

View File

@ -0,0 +1,922 @@
#define __APICALL_EXTERN
#include "runtimeLinker.h"
#undef __APICALL_EXTERN
#include "core/fileManager/fileManager.h"
#include "core/fileManager/types/type_lib.h"
#include "core/kernel/pthread.h"
#include "core/kernel/pthread_intern.h"
#include "core/memory/memory.h"
#include "formats/elf64.h"
#include "logging.h"
#include "memoryLayout.h"
#include "util/moduleLoader.h"
#include "util/plt.h"
#include "util/virtualmemory.h"
#include "utility/progloc.h"
#include "utility/utility.h"
#include <boost/uuid/detail/sha1.hpp>
#include <fstream>
#include <magic_enum/magic_enum.hpp>
#include <mutex>
#include <set>
#include <vector>
LOG_DEFINE_MODULE(RuntimeLinker);
namespace {
// clang-format off
using atexit_func_t = SYSV_ABI void (*)();
using entry_func_t = SYSV_ABI void (*)(EntryParams const* params, atexit_func_t atexit_func);
using module_func_t = SYSV_ABI int (*)(size_t args, const void* argp, atexit_func_t atexit_func);
// clang-format on
struct FrameS {
FrameS* next;
uintptr_t ret_addr;
};
static Program* g_tlsMainProgram = nullptr;
struct RelocateHandlerStack {
uint64_t stack[3];
};
SYSV_ABI void missingRelocationHandler(RelocateHandlerStack s) {
LOG_USE_MODULE(RuntimeLinker);
auto* stack = s.stack;
auto const* payload = reinterpret_cast<RelocateHandlerPayload*>(stack[-1]);
auto const relIndex = stack[0];
Symbols::SymbolInfo symInfo = {.name = "<unknown function>"};
if (payload != nullptr) {
symInfo = payload->format->getSymbolInfo(relIndex);
}
LOG_CRIT(L"Missing Symbol| Name:%S Lib:%S(ver:%d) Mod:%S(major:%d minor:%d)", symInfo.name.data(), symInfo.libraryName.data(), symInfo.libraryVersion,
symInfo.modulName.data(), symInfo.moduleVersionMajor, symInfo.moduleVersionMinor);
}
SYSV_ABI void tlsDynDestruct(void* tlsBlock) {
if (tlsBlock != nullptr) delete[] (uint8_t*)tlsBlock;
}
void patchCall(uint64_t const srcFunction, uint64_t const dstFunction) {
int oldMode;
memory::protect(srcFunction - 1, 20, SceProtRead | SceProtWrite,
&oldMode); // Protect Memory
uint8_t* ptr = (uint8_t*)srcFunction;
ptr[0] = 0xFF;
ptr[1] = 0x25;
ptr[2] = 0x00;
ptr[3] = 0x00;
ptr[4] = 0x00;
ptr[5] = 0x00;
*((uint64_t*)&ptr[6]) = dstFunction;
// memset(ptr + 5, 0x90, 4);
memory::protect(srcFunction - 1, 20, oldMode); // Protect Memory
flushInstructionCache(srcFunction, 20);
}
std::string encode(const uint8_t* in) {
std::string out;
out.resize(12);
auto pOut = out.data();
constexpr const char* tab = {"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-"};
for (auto n = 8 / 3; n--;) {
*pOut++ = tab[(in[0] & 0xfc) >> 2];
*pOut++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*pOut++ = tab[((in[2] & 0xc0) >> 6) + ((in[1] & 0x0f) << 2)];
*pOut++ = tab[in[2] & 0x3f];
in += 3;
}
*pOut++ = tab[(in[0] & 0xfc) >> 2];
*pOut++ = tab[((in[0] & 0x03) << 4) + ((in[1] & 0xf0) >> 4)];
*pOut++ = tab[(in[1] & 0x0f) << 2];
*pOut = '\0';
return out;
}
std::string name2nid(std::string_view name) {
boost::uuids::detail::sha1 sha1;
sha1.process_bytes((unsigned char*)name.data(), name.size());
sha1.process_bytes("\x51\x8d\x64\xa6\x35\xde\xd8\xc1\xE6\xB0\x39\xB1\xC3\xE5\x52\x30", 16);
unsigned hash[5];
sha1.get_digest(hash);
uint64_t const digest = ((uint64_t)hash[0] << 32u) | hash[1];
return encode((uint8_t*)&digest);
}
} // namespace
class InternalLib: public Symbols::IResolve {
std::unordered_map<std::string_view, LibraryId> const m_exportedLibs;
std::unordered_map<std::string_view, LibraryId> const m_importedLibs {}; /// dummy
std::unordered_map<std::string_view, std::shared_ptr<Symbols::SymbolExport>> m_symExport; /// [Module name] symbolsExport
public:
InternalLib(std::string_view const modulName, std::shared_ptr<Symbols::SymbolExport>& symExport)
: m_exportedLibs({std::make_pair(symExport->libraryName, LibraryId {})}) {
m_symExport.insert({modulName, symExport});
}
void addModule(std::shared_ptr<Symbols::SymbolExport>& symExport) {
if (auto it = m_symExport.find(symExport->modulName); it != m_symExport.end()) {
// add to existing
if (it->second->symbolsMap.size() > symExport->symbolsMap.size()) {
it->second->symbolsMap.insert(symExport->symbolsMap.begin(), symExport->symbolsMap.end());
} else {
// add to biggest and swap (owns symExport)
symExport->symbolsMap.insert(it->second->symbolsMap.begin(), it->second->symbolsMap.end());
it->second->symbolsMap.swap(symExport->symbolsMap);
}
} else {
// inster new
m_symExport.insert({symExport->modulName, std::move(symExport)});
}
}
std::unordered_map<std::string_view, LibraryId> const& getExportedLibs() const final { return m_exportedLibs; }
std::unordered_map<std::string_view, LibraryId> const& getImportedLibs() const final { return m_importedLibs; }
std::string_view const getLibName() const { return m_exportedLibs.begin()->first; }
uintptr_t getAddress(std::string_view symName, std::string_view libName, std::string_view modName) const final {
auto it = m_symExport.find(modName);
if (it == m_symExport.end()) return 0;
auto itSym = it->second->symbolsMap.find(std::string(symName));
if (itSym == it->second->symbolsMap.end()) {
// Special case: check libScePosix aswell
if (libName == "libkernel" && modName == "libkernel") {
auto iResolve = accessRuntimeLinker().getIResolve("libScePosix");
if (iResolve != nullptr) return iResolve->getAddress(symName, "libScePosix", modName);
}
// -
return 0;
}
return itSym->second.vaddr;
}
uintptr_t getAddress(std::string_view symName) const final {
for (auto const& mod: m_symExport) {
auto itSym = mod.second->symbolsMap.find(std::string(symName));
return itSym->second.vaddr;
}
return 0;
}
};
class RuntimeLinker: public IRuntimeLinker {
private:
mutable std::recursive_mutex m_mutex_int;
uint64_t m_invalidMemoryAddr = 0;
size_t m_countcreatePrograms = 0;
EntryParams m_entryParams = {
.argc = 1,
.argv = {"psOff", "", ""},
};
/**
* @brief Programlist (<fixed> 0: mainProgram)
*
*/
std::list<std::pair<std::unique_ptr<Program>, std::shared_ptr<IFormat>>> m_programList;
std::unordered_map<std::string_view, std::shared_ptr<InternalLib>> m_internalLibs;
std::unordered_map<std::string_view, std::shared_ptr<Symbols::IResolve>> m_libsMap;
std::unordered_map<std::string_view, Symbols::SymbolIntercept> m_interceptMap;
mutable std::unordered_map<uintptr_t, uintptr_t> m_interceptOrigVaddrMap;
std::unordered_map<uint32_t, Program*> mTlsIndexMap;
std::unordered_map<std::string_view, void*> m_libHandles;
std::vector<uint8_t> m_tlsStaticInitBlock;
std::unordered_map<int, ScePthread_obj> m_threadList;
uint64_t m_curDTVVersion = 1;
std::array<DTVKey, DTV_MAX_KEYS> m_dtvKeys;
bool m_checkOldLib = true; /// Skip check for oldLib in addExport if already found
void initTlsStaticBlock();
void setupTlsStaticBlock();
void loadModules(std::string_view libName);
public:
RuntimeLinker() { m_programList.push_back({}); }
virtual ~RuntimeLinker() = default;
// Program* := access to unique_ptr
Program* findProgram(uint64_t vaddr) final;
Program* findProgramById(size_t id) const final;
void callInitProgramms() final;
void* getTLSAddr(uint32_t index, uint64_t offset) final;
void setTLSKey(uint32_t key, const void* value) final;
void* getTLSKey(uint32_t key) final;
uint32_t createTLSKey(void* destructor) final;
void deleteTLSKey(uint32_t key) final;
void destroyTLSKeys(uint8_t* obj) final;
std::unique_ptr<Program> createProgram(std::filesystem::path const filepath, uint64_t const baseSize, uint64_t const baseSizeAligned, uint64_t const alocSize,
bool useStaticTLS) final {
std::unique_lock const lock(m_mutex_int);
if (useStaticTLS) ++m_countcreatePrograms;
auto inst = std::make_unique<Program>(filepath, baseSize, baseSizeAligned, IMAGE_BASE, alocSize, useStaticTLS);
inst->id = accessFileManager().addFile(createType_lib(inst.get()), filepath);
return inst;
}
uint64_t getAddrInvalidMemory() final {
std::unique_lock const lock(m_mutex_int);
return m_invalidMemoryAddr;
}
Program* addProgram(std::unique_ptr<Program>&& prog, std::shared_ptr<IFormat> format) final;
int loadStartModule(std::filesystem::path const& path, size_t args, const void* argp, int* pRes) final;
uintptr_t execute() final;
void stopModules() final;
void stopModule(int moduleId) final;
Symbols::IResolve const* getIResolve(std::string_view libName) const final {
auto const it = m_libsMap.find(libName);
if (it == m_libsMap.end()) return nullptr;
return it->second.get();
}
uintptr_t checkIntercept(uintptr_t vaddr, std::string_view symName, std::string_view libName, std::string_view modName) const final {
auto it = m_interceptMap.find(symName);
if (it != m_interceptMap.end()) {
m_interceptOrigVaddrMap[it->second.vaddr] = vaddr;
return it->second.vaddr;
}
return vaddr;
}
void addExport(std::unique_ptr<Symbols::SymbolExport>&& symbols) final {
std::unique_lock const lock(m_mutex_int);
auto it = m_internalLibs.find(symbols->libraryName);
std::shared_ptr<Symbols::SymbolExport> obj = std::move(symbols);
if (it == m_internalLibs.end()) {
auto inserted = m_internalLibs.insert({obj->libraryName, std::make_shared<InternalLib>(obj->modulName, obj)});
auto const libName = inserted.first->first;
m_libsMap.insert({libName, inserted.first->second});
// Check if Old Lib Name
if (m_checkOldLib) {
constexpr std::string_view oldLib = "libSceGnmDriver";
constexpr std::string_view newLib = "libSceGraphicsDriver";
if (libName.compare(newLib) == 0) {
m_checkOldLib = false;
m_libsMap.insert({oldLib, std::make_shared<InternalLib>(oldLib, obj)});
}
}
// -
} else {
it->second->addModule(obj);
}
}
ModulInfo mainModuleInfo() const final {
auto prog = accessMainProg();
return {prog->moduleInfoEx.segments[0].address, prog->moduleInfoEx.segments[0].size, prog->procParamVaddr};
}
SceKernelModuleInfoEx const* getModuleInfoEx(uint64_t vaddr) const final {
std::unique_lock lock(m_mutex_int);
Program const* prog = nullptr;
for (auto&& p: m_programList) {
if (p.second.get()->containsAddr(vaddr, p.first.get()->baseVaddr)) {
prog = p.first.get();
break;
}
}
if (prog == nullptr) return nullptr;
return &prog->moduleInfoEx;
}
std::vector<int> getModules() const final {
std::unique_lock lock(m_mutex_int);
std::vector<int> ret(m_programList.size());
Program const* prog = nullptr;
auto pDst = ret.data();
for (auto& prog: m_programList) {
*pDst++ = prog.first->id;
}
return ret;
}
void* getSymbol(std::string_view symName, std::string_view libName, std::string_view modName) const final {
auto iresolve = getIResolve(libName);
if (iresolve == nullptr) return nullptr;
return (void*)iresolve->getAddress(symName, libName, modName);
}
void* getSymbol(int moduleId, std::string_view symName, bool isNid) const final {
std::unique_lock lock(m_mutex_int);
// Find Module
IFormat const* format = nullptr;
for (auto&& prog: m_programList) {
if (prog.first->id == moduleId) {
format = prog.second.get();
break;
}
}
if (format == nullptr) return nullptr;
// -
std::string name;
if (isNid) {
name = std::string(symName);
} else {
name = name2nid(symName);
name.pop_back();
}
for (auto const& [libName, libId]: format->getExportedLibs()) {
auto iresolve = getIResolve(libName);
if (iresolve == nullptr) continue;
if (auto const addr = (void*)iresolve->getAddress(name); addr != 0) {
return addr;
}
}
return nullptr;
}
void interceptAdd(uintptr_t addr, std::string_view name, std::string_view libraryName, std::string_view modulName) final {
m_interceptMap[name] = Symbols::SymbolIntercept {.vaddr = addr, .name = name, .libraryName = libraryName, .modulName = modulName};
}
uintptr_t interceptGetAddr(uintptr_t addr) const final { return m_interceptOrigVaddrMap[addr]; }
bool interceptInternal(Program* prog, uintptr_t progaddr, uintptr_t iaddr);
void cxa_add_atexit(CxaDestructor&& dest, int moduleId) final {
std::unique_lock const lock(m_mutex_int);
auto prog = findProgramById(moduleId);
if (prog == nullptr) return;
prog->cxa.push_back(std::move(dest));
}
void cxa_finalize(int moduleId) final {
std::unique_lock const lock(m_mutex_int);
auto prog = findProgramById(moduleId);
if (prog == nullptr) return;
for (auto& c: prog->cxa) {
c.destructor_func(c.destructor_object);
}
prog->cxa.clear();
}
void initTLS(uint8_t* tls) final;
uint64_t getTLSStaticBlockSize() const final { return m_tlsStaticInitBlock.size(); }
std::vector<std::pair<uint64_t, uint64_t>> getExecSections() const final { return m_programList.begin()->second->getExecSections(); }
EntryParams const* getEntryParams() const final { return &m_entryParams; };
Program* accessMainProg() final {
std::unique_lock const lock(m_mutex_int);
return m_programList.begin()->first.get();
}
Program const* accessMainProg() const {
std::unique_lock const lock(m_mutex_int);
return m_programList.begin()->first.get();
}
};
void RuntimeLinker::initTLS(uint8_t* obj) {
LOG_USE_MODULE(RuntimeLinker);
if (!m_tlsStaticInitBlock.empty()) memcpy(&obj[8], m_tlsStaticInitBlock.data(), m_tlsStaticInitBlock.size());
auto dtvAddress = pthread::getDTV(obj);
m_threadList[pthread::getThreadId(obj)] = obj;
dtvAddress[0] = m_curDTVVersion;
for (auto const& prog: m_programList) {
if (!prog.first->useStaticTLS) break; // since m_programList is in sequence
auto& slot = dtvAddress[prog.first->tls.index];
slot = (uint64_t)&obj[8 + prog.first->tls.offset];
LOG_TRACE(L"TLS(%s) slot:%u addr:0x%08llx", prog.first->filename.c_str(), prog.first->tls.index, slot);
}
}
Program* RuntimeLinker::findProgram(uint64_t vaddr) {
for (auto&& p: m_programList) {
if (p.second.get()->containsAddr(vaddr, p.first.get()->baseVaddr)) {
return p.first.get();
}
}
return nullptr;
}
Program* RuntimeLinker::findProgramById(size_t id) const {
for (auto&& p: m_programList) {
if (p.first->id == id) {
return p.first.get();
}
}
return nullptr;
}
Program* RuntimeLinker::addProgram(std::unique_ptr<Program>&& prog, std::shared_ptr<IFormat> format) {
LOG_USE_MODULE(RuntimeLinker);
LOG_INFO(L"adding %s: main:%S id:%llu", prog->filename.c_str(), util::getBoolStr(prog->isMainProg), prog->id);
prog->relocatePayload.format = format.get();
prog->relocatePayload.prog = prog.get();
format->setupMissingRelocationHandler(prog.get(), reinterpret_cast<void*>(missingRelocationHandler), &prog->relocatePayload);
std::unique_lock const lock(m_mutex_int);
for (auto const& item: format->getExportedLibs()) {
m_libsMap.insert({item.first, format});
}
// Place mainprogram at index 0
if (prog->isMainProg) {
prog->failGlobalUnresolved = false;
g_tlsMainProgram = prog.get();
m_programList.begin()->first.swap(prog);
m_programList.begin()->second.swap(format);
return m_programList.begin()->first.get();
} else {
if (util::endsWith(prog->path.parent_path().string(), "_module")) {
prog->failGlobalUnresolved = false;
}
return m_programList.emplace_back(std::make_pair(std::move(prog), std::move(format))).first.get();
}
}
void RuntimeLinker::loadModules(std::string_view libName) {
LOG_USE_MODULE(RuntimeLinker);
auto funcLoad = [this](std::string_view name) {
LOG_USE_MODULE(RuntimeLinker);
if (!m_libHandles.contains(name)) {
LOG_DEBUG(L"Needs library %S", name.data());
// 1/2 Sepcial case: old->new, build filepath
auto filepath = std::format(L"{}/modules/", util::getProgramLoc());
if (name == "libSceGnmDriver") {
filepath += L"libSceGraphicsDriver";
} else {
filepath += std::wstring(name.begin(), name.end());
}
filepath += L".dll";
//- filepath
if (std::filesystem::exists(filepath) && !m_libHandles.contains(name)) {
LOG_DEBUG(L" load library %s", filepath.c_str());
auto [handle, symbols] = loadModule(name.data(), filepath.c_str(), 1);
// 2/2 Sepcial case: old->new
if (name == "libSceGnmDriver") {
symbols->modulName = name;
}
// -special case
m_libHandles.emplace(std::make_pair(name, handle));
accessRuntimeLinker().addExport(std::move(symbols));
}
}
};
funcLoad(libName);
// todo include dependency into lib
if (libName == "libSceNpManager") {
funcLoad("libSceNpManagerForToolkit");
}
// ScePosix is sometimes not loaded together with libkernel -> just allways load it
else if (libName == "libkernel") {
funcLoad("libScePosix");
}
}
int RuntimeLinker::loadStartModule(std::filesystem::path const& path, size_t argc, const void* argp, int* pRes) {
LOG_USE_MODULE(RuntimeLinker);
std::unique_lock const lock(m_mutex_int);
// check if already loaded
auto fileName = path.filename().string();
if (auto it = std::find_if(m_programList.begin(), m_programList.end(),
[&fileName](std::pair<std::unique_ptr<Program>, std::shared_ptr<IFormat>>& rhs) { return rhs.first->filename == fileName; });
it != m_programList.end()) {
return it->first->id;
}
// -
auto ifFile = util::openFile(path);
if (!ifFile) {
LOG_ERR(L"Couldn't open file %s", path.c_str());
return getErr(ErrCode::_ENOENT);
}
// Load Module
std::shared_ptr<IFormat> format = buildParser_Elf64(std::filesystem::path(path), std::move(ifFile));
if (!format->init()) {
return 0;
}
uint64_t baseSize = 0, baseSizeAligned = 0, allocSize = 0;
format->getAllocInfo(baseSize, baseSizeAligned, allocSize);
auto program = accessRuntimeLinker().createProgram(format->getFilepath(), baseSize, baseSizeAligned, allocSize, false);
if (!format->load2Mem(program.get())) {
LOG_CRIT(L"<--- Parse (%s): Error:accessRuntimeLinker", format->getFilename().c_str());
return 0;
}
auto pProgram = accessRuntimeLinker().addProgram(std::move(program), format);
// - Module loaded
pProgram->tls.offset = 0; // Dynamically loaded modules use their own blocks
auto const tlsIndex = createTLSKey((void*)tlsDynDestruct);
pProgram->tls.index = tlsIndex;
pProgram->moduleInfoEx.tls_index = tlsIndex;
mTlsIndexMap[tlsIndex] = pProgram;
// check imports
LOG_DEBUG(L"Load for %S", pProgram->filename.string().c_str());
for (auto const& impLib: format->getImportedLibs()) {
loadModules(impLib.first);
}
// - imports
// relocate
auto libName = pProgram->filename.stem().string();
for (auto& prog: m_programList) {
if (prog.second->getImportedLibs().find(libName) == prog.second->getImportedLibs().end()) continue;
prog.second->relocate(prog.first.get(), m_invalidMemoryAddr, libName);
}
format->relocate(pProgram, m_invalidMemoryAddr, "");
uintptr_t const entryAddr = pProgram->entryOffAddr + pProgram->baseVaddr;
LOG_INFO(L"-> Starting %s entry:0x%08llx tlsIndex:%u", pProgram->filename.c_str(), entryAddr, tlsIndex);
int result = ((module_func_t)entryAddr)(argc, argp, nullptr);
if (pRes != nullptr) *pRes = result;
pProgram->started = true;
LOG_INFO(L"<- Started %s entry:0x%08llx", pProgram->filename.c_str(), entryAddr);
// callInitProgramms();
return pProgram->id;
}
/**
* @brief Access Static DTV
* @return void* address
*/
void* tlsMainGetAddr(int32_t offset) {
LOG_USE_MODULE(RuntimeLinker);
uint64_t const tlsAddr = (uint64_t)&pthread::getDTV(pthread::getSelf())[0];
LOG_TRACE(L"[%d] tlsMainGetAddr addr:0x%08llx offset:0x%0lx", pthread::getThreadId(), tlsAddr, offset);
return &pthread::getDTV(pthread::getSelf())[0] + offset; // dtv
}
void* tlsMainGetAddr64(int64_t offset) {
LOG_USE_MODULE(RuntimeLinker);
uint64_t const tlsAddr = (uint64_t)&pthread::getDTV(pthread::getSelf())[0];
LOG_TRACE(L"[%d] tlsMainGetAddr64 addr:0x%08llx offset:0x%08llx", pthread::getThreadId(), tlsAddr, offset);
return &pthread::getDTV(pthread::getSelf())[0] + offset; // dtv
}
void* RuntimeLinker::getTLSAddr(uint32_t index, uint64_t offset) {
std::unique_lock const lock(m_mutex_int);
LOG_USE_MODULE(RuntimeLinker);
if (index <= m_countcreatePrograms) {
auto& tlsAddr = pthread::getDTV(pthread::getSelf())[index];
LOG_TRACE(L"[%d] getTlsAddr key:%d addr:0x%08llx offset:0x%08llx", pthread::getThreadId(), index, tlsAddr, offset);
return (void*)((uint8_t*)tlsAddr + offset);
}
// Lookup module and get its tls
auto const prog = mTlsIndexMap[index];
auto& tlsAddr = pthread::getDTV(pthread::getSelf())[prog->tls.index];
if (tlsAddr == 0) {
LOG_TRACE(L"[%d] create tls key:%d", pthread::getThreadId(), index);
auto obj = std::make_unique<uint8_t[]>(prog->tls.sizeImage).release();
memcpy(obj, (void*)prog->tls.vaddrImage, prog->tls.sizeImage);
tlsAddr = (uint64_t)obj;
}
LOG_TRACE(L"[%d] getTlsAddr key:%d addr:0x%08llx offset:0x%08llx", pthread::getThreadId(), index, tlsAddr, offset);
return (void*)((uint8_t*)tlsAddr + offset);
}
void RuntimeLinker::setTLSKey(uint32_t index, const void* value) {
LOG_USE_MODULE(RuntimeLinker);
std::unique_lock const lock(m_mutex_int);
auto dtv = pthread::getDTV(pthread::getSelf());
auto pDtvKey = &dtv[index];
LOG_TRACE(L"[thread:%d] TLS Key| -> key:%d value:0x%08llx", pthread::getThreadId(), index, (uint64_t)value);
*pDtvKey = (uint64_t)value;
}
void* RuntimeLinker::getTLSKey(uint32_t index) {
LOG_USE_MODULE(RuntimeLinker);
std::unique_lock const lock(m_mutex_int);
auto dtv = pthread::getDTV(pthread::getSelf());
auto pDtvKey = &dtv[index];
LOG_TRACE(L"[thread:%d] TLS Key| <- key:%d value:0x%08llx", pthread::getThreadId(), index, pDtvKey[index]);
return (void*)*pDtvKey;
}
uint32_t RuntimeLinker::createTLSKey(void* destructor) {
LOG_USE_MODULE(RuntimeLinker);
std::unique_lock const lock(m_mutex_int);
for (uint64_t n = m_countcreatePrograms; n < m_dtvKeys.size(); ++n) {
if (!m_dtvKeys[n].used) {
m_dtvKeys[n].used = true;
m_dtvKeys[n].destructor = (pthread_key_destructor_func_t)destructor;
LOG_TRACE(L"-> TLS Key:%d destructor:0x%08llx", n, reinterpret_cast<uint64_t>(destructor));
return n;
}
}
LOG_ERR(L"Not enough dtv space");
return -1;
}
void RuntimeLinker::deleteTLSKey(uint32_t key) {
LOG_USE_MODULE(RuntimeLinker);
std::unique_lock const lock(m_mutex_int);
auto destructor = m_dtvKeys[key].destructor;
for (auto obj: m_threadList) {
auto dtv = pthread::getDTV(obj.second);
dtv[key] = 0;
}
m_dtvKeys[key].used = false;
m_dtvKeys[key].destructor = nullptr;
LOG_DEBUG(L"<- TLS Key:%d thread:%d", key, pthread::getThreadId());
}
void RuntimeLinker::destroyTLSKeys(uint8_t* obj) {
auto pDtvKey = pthread::getDTV(obj);
std::unique_lock const lock(m_mutex_int);
for (uint64_t n = m_countcreatePrograms; n < m_dtvKeys.size(); ++n, ++pDtvKey) {
if (m_dtvKeys[n].destructor != nullptr) {
if (pDtvKey[n] != 0) m_dtvKeys[n].destructor((void*)pDtvKey[n]);
}
}
m_threadList.erase(pthread::getThreadId(obj));
}
void RuntimeLinker::stopModules() {
LOG_USE_MODULE(RuntimeLinker);
// Stop Modules
for (auto& prog: m_programList) {
LOG_INFO(L"Stopping module %s", prog.first->filename.c_str());
// prog.second->dtDeinit(prog.first->baseVaddr);
for (auto& c: prog.first->cxa) {
c.destructor_func(c.destructor_object);
}
prog.first->cxa.clear();
}
// // Custom Libs
// for (auto const& item: m_libHandles) {
// unloadModule(item.second);
// }
}
void RuntimeLinker::stopModule(int moduleId) {
LOG_USE_MODULE(RuntimeLinker);
for (auto itProg = m_programList.begin(); itProg != m_programList.end(); ++itProg) {
if (itProg->first->id == moduleId) {
LOG_INFO(L"Stopping module %s", itProg->first->filename.c_str());
for (auto& c: itProg->first->cxa) {
c.destructor_func(c.destructor_object);
}
itProg->first->cxa.clear();
m_programList.erase(itProg);
break;
}
}
}
void RuntimeLinker::callInitProgramms() {
LOG_USE_MODULE(RuntimeLinker);
struct CallGraph {
Program* const program;
IFormat* const iFormat;
std::vector<CallGraph> childs;
CallGraph(Program* program, IFormat* format): program(program), iFormat(format) {}
};
auto itMainProg = m_programList.begin();
CallGraph callGraph({itMainProg->first.get(), itMainProg->second.get()});
// Get dependencies
for (auto const& impLib: itMainProg->second->getImportedLibs()) {
for (auto itImp = std::next(m_programList.begin()); itImp != m_programList.end(); ++itImp) {
auto const& expLib = itImp->second->getExportedLibs();
if (expLib.find(impLib.first) != expLib.end()) {
LOG_DEBUG(L"%s needs %S", itMainProg->first->filename.c_str(), impLib.first.data());
callGraph.childs.push_back({itImp->first.get(), itImp->second.get()});
break;
}
}
}
for (auto& parent: callGraph.childs) {
auto const& impParent = parent.iFormat->getImportedLibs();
for (auto itImp = std::next(m_programList.begin()); itImp != m_programList.end(); ++itImp) {
for (auto const& expLib: itImp->second->getExportedLibs()) {
if (impParent.find(expLib.first) != impParent.end()) {
LOG_DEBUG(L"%s needs %s", parent.program->filename.c_str(), itImp->first->filename.c_str());
parent.childs.push_back({itImp->first.get(), itImp->second.get()});
}
}
}
}
//- dependencies
// Startup modules
auto const startModule = [=](CallGraph const& item) {
if (item.program->started) return;
item.program->started = true;
LOG_INFO(L"Starting %s", item.program->filename.c_str());
item.iFormat->dtInit(item.program->baseVaddr);
};
for (auto& parent: callGraph.childs) {
for (auto& child: parent.childs) {
startModule(child);
}
startModule(parent);
}
}
void RuntimeLinker::initTlsStaticBlock() {
LOG_USE_MODULE(RuntimeLinker);
// Set TLS Static Block
size_t tlsStaticSize = 0;
{
int64_t offsetPre = 0;
for (auto const& prog: m_programList) {
offsetPre = util::alignDown(offsetPre - prog.first->tls.sizeImage, prog.first->tls.alignment);
}
tlsStaticSize = util::alignUp(std::abs(offsetPre), 32);
}
m_tlsStaticInitBlock.resize(tlsStaticSize);
int64_t offset = tlsStaticSize;
uint32_t indexCount = 0;
for (auto const& prog: m_programList) {
offset = util::alignDown(offset - prog.first->tls.sizeImage, prog.first->tls.alignment);
prog.first->moduleInfoEx.tls_index = ++indexCount; // has to start with 1
prog.first->moduleInfoEx.tls_offset = offset;
prog.first->tls.offset = offset;
prog.first->tls.index = prog.first->moduleInfoEx.tls_index;
m_dtvKeys[prog.first->tls.index].used = true;
m_dtvKeys[prog.first->tls.index].destructor = nullptr;
LOG_DEBUG(L"%s| tls index:%u offset:0x%08llx alignment:%d", prog.first->filename.c_str(), prog.first->tls.index, prog.first->tls.offset,
prog.first->tls.alignment);
}
// -
assert((int64_t)offset >= 0);
}
void RuntimeLinker::setupTlsStaticBlock() {
for (auto const& prog: m_programList) {
memcpy(&m_tlsStaticInitBlock[prog.first->tls.offset], (void*)prog.first->tls.vaddrImage, prog.first->tls.sizeImage);
}
}
bool RuntimeLinker::interceptInternal(Program* prog, uintptr_t progoffset, uintptr_t iaddr) {
#pragma pack(push, 1)
struct jumper {
const uint16_t movrax = 0xb848;
uint64_t addr;
const uint16_t jmprax = 0xe0ff;
};
#pragma pack(pop)
const auto progaddr = prog->baseVaddr + progoffset;
int iProts[2] = {0, 0};
const jumper j = {.addr = iaddr};
if (!memory::protect(progaddr, sizeof(jumper), 7 /* Set exec, read and write prot to the program's code */, &iProts[0])) return false;
::memcpy((void*)progaddr, (const void*)&j, sizeof(jumper)); // Copy the jumper to the prgoram
if (!memory::protect(progaddr, sizeof(jumper), iProts[0] /* Restore the old prot */, &iProts[1])) return false;
return true;
}
uintptr_t RuntimeLinker::execute() {
LOG_USE_MODULE(RuntimeLinker);
LOG_INFO(L"Execute()");
initTlsStaticBlock();
// pthread::initSelfForMainThread(m_tlsStaticInitBlock.size()); // call before runtimelinker
// initTLS(pthread::getSelf());
// Get and load additional Modules needed
{
for (auto const& prog: m_programList) {
LOG_DEBUG(L"Load for %s", prog.first->filename.c_str());
for (auto const& impLib: prog.second->getImportedLibs()) {
loadModules(impLib.first);
}
}
}
// - load Modules
// Relocate all (Set Imported Symbols)
m_invalidMemoryAddr = memory::alloc(INVALID_MEMORY, 4096, 0);
for (auto& prog: m_programList) {
if (prog.first) prog.second->relocate(prog.first.get(), m_invalidMemoryAddr, "");
}
setupTlsStaticBlock(); // relocate may init tls -> copy after relocate
uintptr_t const entryAddr = m_programList.begin()->first->entryOffAddr + m_programList.begin()->first->baseVaddr;
LOG_INFO(L"entry:0x%08llx", entryAddr);
return entryAddr;
}
IRuntimeLinker& accessRuntimeLinker() {
static RuntimeLinker inst;
return inst;
}

View File

@ -0,0 +1,64 @@
#pragma once
#include "formats/IFormat.h"
#include "formats/ISymbols.h"
#include "program.h"
#include "runtimeExport.h"
#include <filesystem>
#include <memory>
class IRuntimeLinker: public IRuntimeExport {
public:
virtual std::unique_ptr<Program> createProgram(std::filesystem::path const filename, uint64_t const baseSize, uint64_t const baseSizeAligned,
uint64_t const alocSize, bool useStaticTLS) = 0;
virtual uint64_t getAddrInvalidMemory() = 0;
virtual void callInitProgramms() = 0;
virtual Program* addProgram(std::unique_ptr<Program>&& prog, std::shared_ptr<IFormat> format) = 0;
virtual Program* findProgram(uint64_t vaddr) = 0;
virtual Program* findProgramById(size_t id) const = 0;
virtual uintptr_t execute() = 0;
virtual void stopModules() = 0;
virtual void stopModule(int id) = 0;
virtual void addExport(std::unique_ptr<Symbols::SymbolExport>&& symbols) = 0;
virtual bool interceptInternal(Program* prog, uintptr_t progaddr, uintptr_t iaddr) = 0;
virtual void interceptAdd(uintptr_t addr, std::string_view name, std::string_view libraryName, std::string_view modulName) = 0;
virtual uintptr_t interceptGetAddr(uintptr_t addr) const = 0;
virtual Symbols::IResolve const* getIResolve(std::string_view libName) const = 0;
virtual uintptr_t checkIntercept(uintptr_t vaddr, std::string_view symName, std::string_view libName, std::string_view modName) const = 0;
virtual std::vector<std::pair<uint64_t, uint64_t>> getExecSections() const = 0;
/**
* @brief get access to the main programm. (unique_ptr)
*
* @return Program* nullptr if no main program is missing
*/
virtual Program* accessMainProg() = 0;
virtual ~IRuntimeLinker() = default;
};
SYSV_ABI void* tlsMainGetAddr(int32_t offset);
SYSV_ABI void* tlsMainGetAddr64(int64_t offset);
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL IRuntimeLinker& accessRuntimeLinker();
__APICALL std::unique_ptr<IFormat> buildParser_Elf64(std::filesystem::path&& path, std::unique_ptr<std::ifstream>&& file);
#undef __APICALL

View File

@ -0,0 +1,231 @@
#include "exceptionHandler.h"
#include "../runtimeLinker.h"
#include "core/memory/memory.h"
#include "core/runtime/util/virtualmemory.h"
#include "logging.h"
#include "utility/progloc.h"
#include <boost/stacktrace.hpp>
#include <magic_enum/magic_enum.hpp>
#include <mutex>
// clang-format off
#include <windows.h>
#include <psapi.h>
#include <DbgHelp.h>
// clang-format on
LOG_DEFINE_MODULE(ExceptionHandler);
namespace {
struct UnwindInfo {
uint8_t Version : 3;
uint8_t Flags : 5;
uint8_t SizeOfProlog;
uint8_t CountOfCodes;
uint8_t FrameRegister : 4;
uint8_t FrameOffset : 4;
ULONG ExceptionHandler;
void* ExceptionData;
};
struct JmpHandlerData {
void setHandler(uint64_t func) { *(uint64_t*)(&m_code[2]) = func; }
// mov rax, 0x1122334455667788
// jmp rax
uint8_t m_code[16] = {0x48, 0xB8, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0xFF, 0xE0};
};
struct ExData {
JmpHandlerData handlerCode;
RUNTIME_FUNCTION runtimeFunction;
UnwindInfo unwindInfo;
};
std::optional<std::pair<uint64_t, std::string>> findModule(uint64_t address) {
std::unordered_map<uint64_t, std::string> modules;
HANDLE hProcess = GetCurrentProcess();
HMODULE hModules[1024];
DWORD cbNeeded;
if (EnumProcessModules(hProcess, hModules, sizeof(hModules), &cbNeeded)) {
MODULEINFO mi;
for (unsigned int i = 0; i < (cbNeeded / sizeof(HMODULE)); i++) {
GetModuleInformation(hProcess, hModules[i], &mi, sizeof(mi));
if ((uint64_t)mi.lpBaseOfDll <= address && address < ((uint64_t)mi.lpBaseOfDll + mi.SizeOfImage)) {
char moduleName[MAX_PATH];
if (GetModuleFileName(hModules[i], moduleName, MAX_PATH)) {
return std::make_pair((uint64_t)mi.lpBaseOfDll, std::string(moduleName));
}
}
}
}
auto prog = accessRuntimeLinker().findProgram(address);
if (prog != nullptr) {
return std::make_pair(prog->baseVaddr, prog->filename.string());
}
return std::nullopt;
}
bool tryGetSymName(PSYMBOL_INFO sym, const void* addr, std::string& name) {
auto proc = GetCurrentProcess();
static std::once_flag init;
std::call_once(init, [proc]() {
LOG_USE_MODULE(ExceptionHandler);
auto dir = std::filesystem::path(util::getProgramLoc()) / "debug";
if (!SymInitializeW(proc, dir.c_str(), true)) {
LOG_ERR(L"Failed to initialize the debug information");
}
});
// Just to be sure that the previous call won't break anything
::memset(sym, 0, sizeof(SYMBOL_INFO));
sym->SizeOfStruct = sizeof(SYMBOL_INFO);
sym->MaxNameLen = 1024;
DWORD disp = 0;
IMAGEHLP_LINE64 line = {
.SizeOfStruct = sizeof(IMAGEHLP_LINE64),
};
if (SymFromAddr(proc, (uint64_t)addr, nullptr, sym)) {
name.assign(sym->Name);
if (SymGetLineFromAddr64(proc, sym->Address, &disp, &line)) {
name += std::format(" ({}:{}:{})", line.FileName, line.LineNumber, disp);
}
return true;
}
return false;
}
void stackTrace(uint64_t addr) {
LOG_USE_MODULE(ExceptionHandler);
bool foundStart = false;
size_t countTraces = 0;
std::vector<char> sym;
sym.resize(1024 + sizeof(SYMBOL_INFO));
// Stack trace
for (auto& trace: boost::stacktrace::basic_stacktrace()) {
if (!foundStart) {
if ((uint64_t)trace.address() == addr)
foundStart = true;
else
continue;
}
if (++countTraces > 4) break;
if (trace.empty()) {
LOG_ERR(L"????");
}
std::string fileName;
if (!tryGetSymName((PSYMBOL_INFO)sym.data(), trace.address(), fileName)) {
// Failed to get the source file name, clearing the string
fileName.clear();
}
auto optModuleInfo = findModule((uint64_t)trace.address());
if (fileName.empty()) {
auto optModuleInfo = findModule((uint64_t)trace.address());
if (optModuleInfo) {
LOG_ERR(L"offset:0x%08llx\t base:0x%08llx\t%S", (uint64_t)trace.address() - optModuleInfo->first, optModuleInfo->first, optModuleInfo->second.c_str());
} else {
LOG_ERR(L"0x%08llx\t", trace.address());
}
} else {
if (optModuleInfo) {
LOG_ERR(L"0x%08llx base:0x%08llx %S\n\t%S", trace.address(), optModuleInfo->first, optModuleInfo->second.c_str(), fileName.c_str());
} else {
LOG_ERR(L"0x%08llx\n\t %S", trace.address(), fileName.c_str());
}
}
}
// -
}
enum class AccessViolationType { Unknown, Read, Write, Execute };
static EXCEPTION_DISPOSITION DefaultExceptionHandler(PEXCEPTION_RECORD exception_record, ULONG64 /*EstablisherFrame*/, PCONTEXT /*ContextRecord*/,
PDISPATCHER_CONTEXT dispatcher_context) {
LOG_USE_MODULE(ExceptionHandler);
auto exceptionAddr = (uint64_t)(exception_record->ExceptionAddress);
auto violationAddr = exception_record->ExceptionInformation[1];
stackTrace(exceptionAddr);
uint64_t baseAddr = 0;
std::string moduleName;
auto optModuleInfo = findModule(exceptionAddr);
if (optModuleInfo) {
baseAddr = optModuleInfo->first;
moduleName = optModuleInfo->second;
}
AccessViolationType violationType;
switch (exception_record->ExceptionInformation[0]) {
case 0: violationType = AccessViolationType::Read; break;
case 1: violationType = AccessViolationType::Write; break;
case 8: violationType = AccessViolationType::Execute; break;
default: LOG_CRIT(L"unknown exception at 0x%08llx, module base:0x%08llx %S", exceptionAddr, baseAddr, moduleName.data()); break;
}
LOG_CRIT(L"Access violation: %S at addr:0x%08llx info:0x%08llx %S, module base:0x%08llx %S", magic_enum::enum_name(violationType).data(), exceptionAddr,
violationAddr, (violationAddr == accessRuntimeLinker().getAddrInvalidMemory() ? L"(Unpatched object)" : L""), baseAddr, moduleName.data());
//);
return ExceptionContinueExecution;
}
} // namespace
namespace ExceptionHandler {
uint64_t getAllocSize() {
return sizeof(ExData);
}
void install(uint64_t imageAddr, uint64_t handlerDstAddr, uint64_t imageSize) {
auto functionTable = new ((void*)handlerDstAddr) ExData;
auto& func = functionTable->runtimeFunction;
func.BeginAddress = 0;
func.EndAddress = imageSize;
func.UnwindData = (uint64_t)&functionTable->unwindInfo - imageAddr;
auto& unwindData = functionTable->unwindInfo;
unwindData.Version = 1;
unwindData.Flags = UNW_FLAG_EHANDLER;
unwindData.SizeOfProlog = 0;
unwindData.CountOfCodes = 0;
unwindData.FrameRegister = 0;
unwindData.FrameOffset = 0;
unwindData.ExceptionHandler = (uint64_t)&functionTable->handlerCode - imageAddr;
unwindData.ExceptionData = nullptr;
functionTable->handlerCode.setHandler((uint64_t)DefaultExceptionHandler);
RtlAddFunctionTable(&functionTable->runtimeFunction, 1, imageAddr);
flushInstructionCache((uint64_t)&functionTable->handlerCode, sizeof(JmpHandlerData));
}
} // namespace ExceptionHandler

View File

@ -0,0 +1,10 @@
#pragma once
#include <memory>
#include <stdint.h>
namespace ExceptionHandler {
uint64_t getAllocSize();
void install(uint64_t imageAddr, uint64_t handlerDstAddr, uint64_t imageSize);
} // namespace ExceptionHandler

View File

@ -0,0 +1,69 @@
#include "moduleLoader.h"
#include "logging.h"
#include <boost/beast/core/detail/base64.hpp>
#include <boost/functional/hash.hpp>
#include <boost/uuid/detail/sha1.hpp>
#include <vector>
#include <windows.h>
#undef min
LOG_DEFINE_MODULE(ModuleLoader);
std::pair<void*, std::unique_ptr<Symbols::SymbolExport>> loadModule(const char* libName, const wchar_t* filepath, int libVersion) {
LOG_USE_MODULE(ModuleLoader);
HMODULE hModule = LoadLibraryW(filepath);
if (hModule == NULL) {
LOG_ERR(L"Couldn't load library %S, err:%d", filepath, GetLastError());
return {};
}
auto const imageDosHeader = (PIMAGE_DOS_HEADER)hModule;
if (imageDosHeader->e_magic != IMAGE_DOS_SIGNATURE) {
LOG_ERR(L"Wrong image signature %x for %S", imageDosHeader->e_magic, filepath);
return {};
}
auto const imageNtHeaders = (PIMAGE_NT_HEADERS)((unsigned char*)imageDosHeader + imageDosHeader->e_lfanew);
if (imageNtHeaders->Signature != IMAGE_NT_SIGNATURE) {
LOG_ERR(L"Wrong PE signature %x for %S", imageDosHeader->e_magic, filepath);
return {};
}
PIMAGE_OPTIONAL_HEADER imageOptionalHeader = (PIMAGE_OPTIONAL_HEADER)&imageNtHeaders->OptionalHeader;
PIMAGE_DATA_DIRECTORY imageExportDataDirectory = &(imageOptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXPORT]);
PIMAGE_EXPORT_DIRECTORY imageExportDirectory = (PIMAGE_EXPORT_DIRECTORY)((unsigned char*)hModule + imageExportDataDirectory->VirtualAddress);
DWORD numberOfNames = imageExportDirectory->NumberOfNames;
PDWORD exportAddressTable = (PDWORD)((unsigned char*)hModule + imageExportDirectory->AddressOfFunctions);
PWORD nameOrdinalsPointer = (PWORD)((unsigned char*)hModule + imageExportDirectory->AddressOfNameOrdinals);
PDWORD exportNamePointerTable = (PDWORD)((unsigned char*)hModule + imageExportDirectory->AddressOfNames);
auto libInfo = std::make_unique<Symbols::SymbolExport>(libName, libVersion, "", 1, 0);
for (size_t n = 0; n < numberOfNames; n++) {
auto name = std::string_view((const char*)((unsigned char*)hModule + exportNamePointerTable[n]));
if (name.ends_with("=")) {
std::string nid(name.data(), 0, name.size() - 1);
auto const pFunc = (uint64_t)((unsigned char*)hModule + exportAddressTable[nameOrdinalsPointer[n]]);
libInfo->symbolsMap.emplace(std::make_pair(nid, Symbols::SymbolExport::Symbol {nid, {}, pFunc, Symbols::SymbolType::Func}));
} else if (name == "MODULE_NAME") {
auto const val = *(char const**)((unsigned char*)hModule + exportAddressTable[nameOrdinalsPointer[n]]);
libInfo->modulName = val;
}
}
if (libInfo->modulName.empty()) {
LOG_CRIT(L"No Module name in %S", libName);
}
return {(void*)hModule, std::move(libInfo)};
}
void unloadModule(void* handle) {
FreeLibrary((HMODULE)handle);
}

View File

@ -0,0 +1,11 @@
#pragma once
#include "../formats/ISymbols.h"
#include <memory>
#include <string>
#include <utility>
#include <vector>
std::pair<void*, std::unique_ptr<Symbols::SymbolExport>> loadModule(const char* name, const wchar_t* filepath, int libVersion);
void unloadModule(void* handle);

55
core/runtime/util/plt.h Normal file
View File

@ -0,0 +1,55 @@
#pragma once
#include <iterator>
#include <stdint.h>
#pragma pack(1)
namespace {
struct JmpWithIndex {
void setHandler(void* handler) {
auto handlerAddr = (int64_t)(handler);
auto ripAddr = (int64_t)(&m_code[10]);
auto offset64 = handlerAddr - ripAddr;
auto offset32 = (uint32_t)offset64;
*(uint32_t*)(&m_code[6]) = offset32;
}
void setIndex(uint32_t index) { *(uint32_t*)(&m_code[1]) = index; }
static uint64_t getSize() { return 16; }
private:
// 68 00 00 00 00 push <index>
// E9 E0 FF FF FF jmp <handler>
uint8_t m_code[16] = {0x68, 0x00, 0x00, 0x00, 0x00, 0xE9, 0x00, 0x00, 0x00, 0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
};
} // namespace
struct CallPlt {
explicit CallPlt(uint32_t table_size) {
for (uint32_t index = 0; index < table_size; index++) {
auto* c = new ((uint8_t*)getAddr(index)) JmpWithIndex;
c->setIndex(index);
c->setHandler(this);
}
}
uint64_t getAddr(uint32_t index) { return (uint64_t)(&m_code[32] + JmpWithIndex::getSize() * index); }
void setPltGot(uint64_t vaddr) { *(uint64_t*)(&m_code[2]) = vaddr; }
static uint64_t getSize(uint32_t tableSize) { return 32 + JmpWithIndex::getSize() * tableSize; }
private:
// 0: 49 bb 88 77 66 55 44 movabs r11,0x1122334455667788
// 7: 33 22 11
// a: 41 ff 73 08 push QWORD PTR [r11+0x8]
// e: 41 ff 63 10 jmp QWORD PTR [r11+0x10]
uint8_t m_code[32] = {0x49, 0xBB, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x41, 0xFF,
0x73, 0x08, 0x41, 0xFF, 0x63, 0x10, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90};
};
#pragma pack()

View File

@ -0,0 +1,38 @@
#include "virtualmemory.h"
#include "core/memory/memory.h"
#include "logging.h"
#include <windows.h>
LOG_DEFINE_MODULE(VIRTUALMEMORY);
namespace {} // namespace
bool flushInstructionCache(uint64_t address, uint64_t size) {
LOG_USE_MODULE(VIRTUALMEMORY);
if (::FlushInstructionCache(GetCurrentProcess(), reinterpret_cast<LPVOID>(static_cast<uintptr_t>(address)), size) == 0) {
LOG_ERR(L"FlushInstructionCache() failed: 0x%04x", static_cast<uint32_t>(GetLastError()));
return false;
}
return true;
}
bool patchReplace(uint64_t vaddr, uint64_t const value) {
int oldMode;
memory::protect(vaddr, 8, SceProtRead | SceProtWrite, &oldMode);
auto* ptr = reinterpret_cast<uint64_t*>(vaddr);
bool ret = (*ptr != value);
*ptr = value;
memory::protect(vaddr, 8, oldMode);
if (memory::isExecute(oldMode)) {
flushInstructionCache(vaddr, 8);
}
return ret;
}

View File

@ -0,0 +1,6 @@
#pragma once
#include <stdint.h>
bool flushInstructionCache(uint64_t address, uint64_t size);
bool patchReplace(uint64_t vaddr, uint64_t const value);

View File

@ -0,0 +1,7 @@
add_library(systemContent OBJECT
systemContent.cpp
)
add_dependencies(systemContent third_party psOff_utility)
file(COPY systemContent.h DESTINATION ${DST_INCLUDE_DIR}/systemContent)

View File

@ -0,0 +1,146 @@
#define __APICALL_EXTERN
#include "systemContent.h"
#undef __APICALL_EXTERN
#include "logging.h"
#include "utility/utility.h"
#include <filesystem>
#include <fstream>
#include <unordered_map>
LOG_DEFINE_MODULE(SYSTEMCONTENT);
namespace {
struct ParamInfo {
uint16_t type;
uint32_t size1;
uint32_t size2;
uint16_t nameOffset;
uint32_t valueOffset;
};
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> loadSFO(std::filesystem::path const& root) {
LOG_USE_MODULE(SYSTEMCONTENT);
auto file = util::openFile(root / L"param.sfo");
if (!file) {
LOG_ERR(L"No param.sfo");
return {};
}
// Check Magic
{
std::array<uint8_t, 8> buffer;
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
LOG_ERR(L"Couldn't read .sfo magic");
return {};
}
uint32_t const magic1 = *(uint32_t const*)&buffer[0];
uint32_t const magic2 = *(uint32_t const*)&buffer[4];
if (magic1 != 0x46535000 && magic2 != 0x00000101) {
LOG_ERR(L".sfo magic error| 0x%08x 0x%08x", magic1, magic2);
return {};
}
}
// - Magic
auto [nameOffset, valueOffset, numParams] = [&]() -> std::tuple<uint32_t, uint32_t, uint32_t> {
std::array<uint8_t, 12> buffer;
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
LOG_ERR(L"Couldn't read .sfo Header");
return {};
}
return std::make_tuple(*(uint32_t const*)&buffer[0], *(uint32_t const*)&buffer[4], *(uint32_t const*)&buffer[8]);
}();
// Read Params
std::vector<ParamInfo> params(numParams);
{
std::array<uint8_t, 16> buffer;
for (uint16_t n = 0; n < numParams; ++n) {
if (!util::readFile(file.get(), L"param.sfo", (char*)buffer.data(), buffer.size())) {
LOG_ERR(L"Couldn't read .sfo paramInfo %llu", n);
return {};
}
params[n] = ParamInfo {.type = *(uint16_t const*)&buffer[2],
.size1 = *(uint32_t const*)&buffer[4],
.size2 = *(uint32_t const*)&buffer[8],
.nameOffset = *(uint16_t const*)&buffer[0],
.valueOffset = *(uint32_t const*)&buffer[12]};
if (params[n].type != 0x0204 && params[n].type != 0x0404) {
LOG_ERR(L"unknown .sfo param %u type:%u", n, params[n].type);
return {};
}
}
}
// - params
// read nameList
const uint32_t nameListSize = valueOffset - nameOffset;
std::vector<uint8_t> nameList(nameListSize);
file->seekg(nameOffset);
if (!util::readFile(file.get(), L"param.sfo", (char*)nameList.data(), nameList.size())) {
LOG_ERR(L"Couldn't read .sfo nameList");
return {};
}
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> paramList;
for (uint16_t n = 0; n < numParams; ++n) {
auto const name = std::string((char*)&nameList[params[n].nameOffset]);
file->seekg(valueOffset + params[n].valueOffset, std::ios_base::beg);
std::vector<uint8_t> paramValue(params[n].size1);
if (!util::readFile(file.get(), L"param.sfo", (char*)paramValue.data(), paramValue.size())) {
LOG_ERR(L"Couldn't read .sfo param %u value");
return {};
}
if (params[n].type == 0x0404)
LOG_DEBUG(L"Read .sfo param[%u] %S| size:%u value(uint):%u", n, name.data(), paramValue.size(), *(uint32_t*)paramValue.data());
else if (params[n].type == 0x0204)
LOG_DEBUG(L"Read .sfo param[%u] %S| size:%u value(string):%S", n, name.data(), paramValue.size(), (char*)paramValue.data());
paramList[std::move(name)] = std::make_pair(params[n].type, std::move(paramValue));
}
return paramList;
}
} // namespace
class SystemContent: public ISystemContent {
private:
std::unordered_map<std::string, std::pair<uint16_t, std::vector<uint8_t>>> m_sfoParams;
public:
void init(std::filesystem::path const& path) final;
std::optional<uint32_t> getInt(std::string_view name) const final {
if (auto it = m_sfoParams.find(name.data()); it != m_sfoParams.end()) {
if (it->second.first != 0x0404) return {};
return {*(uint32_t*)it->second.second.data()};
}
return {};
}
std::optional<std::string_view> getString(std::string_view name) const final {
if (auto it = m_sfoParams.find(name.data()); it != m_sfoParams.end()) {
if (it->second.first != 0x0204) return {};
return {(const char*)it->second.second.data()};
}
return {};
}
};
void SystemContent::init(std::filesystem::path const& root) {
if (!m_sfoParams.empty()) return;
m_sfoParams = loadSFO(root);
}
ISystemContent& accessSystemContent() {
static SystemContent inst;
return inst;
}

View File

@ -0,0 +1,43 @@
#pragma once
#include <optional>
#include <string_view>
namespace std::filesystem {
class path;
}
class ISystemContent {
public:
/**
* @brief Checks if the profided path contain "param.sfo" and reads it
*
* @param path
*/
virtual void init(std::filesystem::path const& path) = 0;
/**
* @brief Get an int value from the sfo
*
* @param name
* @return std::optional<uint32_t>
*/
virtual std::optional<uint32_t> getInt(std::string_view name) const = 0;
/**
* @brief Get an string value from the sfo
*
* @param name
* @return std::optional<std::string_view>
*/
virtual std::optional<std::string_view> getString(std::string_view name) const = 0;
};
#if defined(__APICALL_EXTERN)
#define __APICALL __declspec(dllexport)
#elif defined(__APICALL_IMPORT)
#define __APICALL __declspec(dllimport)
#else
#define __APICALL
#endif
__APICALL ISystemContent& accessSystemContent();
#undef __APICALL

23
core/tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,23 @@
enable_testing()
add_compile_definitions(
BOOST_ALL_NO_LIB
)
link_libraries(gtest_main gmock gmock_main)
add_link_options(/DEBUG)
link_directories(
${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/lib
)
include_directories(
${PRJ_SRC_DIR}
${Vulkan_INCLUDE_DIRS}
${PRJ_SRC_DIR}/tools/logging
)
add_subdirectory(core)
install(TARGETS semaphore_test RUNTIME DESTINATION .)

View File

@ -0,0 +1 @@
add_subdirectory(semaphore)

View File

@ -0,0 +1,17 @@
enable_testing()
add_executable(semaphore_test
entry.cpp
${PRJ_SRC_DIR}/core/kernel/semaphore_fifo.cpp
)
add_test(NAME semaphore_test COMMAND semaphore_test)
add_dependencies(semaphore_test third_party psOff_utility logging_stub)
target_link_libraries(semaphore_test PRIVATE
logging_stub.lib
libboost_thread
libboost_chrono
)

View File

@ -0,0 +1,279 @@
#include "core/kernel/semaphore.h"
#include "modules_include/common.h"
#include <fff/fff.h>
#include <future>
#include <gtest/gtest.h>
DEFINE_FFF_GLOBALS;
namespace pthread {
FAKE_VALUE_FUNC(int, getThreadId);
}
TEST(core_semaphore, init) {
RESET_FAKE(pthread::getThreadId);
pthread::getThreadId_fake.return_val = 4;
constexpr int initCount = 1;
constexpr int maxCount = 10;
{
auto sem = createSemaphore_fifo("test", initCount, maxCount);
EXPECT_TRUE(sem);
EXPECT_EQ(sem->getSignalCounter(), initCount);
}
{
auto sem = createSemaphore_fifo(nullptr, initCount, maxCount);
EXPECT_TRUE(sem);
EXPECT_EQ(sem->getSignalCounter(), initCount);
}
}
TEST(core_semaphore, polling_1) {
RESET_FAKE(pthread::getThreadId);
pthread::getThreadId_fake.return_val = 4;
constexpr int initCount = 1;
constexpr int maxCount = 10;
auto sem = createSemaphore_fifo("test", initCount, maxCount);
auto fut = std::async(std::launch::async, [&sem] {
{
auto res = sem->poll(2);
EXPECT_EQ(res, getErr(ErrCode::_EAGAIN));
}
{
auto res = sem->poll(1);
EXPECT_EQ(res, Ok);
EXPECT_EQ(sem->getSignalCounter(), 0);
}
{
auto res = sem->poll(2);
EXPECT_EQ(res, getErr(ErrCode::_EAGAIN));
}
{
auto resSignal = sem->signal(2);
EXPECT_EQ(resSignal, Ok);
auto res = sem->poll(2);
EXPECT_EQ(res, Ok);
EXPECT_EQ(sem->getSignalCounter(), 0);
}
{
auto resSignal = sem->signal(4);
EXPECT_EQ(resSignal, Ok);
auto res1 = sem->poll(2);
EXPECT_EQ(res1, Ok);
EXPECT_EQ(sem->getSignalCounter(), 2);
auto res2 = sem->poll(2);
EXPECT_EQ(res2, Ok);
EXPECT_EQ(sem->getSignalCounter(), 0);
}
});
auto res = fut.wait_for(std::chrono::milliseconds(10));
EXPECT_NE(res, std::future_status::timeout);
}
TEST(core_semaphore, signal_1) {
RESET_FAKE(pthread::getThreadId);
pthread::getThreadId_fake.return_val = 4;
constexpr int initCount = 1;
constexpr int maxCount = 10;
auto sem = createSemaphore_fifo("test", initCount, maxCount);
{ // Check wait instant release
auto resSignal = sem->signal(1);
EXPECT_EQ(resSignal, Ok);
EXPECT_EQ(sem->getSignalCounter(), 2);
auto fut = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, Ok);
}
});
auto res = fut.wait_for(std::chrono::milliseconds(10));
EXPECT_NE(res, std::future_status::timeout);
}
EXPECT_EQ(sem->getSignalCounter(), 0);
{ // Check timeout
auto resSignal = sem->signal(1);
EXPECT_EQ(resSignal, Ok);
EXPECT_EQ(sem->getSignalCounter(), 1);
auto fut = std::async(std::launch::async, [&sem] {
{
uint32_t micros = 1;
auto res = sem->wait(2, &micros); // wait timeout
EXPECT_EQ(res, getErr(ErrCode::_ETIMEDOUT));
}
});
auto res = fut.wait_for(std::chrono::milliseconds(10));
EXPECT_NE(res, std::future_status::timeout);
}
EXPECT_EQ(sem->getSignalCounter(), 1);
{ // Check wait signal afterwards
auto fut = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, Ok);
}
});
std::this_thread::sleep_for(
std::chrono::microseconds(100)); // this or mock condition var
auto resSignal = sem->signal(1);
EXPECT_EQ(resSignal, Ok);
EXPECT_EQ(sem->getSignalCounter(), 2);
auto res = fut.wait_for(std::chrono::milliseconds(10));
EXPECT_NE(res, std::future_status::timeout);
}
}
TEST(core_semaphore, signal_2) {
RESET_FAKE(pthread::getThreadId);
pthread::getThreadId_fake.custom_fake = []() {
static int counter = 0;
return ++counter;
};
constexpr int initCount = 0;
constexpr int maxCount = 10;
auto sem = createSemaphore_fifo("test", initCount, maxCount);
EXPECT_EQ(sem->getSignalCounter(), 0);
size_t countItems = 0;
{ // Check wait signal afterwards (sequenze release)
auto fut1 = std::async(std::launch::async, [&sem, &countItems] {
{
auto res = sem->wait(2, nullptr); // wait forever
++countItems;
EXPECT_EQ(res, Ok);
}
});
auto fut2 = std::async(std::launch::async, [&sem, &countItems] {
{
auto res = sem->wait(2, nullptr); // wait forever
++countItems;
EXPECT_EQ(res, Ok);
}
});
std::this_thread::sleep_for(
std::chrono::microseconds(100)); // this or mock condition var
{
auto resSignal = sem->signal(2);
EXPECT_EQ(resSignal, Ok);
}
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
EXPECT_NE(res1, std::future_status::timeout);
EXPECT_EQ(sem->getSignalCounter(), 0);
EXPECT_EQ(countItems, 1);
{
auto resSignal = sem->signal(2);
EXPECT_EQ(resSignal, Ok);
}
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
EXPECT_NE(res2, std::future_status::timeout);
EXPECT_EQ(countItems, 2);
}
{ // Check wait signal afterwards (direct release)
auto fut1 = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, Ok);
}
});
auto fut2 = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, Ok);
}
});
std::this_thread::sleep_for(
std::chrono::microseconds(100)); // this or mock condition var
{
auto resSignal = sem->signal(4);
EXPECT_EQ(resSignal, Ok);
}
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
EXPECT_NE(res1, std::future_status::timeout);
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
EXPECT_NE(res2, std::future_status::timeout);
EXPECT_EQ(sem->getSignalCounter(), 0);
}
sem.reset();
}
TEST(core_semaphore, signal_exit) {
RESET_FAKE(pthread::getThreadId);
pthread::getThreadId_fake.custom_fake = []() {
static int counter = 0;
return ++counter;
};
constexpr int initCount = 0;
constexpr int maxCount = 10;
auto sem = createSemaphore_fifo("test", initCount, maxCount);
EXPECT_EQ(sem->getSignalCounter(), 0);
{ // Check wait signal afterwards and exit release
auto fut1 = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, Ok);
}
});
auto fut2 = std::async(std::launch::async, [&sem] {
{
auto res = sem->wait(2, nullptr); // wait forever
EXPECT_EQ(res, getErr(ErrCode::_ECANCELED));
}
});
std::this_thread::sleep_for(
std::chrono::microseconds(100)); // this or mock condition var
auto resSignal = sem->signal(2);
EXPECT_EQ(resSignal, Ok);
auto res1 = fut1.wait_for(std::chrono::microseconds(100));
EXPECT_NE(res1, std::future_status::timeout);
EXPECT_EQ(sem->getSignalCounter(), 0);
auto res2 = fut2.wait_for(std::chrono::microseconds(100));
EXPECT_EQ(res2, std::future_status::timeout);
sem.reset();
}
}

View File

@ -0,0 +1,7 @@
add_library(timer OBJECT
timer.cpp
)
add_dependencies(timer third_party)
file(COPY timer.h DESTINATION ${DST_INCLUDE_DIR}/timer)

View File

@ -0,0 +1,111 @@
#pragma once
#include <cstdint>
#include <windows.h>
namespace System {
struct SysTimeStruct {
uint16_t Year; // NOLINT(readability-identifier-naming)
uint16_t Month; // NOLINT(readability-identifier-naming)
uint16_t Day; // NOLINT(readability-identifier-naming)
uint16_t Hour; // NOLINT(readability-identifier-naming)
uint16_t Minute; // NOLINT(readability-identifier-naming)
uint16_t Second; // NOLINT(readability-identifier-naming)
uint16_t Milliseconds; // NOLINT(readability-identifier-naming)
bool is_invalid; // NOLINT(readability-identifier-naming)
};
struct SysFileTimeStruct {
FILETIME time;
bool is_invalid;
};
// NOLINTNEXTLINE(google-runtime-references)
inline void sys_file_to_system_time_utc(const SysFileTimeStruct& f, SysTimeStruct& t) {
SYSTEMTIME s;
if (f.is_invalid || (FileTimeToSystemTime(&f.time, &s) == 0)) {
t.is_invalid = true;
return;
}
t.is_invalid = false;
t.Year = s.wYear;
t.Month = s.wMonth;
t.Day = s.wDay;
t.Hour = s.wHour;
t.Minute = s.wMinute;
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
t.Milliseconds = s.wMilliseconds;
}
// NOLINTNEXTLINE(google-runtime-references)
inline void sys_time_t_to_system(time_t t, SysTimeStruct& s) {
SysFileTimeStruct ft {};
LONGLONG ll = Int32x32To64(t, 10000000) + 116444736000000000;
ft.time.dwLowDateTime = static_cast<DWORD>(ll);
ft.time.dwHighDateTime = static_cast<DWORD>(static_cast<uint64_t>(ll) >> 32u);
ft.is_invalid = false;
sys_file_to_system_time_utc(ft, s);
}
// NOLINTNEXTLINE(google-runtime-references)
inline void sys_system_to_file_time_utc(const SysTimeStruct& f, SysFileTimeStruct& t) {
SYSTEMTIME s;
s.wYear = f.Year;
s.wMonth = f.Month;
s.wDay = f.Day;
s.wHour = f.Hour;
s.wMinute = f.Minute;
s.wSecond = f.Second;
s.wMilliseconds = f.Milliseconds;
t.is_invalid = (f.is_invalid || (SystemTimeToFileTime(&s, &t.time) == 0));
}
// Retrieves the current local date and time
// NOLINTNEXTLINE(google-runtime-references)
inline void sys_get_system_time(SysTimeStruct& t) {
SYSTEMTIME s;
GetLocalTime(&s);
t.is_invalid = false;
t.Year = s.wYear;
t.Month = s.wMonth;
t.Day = s.wDay;
t.Hour = s.wHour;
t.Minute = s.wMinute;
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
t.Milliseconds = s.wMilliseconds;
}
// Retrieves the current system date and time in Coordinated Universal Time (UTC).
// NOLINTNEXTLINE(google-runtime-references)
inline void sys_get_system_time_utc(SysTimeStruct& t) {
SYSTEMTIME s;
GetSystemTime(&s);
t.is_invalid = false;
t.Year = s.wYear;
t.Month = s.wMonth;
t.Day = s.wDay;
t.Hour = s.wHour;
t.Minute = s.wMinute;
t.Second = (s.wSecond == 60 ? 59 : s.wSecond);
t.Milliseconds = s.wMilliseconds;
}
inline void sys_query_performance_frequency(uint64_t* freq) {
LARGE_INTEGER f;
QueryPerformanceFrequency(&f);
*freq = f.QuadPart;
}
inline void sys_query_performance_counter(uint64_t* counter) {
LARGE_INTEGER c;
QueryPerformanceCounter(&c);
*counter = c.QuadPart;
}
} // namespace System

Some files were not shown because too many files have changed in this diff Show More