mirror of
https://github.com/SysRay/psOff_public.git
synced 2024-11-22 22:09:39 +00:00
core
This commit is contained in:
parent
38806e48f2
commit
b181559a19
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@ -19,7 +19,6 @@
|
||||
"cmake.sourceDirectory": "${workspaceFolder}",
|
||||
"cmake.buildDirectory": "${workspaceFolder}/_build/_Release",
|
||||
"cmake.configureArgs": [
|
||||
// "-DBUILD_CORE=1",
|
||||
"-DISDEBUG=1"
|
||||
],
|
||||
"cmake.configureSettings": {
|
||||
|
@ -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
85
core/CMakeLists.txt
Normal 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
14
core/dmem/CMakeLists.txt
Normal 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
40
core/dmem/imemory.h
Normal 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
124
core/dmem/memoryManager.cpp
Normal 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
55
core/dmem/memoryManager.h
Normal 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
|
545
core/dmem/types/directmem.cpp
Normal file
545
core/dmem/types/directmem.cpp
Normal 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);
|
||||
}
|
267
core/dmem/types/flexiblemem.cpp
Normal file
267
core/dmem/types/flexiblemem.cpp
Normal 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
7
core/dmem/types/memory.h
Normal 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);
|
15
core/fileManager/CMakeLists.txt
Normal file
15
core/fileManager/CMakeLists.txt
Normal 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)
|
273
core/fileManager/fileManager.cpp
Normal file
273
core/fileManager/fileManager.cpp
Normal 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;
|
||||
}
|
133
core/fileManager/fileManager.h
Normal file
133
core/fileManager/fileManager.h
Normal 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
87
core/fileManager/ifile.h
Normal 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;
|
||||
};
|
8
core/fileManager/readme.md
Normal file
8
core/fileManager/readme.md
Normal 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>
|
157
core/fileManager/types/type_file.cpp
Normal file
157
core/fileManager/types/type_file.cpp
Normal 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;
|
||||
}
|
9
core/fileManager/types/type_file.h
Normal file
9
core/fileManager/types/type_file.h
Normal 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);
|
48
core/fileManager/types/type_in.cpp
Normal file
48
core/fileManager/types/type_in.cpp
Normal 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;
|
||||
}
|
7
core/fileManager/types/type_in.h
Normal file
7
core/fileManager/types/type_in.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_in();
|
55
core/fileManager/types/type_lib.cpp
Normal file
55
core/fileManager/types/type_lib.cpp
Normal 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;
|
||||
}
|
6
core/fileManager/types/type_lib.h
Normal file
6
core/fileManager/types/type_lib.h
Normal file
@ -0,0 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
struct Program;
|
||||
std::unique_ptr<IFile> createType_lib(Program* prog);
|
46
core/fileManager/types/type_null.cpp
Normal file
46
core/fileManager/types/type_null.cpp
Normal 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;
|
||||
}
|
7
core/fileManager/types/type_null.h
Normal file
7
core/fileManager/types/type_null.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_null();
|
70
core/fileManager/types/type_out.cpp
Normal file
70
core/fileManager/types/type_out.cpp
Normal 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;
|
||||
}
|
12
core/fileManager/types/type_out.h
Normal file
12
core/fileManager/types/type_out.h
Normal 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);
|
50
core/fileManager/types/type_random.cpp
Normal file
50
core/fileManager/types/type_random.cpp
Normal 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;
|
||||
}
|
7
core/fileManager/types/type_random.h
Normal file
7
core/fileManager/types/type_random.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_random();
|
63
core/fileManager/types/type_rng.cpp
Normal file
63
core/fileManager/types/type_rng.cpp
Normal 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;
|
||||
}
|
7
core/fileManager/types/type_rng.h
Normal file
7
core/fileManager/types/type_rng.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_rng();
|
48
core/fileManager/types/type_zero.cpp
Normal file
48
core/fileManager/types/type_zero.cpp
Normal 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;
|
||||
}
|
7
core/fileManager/types/type_zero.h
Normal file
7
core/fileManager/types/type_zero.h
Normal file
@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "../ifile.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
std::unique_ptr<IFile> createType_zero();
|
9
core/hotkeys/CMakeLists.txt
Normal file
9
core/hotkeys/CMakeLists.txt
Normal 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
149
core/hotkeys/hotkeys.cpp
Normal 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
72
core/hotkeys/hotkeys.h
Normal 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
|
35
core/imports/exports/pm4_custom.h
Normal file
35
core/imports/exports/pm4_custom.h
Normal 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
|
3
core/imports/exports/readme.md
Normal file
3
core/imports/exports/readme.md
Normal file
@ -0,0 +1,3 @@
|
||||
## Exports
|
||||
|
||||
Exports currently provided by the emulator itself.
|
7
core/initParams/CMakeLists.txt
Normal file
7
core/initParams/CMakeLists.txt
Normal 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)
|
101
core/initParams/initParams.cpp
Normal file
101
core/initParams/initParams.cpp
Normal 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");
|
||||
}
|
37
core/initParams/initParams.h
Normal file
37
core/initParams/initParams.h
Normal 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
|
12
core/kernel/CMakeLists.txt
Normal file
12
core/kernel/CMakeLists.txt
Normal 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
13
core/kernel/errors.cpp
Normal 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
33
core/kernel/errors.h
Normal 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
177
core/kernel/eventflag.cpp
Normal 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, µs);
|
||||
}
|
||||
|
||||
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
44
core/kernel/eventflag.h
Normal 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
298
core/kernel/eventqueue.cpp
Normal 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
41
core/kernel/eventqueue.h
Normal 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
|
55
core/kernel/eventqueue_types.h
Normal file
55
core/kernel/eventqueue_types.h
Normal 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
743
core/kernel/filesystem.cpp
Normal 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
138
core/kernel/filesystem.h
Normal 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
1296
core/kernel/pthread.cpp
Normal file
File diff suppressed because it is too large
Load Diff
195
core/kernel/pthread.h
Normal file
195
core/kernel/pthread.h
Normal 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
|
169
core/kernel/pthread_intern.h
Normal file
169
core/kernel/pthread_intern.h
Normal 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;
|
||||
};
|
51
core/kernel/pthread_types.h
Normal file
51
core/kernel/pthread_types.h
Normal 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
40
core/kernel/semaphore.h
Normal 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
|
311
core/kernel/semaphore_fifo.cpp
Normal file
311
core/kernel/semaphore_fifo.cpp
Normal 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);
|
||||
}
|
7
core/memory/CMakeLists.txt
Normal file
7
core/memory/CMakeLists.txt
Normal 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
339
core/memory/memory.cpp
Normal 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, ¶m, 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), ¶m2, 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
60
core/memory/memory.h
Normal 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
|
17
core/networking/CMakeLists.txt
Normal file
17
core/networking/CMakeLists.txt
Normal 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)
|
57
core/networking/networking.cpp
Normal file
57
core/networking/networking.cpp
Normal 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();
|
||||
}
|
165
core/networking/networking.h
Normal file
165
core/networking/networking.h
Normal 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
|
416
core/networking/states/offline.cpp
Normal file
416
core/networking/states/offline.cpp
Normal 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;
|
||||
}
|
122
core/networking/states/offline.h
Normal file
122
core/networking/states/offline.h
Normal 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;
|
||||
};
|
126
core/networking/states/online.h
Normal file
126
core/networking/states/online.h
Normal 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;
|
||||
};
|
97
core/networking/states/online/epoll.cpp
Normal file
97
core/networking/states/online/epoll.cpp
Normal 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;
|
||||
}
|
719
core/networking/states/online/http.cpp
Normal file
719
core/networking/states/online/http.cpp
Normal 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;
|
||||
}
|
196
core/networking/states/online/netctl.cpp
Normal file
196
core/networking/states/online/netctl.cpp
Normal 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;
|
||||
}
|
231
core/networking/states/online/resolver.cpp
Normal file
231
core/networking/states/online/resolver.cpp
Normal 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;
|
||||
}
|
113
core/networking/states/online/socket.cpp
Normal file
113
core/networking/states/online/socket.cpp
Normal 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
10
core/readme.md
Normal 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.
|
16
core/runtime/CMakeLists.txt
Normal file
16
core/runtime/CMakeLists.txt
Normal 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)
|
59
core/runtime/exports/intern.cpp
Normal file
59
core/runtime/exports/intern.cpp
Normal 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
|
16
core/runtime/exports/intern.h
Normal file
16
core/runtime/exports/intern.h
Normal 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
|
26
core/runtime/exports/macro.h
Normal file
26
core/runtime/exports/macro.h
Normal 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)); \
|
||||
}
|
68
core/runtime/formats/IFormat.h
Normal file
68
core/runtime/formats/IFormat.h
Normal 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; }
|
||||
};
|
93
core/runtime/formats/ISymbols.h
Normal file
93
core/runtime/formats/ISymbols.h
Normal 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
|
1542
core/runtime/formats/elf64.cpp
Normal file
1542
core/runtime/formats/elf64.cpp
Normal file
File diff suppressed because it is too large
Load Diff
295
core/runtime/formats/elf64.h
Normal file
295
core/runtime/formats/elf64.h
Normal 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
|
10
core/runtime/memoryLayout.h
Normal file
10
core/runtime/memoryLayout.h
Normal 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
112
core/runtime/procParam.h
Normal 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
73
core/runtime/program.h
Normal 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() {}
|
||||
};
|
146
core/runtime/runtimeExport.h
Normal file
146
core/runtime/runtimeExport.h
Normal 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;
|
||||
// -
|
||||
};
|
922
core/runtime/runtimeLinker.cpp
Normal file
922
core/runtime/runtimeLinker.cpp
Normal 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;
|
||||
}
|
64
core/runtime/runtimeLinker.h
Normal file
64
core/runtime/runtimeLinker.h
Normal 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
|
231
core/runtime/util/exceptionHandler.cpp
Normal file
231
core/runtime/util/exceptionHandler.cpp
Normal 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
|
10
core/runtime/util/exceptionHandler.h
Normal file
10
core/runtime/util/exceptionHandler.h
Normal 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
|
69
core/runtime/util/moduleLoader.cpp
Normal file
69
core/runtime/util/moduleLoader.cpp
Normal 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);
|
||||
}
|
11
core/runtime/util/moduleLoader.h
Normal file
11
core/runtime/util/moduleLoader.h
Normal 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
55
core/runtime/util/plt.h
Normal 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()
|
38
core/runtime/util/virtualmemory.cpp
Normal file
38
core/runtime/util/virtualmemory.cpp
Normal 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;
|
||||
}
|
6
core/runtime/util/virtualmemory.h
Normal file
6
core/runtime/util/virtualmemory.h
Normal 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);
|
7
core/systemContent/CMakeLists.txt
Normal file
7
core/systemContent/CMakeLists.txt
Normal 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)
|
146
core/systemContent/systemContent.cpp
Normal file
146
core/systemContent/systemContent.cpp
Normal 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;
|
||||
}
|
43
core/systemContent/systemContent.h
Normal file
43
core/systemContent/systemContent.h
Normal 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
23
core/tests/CMakeLists.txt
Normal 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 .)
|
1
core/tests/core/CMakeLists.txt
Normal file
1
core/tests/core/CMakeLists.txt
Normal file
@ -0,0 +1 @@
|
||||
add_subdirectory(semaphore)
|
17
core/tests/core/semaphore/CMakeLists.txt
Normal file
17
core/tests/core/semaphore/CMakeLists.txt
Normal 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
|
||||
)
|
279
core/tests/core/semaphore/entry.cpp
Normal file
279
core/tests/core/semaphore/entry.cpp
Normal 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, µs); // 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();
|
||||
}
|
||||
}
|
7
core/timer/CMakeLists.txt
Normal file
7
core/timer/CMakeLists.txt
Normal 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)
|
111
core/timer/SysWindowsTimer.h
Normal file
111
core/timer/SysWindowsTimer.h
Normal 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
Loading…
Reference in New Issue
Block a user