├── .github ├── FUNDING.yml └── workflows │ └── ci.yml ├── include ├── vita3k │ ├── microprofile.h │ └── util │ │ ├── types.h │ │ ├── optional.h │ │ ├── log.h │ │ ├── fs.h │ │ └── string_utils.h ├── modules │ ├── SceCtrl.h │ ├── SceTouch.h │ ├── SceGxm.h │ ├── SceDisplay.h │ ├── SceLibKernel.h │ ├── SceSysmem.h │ └── SceKernelThreadMgr.h ├── load.h ├── config.h ├── netlog.h ├── uam_compiler_iface_c.h ├── display │ └── display_to_dk.h ├── log.h ├── arm-encode.h ├── circ_buf.h ├── vita3k_shader_recompiler_iface_c.h ├── util.h ├── deko_utils.h ├── sce-elf.h ├── self.h ├── module.h ├── gxm │ ├── util.h │ └── gxm_to_dk.h └── sce-elf-defs.h ├── .clang-format-ignore ├── .devcontainer └── devcontainer.json ├── .gitmodules ├── source ├── uam_compiler_iface_c.cpp ├── module.c ├── log.c ├── util.c ├── netlog.c ├── modules │ ├── SceTouch.c │ ├── SceCtrl.c │ ├── SceSysmem.c │ ├── SceKernelThreadMgr.c │ ├── SceDisplay.c │ └── SceLibKernel.c ├── vita3k_shader_recompiler_iface_c.cpp ├── main.c ├── deko_utils.c └── load.c ├── .gitignore ├── .clang-format ├── licenses ├── LICENSE.miniz └── COPYING.txt ├── external └── CMakeLists.txt ├── CMakePresets.json ├── CMakeLists.txt ├── README.md └── vita2hos.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: xerpi 2 | -------------------------------------------------------------------------------- /include/vita3k/microprofile.h: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /include/vita3k/util/types.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | -------------------------------------------------------------------------------- /include/vita3k/util/optional.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | using std::optional; 4 | -------------------------------------------------------------------------------- /include/modules/SceCtrl.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_CTRL_H 2 | #define SCE_CTRL_H 3 | 4 | int SceCtrl_init(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/modules/SceTouch.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_TOUCH_H 2 | #define SCE_TOUCH_H 3 | 4 | int SceTouch_init(void); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /.clang-format-ignore: -------------------------------------------------------------------------------- 1 | external/* 2 | include/arm-encode.h 3 | include/circ_buf.h 4 | include/sce-elf-defs.h 5 | include/sce-elf.h 6 | include/self.h 7 | -------------------------------------------------------------------------------- /include/load.h: -------------------------------------------------------------------------------- 1 | #ifndef LOAD_H 2 | #define LOAD_H 3 | 4 | #include 5 | 6 | int load_file(Jit *jit, const char *filename, void **entry); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /include/modules/SceGxm.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_GXM_H 2 | #define SCE_GXM_H 3 | 4 | #include 5 | 6 | int SceGxm_init(DkDevice dk_device); 7 | int SceGxm_finish(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "image": "ghcr.io/vita2hos/devcontainer/vita2hos:latest", 3 | "remoteUser": "vita2hos", 4 | "remoteEnv": { 5 | "SWITCHIP": "${localEnv:SWITCHIP}" 6 | } 7 | } -------------------------------------------------------------------------------- /include/modules/SceDisplay.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_DISPLAY_H 2 | #define SCE_DISPLAY_H 3 | 4 | #include 5 | 6 | int SceDisplay_init(DkDevice dk_device); 7 | int SceDisplay_finish(void); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /include/config.h: -------------------------------------------------------------------------------- 1 | #ifndef CONIFG_H 2 | #define CONFIG_H 3 | 4 | #define VITA2HOS_ROOT_PATH "/vita2hos" 5 | #define VITA2HOS_EXE_FILE VITA2HOS_ROOT_PATH "/executable" 6 | #define VITA2HOS_DUMP_PATH VITA2HOS_ROOT_PATH "/dump" 7 | #define VITA2HOS_DUMP_SHADER_PATH VITA2HOS_DUMP_PATH "/shader" 8 | 9 | #endif -------------------------------------------------------------------------------- /include/netlog.h: -------------------------------------------------------------------------------- 1 | #ifndef NETLOG_H 2 | #define NETLOG_H 3 | 4 | #include 5 | 6 | /* Port 28771 (nc -ulk 28771) */ 7 | #define MULTICAST_ADDR "224.0.0.1" 8 | #define PORT 28771 9 | 10 | int netlog_init(void); 11 | int netlog_deinit(void); 12 | ssize_t netlog_write(const void *buf, size_t size); 13 | 14 | #endif -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Vita3K"] 2 | path = external/Vita3K 3 | url = https://github.com/Vita3K/Vita3K.git 4 | shallow = true 5 | [submodule "vita-headers"] 6 | path = external/vita-headers 7 | url = https://github.com/vitasdk/vita-headers.git 8 | shallow = true 9 | [submodule "external/mlib"] 10 | path = external/mlib 11 | url = https://github.com/P-p-H-d/mlib.git 12 | shallow = true 13 | -------------------------------------------------------------------------------- /include/modules/SceLibKernel.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_LIBKERNEL_H 2 | #define SCE_LIBKERNEL_H 3 | 4 | #include 5 | 6 | #define SCE_ERROR_ERRNO_ENODEV 0x80010013 7 | #define SCE_ERROR_ERRNO_EMFILE 0x80010018 8 | #define SCE_ERROR_ERRNO_EBADF 0x80010009 9 | 10 | int SceLibKernel_init(void); 11 | UEvent *SceLibKernel_get_process_exit_uevent(void); 12 | int SceLibKernel_get_process_exit_res(); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /source/uam_compiler_iface_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "uam_compiler_iface_c.h" 4 | 5 | extern "C" { 6 | 7 | bool uam_compiler_compile_glsl(pipeline_stage stage, const char *glsl_source, void *buffer, 8 | uint32_t *size) 9 | { 10 | DekoCompiler compiler{ stage }; 11 | bool rc = compiler.CompileGlsl(glsl_source); 12 | if (!rc) 13 | return false; 14 | 15 | compiler.OutputDkshToMemory(buffer, size); 16 | 17 | return rc; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /include/modules/SceSysmem.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_SYSMEM_H 2 | #define SCE_SYSMEM_H 3 | 4 | #include 5 | 6 | typedef struct VitaMemBlockInfo { 7 | uint32_t index; 8 | SceUID uid; 9 | void *base; 10 | uint32_t size; 11 | DkMemBlock dk_memblock; 12 | } VitaMemBlockInfo; 13 | 14 | int SceSysmem_init(DkDevice dk_device); 15 | SceUID SceSysmem_get_next_uid(void); 16 | VitaMemBlockInfo *SceSysmem_get_vita_memblock_info_for_addr(const void *addr); 17 | DkMemBlock SceSysmem_get_dk_memblock_for_addr(const void *addr); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /include/uam_compiler_iface_c.h: -------------------------------------------------------------------------------- 1 | #ifndef UAM_COMPILER_IFACE_C_H 2 | #define UAM_COMPILER_IFACE_C_H 3 | 4 | typedef struct gl_shader_program gl_shader_program; 5 | typedef struct tgsi_token tgsi_token; 6 | typedef enum pipeline_stage pipeline_stage; 7 | 8 | #include "uam/glsl_frontend.h" 9 | 10 | #ifdef __cplusplus 11 | extern "C" { 12 | #endif 13 | 14 | bool uam_compiler_compile_glsl(pipeline_stage stage, const char *glsl_source, void *buffer, 15 | uint32_t *size); 16 | 17 | #ifdef __cplusplus 18 | } 19 | #endif 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /include/modules/SceKernelThreadMgr.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_KERNEL_THREAD_MGR_H 2 | #define SCE_KERNEL_THREAD_MGR_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | uint32_t index; 8 | Thread thread; 9 | SceUID uid; 10 | char name[32]; 11 | SceKernelThreadEntry entry; 12 | SceSize arglen; 13 | void *argp; 14 | int return_status; 15 | void **vita_tls; 16 | } VitaThreadInfo; 17 | 18 | int SceKernelThreadMgr_init(void); 19 | int SceKernelThreadMgr_finish(void); 20 | VitaThreadInfo *SceKernelThreadMgr_get_thread_info(void); 21 | int SceKernelThreadMgr_main_entry(SceKernelThreadEntry entry, int args, void *argp); 22 | 23 | #endif 24 | -------------------------------------------------------------------------------- /source/module.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "module.h" 6 | 7 | const void *module_get_func_export(uint32_t lib_nid, uint32_t func_nid) 8 | { 9 | size_t num_libs = NUM_EXPORTED_LIBS; 10 | 11 | for (size_t i = 0; i < num_libs; i++) { 12 | if (__start_exported_libraries[i].nid == lib_nid) { 13 | const library_t *const lib = &__start_exported_libraries[i]; 14 | size_t num_funcs = NUM_FUNCS(lib); 15 | 16 | for (size_t j = 0; j < num_funcs; j++) { 17 | if (lib->func_nidtable_start[j] == func_nid) 18 | return lib->func_table_start[j]; 19 | } 20 | } 21 | } 22 | 23 | return NULL; 24 | } -------------------------------------------------------------------------------- /include/display/display_to_dk.h: -------------------------------------------------------------------------------- 1 | #ifndef DISPLAY_TO_DK_H 2 | #define DISPLAY_TO_DK_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | static inline uint32_t display_pixelformat_bytes_per_pixel(SceDisplayPixelFormat format) 9 | { 10 | switch (format) { 11 | case SCE_DISPLAY_PIXELFORMAT_A8B8G8R8: 12 | return 4; 13 | default: 14 | UNREACHABLE("Unsupported SceDisplayPixelFormat"); 15 | } 16 | } 17 | 18 | static inline DkImageFormat display_pixelformat_to_dk_image_format(SceDisplayPixelFormat format) 19 | { 20 | switch (format) { 21 | case SCE_DISPLAY_PIXELFORMAT_A8B8G8R8: 22 | return DkImageFormat_RGBA8_Unorm; 23 | default: 24 | UNREACHABLE("Unsupported SceDisplayPixelFormat"); 25 | } 26 | } 27 | 28 | #endif 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # Switch 55 | build/ 56 | build_debug/ 57 | build_release/ 58 | *.npdm 59 | *.nso 60 | *.nsp 61 | 62 | # Other 63 | *.txt 64 | *.lst 65 | *.jpg 66 | 67 | # CMake 68 | !CMakeLists.txt 69 | 70 | # VSCode 71 | .vscode/ 72 | -------------------------------------------------------------------------------- /include/log.h: -------------------------------------------------------------------------------- 1 | #ifndef LOG_H 2 | #define LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "util.h" 10 | 11 | #ifdef __cplusplus 12 | extern "C" { 13 | #endif 14 | 15 | typedef enum { 16 | LOG_DEVICE_SVC = BIT(0), 17 | LOG_DEVICE_NETLOG = BIT(1), 18 | LOG_DEVICE_FILE = BIT(2), 19 | } log_device_t; 20 | 21 | void log_init(uint32_t device_mask); 22 | void log_print(const char *str); 23 | void log_printf(const char *format, ...) __attribute__((format(printf, 1, 2))); 24 | 25 | #define LOG(str, ...) log_printf(str, ##__VA_ARGS__) 26 | 27 | #ifdef NDEBUG 28 | #define LOG_DEBUG(str, ...) (void)0 29 | #else 30 | #define LOG_DEBUG(str, ...) log_printf(str, ##__VA_ARGS__) 31 | #endif 32 | 33 | void NORETURN fatal_error(const char *dialog_message, const char *fullscreen_message); 34 | 35 | #ifdef __cplusplus 36 | } 37 | #endif 38 | 39 | #endif 40 | -------------------------------------------------------------------------------- /include/arm-encode.h: -------------------------------------------------------------------------------- 1 | #ifndef ARM_ENCODE_H 2 | #define ARM_ENCODE_H 3 | 4 | #include 5 | 6 | static inline uint32_t arm_encode_movw(uint16_t reg, uint16_t immed) 7 | { 8 | // 1110 0011 0000 XXXX YYYY XXXXXXXXXXXX 9 | // where X is the immediate and Y is the register 10 | // Upper bits == 0xE30 11 | return ((uint32_t)0xE30 << 20) | ((uint32_t)(immed & 0xF000) << 4) | (immed & 0xFFF) | (reg << 12); 12 | } 13 | 14 | static inline uint32_t arm_encode_movt(uint16_t reg, uint16_t immed) 15 | { 16 | // 1110 0011 0100 XXXX YYYY XXXXXXXXXXXX 17 | // where X is the immediate and Y is the register 18 | // Upper bits == 0xE34 19 | return ((uint32_t)0xE34 << 20) | ((uint32_t)(immed & 0xF000) << 4) | (immed & 0xFFF) | (reg << 12); 20 | } 21 | 22 | static inline uint32_t arm_encode_bx(uint16_t reg) 23 | { 24 | // 1110 0001 0010 111111111111 0001 YYYY 25 | // BX Rn has 0xE12FFF1 as top bytes 26 | return ((uint32_t)0xE12FFF1 << 4) | reg; 27 | } 28 | 29 | #define arm_encode_ret() arm_encode_bx(14) 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | BasedOnStyle: LLVM 2 | IndentWidth: 4 3 | UseTab: Never 4 | BreakBeforeBraces: Linux 5 | AlignConsecutiveMacros: Consecutive 6 | AllowShortEnumsOnASingleLine: false 7 | AllowShortIfStatementsOnASingleLine: Never 8 | AllowShortFunctionsOnASingleLine: false 9 | ColumnLimit: 100 10 | AlignArrayOfStructures: Right 11 | IncludeBlocks: Regroup 12 | IncludeIsMainRegex: '(_.*)?$' 13 | 14 | BreakArrays: true 15 | Cpp11BracedListStyle: false 16 | 17 | # Include block order goes like this 18 | # - config.h style files, including ../config.h 19 | # - system headers (<>) 20 | # - All m1n1 headers, starting with the "this file" header, rest sorted 21 | # - 3rd party code headers 22 | # - build artifact headers (stuff outside of src/) 23 | IncludeCategories: 24 | - Regex: '^"(\.\./)*build/build_.*\.h"$' 25 | Priority: -3 26 | - Regex: '^"(\.\./)*config\.h"$' 27 | Priority: -2 28 | - Regex: '^<' 29 | Priority: -1 30 | - Regex: '^"\.\./' 31 | Priority: 3 32 | - Regex: '/' 33 | Priority: 2 34 | - Regex: '.*' 35 | Priority: 0 36 | SortPriority: 1 37 | -------------------------------------------------------------------------------- /include/circ_buf.h: -------------------------------------------------------------------------------- 1 | /* SPDX-License-Identifier: GPL-2.0 */ 2 | /* 3 | * See Documentation/core-api/circular-buffers.rst for more information. 4 | */ 5 | 6 | #ifndef _LINUX_CIRC_BUF_H 7 | #define _LINUX_CIRC_BUF_H 1 8 | 9 | /* Return count in buffer. */ 10 | #define CIRC_CNT(head,tail,size) (((head) - (tail)) & ((size)-1)) 11 | 12 | /* Return space available, 0..size-1. We always leave one free char 13 | as a completely full buffer has head == tail, which is the same as 14 | empty. */ 15 | #define CIRC_SPACE(head,tail,size) CIRC_CNT((tail),((head)+1),(size)) 16 | 17 | /* Return count up to the end of the buffer. Carefully avoid 18 | accessing head and tail more than once, so they can change 19 | underneath us without returning inconsistent results. */ 20 | #define CIRC_CNT_TO_END(head,tail,size) \ 21 | ({int end = (size) - (tail); \ 22 | int n = ((head) + end) & ((size)-1); \ 23 | n < end ? n : end;}) 24 | 25 | /* Return space available up to the end of the buffer. */ 26 | #define CIRC_SPACE_TO_END(head,tail,size) \ 27 | ({int end = (size) - 1 - (head); \ 28 | int n = (end + (tail)) & ((size)-1); \ 29 | n <= end ? n : end+1;}) 30 | 31 | #endif /* _LINUX_CIRC_BUF_H */ -------------------------------------------------------------------------------- /licenses/LICENSE.miniz: -------------------------------------------------------------------------------- 1 | Copyright 2013-2014 RAD Game Tools and Valve Software 2 | Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC 3 | 4 | All Rights Reserved. 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in 14 | all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /include/vita3k_shader_recompiler_iface_c.h: -------------------------------------------------------------------------------- 1 | #ifndef VITA3K_SHADER_RECOMPILER_IFACE_C_H 2 | #define VITA3K_SHADER_RECOMPILER_IFACE_C_H 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | bool convert_gxp_to_spirv_c(uint32_t **spirv, uint32_t *num_instr, const SceGxmProgram *program, 9 | const char *shader_name, bool support_shader_interlock, 10 | bool support_texture_barrier, bool direct_fragcolor, bool spirv_shader, 11 | const SceGxmVertexAttribute *hint_attributes, 12 | uint32_t num_hint_attributes, bool maskupdate, bool force_shader_debug, 13 | bool (*dumper)(const char *ext, const char *dump)); 14 | 15 | bool convert_gxp_to_glsl_c(char **glsl, const SceGxmProgram *program, const char *shader_name, 16 | bool support_shader_interlock, bool support_texture_barrier, 17 | bool direct_fragcolor, bool spirv_shader, 18 | const SceGxmVertexAttribute *hint_attributes, 19 | uint32_t num_hint_attributes, bool maskupdate, bool force_shader_debug, 20 | bool (*dumper)(const char *ext, const char *dump)); 21 | 22 | #ifdef __cplusplus 23 | } 24 | #endif 25 | 26 | #endif 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | permissions: 4 | id-token: write 5 | contents: read 6 | packages: read 7 | 8 | on: 9 | push: 10 | branches: 11 | - "main" 12 | pull_request: 13 | branches: 14 | - "main" 15 | 16 | jobs: 17 | build: 18 | name: Build 19 | runs-on: ubuntu-latest 20 | container: 21 | image: ghcr.io/vita2hos/devcontainer/vita2hos:latest 22 | credentials: 23 | username: ${{ github.actor }} 24 | password: ${{ secrets.GHCR_PAT_VITA2HOS }} 25 | options: --user root 26 | strategy: 27 | matrix: 28 | build_type: [Debug, Release] 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v4 32 | with: 33 | submodules: recursive 34 | 35 | - name: Configure 36 | run: cmake --preset ${{ matrix.build_type }} 37 | 38 | - name: Build 39 | run: cmake --build --preset ${{ matrix.build_type }} 40 | 41 | - name: Upload build artifacts 42 | uses: actions/upload-artifact@v4 43 | with: 44 | name: Build_${{ matrix.build_type }}_${{ github.sha }} 45 | if-no-files-found: error 46 | path: | 47 | build/${{ matrix.build_type }}/vita2hos.elf 48 | build/${{ matrix.build_type }}/vita2hos.map 49 | build/${{ matrix.build_type }}/vita2hos.nsp 50 | -------------------------------------------------------------------------------- /external/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Use Vita3K's glslang (installed glslang doesn't contain SpvBuilder.h, etc) 2 | set(ENABLE_HLSL OFF CACHE BOOL "" FORCE) 3 | set(ENABLE_GLSLANG_BINARIES OFF CACHE BOOL "" FORCE) 4 | set(ENABLE_SPVREMAPPER OFF CACHE BOOL "" FORCE) 5 | set(ENABLE_OPT OFF CACHE BOOL "" FORCE) 6 | set(GLSLANG_TESTS OFF CACHE BOOL "" FORCE) 7 | add_subdirectory(Vita3K/external/glslang) 8 | 9 | # Shim libraries for Vita3K 10 | add_library(vita3k_shim INTERFACE) 11 | target_include_directories(vita3k_shim INTERFACE ${PROJECT_SOURCE_DIR}/include/vita3k) 12 | 13 | add_library(util INTERFACE) 14 | target_link_libraries(util INTERFACE vita3k_shim) 15 | target_include_directories(util INTERFACE Vita3K/vita3k/util/include) 16 | 17 | add_library(features INTERFACE) 18 | target_link_libraries(features INTERFACE vita3k_shim) 19 | target_include_directories(features INTERFACE Vita3K/vita3k/features/include) 20 | 21 | # Vita3K gxm 22 | add_subdirectory(Vita3K/vita3k/gxm) 23 | target_link_libraries(gxm INTERFACE vita3k_shim) 24 | target_include_directories(gxm PRIVATE 25 | ${PROJECT_SOURCE_DIR}/include/vita3k 26 | ) 27 | 28 | # Vita3K shader 29 | add_subdirectory(Vita3K/vita3k/shader) 30 | target_include_directories(shader PRIVATE 31 | ${NX_ROOT}/include/spirv_cross 32 | ${NX_ROOT}/include/glslang 33 | ) 34 | 35 | # M*LIB 36 | add_library(mlib INTERFACE) 37 | target_include_directories(mlib INTERFACE mlib) -------------------------------------------------------------------------------- /source/log.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "log.h" 6 | #include "netlog.h" 7 | #include "util.h" 8 | 9 | static Mutex g_log_mutex; 10 | static uint32_t g_device_mask; 11 | 12 | const char log_file_path[] = "sdmc:/vita2hos/log.txt"; 13 | 14 | void file_log(const char *str) 15 | { 16 | FILE *fp; 17 | 18 | fp = fopen(log_file_path, "a"); 19 | if (!fp) 20 | return; 21 | 22 | fputs(str, fp); 23 | fputc('\n', fp); 24 | 25 | fclose(fp); 26 | } 27 | 28 | void log_init(uint32_t device_mask) 29 | { 30 | mutexInit(&g_log_mutex); 31 | g_device_mask = device_mask; 32 | } 33 | 34 | void log_print(const char *str) 35 | { 36 | const size_t len = strlen(str); 37 | 38 | mutexLock(&g_log_mutex); 39 | 40 | if (g_device_mask & LOG_DEVICE_SVC) { 41 | svcOutputDebugString(str, len); 42 | svcOutputDebugString("\n", 1); 43 | } 44 | 45 | if (g_device_mask & LOG_DEVICE_NETLOG) { 46 | netlog_write(str, len); 47 | netlog_write("\n", 1); 48 | } 49 | 50 | if (g_device_mask & LOG_DEVICE_FILE) 51 | file_log(str); 52 | 53 | mutexUnlock(&g_log_mutex); 54 | } 55 | 56 | void log_printf(const char *restrict format, ...) 57 | { 58 | char buf[512]; 59 | va_list argptr; 60 | 61 | va_start(argptr, format); 62 | vsnprintf(buf, sizeof(buf), format, argptr); 63 | va_end(argptr); 64 | 65 | log_print(buf); 66 | } 67 | -------------------------------------------------------------------------------- /source/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.h" 5 | #include "util.h" 6 | 7 | int util_load_file(const char *filename, void **data, uint32_t *size) 8 | { 9 | FILE *fp; 10 | 11 | fp = fopen(filename, "rb"); 12 | if (!fp) { 13 | LOG("util_load_file: could not open file \"%s\". Error: %s", filename, strerror(errno)); 14 | return -1; 15 | } 16 | 17 | fseek(fp, 0, SEEK_END); 18 | *size = ftell(fp); 19 | fseek(fp, 0, SEEK_SET); 20 | 21 | *data = malloc(*size); 22 | if (!*data) { 23 | LOG("util_load_file: could not allocate memory"); 24 | fclose(fp); 25 | return -1; 26 | } 27 | 28 | if (fread(*data, 1, *size, fp) != *size) { 29 | LOG("util_load_file: could not read the file"); 30 | free(*data); 31 | fclose(fp); 32 | return -1; 33 | } 34 | 35 | fclose(fp); 36 | 37 | return 0; 38 | } 39 | 40 | int util_write_binary_file(const char *filename, const void *data, uint32_t size) 41 | { 42 | FILE *fp; 43 | 44 | fp = fopen(filename, "wb"); 45 | if (!fp) 46 | return -1; 47 | 48 | fwrite(data, 1, size, fp); 49 | fclose(fp); 50 | 51 | return 0; 52 | } 53 | 54 | int util_write_text_file(const char *filename, const char *data) 55 | { 56 | FILE *fp; 57 | 58 | fp = fopen(filename, "w"); 59 | if (!fp) 60 | return -1; 61 | 62 | fputs(data, fp); 63 | fclose(fp); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /CMakePresets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "cmakeMinimumRequired": { 4 | "major": 3, 5 | "minor": 19, 6 | "patch": 0 7 | }, 8 | "configurePresets": [ 9 | { 10 | "name": "Debug", 11 | "hidden": false, 12 | "generator": "Ninja", 13 | "binaryDir": "${sourceDir}/build/Debug", 14 | "cacheVariables": { 15 | "CMAKE_BUILD_TYPE": "Debug", 16 | "CMAKE_TOOLCHAIN_FILE": "$env{DEVKITPRO}/cmake/devkitARM.cmake", 17 | "CMAKE_EXPORT_COMPILE_COMMANDS": "YES", 18 | "CMAKE_COLOR_DIAGNOSTICS": "YES" 19 | } 20 | }, 21 | { 22 | "name": "Release", 23 | "hidden": false, 24 | "generator": "Ninja", 25 | "binaryDir": "${sourceDir}/build/Release", 26 | "cacheVariables": { 27 | "CMAKE_BUILD_TYPE": "Release", 28 | "CMAKE_TOOLCHAIN_FILE": "$env{DEVKITPRO}/cmake/devkitARM.cmake", 29 | "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", 30 | "CMAKE_COLOR_DIAGNOSTICS": "ON" 31 | } 32 | } 33 | ], 34 | "buildPresets": [ 35 | { 36 | "name": "Debug", 37 | "hidden": false, 38 | "configurePreset": "Debug" 39 | }, 40 | { 41 | "name": "Release", 42 | "hidden": false, 43 | "configurePreset": "Release" 44 | } 45 | ] 46 | } -------------------------------------------------------------------------------- /include/util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H 2 | #define UTIL_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) 9 | #define MIN2(x, y) (((x) < (y)) ? (x) : (y)) 10 | #define MAX2(x, y) (((x) > (y)) ? (x) : (y)) 11 | #define ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) 12 | #define ROUNDUP32(x) (((u32)(x) + 0x1f) & ~0x1f) 13 | #define ROUNDDOWN32(x) (((u32)(x) - 0x1f) & ~0x1f) 14 | 15 | #define UNUSED(x) (void)(x) 16 | #define MEMBER_SIZE(type, member) sizeof(((type *)0)->member) 17 | 18 | #define STRINGIFY(x) #x 19 | #define TOSTRING(x) STRINGIFY(x) 20 | 21 | #ifndef NORETURN 22 | #define NORETURN __attribute__((noreturn)) 23 | #endif 24 | 25 | #define UNREACHABLE(str) \ 26 | do { \ 27 | assert(!"" str); \ 28 | __builtin_unreachable(); \ 29 | } while (0) 30 | 31 | static inline uint32_t next_pow2(uint32_t x) 32 | { 33 | uint32_t val = 1; 34 | 35 | while (val < x) 36 | val = val << 1; 37 | 38 | return val; 39 | } 40 | 41 | static inline uint32_t highest_set_bit(int32_t num) 42 | { 43 | for (int i = 31; i >= 0; i--) { 44 | if (num & (1 << i)) 45 | return i; 46 | } 47 | 48 | return 0; 49 | }; 50 | 51 | int util_load_file(const char *filename, void **data, uint32_t *size); 52 | int util_write_binary_file(const char *filename, const void *data, uint32_t size); 53 | int util_write_text_file(const char *filename, const char *data); 54 | 55 | #endif 56 | -------------------------------------------------------------------------------- /source/netlog.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "netlog.h" 10 | 11 | static int g_sock = -1; 12 | 13 | int netlog_init(void) 14 | { 15 | int ret, sock, flags; 16 | unsigned char ttl; 17 | struct sockaddr_in dest_addr; 18 | 19 | /* Create a UDP socket */ 20 | sock = socket(AF_INET, SOCK_DGRAM, 0); 21 | if (sock < 0) 22 | return sock; 23 | 24 | /* Set to non-blocking */ 25 | flags = fcntl(sock, F_GETFL, 0); 26 | if (flags == -1) { 27 | close(sock); 28 | return flags; 29 | } 30 | 31 | ret = fcntl(sock, F_SETFL, flags | O_NONBLOCK); 32 | if (ret != 0) { 33 | close(sock); 34 | return ret; 35 | } 36 | 37 | /* Set up the destination address (multicast group) */ 38 | dest_addr = (struct sockaddr_in){ 39 | .sin_family = AF_INET, 40 | .sin_port = htons(PORT), 41 | .sin_addr.s_addr = inet_addr(MULTICAST_ADDR), 42 | }; 43 | 44 | /* Optional: Set TTL (Time To Live) for multicast packets */ 45 | ttl = 1; /* Restrict to local network */ 46 | ret = setsockopt(sock, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); 47 | if (ret < 0) { 48 | close(sock); 49 | return ret; 50 | } 51 | 52 | /* Connect the socket to the multicast group */ 53 | ret = connect(sock, (struct sockaddr *)&dest_addr, sizeof(dest_addr)); 54 | if (ret < 0) { 55 | close(sock); 56 | return ret; 57 | } 58 | 59 | g_sock = sock; 60 | 61 | return 0; 62 | } 63 | 64 | int netlog_deinit(void) 65 | { 66 | if (g_sock >= 0) { 67 | close(g_sock); 68 | g_sock = -1; 69 | } 70 | 71 | return 0; 72 | } 73 | 74 | ssize_t netlog_write(const void *buf, size_t size) 75 | { 76 | return write(g_sock, buf, size); 77 | } -------------------------------------------------------------------------------- /include/vita3k/util/log.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_LOG_H 2 | #define UTIL_LOG_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "../log.h" 12 | 13 | #undef LOG_TRACE 14 | template void LOG_TRACE(std::string_view fmt, Args &&...args) 15 | { 16 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 17 | } 18 | 19 | #undef LOG_DEBUG 20 | template void LOG_DEBUG(std::string_view fmt, Args &&...args) 21 | { 22 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 23 | } 24 | 25 | #undef LOG_INFO 26 | template void LOG_INFO(std::string_view fmt, Args &&...args) 27 | { 28 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 29 | } 30 | 31 | #undef LOG_WARN 32 | template void LOG_WARN(std::string_view fmt, Args &&...args) 33 | { 34 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 35 | } 36 | 37 | #undef LOG_ERROR 38 | template void LOG_ERROR(std::string_view fmt, Args &&...args) 39 | { 40 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 41 | } 42 | 43 | #undef LOG_CRITICAL 44 | template void LOG_CRITICAL(std::string_view fmt, Args &&...args) 45 | { 46 | log_print(fmt::format(fmt::runtime(fmt), std::forward(args)...).c_str()); 47 | } 48 | 49 | #undef LOG_INFO_ONCE 50 | #define LOG_INFO_ONCE LOG_INFO 51 | 52 | template std::string log_hex(T val) 53 | { 54 | using unsigned_type = typename std::make_unsigned::type; 55 | return fmt::format("0x{:0X}", static_cast(val)); 56 | } 57 | 58 | template std::string log_hex_full(T val) 59 | { 60 | std::stringstream ss; 61 | ss << "0x"; 62 | ss << std::setfill('0') << std::setw(sizeof(T) * 2) << std::hex << val; 63 | return ss.str(); 64 | } 65 | 66 | template <> struct fmt::formatter : formatter { 67 | auto format(SceGxmAttributeFormat fmt, format_context &ctx) const 68 | { 69 | return formatter::format((uint32_t)fmt, ctx); 70 | } 71 | }; 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /include/deko_utils.h: -------------------------------------------------------------------------------- 1 | #ifndef DEKO_UTILS_H 2 | #define DEKO_UTILS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "util.h" 9 | 10 | #include "gxm/util.h" 11 | 12 | typedef struct { 13 | uint32_t size; 14 | uint16_t width; 15 | uint16_t height; 16 | DkMemBlock memblock; 17 | DkImage image; 18 | DkImageView view; 19 | } dk_surface_t; 20 | 21 | static inline DkMemBlock dk_alloc_memblock(DkDevice device, uint32_t size, uint32_t flags) 22 | { 23 | DkMemBlockMaker memblock_maker; 24 | dkMemBlockMakerDefaults(&memblock_maker, device, ALIGN(size, DK_MEMBLOCK_ALIGNMENT)); 25 | memblock_maker.flags = flags; 26 | return dkMemBlockCreate(&memblock_maker); 27 | } 28 | 29 | static inline DkMemBlock dk_map_memblock(DkDevice device, void *addr, uint32_t size, uint32_t flags) 30 | { 31 | DkMemBlockMaker memblock_maker; 32 | dkMemBlockMakerDefaults(&memblock_maker, device, ALIGN(size, DK_MEMBLOCK_ALIGNMENT)); 33 | memblock_maker.flags = flags; 34 | memblock_maker.storage = addr; 35 | return dkMemBlockCreate(&memblock_maker); 36 | } 37 | 38 | static inline ptrdiff_t dk_memblock_cpu_addr_offset(const DkMemBlock memblock, const void *cpu_addr) 39 | { 40 | ptrdiff_t offset = (uintptr_t)cpu_addr - (uintptr_t)dkMemBlockGetCpuAddr(memblock); 41 | assert(offset >= 0); 42 | return offset; 43 | } 44 | 45 | void dk_surface_create(DkDevice device, dk_surface_t *surface, uint32_t width, uint32_t height, 46 | DkImageFormat format, uint32_t flags); 47 | void dk_surface_destroy(dk_surface_t *surface); 48 | 49 | void dk_cmdbuf_copy_image(DkCmdBuf cmdbuf, DkImage const *src_image, uint32_t src_width, 50 | uint32_t src_height, DkImage const *dst_image, uint32_t dst_width, 51 | uint32_t dst_height); 52 | 53 | bool dk_image_for_gxm_color_surface(DkDevice device, DkImage *image, 54 | const SceGxmColorSurfaceInner *surface); 55 | bool dk_image_for_gxm_ds_surface(DkDevice device, DkImage *image, uint32_t width, uint32_t height, 56 | const SceGxmDepthStencilSurface *surface); 57 | bool dk_image_for_existing_framebuffer(DkDevice device, DkImage *image, const void *addr, 58 | uint32_t width, uint32_t height, uint32_t stride, 59 | SceDisplayPixelFormat pixelfmt); 60 | 61 | #endif -------------------------------------------------------------------------------- /source/modules/SceTouch.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "log.h" 5 | #include "module.h" 6 | #include "util.h" 7 | 8 | #define MAX_TOUCH_STATES 8 9 | 10 | EXPORT(SceTouch, 0xFF082DF0, int, sceTouchPeek, SceUInt32 port, SceTouchData *pData, 11 | SceUInt32 nBufs) 12 | { 13 | HidTouchScreenState states[MAX_TOUCH_STATES]; 14 | int total; 15 | 16 | if (port == SCE_TOUCH_PORT_BACK) 17 | return 0; 18 | 19 | if (nBufs > MAX_TOUCH_STATES) 20 | nBufs = MAX_TOUCH_STATES; 21 | 22 | total = hidGetTouchScreenStates(states, nBufs); 23 | 24 | for (int i = 0; i < total; i++) { 25 | pData[i].timeStamp = states[i].sampling_number; 26 | pData[i].status = 0; 27 | pData[i].reportNum = states[i].count; 28 | 29 | for (int j = 0; j < states[i].count; j++) { 30 | pData[i].report[j].id = states[i].touches[j].finger_id; 31 | pData[i].report[j].force = 128; 32 | pData[i].report[j].x = (states[i].touches[j].x * 1920) / 1280; 33 | pData[i].report[j].y = (states[i].touches[j].y * 1088) / 720; 34 | pData[i].report[j].info = 0; 35 | } 36 | } 37 | 38 | return total; 39 | } 40 | 41 | EXPORT(SceTouch, 0x10A2CA25, int, sceTouchGetPanelInfo, SceUInt32 port, 42 | SceTouchPanelInfo *pPanelInfo) 43 | { 44 | switch (port) { 45 | case SCE_TOUCH_PORT_FRONT: 46 | pPanelInfo->minAaX = 0; 47 | pPanelInfo->minAaY = 0; 48 | pPanelInfo->maxAaX = 1919; 49 | pPanelInfo->maxAaY = 1087; 50 | pPanelInfo->minDispX = 0; 51 | pPanelInfo->minDispY = 0; 52 | pPanelInfo->maxDispX = 1919; 53 | pPanelInfo->maxDispY = 1087; 54 | pPanelInfo->minForce = 1; 55 | pPanelInfo->maxForce = 128; 56 | return 0; 57 | case SCE_TOUCH_PORT_BACK: 58 | pPanelInfo->minAaX = 0; 59 | pPanelInfo->minAaY = 108; 60 | pPanelInfo->maxAaX = 1919; 61 | pPanelInfo->maxAaY = 889; 62 | pPanelInfo->minDispX = 0; 63 | pPanelInfo->minDispY = 0; 64 | pPanelInfo->maxDispX = 1919; 65 | pPanelInfo->maxDispY = 1087; 66 | pPanelInfo->minForce = 1; 67 | pPanelInfo->maxForce = 128; 68 | return 0; 69 | default: 70 | return SCE_TOUCH_ERROR_INVALID_ARG; 71 | } 72 | } 73 | 74 | DECLARE_LIBRARY(SceTouch, 0x3e4f4a81); 75 | 76 | int SceTouch_init(void) 77 | { 78 | hidInitializeTouchScreen(); 79 | 80 | return 0; 81 | } 82 | -------------------------------------------------------------------------------- /source/modules/SceCtrl.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "log.h" 6 | #include "module.h" 7 | #include "util.h" 8 | 9 | static PadState g_pad; 10 | 11 | EXPORT(SceCtrl, 0xA9C3CED6, int, sceCtrlPeekBufferPositive, int port, SceCtrlData *pad_data, 12 | int count) 13 | { 14 | u64 buttons; 15 | HidAnalogStickState analog_stick_l, analog_stick_r; 16 | u32 vita_buttons = 0; 17 | 18 | padUpdate(&g_pad); 19 | buttons = padGetButtons(&g_pad); 20 | analog_stick_l = padGetStickPos(&g_pad, 0); 21 | analog_stick_r = padGetStickPos(&g_pad, 1); 22 | 23 | if (buttons & HidNpadButton_Minus) 24 | vita_buttons |= SCE_CTRL_SELECT; 25 | if (buttons & HidNpadButton_StickL) 26 | vita_buttons |= SCE_CTRL_L3; 27 | if (buttons & HidNpadButton_StickR) 28 | vita_buttons |= SCE_CTRL_R3; 29 | if (buttons & HidNpadButton_Plus) 30 | vita_buttons |= SCE_CTRL_START; 31 | if (buttons & HidNpadButton_Up) 32 | vita_buttons |= SCE_CTRL_UP; 33 | if (buttons & HidNpadButton_Right) 34 | vita_buttons |= SCE_CTRL_RIGHT; 35 | if (buttons & HidNpadButton_Down) 36 | vita_buttons |= SCE_CTRL_DOWN; 37 | if (buttons & HidNpadButton_Left) 38 | vita_buttons |= SCE_CTRL_LEFT; 39 | if (buttons & HidNpadButton_L) 40 | vita_buttons |= SCE_CTRL_LTRIGGER; 41 | if (buttons & HidNpadButton_R) 42 | vita_buttons |= SCE_CTRL_RTRIGGER; 43 | if (buttons & HidNpadButton_ZL) 44 | vita_buttons |= SCE_CTRL_L1; 45 | if (buttons & HidNpadButton_ZR) 46 | vita_buttons |= SCE_CTRL_R1; 47 | if (buttons & HidNpadButton_X) 48 | vita_buttons |= SCE_CTRL_TRIANGLE; 49 | if (buttons & HidNpadButton_A) 50 | vita_buttons |= SCE_CTRL_CIRCLE; 51 | if (buttons & HidNpadButton_B) 52 | vita_buttons |= SCE_CTRL_CROSS; 53 | if (buttons & HidNpadButton_Y) 54 | vita_buttons |= SCE_CTRL_SQUARE; 55 | 56 | pad_data->buttons = vita_buttons; 57 | 58 | pad_data->lx = (255 * (analog_stick_l.x - JOYSTICK_MIN)) / (JOYSTICK_MAX - JOYSTICK_MIN); 59 | pad_data->ly = 60 | (255 * ((255 - analog_stick_l.y) - JOYSTICK_MIN)) / (JOYSTICK_MAX - JOYSTICK_MIN); 61 | pad_data->rx = (255 * (analog_stick_r.x - JOYSTICK_MIN)) / (JOYSTICK_MAX - JOYSTICK_MIN); 62 | pad_data->ry = 63 | (255 * ((255 - analog_stick_r.y) - JOYSTICK_MIN)) / (JOYSTICK_MAX - JOYSTICK_MIN); 64 | 65 | return 0; 66 | } 67 | 68 | DECLARE_LIBRARY(SceCtrl, 0xD197E3C7); 69 | 70 | int SceCtrl_init(void) 71 | { 72 | padConfigureInput(1, HidNpadStyleSet_NpadStandard); 73 | padInitializeAny(&g_pad); 74 | 75 | return 0; 76 | } 77 | -------------------------------------------------------------------------------- /include/sce-elf.h: -------------------------------------------------------------------------------- 1 | #ifndef SCE_ELF_H 2 | #define SCE_ELF_H 3 | 4 | #include 5 | 6 | #define SCEMAG0 'S' 7 | #define SCEMAG1 'C' 8 | #define SCEMAG2 'E' 9 | #define SCEMAG3 0 10 | 11 | /* SCE-specific definitions for e_type: */ 12 | #define ET_SCE_EXEC 0xFE00 /* SCE Executable file */ 13 | #define ET_SCE_RELEXEC 0xFE04 /* SCE Relocatable file */ 14 | #define ET_SCE_STUBLIB 0xFE0C /* SCE SDK Stubs */ 15 | #define ET_SCE_DYNAMIC 0xFE18 /* Unused */ 16 | #define ET_SCE_PSPRELEXEC 0xFFA0 /* Unused (PSP ELF only) */ 17 | #define ET_SCE_PPURELEXEC 0xFFA4 /* Unused (SPU ELF only) */ 18 | #define ET_SCE_UNK 0xFFA5 /* Unknown */ 19 | 20 | /* SCE-specific definitions for sh_type: */ 21 | #define SHT_SCE_RELA 0x60000000 /* SCE Relocations */ 22 | #define SHT_SCENID 0x61000001 /* Unused (PSP ELF only) */ 23 | #define SHT_SCE_PSPRELA 0x700000A0 /* Unused (PSP ELF only) */ 24 | #define SHT_SCE_ARMRELA 0x700000A4 /* Unused (PSP ELF only) */ 25 | 26 | /* SCE-specific definitions for p_type: */ 27 | #define PT_SCE_RELA 0x60000000 /* SCE Relocations */ 28 | #define PT_SCE_COMMENT 0x6FFFFF00 /* Unused */ 29 | #define PT_SCE_VERSION 0x6FFFFF01 /* Unused */ 30 | #define PT_SCE_UNK 0x70000001 /* Unknown */ 31 | #define PT_SCE_PSPRELA 0x700000A0 /* Unused (PSP ELF only) */ 32 | #define PT_SCE_PPURELA 0x700000A4 /* Unused (SPU ELF only) */ 33 | 34 | #define NID_MODULE_STOP 0x79F8E492 35 | #define NID_MODULE_EXIT 0x913482A9 36 | #define NID_MODULE_START 0x935CD196 37 | #define NID_MODULE_BOOTSTART 0x5C424D40 38 | #define NID_MODULE_INFO 0x6C2224BA 39 | #define NID_PROCESS_PARAM 0x70FBA1E7 40 | #define NID_MODULE_SDK_VERSION 0x936C8A78 41 | 42 | #define PSP2_SDK_VERSION 0x03570011 43 | 44 | typedef union { 45 | Elf32_Word r_type; 46 | struct { 47 | Elf32_Word r_opt1; 48 | Elf32_Word r_opt2; 49 | } r_short; 50 | struct { 51 | Elf32_Word r_type; 52 | Elf32_Word r_addend; 53 | Elf32_Word r_offset; 54 | } r_long; 55 | struct { 56 | Elf32_Word r_word1; 57 | Elf32_Word r_word2; 58 | Elf32_Word r_word3; 59 | } r_raw; 60 | } SCE_Rel; 61 | 62 | /* Macros to get SCE reloc values */ 63 | #define SCE_REL_SHORT_OFFSET(x) (((x).r_opt1 >> 20) | ((x).r_opt2 & 0x3FF) << 12) 64 | #define SCE_REL_SHORT_ADDEND(x) ((x).r_opt2 >> 10) 65 | #define SCE_REL_LONG_OFFSET(x) ((x).r_offset) 66 | #define SCE_REL_LONG_ADDEND(x) ((x).r_addend) 67 | #define SCE_REL_LONG_CODE2(x) (((x).r_type >> 20) & 0xFF) 68 | #define SCE_REL_LONG_DIST2(x) (((x).r_type >> 28) & 0xF) 69 | #define SCE_REL_IS_SHORT(x) (((x).r_type) & 0xF) 70 | #define SCE_REL_CODE(x) (((x).r_type >> 8) & 0xFF) 71 | #define SCE_REL_SYMSEG(x) (((x).r_type >> 4) & 0xF) 72 | #define SCE_REL_DATSEG(x) (((x).r_type >> 16) & 0xF) 73 | 74 | #define SCE_ELF_DEFS_HOST 75 | #include "sce-elf-defs.h" 76 | #undef SCE_ELF_DEFS_HOST 77 | 78 | #define SCE_ELF_DEFS_TARGET 79 | #include "sce-elf-defs.h" 80 | #undef SCE_ELF_DEFS_TARGET 81 | 82 | #endif 83 | -------------------------------------------------------------------------------- /include/self.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // some info taken from the wiki, see http://vitadevwiki.com/index.php?title=SELF_File_Format 6 | 7 | #pragma pack(push, 1) 8 | typedef struct { 9 | uint32_t magic; /* 53434500 = SCE\0 */ 10 | uint32_t version; /* header version 3*/ 11 | uint16_t sdk_type; /* */ 12 | uint16_t header_type; /* 1 self, 2 unknown, 3 pkg */ 13 | uint32_t metadata_offset; /* metadata offset */ 14 | uint64_t header_len; /* self header length */ 15 | uint64_t elf_filesize; /* ELF file length */ 16 | uint64_t self_filesize; /* SELF file length */ 17 | uint64_t unknown; /* UNKNOWN */ 18 | uint64_t self_offset; /* SELF offset */ 19 | uint64_t appinfo_offset; /* app info offset */ 20 | uint64_t elf_offset; /* ELF #1 offset */ 21 | uint64_t phdr_offset; /* program header offset */ 22 | uint64_t shdr_offset; /* section header offset */ 23 | uint64_t section_info_offset; /* section info offset */ 24 | uint64_t sceversion_offset; /* version offset */ 25 | uint64_t controlinfo_offset; /* control info offset */ 26 | uint64_t controlinfo_size; /* control info size */ 27 | uint64_t padding; 28 | } SCE_header; 29 | 30 | typedef struct { 31 | uint64_t authid; /* auth id */ 32 | uint32_t vendor_id; /* vendor id */ 33 | uint32_t self_type; /* app type */ 34 | uint64_t version; /* app version */ 35 | uint64_t padding; /* UNKNOWN */ 36 | } SCE_appinfo; 37 | 38 | typedef struct { 39 | uint32_t unk1; 40 | uint32_t unk2; 41 | uint32_t unk3; 42 | uint32_t unk4; 43 | } SCE_version; 44 | 45 | typedef struct { 46 | uint32_t type; 47 | uint32_t size; 48 | uint32_t unk; 49 | uint32_t pad; 50 | } SCE_controlinfo; 51 | 52 | typedef struct { 53 | SCE_controlinfo common; 54 | char unk[0x100]; 55 | } SCE_controlinfo_5; 56 | 57 | typedef struct { 58 | SCE_controlinfo common; 59 | uint32_t is_used; /* always set to 1 */ 60 | uint32_t attr; /* controls several app settings */ 61 | uint32_t phycont_memsize; /* physically contiguous memory budget */ 62 | uint32_t total_memsize; /* total memory budget (user + phycont) */ 63 | uint32_t filehandles_limit; /* max number of opened filehandles simultaneously */ 64 | uint32_t dir_max_level; /* max depth for directories support */ 65 | uint32_t encrypt_mount_max; /* UNKNOWN */ 66 | uint32_t redirect_mount_max; /* UNKNOWN */ 67 | char unk[0xE0]; 68 | } SCE_controlinfo_6; 69 | 70 | typedef struct { 71 | SCE_controlinfo common; 72 | char unk[0x40]; 73 | } SCE_controlinfo_7; 74 | 75 | typedef struct { 76 | uint64_t offset; 77 | uint64_t length; 78 | uint64_t compression; // 1 = uncompressed, 2 = compressed 79 | uint64_t encryption; // 1 = encrypted, 2 = plain 80 | } segment_info; 81 | #pragma pack(pop) 82 | 83 | enum { 84 | HEADER_LEN = 0x1000 85 | }; 86 | -------------------------------------------------------------------------------- /include/module.h: -------------------------------------------------------------------------------- 1 | #ifndef MODULE_H 2 | #define MODULE_H 3 | 4 | #include 5 | 6 | typedef struct { 7 | const char *name; 8 | uint32_t nid; 9 | const uint32_t *func_nidtable_start; 10 | const uint32_t *func_nidtable_stop; 11 | const void **func_table_start; 12 | const void **func_table_stop; 13 | } library_t; 14 | 15 | extern const library_t __start_exported_libraries[]; 16 | extern const library_t __stop_exported_libraries[]; 17 | 18 | #define DECLARE_LIBRARY(_name, _nid) \ 19 | extern const uint32_t __start_##_name##_func_nidtable[]; \ 20 | extern const uint32_t __stop_##_name##_func_nidtable[]; \ 21 | extern const void *__start_##_name##_func_table[]; \ 22 | extern const void *__stop_##_name##_func_table[]; \ 23 | static const library_t _name##_library \ 24 | __attribute__((used, section("exported_libraries"))) = { \ 25 | .name = #_name, \ 26 | .nid = _nid, \ 27 | .func_nidtable_start = __start_##_name##_func_nidtable, \ 28 | .func_nidtable_stop = __stop_##_name##_func_nidtable, \ 29 | .func_table_start = __start_##_name##_func_table, \ 30 | .func_table_stop = __stop_##_name##_func_table \ 31 | } 32 | 33 | #define EXPORT_FUNC(library, nid, func) \ 34 | static const uint32_t library##_##name##_##nid##_nid \ 35 | __attribute__((used, section(#library "_func_nidtable"))) = nid; \ 36 | static const void *const library##_##name##_##nid##_func \ 37 | __attribute__((used, section(#library "_func_table"))) = &func; 38 | 39 | #define EXPORT(library, nid, type, name, ...) \ 40 | type name(__VA_ARGS__); \ 41 | EXPORT_FUNC(library, nid, name) \ 42 | type name(__VA_ARGS__) 43 | 44 | #define NUM_EXPORTED_LIBS \ 45 | (((uintptr_t)&__stop_exported_libraries - (uintptr_t)&__start_exported_libraries) / \ 46 | sizeof(library_t)) 47 | #define NUM_FUNCS(lib) \ 48 | (((uintptr_t)lib->func_table_stop - (uintptr_t)lib->func_table_start) / sizeof(void *)) 49 | 50 | const void *module_get_func_export(uint32_t lib_nid, uint32_t func_nid); 51 | 52 | #endif -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.13) 2 | 3 | set(VITA2HOS_MAJOR 0) 4 | set(VITA2HOS_MINOR 4) 5 | set(VITA2HOS_PATCH 0) 6 | execute_process(COMMAND 7 | git describe --dirty --always --exclude '*' 8 | OUTPUT_VARIABLE VITA2HOS_HASH 9 | OUTPUT_STRIP_TRAILING_WHITESPACE 10 | ) 11 | 12 | project( 13 | vita2hos 14 | VERSION ${VITA2HOS_MAJOR}.${VITA2HOS_MINOR}.${VITA2HOS_PATCH} 15 | LANGUAGES C CXX 16 | ) 17 | 18 | # Overwrite libnx location 19 | set(NX_ROOT ${DEVKITPRO}/libnx32) 20 | 21 | include(Platform/NintendoSwitch) 22 | 23 | set(CMAKE_CXX_STANDARD 20) 24 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 25 | set(CMAKE_CXX_EXTENSIONS OFF) 26 | 27 | set(VITA_HEADERS ${CMAKE_SOURCE_DIR}/external/vita-headers) 28 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${NX_ARCH_SETTINGS} ${NX_COMMON_FLAGS} -idirafter ${VITA_HEADERS}/include") 29 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${NX_ARCH_SETTINGS} ${NX_COMMON_FLAGS} -idirafter ${VITA_HEADERS}/include") 30 | 31 | add_compile_options( 32 | -Wall 33 | -Wimplicit-fallthrough=3 34 | -Wdouble-promotion 35 | -fstack-usage 36 | -fdata-sections 37 | -ffunction-sections 38 | ) 39 | 40 | add_subdirectory(external) 41 | 42 | add_executable(vita2hos 43 | source/deko_utils.c 44 | source/load.c 45 | source/log.c 46 | source/main.c 47 | source/module.c 48 | source/netlog.c 49 | source/uam_compiler_iface_c.cpp 50 | source/util.c 51 | source/vita3k_shader_recompiler_iface_c.cpp 52 | source/modules/SceCtrl.c 53 | source/modules/SceDisplay.c 54 | source/modules/SceGxm.c 55 | source/modules/SceKernelThreadMgr.c 56 | source/modules/SceLibKernel.c 57 | source/modules/SceSysmem.c 58 | source/modules/SceTouch.c 59 | ) 60 | 61 | target_include_directories(vita2hos PRIVATE 62 | include 63 | ${NX_ROOT}/include/uam/mesa-imported 64 | ) 65 | 66 | target_compile_options(vita2hos PRIVATE 67 | -Wextra 68 | -Wno-unused-parameter 69 | ) 70 | 71 | target_compile_definitions(vita2hos PRIVATE 72 | VITA2HOS_MAJOR="${VITA2HOS_MAJOR}" 73 | VITA2HOS_MINOR="${VITA2HOS_MINOR}" 74 | VITA2HOS_PATCH="${VITA2HOS_PATCH}" 75 | VITA2HOS_HASH="${VITA2HOS_HASH}" 76 | ) 77 | 78 | target_link_libraries(vita2hos PRIVATE 79 | shader 80 | spirv-cross-core 81 | fmt 82 | uam 83 | miniz 84 | mlib 85 | $<$:deko3dd> 86 | $<$:deko3d> 87 | ) 88 | 89 | target_link_directories(vita2hos PRIVATE 90 | ${NX_ROOT}/lib 91 | ) 92 | 93 | target_link_options(vita2hos PRIVATE 94 | -specs=${NX_ROOT}/switch32.specs 95 | -Wl,-Map,${CMAKE_PROJECT_NAME}.map 96 | ) 97 | 98 | find_program(NX_ELF2NSO_EXE NAMES elf2nso HINTS "${DEVKITPRO}/tools/bin") 99 | find_program(NX_NPDMTOOL_EXE NAMES npdmtool HINTS "${DEVKITPRO}/tools/bin") 100 | find_program(NX_BUILD_PFS0_EXE NAMES build_pfs0 HINTS "${DEVKITPRO}/tools/bin") 101 | 102 | nx_create_exefs(${CMAKE_PROJECT_NAME} 103 | CONFIG ${CMAKE_PROJECT_NAME}.json 104 | ) 105 | 106 | add_custom_target(send 107 | COMMAND curl -T ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_PROJECT_NAME}.nsp --user switch:switch ftp://$ENV{SWITCHIP}:5000/vita2hos/vita2hos.nsp 108 | DEPENDS ${CMAKE_PROJECT_NAME}_nsp 109 | ) -------------------------------------------------------------------------------- /source/vita3k_shader_recompiler_iface_c.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "vita3k_shader_recompiler_iface_c.h" 6 | 7 | extern "C" { 8 | 9 | static shader::GeneratedShader 10 | convert_gxp_internal(const SceGxmProgram *program, const char *shader_name, 11 | bool support_shader_interlock, bool support_texture_barrier, 12 | bool direct_fragcolor, bool spirv_shader, 13 | const SceGxmVertexAttribute *hint_attributes, uint32_t num_hint_attributes, 14 | bool maskupdate, bool force_shader_debug, 15 | bool (*dumper)(const char *ext, const char *dump), shader::Target target) 16 | { 17 | shader::GeneratedShader shader; 18 | shader::Hints hints; 19 | FeatureState features; 20 | features.support_shader_interlock = support_shader_interlock; 21 | features.support_texture_barrier = support_texture_barrier; 22 | features.direct_fragcolor = direct_fragcolor; 23 | features.spirv_shader = spirv_shader; 24 | features.use_mask_bit = false; 25 | 26 | std::vector hint_attribs; 27 | for (uint32_t i = 0; i < num_hint_attributes; i++) 28 | hint_attribs.push_back(hint_attributes[i]); 29 | 30 | hints.attributes = &hint_attribs; 31 | /* TODO: Fill this properly */ 32 | hints.color_format = SCE_GXM_COLOR_FORMAT_U8U8U8U8_ABGR; 33 | std::fill_n(hints.vertex_textures, SCE_GXM_MAX_TEXTURE_UNITS, 34 | SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR); 35 | std::fill_n(hints.fragment_textures, SCE_GXM_MAX_TEXTURE_UNITS, 36 | SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR); 37 | 38 | std::function dumper_f = 39 | [dumper](const std::string &ext, const std::string &dump) { 40 | if (dumper) 41 | return dumper(ext.c_str(), dump.c_str()); 42 | return true; 43 | }; 44 | 45 | return shader::convert_gxp(*program, std::string(shader_name), features, target, hints, 46 | maskupdate, force_shader_debug, dumper_f); 47 | } 48 | 49 | bool convert_gxp_to_spirv_c(uint32_t **spirv, uint32_t *num_instr, const SceGxmProgram *program, 50 | const char *shader_name, bool support_shader_interlock, 51 | bool support_texture_barrier, bool direct_fragcolor, bool spirv_shader, 52 | const SceGxmVertexAttribute *hint_attributes, 53 | uint32_t num_hint_attributes, bool maskupdate, bool force_shader_debug, 54 | bool (*dumper)(const char *ext, const char *dump)) 55 | { 56 | shader::GeneratedShader shader; 57 | 58 | shader = convert_gxp_internal(program, shader_name, support_shader_interlock, 59 | support_texture_barrier, direct_fragcolor, spirv_shader, 60 | hint_attributes, num_hint_attributes, maskupdate, 61 | force_shader_debug, dumper, shader::Target::SpirVOpenGL); 62 | 63 | *num_instr = shader.spirv.size(); 64 | *spirv = (uint32_t *)malloc(sizeof(uint32_t) * shader.spirv.size()); 65 | memcpy(*spirv, shader.spirv.data(), sizeof(uint32_t) * shader.spirv.size()); 66 | 67 | return true; 68 | } 69 | 70 | bool convert_gxp_to_glsl_c(char **glsl, const SceGxmProgram *program, const char *shader_name, 71 | bool support_shader_interlock, bool support_texture_barrier, 72 | bool direct_fragcolor, bool spirv_shader, 73 | const SceGxmVertexAttribute *hint_attributes, 74 | uint32_t num_hint_attributes, bool maskupdate, bool force_shader_debug, 75 | bool (*dumper)(const char *ext, const char *dump)) 76 | { 77 | shader::GeneratedShader shader; 78 | 79 | shader = convert_gxp_internal(program, shader_name, support_shader_interlock, 80 | support_texture_barrier, direct_fragcolor, spirv_shader, 81 | hint_attributes, num_hint_attributes, maskupdate, 82 | force_shader_debug, dumper, shader::Target::GLSLOpenGL); 83 | 84 | *glsl = (char *)malloc(shader.glsl.size() + 1); 85 | strcpy(*glsl, shader.glsl.c_str()); 86 | 87 | return true; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /include/vita3k/util/fs.h: -------------------------------------------------------------------------------- 1 | // Vita3K emulator project 2 | // Copyright (C) 2021 Vita3K team 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License along 15 | // with this program; if not, write to the Free Software Foundation, Inc., 16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | #include "string_utils.h" 25 | 26 | namespace fs = std::filesystem; 27 | 28 | class Root 29 | { 30 | fs::path base_path; 31 | fs::path pref_path; 32 | 33 | public: 34 | void set_base_path(const fs::path &p) 35 | { 36 | base_path = p; 37 | } 38 | 39 | fs::path get_base_path() const 40 | { 41 | return base_path; 42 | } 43 | 44 | std::string get_base_path_string() const 45 | { 46 | return base_path.string(); 47 | } 48 | 49 | void set_pref_path(const fs::path &p) 50 | { 51 | pref_path = p; 52 | } 53 | 54 | fs::path get_pref_path() const 55 | { 56 | return pref_path; 57 | } 58 | 59 | std::string get_pref_path_string() const 60 | { 61 | return pref_path.string(); 62 | } 63 | }; // class root 64 | 65 | namespace fs_utils 66 | { 67 | 68 | /** 69 | * \brief Construct a file name (optionally with an extension) to be placed in a Vita3K directory. 70 | * \param base_path The main output path for the file. 71 | * \param folder_path The sub-directory/sub-directories to output to. 72 | * \param file_name The name of the file. 73 | * \param extension The extension of the file (optional) 74 | * \return A complete Boost.Filesystem file path normalized. 75 | */ 76 | static inline fs::path construct_file_name(const fs::path &base_path, const fs::path &folder_path, 77 | const fs::path &file_name, const fs::path &extension) 78 | { 79 | fs::path full_file_path{ base_path / folder_path / file_name }; 80 | if (!extension.empty()) 81 | full_file_path.replace_extension(extension); 82 | 83 | return full_file_path; 84 | } 85 | 86 | static inline std::string path_to_utf8(const fs::path &path) 87 | { 88 | if constexpr (sizeof(fs::path::value_type) == sizeof(wchar_t)) { 89 | return string_utils::wide_to_utf(path.wstring()); 90 | } else { 91 | return path.string(); 92 | } 93 | } 94 | 95 | static inline fs::path utf8_to_path(const std::string &str) 96 | { 97 | if constexpr (sizeof(fs::path::value_type) == sizeof(wchar_t)) { 98 | return fs::path{ string_utils::utf_to_wide(str) }; 99 | } else { 100 | return fs::path{ str }; 101 | } 102 | } 103 | 104 | static inline fs::path path_concat(const fs::path &path1, const fs::path &path2) 105 | { 106 | return fs::path{ path1.native() + path2.native() }; 107 | } 108 | 109 | static inline void dump_data(const fs::path &path, const void *data, const std::streamsize size) 110 | { 111 | std::ofstream of{ path, std::ofstream::binary }; 112 | if (!of.fail()) { 113 | of.write(static_cast(data), size); 114 | of.close(); 115 | } 116 | } 117 | 118 | template static inline bool read_data(const fs::path &path, std::vector &data) 119 | { 120 | data.clear(); 121 | std::ifstream file(path, std::ios::binary | std::ios::ate); 122 | if (!file.is_open()) { 123 | return false; 124 | } 125 | 126 | // Get the size of the file 127 | std::streamsize size = file.tellg(); 128 | if (size <= 0) { 129 | return false; 130 | } 131 | 132 | // Resize the vector to fit the file content 133 | data.resize(size); 134 | 135 | // Go back to the beginning of the file and read the content 136 | file.seekg(0, std::ios::beg); 137 | if (!file.read(reinterpret_cast(data.data()), size)) { 138 | return false; 139 | } 140 | return true; 141 | } 142 | 143 | static inline bool read_data(const fs::path &path, std::vector &data) 144 | { 145 | return read_data(path, data); 146 | } 147 | 148 | static inline bool read_data(const fs::path &path, std::vector &data) 149 | { 150 | return read_data(path, data); 151 | } 152 | 153 | static inline bool read_data(const fs::path &path, std::vector &data) 154 | { 155 | return read_data(path, data); 156 | } 157 | 158 | } // namespace fs_utils 159 | -------------------------------------------------------------------------------- /source/main.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "load.h" 17 | #include "log.h" 18 | #include "module.h" 19 | #include "netlog.h" 20 | 21 | #include "modules/SceCtrl.h" 22 | #include "modules/SceDisplay.h" 23 | #include "modules/SceGxm.h" 24 | #include "modules/SceKernelThreadMgr.h" 25 | #include "modules/SceLibKernel.h" 26 | #include "modules/SceSysmem.h" 27 | #include "modules/SceTouch.h" 28 | 29 | static void dk_debug_callback(void *userData, const char *context, DkResult result, 30 | const char *message) 31 | { 32 | char description[256]; 33 | 34 | if (result == DkResult_Success) { 35 | LOG("deko3d debug callback: context: %s, message: %s, result %d", context, message, result); 36 | } else { 37 | snprintf(description, sizeof(description), "context: %s, message: %s, result %d", context, 38 | message, result); 39 | fatal_error("deko3d fatal error.", description); 40 | } 41 | } 42 | 43 | static int launch(SceKernelThreadEntry entry) 44 | { 45 | DkDeviceMaker device_maker; 46 | DkDevice dk_device; 47 | int ret; 48 | 49 | /* Initialize deko3d device */ 50 | dkDeviceMakerDefaults(&device_maker); 51 | device_maker.userData = NULL; 52 | device_maker.cbDebug = dk_debug_callback; 53 | device_maker.flags = DkDeviceFlags_YAxisPointsDown; 54 | dk_device = dkDeviceCreate(&device_maker); 55 | 56 | /* Init modules */ 57 | ret = SceSysmem_init(dk_device); 58 | if (ret != 0) 59 | goto done; 60 | ret = SceLibKernel_init(); 61 | if (ret != 0) 62 | goto done; 63 | ret = SceKernelThreadMgr_init(); 64 | if (ret != 0) 65 | goto done; 66 | ret = SceDisplay_init(dk_device); 67 | if (ret != 0) 68 | goto done; 69 | ret = SceGxm_init(dk_device); 70 | if (ret != 0) 71 | goto done; 72 | ret = SceCtrl_init(); 73 | if (ret != 0) 74 | goto done; 75 | ret = SceTouch_init(); 76 | if (ret != 0) 77 | goto done; 78 | 79 | LOG("Entering SceKernelThreadMgr..."); 80 | 81 | ret = SceKernelThreadMgr_main_entry(entry, 0, NULL); 82 | 83 | LOG("SceKernelThreadMgr returned..."); 84 | 85 | SceGxm_finish(); 86 | SceDisplay_finish(); 87 | SceKernelThreadMgr_finish(); 88 | 89 | dkDeviceDestroy(dk_device); 90 | 91 | LOG("Process finished! Returned: %d", ret); 92 | 93 | done: 94 | return ret; 95 | } 96 | 97 | static void create_vita2hos_paths() 98 | { 99 | mkdir(VITA2HOS_ROOT_PATH, 0755); 100 | mkdir(VITA2HOS_DUMP_PATH, 0755); 101 | mkdir(VITA2HOS_DUMP_SHADER_PATH, 0755); 102 | } 103 | 104 | int main(int argc, char *argv[]) 105 | { 106 | time_t now; 107 | char timestr[32]; 108 | Jit jit; 109 | void *entry; 110 | int ret; 111 | 112 | /* Initialize the socket library */ 113 | socketInitializeDefault(); 114 | 115 | /* Initialize netlog */ 116 | netlog_init(); 117 | 118 | log_init(LOG_DEVICE_SVC | LOG_DEVICE_NETLOG); 119 | 120 | LOG("vita2hos " VITA2HOS_MAJOR "." VITA2HOS_MINOR "." VITA2HOS_PATCH "-" VITA2HOS_HASH 121 | " (" __DATE__ " " __TIME__ ")"); 122 | 123 | now = time(NULL); 124 | strftime(timestr, sizeof(timestr), "%c", localtime(&now)); 125 | LOG("Starting at %s", timestr); 126 | 127 | create_vita2hos_paths(); 128 | 129 | ret = load_file(&jit, VITA2HOS_EXE_FILE, &entry); 130 | if (ret == 0) { 131 | LOG("Launching PlayStation Vita executable!"); 132 | 133 | /* Jump to Vita's executable entrypoint */ 134 | ret = launch(entry); 135 | 136 | LOG("Returned from launch with result: %d", ret); 137 | 138 | /* Close the JIT */ 139 | ret = jitClose(&jit); 140 | LOG("jitClose() returned: 0x%x", ret); 141 | } else { 142 | fatal_error("Error loading PlayStation Vita executable.", 143 | "Make sure to place it to '" VITA2HOS_EXE_FILE "'"); 144 | } 145 | 146 | return 0; 147 | } 148 | 149 | void NORETURN fatal_error(const char *dialog_message, const char *fullscreen_message) 150 | { 151 | extern u32 __nx_applet_exit_mode; 152 | ErrorApplicationConfig c; 153 | 154 | LOG("%s %s", dialog_message, fullscreen_message); 155 | 156 | errorApplicationCreate(&c, dialog_message, fullscreen_message); 157 | errorApplicationShow(&c); 158 | 159 | __nx_applet_exit_mode = 1; 160 | exit(1); 161 | } 162 | 163 | void NORETURN __assert_func(const char *file, int line, const char *func, const char *failedexpr) 164 | { 165 | char message[256]; 166 | 167 | snprintf(message, sizeof(message), "assertion \"%s\" failed: file \"%s\", line %d%s%s\n", 168 | failedexpr, file, line, func ? ", function: " : "", func ? func : ""); 169 | 170 | fatal_error("Assertion failed.", message); 171 | } 172 | -------------------------------------------------------------------------------- /source/deko_utils.c: -------------------------------------------------------------------------------- 1 | #include "deko_utils.h" 2 | 3 | #include "display/display_to_dk.h" 4 | #include "gxm/gxm_to_dk.h" 5 | #include "modules/SceSysmem.h" 6 | 7 | void dk_surface_create(DkDevice device, dk_surface_t *surface, uint32_t width, uint32_t height, 8 | DkImageFormat format, uint32_t flags) 9 | { 10 | DkImageLayoutMaker maker; 11 | DkImageLayout layout; 12 | uint32_t alignment; 13 | 14 | dkImageLayoutMakerDefaults(&maker, device); 15 | maker.flags = flags; 16 | maker.format = format; 17 | maker.dimensions[0] = width; 18 | maker.dimensions[1] = height; 19 | dkImageLayoutInitialize(&layout, &maker); 20 | 21 | alignment = dkImageLayoutGetAlignment(&layout); 22 | surface->size = dkImageLayoutGetSize(&layout); 23 | surface->width = width; 24 | surface->height = height; 25 | surface->memblock = dk_alloc_memblock(device, ALIGN(surface->size, alignment), 26 | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image); 27 | dkImageInitialize(&surface->image, &layout, surface->memblock, 0); 28 | dkImageViewDefaults(&surface->view, &surface->image); 29 | } 30 | 31 | void dk_surface_destroy(dk_surface_t *surface) 32 | { 33 | dkMemBlockDestroy(surface->memblock); 34 | } 35 | 36 | void dk_cmdbuf_copy_image(DkCmdBuf cmdbuf, DkImage const *src_image, uint32_t src_width, 37 | uint32_t src_height, DkImage const *dst_image, uint32_t dst_width, 38 | uint32_t dst_height) 39 | { 40 | DkImageView src_view, dst_view; 41 | DkImageRect src_rect, dst_rect; 42 | 43 | src_rect.x = src_rect.y = src_rect.z = 0; 44 | src_rect.width = src_width; 45 | src_rect.height = src_height; 46 | src_rect.depth = 1; 47 | 48 | dst_rect.x = dst_rect.y = dst_rect.z = 0; 49 | dst_rect.width = dst_width; 50 | dst_rect.height = dst_height; 51 | dst_rect.depth = 1; 52 | 53 | dkImageViewDefaults(&src_view, src_image); 54 | dkImageViewDefaults(&dst_view, dst_image); 55 | dkCmdBufBlitImage(cmdbuf, &src_view, &src_rect, &dst_view, &dst_rect, 0, 1); 56 | } 57 | 58 | bool dk_image_for_gxm_color_surface(DkDevice device, DkImage *image, 59 | const SceGxmColorSurfaceInner *surface) 60 | { 61 | DkImageLayoutMaker maker; 62 | DkImageLayout layout; 63 | DkMemBlock block = SceSysmem_get_dk_memblock_for_addr(surface->data); 64 | 65 | if (!block) 66 | return false; 67 | 68 | dkImageLayoutMakerDefaults(&maker, device); 69 | maker.flags = 70 | gxm_color_surface_type_to_dk_image_flags(surface->surfaceType) | DkImageFlags_Usage2DEngine; 71 | maker.format = gxm_color_format_to_dk_image_format(surface->colorFormat); 72 | maker.dimensions[0] = surface->width; 73 | maker.dimensions[1] = surface->height; 74 | maker.pitchStride = 75 | surface->strideInPixels * gxm_color_format_bytes_per_pixel(surface->colorFormat); 76 | dkImageLayoutInitialize(&layout, &maker); 77 | dkImageInitialize(image, &layout, block, dk_memblock_cpu_addr_offset(block, surface->data)); 78 | 79 | return true; 80 | } 81 | 82 | bool dk_image_for_gxm_ds_surface(DkDevice device, DkImage *image, uint32_t width, uint32_t height, 83 | const SceGxmDepthStencilSurface *surface) 84 | { 85 | DkImageLayoutMaker maker; 86 | DkImageLayout layout; 87 | DkMemBlock block = SceSysmem_get_dk_memblock_for_addr(surface->depthData); 88 | 89 | if (!block) 90 | return false; 91 | 92 | dkImageLayoutMakerDefaults(&maker, device); 93 | maker.flags = gxm_ds_surface_type_to_dk_image_flags(gxm_ds_surface_get_type(surface)) | 94 | DkImageFlags_Usage2DEngine; 95 | maker.format = gxm_ds_format_to_dk_image_format(gxm_ds_surface_get_format(surface)); 96 | maker.dimensions[0] = width; 97 | maker.dimensions[1] = height; 98 | dkImageLayoutInitialize(&layout, &maker); 99 | dkImageInitialize(image, &layout, block, 100 | dk_memblock_cpu_addr_offset(block, surface->depthData)); 101 | 102 | return true; 103 | } 104 | 105 | bool dk_image_for_existing_framebuffer(DkDevice device, DkImage *image, const void *addr, 106 | uint32_t width, uint32_t height, uint32_t stride, 107 | SceDisplayPixelFormat pixelfmt) 108 | { 109 | DkImageLayoutMaker maker; 110 | DkImageLayout layout; 111 | DkMemBlock block = SceSysmem_get_dk_memblock_for_addr(addr); 112 | 113 | if (!block) 114 | return false; 115 | 116 | dkImageLayoutMakerDefaults(&maker, device); 117 | maker.flags = DkImageFlags_PitchLinear | DkImageFlags_Usage2DEngine; 118 | maker.format = display_pixelformat_to_dk_image_format(pixelfmt); 119 | maker.dimensions[0] = width; 120 | maker.dimensions[1] = height; 121 | maker.pitchStride = stride * display_pixelformat_bytes_per_pixel(pixelfmt); 122 | dkImageLayoutInitialize(&layout, &maker); 123 | dkImageInitialize(image, &layout, block, dk_memblock_cpu_addr_offset(block, addr)); 124 | 125 | return true; 126 | } -------------------------------------------------------------------------------- /source/modules/SceSysmem.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "modules/SceSysmem.h" 10 | #include "deko_utils.h" 11 | #include "log.h" 12 | #include "module.h" 13 | #include "util.h" 14 | 15 | static _Atomic SceUID g_last_uid = 1; 16 | static DkDevice g_dk_device; 17 | 18 | DICT_DEF2(vita_memblock_info_dict, SceUID, M_DEFAULT_OPLIST, VitaMemBlockInfo *, M_POD_OPLIST) 19 | static vita_memblock_info_dict_t g_vita_memblock_infos; 20 | static RwLock g_vita_memblock_infos_lock; 21 | 22 | static VitaMemBlockInfo *get_memblock_info_for_uid(SceUID uid) 23 | { 24 | VitaMemBlockInfo *block; 25 | 26 | rwlockReadLock(&g_vita_memblock_infos_lock); 27 | block = *vita_memblock_info_dict_get(g_vita_memblock_infos, uid); 28 | rwlockReadUnlock(&g_vita_memblock_infos_lock); 29 | 30 | return block; 31 | } 32 | 33 | EXPORT(SceSysmem, 0xB9D5EBDE, SceUID, sceKernelAllocMemBlock, const char *name, 34 | SceKernelMemBlockType type, SceSize size, SceKernelAllocMemBlockOpt *opt) 35 | { 36 | VitaMemBlockInfo *block; 37 | uint32_t alignment; 38 | void *base; 39 | uint32_t memblock_flags; 40 | 41 | LOG("sceKernelAllocMemBlock: name: %s, size: 0x%x", name, size); 42 | 43 | block = malloc(sizeof(*block)); 44 | if (!block) 45 | return SCE_KERNEL_ERROR_NO_MEMORY; 46 | 47 | if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW) 48 | alignment = 256 * 1024; 49 | else 50 | alignment = 4 * 1024; 51 | 52 | base = aligned_alloc(alignment, size); 53 | if (!base) { 54 | free(block); 55 | return SCE_KERNEL_ERROR_NO_MEMORY; 56 | } 57 | 58 | memset(block, 0, sizeof(*block)); 59 | block->uid = SceSysmem_get_next_uid(); 60 | block->base = base; 61 | block->size = size; 62 | 63 | switch (type) { 64 | case SCE_KERNEL_MEMBLOCK_TYPE_USER_RW: 65 | memblock_flags = DkMemBlockFlags_CpuCached | DkMemBlockFlags_GpuCached; 66 | break; 67 | case SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE: 68 | case SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW: 69 | case SCE_KERNEL_MEMBLOCK_TYPE_USER_MAIN_PHYCONT_RW: 70 | case SCE_KERNEL_MEMBLOCK_TYPE_USER_MAIN_PHYCONT_NC_RW: 71 | default: 72 | memblock_flags = DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached; 73 | break; 74 | } 75 | 76 | /* We also map all the allocated memory blocks to the GPU */ 77 | block->dk_memblock = dk_map_memblock(g_dk_device, block->base, block->size, 78 | memblock_flags | DkMemBlockFlags_Image); 79 | 80 | rwlockWriteLock(&g_vita_memblock_infos_lock); 81 | vita_memblock_info_dict_set_at(g_vita_memblock_infos, block->uid, block); 82 | rwlockWriteUnlock(&g_vita_memblock_infos_lock); 83 | 84 | return block->uid; 85 | } 86 | 87 | EXPORT(SceSysmem, 0xA91E15EE, int, sceKernelFreeMemBlock, SceUID uid) 88 | { 89 | VitaMemBlockInfo *block = get_memblock_info_for_uid(uid); 90 | 91 | if (!block) 92 | return SCE_KERNEL_ERROR_INVALID_UID; 93 | 94 | dkMemBlockDestroy(block->dk_memblock); 95 | free(block->base); 96 | rwlockWriteLock(&g_vita_memblock_infos_lock); 97 | vita_memblock_info_dict_erase(g_vita_memblock_infos, uid); 98 | rwlockWriteUnlock(&g_vita_memblock_infos_lock); 99 | 100 | return 0; 101 | } 102 | 103 | EXPORT(SceSysmem, 0xB8EF5818, int, sceKernelGetMemBlockBase, SceUID uid, void **base) 104 | { 105 | VitaMemBlockInfo *block = get_memblock_info_for_uid(uid); 106 | 107 | if (!block) 108 | return SCE_KERNEL_ERROR_INVALID_UID; 109 | 110 | if (!base) 111 | return SCE_KERNEL_ERROR_ILLEGAL_ADDR; 112 | 113 | *base = block->base; 114 | 115 | return 0; 116 | } 117 | 118 | DECLARE_LIBRARY(SceSysmem, 0x37fe725a); 119 | 120 | int SceSysmem_init(DkDevice dk_device) 121 | { 122 | g_dk_device = dk_device; 123 | vita_memblock_info_dict_init(g_vita_memblock_infos); 124 | rwlockInit(&g_vita_memblock_infos_lock); 125 | 126 | return 0; 127 | } 128 | 129 | SceUID SceSysmem_get_next_uid(void) 130 | { 131 | return atomic_fetch_add(&g_last_uid, 1); 132 | } 133 | 134 | VitaMemBlockInfo *SceSysmem_get_vita_memblock_info_for_addr(const void *addr) 135 | { 136 | VitaMemBlockInfo *block = NULL; 137 | vita_memblock_info_dict_it_t it; 138 | struct vita_memblock_info_dict_pair_s *pair; 139 | 140 | rwlockReadLock(&g_vita_memblock_infos_lock); 141 | for (vita_memblock_info_dict_it(it, g_vita_memblock_infos); !vita_memblock_info_dict_end_p(it); 142 | vita_memblock_info_dict_next(it)) { 143 | pair = vita_memblock_info_dict_ref(it); 144 | if (addr >= pair->value->base && addr < (pair->value->base + pair->value->size)) { 145 | block = pair->value; 146 | break; 147 | } 148 | } 149 | rwlockReadUnlock(&g_vita_memblock_infos_lock); 150 | 151 | return block; 152 | } 153 | 154 | DkMemBlock SceSysmem_get_dk_memblock_for_addr(const void *addr) 155 | { 156 | VitaMemBlockInfo *info = SceSysmem_get_vita_memblock_info_for_addr(addr); 157 | if (!info) 158 | return NULL; 159 | return info->dk_memblock; 160 | } 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vita2hos 2 | 3 | A PlayStation Vita to Horizon OS (Nintendo Switch OS) translation layer (**_not_** an emulator) 4 | 5 | ## Overview 6 | 7 | **vita2hos** is a translation layer that enables native execution of PlayStation Vita executables (ARMv7), to run natively on the Nintendo Switch's ARMv8 CPU in AArch32 (32-bit) mode.\ 8 | It achieves this by redirecting the [module](https://wiki.henkaku.xyz/vita/Modules) imports of the PlayStation Vita executable to corresponding routines that utilize native Nintendo Switch Horizon OS services, effectively replicating the behavior of the original PlayStation Vita OS modules. 9 | 10 | ## Usage 11 | 12 | ### On a Real Nintendo Switch Console 13 | 14 | 1. **Transfer the NSP File:** 15 | - Copy `vita2hos.nsp` to your microSD card, e.g., `sd:/vita2hos/vita2hos.nsp`. 16 | 17 | 2. **Configure Atmosphère:** 18 | - Create an `override_config.ini` file in the `sd:/atmosphere/config/` directory with the following content: 19 | 20 | ```ini 21 | [hbl_config] 22 | override_any_app=true 23 | override_any_app_key=R 24 | override_any_app_address_space=32_bit 25 | ; Adjust the path according to the location of your file 26 | path=vita2hos/vita2hos.nsp 27 | ``` 28 | 29 | - *Note:* With this configuration, the homebrew menu will be overridden by **vita2hos**. To restore the homebrew menu, rename or remove the `override_config.ini` file and restart your Switch. Currently, multiple `path` entries are not supported in `override_config.ini`. 30 | 31 | 3. **Add PlayStation Vita Executable:** 32 | - Place your PlayStation Vita executable (`.velf`, `.self`, or `eboot.bin`) in the `sd:/vita2hos/` directory and rename it to `executable` (without any file extension).\ 33 | The resulting full path should be `sd:/vita2hos/executable`. 34 | 35 | 4. **Launch vita2hos:** 36 | - Boot or reboot your Switch, and start any game while holding down the `R` button. 37 | - *Caution:* Using **vita2hos** via applet mode (accessed through the album button) may result in a fatal error and is not recommended. 38 | 39 | 5. **Enjoy!** 40 | 41 | ### On an Emulator 42 | 43 | 1. **Prepare the PlayStation Vita Executable:** 44 | - Place your PlayStation Vita executable (`.velf`, `.self`, or `eboot.bin`) into the `sd:/vita2hos/` directory and rename it to `executable` (without any file extension). 45 | 46 | - **For Yuzu-based Emulators:** 47 | - Access the SD directory via _File_ → _Open yuzu Folder_ → `sdmc/`. 48 | 49 | - **For Ryujinx-based Emulators:** 50 | - Access the SD directory via _File_ → _Open Ryujinx Folder_ → `sdcard/`. 51 | 52 | 2. **Run vita2hos:** 53 | - Launch `vita2hos.nsp`. 54 | 55 | 3. **Enjoy!** 56 | 57 | ## Project Status and Compatibility 58 | 59 | **vita2hos** is in its early development stages and currently supports running simple CPU-rendered PlayStation Vita homebrew applications. Additionally, initial 3D graphics support is available—including texturing, depth, and stencil—enabling the execution of vitasdk's GXM samples. 60 | 61 | ## Building from Source 62 | 63 | 1. **Clone the Repository with Submodules:** 64 | 65 | Ensure you clone the repository along with its submodules to include all necessary components: 66 | 67 | ```bash 68 | git clone --recurse-submodules https://github.com/xerpi/vita2hos.git 69 | ``` 70 | 71 | 2. **Configure vita2hos:** 72 | - For a Debug build: 73 | 74 | ```bash 75 | cmake --preset Debug 76 | ``` 77 | 78 | - For a Release build: 79 | 80 | ```bash 81 | cmake --preset Release 82 | ``` 83 | 84 | 3. **Build vita2hos:** 85 | - For a Debug build: 86 | 87 | ```bash 88 | cmake --build --preset Debug 89 | ``` 90 | 91 | - For a Release build: 92 | 93 | ```bash 94 | cmake --build --preset Release 95 | ``` 96 | 97 | 4. **Output:** 98 | - The `vita2hos.nsp` file will be generated in the corresponding build directory upon successful compilation. 99 | 100 | *Note:* The `CMakePresets.json` file defines the build configurations, including the generator (e.g., Ninja), build directories, toolchain file, and other cache variables. Ensure that your environment variable `DEVKITPRO` is set correctly, as it's referenced in the `CMAKE_TOOLCHAIN_FILE` path within the presets. 101 | 102 | ## Special Thanks 103 | 104 | - **[Vita3K](https://vita3k.org/):** 105 | - **vita2hos** utilizes Vita3K's shader recompiler, and portions of its code are based on Vita3K's implementation. Consider [donating](https://vita3k.org/#donate) and [contributing](https://vita3k.org/#contribute) to Vita3K! 106 | 107 | - **[UAM - deko3d Shader Compiler](https://github.com/devkitPro/uam):** 108 | - **vita2hos** employs UAM (deko3d's shader compiler) for shader compilation. Contributions and donations to this project are highly appreciated! 109 | 110 | - **[Ryujinx](https://ryujinx.org/)** 111 | 112 | - **[yuzu](https://yuzu-emu.org/)** 113 | 114 | - **[Atmosphère](https://github.com/Atmosphere-NX/Atmosphere)** 115 | 116 | - **[Switchbrew](https://github.com/switchbrew/)** 117 | 118 | Special acknowledgments to @PixelyIon and @SciresM for their assistance, and to all testers, especially @TSRBerry. 119 | 120 | ## Disclaimer 121 | 122 | - **Nintendo Switch** is a trademark of **Nintendo Co., Ltd**. 123 | - **PlayStation Vita** is a trademark of **Sony Interactive Entertainment**. 124 | -------------------------------------------------------------------------------- /include/vita3k/util/string_utils.h: -------------------------------------------------------------------------------- 1 | // Vita3K emulator project 2 | // Copyright (C) 2025 Vita3K team 3 | // 4 | // This program is free software; you can redistribute it and/or modify 5 | // it under the terms of the GNU General Public License as published by 6 | // the Free Software Foundation; either version 2 of the License, or 7 | // (at your option) any later version. 8 | // 9 | // This program is distributed in the hope that it will be useful, 10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 | // GNU General Public License for more details. 13 | // 14 | // You should have received a copy of the GNU General Public License along 15 | // with this program; if not, write to the Free Software Foundation, Inc., 16 | // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 17 | 18 | #pragma once 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | namespace string_utils 31 | { 32 | 33 | std::vector split_string(const std::string &str, char delimiter) 34 | { 35 | std::istringstream str_stream(str); 36 | std::string segment; 37 | std::vector seglist; 38 | 39 | const size_t num_segments = 40 | std::count_if(str.begin(), str.end(), [&](char c) { return c == delimiter; }) + 41 | (str.empty() ? 1 : 0); 42 | 43 | seglist.reserve(num_segments); 44 | 45 | while (std::getline(str_stream, segment, delimiter)) { 46 | seglist.push_back(segment); 47 | } 48 | return seglist; 49 | } 50 | 51 | std::wstring utf_to_wide(const std::string &str) 52 | { 53 | std::wstring_convert> myconv; 54 | return myconv.from_bytes(str); 55 | } 56 | 57 | std::string wide_to_utf(const std::wstring &str) 58 | { 59 | std::wstring_convert> myconv; 60 | return myconv.to_bytes(str); 61 | } 62 | 63 | std::string remove_special_chars(std::string str) 64 | { 65 | for (char &c : str) { 66 | switch (c) { 67 | case '/': 68 | case '\\': 69 | case ':': 70 | case '?': 71 | case '"': 72 | case '<': 73 | case '>': 74 | case '|': 75 | case '*': 76 | c = '_'; 77 | break; 78 | default: 79 | continue; 80 | } 81 | } 82 | return str; 83 | } 84 | 85 | // Based on: https://stackoverflow.com/a/23135441 86 | // Search and replace "in" with "out" in the given string 87 | void replace(std::string &str, const std::string &in, const std::string &out) 88 | { 89 | size_t pos = 0; 90 | while ((pos = str.find(in, pos)) != std::string::npos) { 91 | str.replace(pos, in.length(), out); 92 | pos += out.length(); 93 | } 94 | } 95 | 96 | std::vector string_to_byte_array(const std::string &string) 97 | { 98 | std::vector hex_bytes; 99 | hex_bytes.reserve(string.length() / 2); 100 | 101 | if (string.length() % 2 != 0) 102 | LOG_WARN("Hex string length ({}) is not even", string.length()); 103 | 104 | for (size_t i = 0; i < string.length(); i += 2) { 105 | std::string byte = string.substr(i, 2); 106 | hex_bytes.push_back(static_cast(std::strtoul(byte.c_str(), nullptr, 16))); 107 | } 108 | return hex_bytes; 109 | } 110 | 111 | #ifdef _MSC_VER 112 | std::string utf16_to_utf8(const std::u16string &str) 113 | { 114 | std::wstring_convert, int16_t> myconv; 115 | auto p = reinterpret_cast(str.data()); 116 | return myconv.to_bytes(p, p + str.size()); 117 | } 118 | 119 | std::u16string utf8_to_utf16(const std::string &str) 120 | { 121 | static_assert(sizeof(std::wstring::value_type) == sizeof(std::u16string::value_type), 122 | "std::wstring and std::u16string are expected to have the same character size"); 123 | 124 | std::wstring_convert, int16_t> myconv; 125 | const char *p = str.data(); 126 | auto a = myconv.from_bytes(p, p + str.size()); 127 | return std::u16string(a.begin(), a.end()); 128 | } 129 | #else 130 | std::string utf16_to_utf8(const std::u16string &str) 131 | { 132 | std::wstring_convert, char16_t> myconv; 133 | return myconv.to_bytes(str); 134 | } 135 | 136 | std::u16string utf8_to_utf16(const std::string &str) 137 | { 138 | std::wstring_convert, char16_t> myconv; 139 | return myconv.from_bytes(str); 140 | } 141 | #endif 142 | 143 | std::string toupper(const std::string &s) 144 | { 145 | std::string r = s; 146 | std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c) { return std::toupper(c); }); 147 | return r; 148 | } 149 | 150 | std::string tolower(const std::string &s) 151 | { 152 | std::string r = s; 153 | std::transform(r.begin(), r.end(), r.begin(), [](unsigned char c) { return std::tolower(c); }); 154 | return r; 155 | } 156 | 157 | int stoi_def(const std::string &str, int default_value, const char *name) 158 | { 159 | try { 160 | return std::stoi(str); 161 | } catch (std::invalid_argument &_) { 162 | LOG_ERROR("Invalid {}: \"{}\"", name, str); 163 | } catch (std::out_of_range &_) { 164 | LOG_ERROR("Out of range {}: \"{}\"", name, str); 165 | } 166 | return default_value; 167 | } 168 | 169 | } // namespace string_utils 170 | -------------------------------------------------------------------------------- /vita2hos.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vita2hos", 3 | "version": "0.0.3", 4 | "program_id": "0x010000000000100D", 5 | "program_id_range_min": "0x010000000000100D", 6 | "program_id_range_max": "0x010000000000100D", 7 | "main_thread_stack_size": "0x100000", 8 | "main_thread_priority": 44, 9 | "default_cpu_id": 0, 10 | "pool_partition": 0, 11 | "is_64_bit": false, 12 | "address_space_type": 0, 13 | "is_retail": true, 14 | "system_resource_size": "0", 15 | "disable_device_address_space_merge": false, 16 | "optimize_memory_allocation": false, 17 | "signature_key_generation": 0, 18 | "filesystem_access": { 19 | "permissions": "0xFFFFFFFFFFFFFFFF" 20 | }, 21 | "service_host": [ 22 | "*" 23 | ], 24 | "service_access": [ 25 | "*" 26 | ], 27 | "kernel_capabilities": [ 28 | { 29 | "type": "kernel_flags", 30 | "value": { 31 | "highest_thread_priority": 59, 32 | "lowest_thread_priority": 28, 33 | "highest_cpu_id": 2, 34 | "lowest_cpu_id": 0 35 | } 36 | }, 37 | { 38 | "type": "syscalls", 39 | "value": { 40 | "svcUnknown00": "0x00", 41 | "svcSetHeapSize": "0x01", 42 | "svcSetMemoryPermission": "0x02", 43 | "svcSetMemoryAttribute": "0x03", 44 | "svcMapMemory": "0x04", 45 | "svcUnmapMemory": "0x05", 46 | "svcQueryMemory": "0x06", 47 | "svcExitProcess": "0x07", 48 | "svcCreateThread": "0x08", 49 | "svcStartThread": "0x09", 50 | "svcExitThread": "0x0A", 51 | "svcSleepThread": "0x0B", 52 | "svcGetThreadPriority": "0x0C", 53 | "svcSetThreadPriority": "0x0D", 54 | "svcGetThreadCoreMask": "0x0E", 55 | "svcSetThreadCoreMask": "0x0F", 56 | "svcGetCurrentProcessorNumber": "0x10", 57 | "svcSignalEvent": "0x11", 58 | "svcClearEvent": "0x12", 59 | "svcMapSharedMemory": "0x13", 60 | "svcUnmapSharedMemory": "0x14", 61 | "svcCreateTransferMemory": "0x15", 62 | "svcCloseHandle": "0x16", 63 | "svcResetSignal": "0x17", 64 | "svcWaitSynchronization": "0x18", 65 | "svcCancelSynchronization": "0x19", 66 | "svcArbitrateLock": "0x1A", 67 | "svcArbitrateUnlock": "0x1B", 68 | "svcWaitProcessWideKeyAtomic": "0x1C", 69 | "svcSignalProcessWideKey": "0x1D", 70 | "svcGetSystemTick": "0x1E", 71 | "svcConnectToNamedPort": "0x1F", 72 | "svcSendSyncRequestLight": "0x20", 73 | "svcSendSyncRequest": "0x21", 74 | "svcSendSyncRequestWithUserBuffer": "0x22", 75 | "svcSendAsyncRequestWithUserBuffer": "0x23", 76 | "svcGetProcessId": "0x24", 77 | "svcGetThreadId": "0x25", 78 | "svcBreak": "0x26", 79 | "svcOutputDebugString": "0x27", 80 | "svcReturnFromException": "0x28", 81 | "svcGetInfo": "0x29", 82 | "svcFlushEntireDataCache": "0x2A", 83 | "svcFlushDataCache": "0x2B", 84 | "svcMapPhysicalMemory": "0x2C", 85 | "svcUnmapPhysicalMemory": "0x2D", 86 | "svcGetDebugFutureThreadInfo": "0x2E", 87 | "svcGetLastThreadInfo": "0x2F", 88 | "svcGetResourceLimitLimitValue": "0x30", 89 | "svcGetResourceLimitCurrentValue": "0x31", 90 | "svcSetThreadActivity": "0x32", 91 | "svcGetThreadContext3": "0x33", 92 | "svcWaitForAddress": "0x34", 93 | "svcSignalToAddress": "0x35", 94 | "svcSynchronizePreemptionState": "0x36", 95 | "svcGetResourceLimitPeakValue": "0x37", 96 | "svcUnknown38": "0x38", 97 | "svcUnknown39": "0x39", 98 | "svcUnknown3a": "0x3A", 99 | "svcUnknown3b": "0x3B", 100 | "svcKernelDebug": "0x3C", 101 | "svcChangeKernelTraceState": "0x3D", 102 | "svcUnknown3e": "0x3E", 103 | "svcUnknown3f": "0x3F", 104 | "svcCreateSession": "0x40", 105 | "svcAcceptSession": "0x41", 106 | "svcReplyAndReceiveLight": "0x42", 107 | "svcReplyAndReceive": "0x43", 108 | "svcReplyAndReceiveWithUserBuffer": "0x44", 109 | "svcCreateEvent": "0x45", 110 | "svcUnknown46": "0x46", 111 | "svcUnknown47": "0x47", 112 | "svcMapPhysicalMemoryUnsafe": "0x48", 113 | "svcUnmapPhysicalMemoryUnsafe": "0x49", 114 | "svcSetUnsafeLimit": "0x4A", 115 | "svcCreateCodeMemory": "0x4B", 116 | "svcControlCodeMemory": "0x4C", 117 | "svcSleepSystem": "0x4D", 118 | "svcReadWriteRegister": "0x4E", 119 | "svcSetProcessActivity": "0x4F", 120 | "svcCreateSharedMemory": "0x50", 121 | "svcMapTransferMemory": "0x51", 122 | "svcUnmapTransferMemory": "0x52", 123 | "svcQueryIoMapping": "0x55", 124 | "svcFlushProcessDataCache": "0x5F", 125 | "svcDebugActiveProcess": "0x60", 126 | "svcBreakDebugProcess": "0x61", 127 | "svcTerminateDebugProcess": "0x62", 128 | "svcGetDebugEvent": "0x63", 129 | "svcContinueDebugEvent": "0x64", 130 | "svcGetProcessList": "0x65", 131 | "svcGetThreadList": "0x66", 132 | "svcGetDebugThreadContext": "0x67", 133 | "svcSetDebugThreadContext": "0x68", 134 | "svcQueryDebugProcessMemory": "0x69", 135 | "svcReadDebugProcessMemory": "0x6A", 136 | "svcWriteDebugProcessMemory": "0x6B", 137 | "svcSetHardwareBreakPoint": "0x6C", 138 | "svcGetDebugThreadParam": "0x6D", 139 | "svcGetSystemInfo": "0x6F", 140 | "svcConnectToPort": "0x72", 141 | "svcSetProcessMemoryPermission": "0x73", 142 | "svcMapProcessMemory": "0x74", 143 | "svcUnmapProcessMemory": "0x75", 144 | "svcQueryProcessMemory": "0x76", 145 | "svcMapProcessCodeMemory": "0x77", 146 | "svcUnmapProcessCodeMemory": "0x78", 147 | "svcCallSecureMonitor": "0x7F" 148 | } 149 | }, 150 | { 151 | "type": "application_type", 152 | "value": 1 153 | }, 154 | { 155 | "type": "min_kernel_version", 156 | "value": "0x30" 157 | }, 158 | { 159 | "type": "handle_table_size", 160 | "value": 512 161 | }, 162 | { 163 | "type": "debug_flags", 164 | "value": { 165 | "allow_debug": true, 166 | "force_debug": true 167 | } 168 | } 169 | ] 170 | } 171 | -------------------------------------------------------------------------------- /source/modules/SceKernelThreadMgr.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "log.h" 8 | #include "module.h" 9 | #include "util.h" 10 | 11 | #include "modules/SceKernelThreadMgr.h" 12 | #include "modules/SceLibKernel.h" 13 | #include "modules/SceSysmem.h" 14 | 15 | #define SCE_KERNEL_HIGHEST_PRIORITY_USER 64 16 | #define SCE_KERNEL_LOWEST_PRIORITY_USER 191 17 | #define SCE_KERNEL_DEFAULT_PRIORITY ((SceInt32)0x10000100) 18 | #define SCE_KERNEL_DEFAULT_PRIORITY_GAME_APP 160 19 | #define SCE_KERNEL_DEFAULT_PRIORITY_USER SCE_KERNEL_DEFAULT_PRIORITY 20 | #define SCE_KERNEL_THREAD_STACK_SIZE_DEFAULT_USER_MAIN (256 * 1024) 21 | #define SCE_KERNEL_TLS_SIZE 0x800 22 | 23 | #define HOS_HIGHEST_PRIORITY 28 24 | #define HOS_LOWEST_PRIORITY 59 25 | 26 | static s32 g_vita_thread_info_tls_slot_id; 27 | 28 | DICT_DEF2(vita_thread_info_dict, SceUID, M_DEFAULT_OPLIST, VitaThreadInfo *, M_POD_OPLIST) 29 | static vita_thread_info_dict_t g_vita_thread_infos; 30 | static RwLock g_vita_thread_infos_lock; 31 | 32 | static inline int vita_priority_to_hos_priority(int priority) 33 | { 34 | if ((priority & SCE_KERNEL_DEFAULT_PRIORITY) == SCE_KERNEL_DEFAULT_PRIORITY) 35 | priority = SCE_KERNEL_DEFAULT_PRIORITY_GAME_APP + (priority & ~SCE_KERNEL_DEFAULT_PRIORITY); 36 | 37 | if ((priority < SCE_KERNEL_HIGHEST_PRIORITY_USER) || 38 | (priority > SCE_KERNEL_LOWEST_PRIORITY_USER)) 39 | return SCE_KERNEL_ERROR_ILLEGAL_PRIORITY; 40 | 41 | return HOS_HIGHEST_PRIORITY + 42 | ((priority - SCE_KERNEL_HIGHEST_PRIORITY_USER) * 43 | (HOS_LOWEST_PRIORITY - HOS_HIGHEST_PRIORITY)) / 44 | (SCE_KERNEL_LOWEST_PRIORITY_USER - SCE_KERNEL_HIGHEST_PRIORITY_USER); 45 | } 46 | 47 | static VitaThreadInfo *get_thread_info_for_uid(SceUID thid) 48 | { 49 | VitaThreadInfo *ti; 50 | 51 | rwlockReadLock(&g_vita_thread_infos_lock); 52 | ti = *vita_thread_info_dict_get(g_vita_thread_infos, thid); 53 | rwlockReadUnlock(&g_vita_thread_infos_lock); 54 | 55 | return ti; 56 | } 57 | 58 | static void NORETURN thread_entry_wrapper(void *arg) 59 | { 60 | VitaThreadInfo *ti = arg; 61 | int ret; 62 | 63 | threadTlsSet(g_vita_thread_info_tls_slot_id, ti); 64 | 65 | ret = ti->entry(ti->arglen, ti->argp); 66 | 67 | LOG("Thread 0x%x returned with: 0x%x", ti->uid, ret); 68 | 69 | threadExit(); 70 | } 71 | 72 | static SceUID create_thread(const char *name, SceKernelThreadEntry entry, int initPriority, 73 | SceSize stackSize) 74 | { 75 | VitaThreadInfo *ti; 76 | Result res; 77 | int priority = vita_priority_to_hos_priority(initPriority); 78 | 79 | if (priority < 0) 80 | return priority; 81 | 82 | ti = malloc(sizeof(*ti)); 83 | if (!ti) { 84 | LOG("Could not allocate thread info for thread \"%s\"", name); 85 | return SCE_KERNEL_ERROR_NO_MEMORY; 86 | } 87 | 88 | memset(ti, 0, sizeof(*ti)); 89 | ti->uid = SceSysmem_get_next_uid(); 90 | strncpy(ti->name, name, sizeof(ti->name) - 1); 91 | ti->entry = entry; 92 | ti->vita_tls = malloc(SCE_KERNEL_TLS_SIZE); 93 | 94 | res = threadCreate(&ti->thread, thread_entry_wrapper, ti, NULL, stackSize, priority, -2); 95 | if (R_FAILED(res)) { 96 | LOG("Error creating thread: 0x%" PRIx32, res); 97 | free(ti->vita_tls); 98 | free(ti); 99 | return SCE_KERNEL_ERROR_THREAD_ERROR; 100 | } 101 | 102 | LOG("Created thread \"%s\", thid: 0x%x", name, ti->uid); 103 | 104 | rwlockWriteLock(&g_vita_thread_infos_lock); 105 | vita_thread_info_dict_set_at(g_vita_thread_infos, ti->uid, ti); 106 | rwlockWriteUnlock(&g_vita_thread_infos_lock); 107 | 108 | return ti->uid; 109 | } 110 | 111 | static int start_thread(SceUID thid, SceSize arglen, void *argp) 112 | { 113 | VitaThreadInfo *ti = get_thread_info_for_uid(thid); 114 | Result res; 115 | 116 | if (!ti) 117 | return SCE_KERNEL_ERROR_UNKNOWN_THREAD_ID; 118 | 119 | if (arglen && argp) { 120 | ti->argp = malloc(arglen); 121 | if (!ti->argp) 122 | return SCE_KERNEL_ERROR_NO_MEMORY; 123 | 124 | memcpy(ti->argp, argp, arglen); 125 | ti->arglen = arglen; 126 | } 127 | 128 | res = threadStart(&ti->thread); 129 | if (R_FAILED(res)) { 130 | LOG("Error starting thread 0x%x: 0x%" PRIx32, thid, res); 131 | return SCE_KERNEL_ERROR_THREAD_ERROR; 132 | } 133 | 134 | return 0; 135 | } 136 | 137 | EXPORT(SceLibKernel, 0xC5C11EE7, SceUID, sceKernelCreateThread, const char *name, 138 | SceKernelThreadEntry entry, int initPriority, SceSize stackSize, SceUInt attr, 139 | int cpuAffinityMask, const SceKernelThreadOptParam *option) 140 | { 141 | return create_thread(name, entry, initPriority, stackSize); 142 | } 143 | 144 | EXPORT(SceThreadmgr, 0x1BBDE3D9, int, sceKernelDeleteThread, SceUID thid) 145 | { 146 | VitaThreadInfo *ti = get_thread_info_for_uid(thid); 147 | Result res; 148 | 149 | if (!ti) 150 | return SCE_KERNEL_ERROR_UNKNOWN_THREAD_ID; 151 | 152 | res = threadClose(&ti->thread); 153 | if (R_FAILED(res)) { 154 | LOG("Error closing thread 0x%x exit: 0x%" PRIx32, thid, res); 155 | return SCE_KERNEL_ERROR_THREAD_ERROR; 156 | } 157 | 158 | free(ti->argp); 159 | free(ti->vita_tls); 160 | rwlockWriteLock(&g_vita_thread_infos_lock); 161 | vita_thread_info_dict_erase(g_vita_thread_infos, thid); 162 | rwlockWriteUnlock(&g_vita_thread_infos_lock); 163 | free(ti); 164 | LOG("Deleted thread 0x%x", thid); 165 | 166 | return 0; 167 | } 168 | 169 | EXPORT(SceLibKernel, 0xF08DE149, int, sceKernelStartThread, SceUID thid, SceSize arglen, void *argp) 170 | { 171 | return start_thread(thid, arglen, argp); 172 | } 173 | 174 | EXPORT(SceThreadmgrCoredumpTime, 0x0C8A38E1, int NORETURN, sceKernelExitThread, int status) 175 | { 176 | VitaThreadInfo *ti = SceKernelThreadMgr_get_thread_info(); 177 | 178 | ti->return_status = status; 179 | threadExit(); 180 | } 181 | 182 | EXPORT(SceThreadmgr, 0x1D17DECF, int NORETURN, sceKernelExitDeleteThread, int status) 183 | { 184 | sceKernelExitThread(status); 185 | // TODO: Delete 186 | } 187 | 188 | EXPORT(SceLibKernel, 0xDDB395A9, int, sceKernelWaitThreadEnd, SceUID thid, int *stat, 189 | SceUInt *timeout) 190 | { 191 | VitaThreadInfo *ti = get_thread_info_for_uid(thid); 192 | uint64_t ns; 193 | Result res; 194 | 195 | if (!ti) 196 | return SCE_KERNEL_ERROR_UNKNOWN_THREAD_ID; 197 | 198 | if (!timeout) 199 | ns = UINT64_MAX; 200 | else 201 | ns = *timeout * 1000ull; 202 | 203 | res = waitSingle(waiterForThread(&ti->thread), ns); 204 | if (R_FAILED(res)) { 205 | LOG("Error waiting for thread 0x%x exit: 0x%" PRIx32, thid, res); 206 | return SCE_KERNEL_ERROR_THREAD_ERROR; 207 | } 208 | 209 | if (stat) 210 | *stat = ti->return_status; 211 | 212 | return 0; 213 | } 214 | 215 | int SceKernelThreadMgr_main_entry(SceKernelThreadEntry entry, int args, void *argp) 216 | { 217 | SceUID thid; 218 | UEvent *process_exit_event; 219 | int ret; 220 | Result res; 221 | 222 | thid = create_thread("
", entry, SCE_KERNEL_DEFAULT_PRIORITY_USER, 223 | SCE_KERNEL_THREAD_STACK_SIZE_DEFAULT_USER_MAIN); 224 | if (thid < 0) 225 | return thid; 226 | 227 | ret = start_thread(thid, args, argp); 228 | if (ret < 0) { 229 | sceKernelDeleteThread(thid); 230 | return SCE_KERNEL_ERROR_THREAD_ERROR; 231 | } 232 | 233 | process_exit_event = SceLibKernel_get_process_exit_uevent(); 234 | 235 | res = waitSingle(waiterForUEvent(process_exit_event), -1); 236 | if (R_FAILED(res)) { 237 | LOG("Error waiting for the process to finish: 0x%" PRIx32, res); 238 | return -1; 239 | } 240 | 241 | // TODO: Also wait for all threads to finish? 242 | sceKernelWaitThreadEnd(thid, NULL, NULL); 243 | sceKernelDeleteThread(thid); 244 | 245 | return 0; 246 | } 247 | 248 | EXPORT(SceThreadmgr, 0x4B675D05, int, sceKernelDelayThread, SceUInt delay) 249 | { 250 | svcSleepThread((s64)delay * 1000); 251 | return 0; 252 | } 253 | 254 | EXPORT(SceThreadmgr, 0xBACA6891, void *, sceKernelGetThreadTLSAddr, SceUID thid, int key) 255 | { 256 | VitaThreadInfo *ti = get_thread_info_for_uid(thid); 257 | 258 | if (!ti) 259 | return NULL; 260 | 261 | if (key >= 0 && key <= 0x100) 262 | return &ti->vita_tls[key]; 263 | 264 | return NULL; 265 | } 266 | 267 | /* SceLibKernel declared in SceLibKernel.c */ 268 | DECLARE_LIBRARY(SceThreadmgr, 0x859a24b1); 269 | DECLARE_LIBRARY(SceThreadmgrCoredumpTime, 0x5E8D0E22); 270 | 271 | int SceKernelThreadMgr_init(void) 272 | { 273 | g_vita_thread_info_tls_slot_id = threadTlsAlloc(NULL); 274 | vita_thread_info_dict_init(g_vita_thread_infos); 275 | rwlockInit(&g_vita_thread_infos_lock); 276 | 277 | return 0; 278 | } 279 | 280 | int SceKernelThreadMgr_finish(void) 281 | { 282 | threadTlsFree(g_vita_thread_info_tls_slot_id); 283 | vita_thread_info_dict_clear(g_vita_thread_infos); 284 | 285 | return 0; 286 | } 287 | 288 | VitaThreadInfo *SceKernelThreadMgr_get_thread_info(void) 289 | { 290 | return threadTlsGet(g_vita_thread_info_tls_slot_id); 291 | } 292 | -------------------------------------------------------------------------------- /include/gxm/util.h: -------------------------------------------------------------------------------- 1 | #ifndef GXM_UTIL_H 2 | #define GXM_UTIL_H 3 | 4 | #include 5 | #include 6 | 7 | #define SCE_GXM_MAX_SCENES_PER_RENDERTARGET 8 8 | 9 | #define SCE_GXM_NOTIFICATION_COUNT 512 10 | 11 | #define SCE_GXM_COLOR_BASE_FORMAT_MASK 0xF1800000U 12 | #define SCE_GXM_TEXTURE_BASE_FORMAT_MASK 0x9f000000U 13 | 14 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_DISABLE_BIT 1 15 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_STRIDE_OFFSET 3 16 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_STRIDE_MASK 0xF 17 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_TYPE_OFFSET 12 18 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_TYPE_MASK 0xFF 19 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_FORMAT_OFFSET 21 20 | #define SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_FORMAT_MASK 0x7FF 21 | 22 | #define SCE_GXM_DEPTH_STENCIL_BG_CTRL_STENCIL_MASK 0xFF 23 | #define SCE_GXM_DEPTH_STENCIL_BG_CTRL_MASK_BIT 0x100 24 | 25 | typedef struct { 26 | // Control Word 0 27 | uint32_t unk0 : 3; 28 | uint32_t vaddr_mode : 3; 29 | uint32_t uaddr_mode : 3; 30 | uint32_t mip_filter : 1; 31 | uint32_t min_filter : 2; 32 | uint32_t mag_filter : 2; 33 | uint32_t unk1 : 3; 34 | uint32_t mip_count : 4; 35 | uint32_t lod_bias : 6; 36 | uint32_t gamma_mode : 2; 37 | uint32_t unk2 : 2; 38 | uint32_t format0 : 1; 39 | // Control Word 1 40 | union { 41 | struct { 42 | uint32_t height : 12; 43 | uint32_t width : 12; 44 | }; 45 | 46 | struct { 47 | uint32_t height_base2 : 4; 48 | uint32_t unknown1 : 12; 49 | uint32_t width_base2 : 4; 50 | uint32_t unknown2 : 4; 51 | }; 52 | 53 | struct { 54 | uint32_t whblock : 24; 55 | uint32_t base_format : 5; 56 | uint32_t type : 3; 57 | }; 58 | }; 59 | // Control Word 2 60 | uint32_t lod_min0 : 2; 61 | uint32_t data_addr : 30; 62 | // Control Word 3 63 | uint32_t palette_addr : 26; 64 | uint32_t lod_min1 : 2; 65 | uint32_t swizzle_format : 3; 66 | uint32_t normalize_mode : 1; 67 | } SceGxmTextureInner; 68 | static_assert(sizeof(SceGxmTextureInner) == sizeof(SceGxmTexture), "Incorrect size"); 69 | 70 | typedef struct { 71 | // opaque start 72 | uint32_t disabled : 1; 73 | uint32_t downscale : 1; 74 | uint32_t pad : 30; 75 | uint32_t width; 76 | uint32_t height; 77 | uint32_t strideInPixels; 78 | void *data; 79 | SceGxmColorFormat colorFormat; 80 | SceGxmColorSurfaceType surfaceType; 81 | // opaque end 82 | uint32_t outputRegisterSize; 83 | SceGxmTexture backgroundTex; 84 | } SceGxmColorSurfaceInner; 85 | static_assert(sizeof(SceGxmColorSurfaceInner) == sizeof(SceGxmColorSurface), "Incorrect size"); 86 | 87 | typedef struct SceGxmProgram { 88 | uint32_t magic; // should be "GXP\0" 89 | 90 | uint8_t major_version; // min 1 91 | uint8_t minor_version; // min 4 92 | uint16_t sdk_version; // 0x350 - 3.50 93 | 94 | uint32_t 95 | size; // size of file - ignoring padding bytes at the end after SceGxmProgramParameter table 96 | 97 | uint32_t binary_guid; 98 | uint32_t source_guid; 99 | 100 | uint32_t program_flags; 101 | 102 | uint32_t buffer_flags; // Buffer flags. 2 bits per buffer. 0x1 - loaded into registers. 0x2 - 103 | // read from memory 104 | 105 | uint32_t texunit_flags[2]; // Tex unit flags. 4 bits per tex unit. 0x1 is non dependent read, 106 | // 0x2 is dependent. 107 | 108 | uint32_t parameter_count; 109 | uint32_t 110 | parameters_offset; // Number of bytes from the start of this field to the first parameter. 111 | uint32_t varyings_offset; // offset to vertex outputs / fragment inputs, relative to this field 112 | 113 | uint16_t primary_reg_count; // (PAs) 114 | uint16_t secondary_reg_count; // (SAs) 115 | uint32_t temp_reg_count1; 116 | uint16_t temp_reg_count2; // Temp reg count in selective rate(programmable blending) phase 117 | 118 | uint16_t primary_program_phase_count; 119 | uint32_t primary_program_instr_count; 120 | uint32_t primary_program_offset; 121 | 122 | uint32_t secondary_program_instr_count; 123 | uint32_t secondary_program_offset; // relative to the beginning of this field 124 | uint32_t secondary_program_offset_end; // relative to the beginning of this field 125 | 126 | uint32_t scratch_buffer_count; 127 | uint32_t thread_buffer_count; 128 | uint32_t literal_buffer_count; 129 | 130 | uint32_t data_buffer_count; 131 | uint32_t texture_buffer_count; 132 | uint32_t default_uniform_buffer_count; 133 | 134 | uint32_t literal_buffer_data_offset; 135 | 136 | uint32_t compiler_version; // The version is shifted 4 bits to the left. 137 | 138 | uint32_t literals_count; 139 | uint32_t literals_offset; 140 | uint32_t uniform_buffer_count; 141 | uint32_t uniform_buffer_offset; 142 | 143 | uint32_t dependent_sampler_count; 144 | uint32_t dependent_sampler_offset; 145 | uint32_t texture_buffer_dependent_sampler_count; 146 | uint32_t texture_buffer_dependent_sampler_offset; 147 | uint32_t container_count; 148 | uint32_t container_offset; 149 | uint32_t sampler_query_info_offset; // Offset to array of uint16_t 150 | } SceGxmProgram; 151 | 152 | typedef struct SceGxmProgramParameter { 153 | int32_t name_offset; // Number of bytes from the start of this structure to the name string. 154 | struct { 155 | uint16_t category : 4; // SceGxmParameterCategory 156 | uint16_t type : 4; // SceGxmParameterType - applicable for constants, not applicable for 157 | // samplers (select type like float, half, fixed ...) 158 | uint16_t component_count : 4; // applicable for constants, not applicable for samplers 159 | // (select size like float2, float3, float3 ...) 160 | uint16_t container_index : 4; // applicable for constants, not applicable for samplers 161 | // (buffer, default, texture) 162 | }; 163 | uint8_t semantic; // applicable only for for vertex attributes, for everything else it's 0 164 | uint8_t semantic_index; 165 | uint32_t array_size; 166 | int32_t resource_index; 167 | } SceGxmProgramParameter; 168 | 169 | static inline uint32_t gxm_parameter_type_size(SceGxmParameterType type) 170 | { 171 | switch (type) { 172 | case SCE_GXM_PARAMETER_TYPE_U8: 173 | case SCE_GXM_PARAMETER_TYPE_S8: 174 | return 1; 175 | case SCE_GXM_PARAMETER_TYPE_F16: 176 | case SCE_GXM_PARAMETER_TYPE_U16: 177 | case SCE_GXM_PARAMETER_TYPE_S16: 178 | return 2; 179 | case SCE_GXM_PARAMETER_TYPE_F32: 180 | case SCE_GXM_PARAMETER_TYPE_U32: 181 | case SCE_GXM_PARAMETER_TYPE_S32: 182 | default: 183 | return 4; 184 | } 185 | } 186 | 187 | static inline void *gxm_texture_get_data(const SceGxmTextureInner *texture) 188 | { 189 | return (void *)(texture->data_addr << 2); 190 | } 191 | 192 | static inline uint32_t gxm_texture_get_type(const SceGxmTextureInner *texture) 193 | { 194 | return texture->type << 29; 195 | } 196 | 197 | static inline size_t gxm_texture_get_width(const SceGxmTextureInner *texture) 198 | { 199 | if (gxm_texture_get_type(texture) != SCE_GXM_TEXTURE_SWIZZLED && 200 | gxm_texture_get_type(texture) != SCE_GXM_TEXTURE_CUBE) 201 | return texture->width + 1; 202 | return 1ull << (texture->width_base2 & 0xF); 203 | } 204 | 205 | static inline size_t gxm_texture_get_height(const SceGxmTextureInner *texture) 206 | { 207 | if (gxm_texture_get_type(texture) != SCE_GXM_TEXTURE_SWIZZLED && 208 | gxm_texture_get_type(texture) != SCE_GXM_TEXTURE_CUBE) 209 | return texture->height + 1; 210 | return 1ull << (texture->height_base2 & 0xF); 211 | } 212 | 213 | static inline SceGxmTextureFormat gxm_texture_get_format(const SceGxmTextureInner *texture) 214 | { 215 | return (SceGxmTextureFormat)(texture->base_format << 24 | texture->format0 << 31 | 216 | texture->swizzle_format << 12); 217 | } 218 | 219 | static inline SceGxmTextureBaseFormat gxm_texture_get_base_format(SceGxmTextureFormat src) 220 | { 221 | return (SceGxmTextureBaseFormat)(src & SCE_GXM_TEXTURE_BASE_FORMAT_MASK); 222 | } 223 | 224 | static inline size_t gxm_texture_get_stride_in_bytes(const SceGxmTextureInner *texture) 225 | { 226 | return ((texture->mip_filter | (texture->min_filter << 1) | (texture->mip_count << 3) | 227 | (texture->lod_bias << 7)) + 228 | 1) * 229 | 4; 230 | } 231 | 232 | static inline bool gxm_base_format_is_paletted_format(SceGxmTextureBaseFormat base_format) 233 | { 234 | return base_format == SCE_GXM_TEXTURE_BASE_FORMAT_P8 || 235 | base_format == SCE_GXM_TEXTURE_BASE_FORMAT_P4; 236 | } 237 | 238 | static inline SceGxmDepthStencilFormat 239 | gxm_ds_surface_get_format(const SceGxmDepthStencilSurface *surface) 240 | { 241 | return (surface->zlsControl >> SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_FORMAT_OFFSET) & 242 | SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_FORMAT_MASK; 243 | } 244 | 245 | static inline SceGxmDepthStencilSurfaceType 246 | gxm_ds_surface_get_type(const SceGxmDepthStencilSurface *surface) 247 | { 248 | return (surface->zlsControl & (SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_TYPE_MASK 249 | << SCE_GXM_DEPTH_STENCIL_ZLS_CTRL_TYPE_OFFSET)); 250 | } 251 | 252 | #endif 253 | -------------------------------------------------------------------------------- /source/modules/SceDisplay.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "modules/SceDisplay.h" 8 | #include "deko_utils.h" 9 | #include "log.h" 10 | #include "module.h" 11 | #include "util.h" 12 | 13 | #include "display/display_to_dk.h" 14 | #include "modules/SceSysmem.h" 15 | 16 | #define SWAPCHAIN_SIZE 2 17 | #define CMDBUF_SIZE 4 * 1024 18 | 19 | static DkDevice g_dk_device; 20 | static DkQueue g_transfer_queue; 21 | static DkMemBlock g_cmdbuf_memblock; 22 | static DkCmdBuf g_cmdbuf; 23 | static DkMemBlock g_framebuffer_memblock; 24 | static DkImage g_swapchain_images[SWAPCHAIN_SIZE]; 25 | static DkSwapchain g_swapchain; 26 | static bool g_swapchain_created; 27 | static uint32_t g_swapchain_image_width; 28 | static uint32_t g_swapchain_image_height; 29 | static Thread g_presenter_thread; 30 | static LEvent g_presenter_thread_run; 31 | static Mutex g_vblank_mutex; 32 | static CondVar g_vblank_condvar; 33 | static SceDisplayFrameBuf g_vita_conf_fb; 34 | 35 | static void create_swapchain(uint32_t width, uint32_t height) 36 | { 37 | DkImageLayoutMaker image_layout_maker; 38 | DkImageLayout framebuffer_layout; 39 | DkSwapchainMaker swapchain_maker; 40 | DkImage const *swapchain_images_ptrs[SWAPCHAIN_SIZE]; 41 | uint32_t framebuffer_size, framebuffer_align; 42 | 43 | /* Calculate layout for the framebuffers */ 44 | dkImageLayoutMakerDefaults(&image_layout_maker, g_dk_device); 45 | image_layout_maker.flags = DkImageFlags_Usage2DEngine | DkImageFlags_UsagePresent; 46 | image_layout_maker.format = DkImageFormat_RGBA8_Unorm; 47 | image_layout_maker.dimensions[0] = width; 48 | image_layout_maker.dimensions[1] = height; 49 | dkImageLayoutInitialize(&framebuffer_layout, &image_layout_maker); 50 | 51 | /* Retrieve necessary size and alignment for the framebuffers */ 52 | framebuffer_size = dkImageLayoutGetSize(&framebuffer_layout); 53 | framebuffer_align = dkImageLayoutGetAlignment(&framebuffer_layout); 54 | framebuffer_size = ALIGN(framebuffer_size, framebuffer_align); 55 | 56 | /* Create a memory block that will host the framebuffers */ 57 | g_framebuffer_memblock = dk_alloc_memblock(g_dk_device, SWAPCHAIN_SIZE * framebuffer_size, 58 | DkMemBlockFlags_GpuCached | DkMemBlockFlags_Image); 59 | 60 | /* Initialize the framebuffers with the layout and backing memory we've just created */ 61 | for (uint32_t i = 0; i < SWAPCHAIN_SIZE; i++) { 62 | swapchain_images_ptrs[i] = &g_swapchain_images[i]; 63 | dkImageInitialize(&g_swapchain_images[i], &framebuffer_layout, g_framebuffer_memblock, 64 | i * framebuffer_size); 65 | } 66 | 67 | /* Create a swapchain out of the framebuffers we've just initialized */ 68 | dkSwapchainMakerDefaults(&swapchain_maker, g_dk_device, nwindowGetDefault(), 69 | swapchain_images_ptrs, SWAPCHAIN_SIZE); 70 | g_swapchain = dkSwapchainCreate(&swapchain_maker); 71 | 72 | g_swapchain_image_width = width; 73 | g_swapchain_image_height = height; 74 | g_swapchain_created = true; 75 | } 76 | 77 | static void presenter_thread_func(void *arg) 78 | { 79 | DkCmdList cmdlist; 80 | DkImage src_image; 81 | DkFence acquire_fence; 82 | const void *base; 83 | uint32_t width, height, stride; 84 | uint32_t pixelfmt; 85 | int slot; 86 | 87 | while (leventTryWait(&g_presenter_thread_run)) { 88 | /* Make sure Vita has configured a framebuffer */ 89 | if (!g_vita_conf_fb.base) 90 | goto next_frame_sleep; 91 | 92 | base = g_vita_conf_fb.base; 93 | width = g_vita_conf_fb.width; 94 | height = g_vita_conf_fb.height; 95 | stride = g_vita_conf_fb.pitch; 96 | pixelfmt = g_vita_conf_fb.pixelformat; 97 | 98 | /* Reconfigure if sizes change and create the swapchain for the first time */ 99 | if (!g_swapchain_created) { 100 | create_swapchain(width, height); 101 | } else if (g_swapchain_image_width != width || g_swapchain_image_height != height) { 102 | dkQueueWaitIdle(g_transfer_queue); 103 | dkSwapchainDestroy(g_swapchain); 104 | dkMemBlockDestroy(g_framebuffer_memblock); 105 | create_swapchain(width, height); 106 | } 107 | 108 | /* Build a DkImage for the source PSVita-configured framebuffer */ 109 | if (!dk_image_for_existing_framebuffer(g_dk_device, &src_image, base, width, height, stride, 110 | pixelfmt)) 111 | goto next_frame_sleep; 112 | 113 | /* Acquire a framebuffer from the swapchain */ 114 | dkSwapchainAcquireImage(g_swapchain, &slot, &acquire_fence); 115 | 116 | /* Wait for the acquire fence to be signaled before starting the 2D transfer */ 117 | dkCmdBufWaitFence(g_cmdbuf, &acquire_fence); 118 | 119 | /* 2D copy command from the PSVita-configured framebuffer to the swapchain framebuffer */ 120 | dk_cmdbuf_copy_image(g_cmdbuf, &src_image, width, height, &g_swapchain_images[slot], width, 121 | height); 122 | 123 | /* Finish the command list */ 124 | cmdlist = dkCmdBufFinishList(g_cmdbuf); 125 | 126 | /* Submit the command list */ 127 | dkQueueSubmitCommands(g_transfer_queue, cmdlist); 128 | 129 | /* Present the new frame once the transfer finishes */ 130 | dkQueuePresentImage(g_transfer_queue, g_swapchain, slot); 131 | 132 | /* Wait until the acquire fence is signalled: the new frame has been presented */ 133 | dkFenceWait(&acquire_fence, -1); 134 | 135 | /* Notify that there has been a "VBlank" (new frame presented) */ 136 | condvarWakeAll(&g_vblank_condvar); 137 | 138 | /* Make sure the transfer has finished (this should have happened when the acquire fence got 139 | * signalled) */ 140 | dkQueueWaitIdle(g_transfer_queue); 141 | 142 | /* The transfer has finished and the queue is idle, we can reset the command buffer */ 143 | dkCmdBufClear(g_cmdbuf); 144 | 145 | continue; 146 | 147 | next_frame_sleep: 148 | svcSleepThread(16666667ull); 149 | } 150 | 151 | threadExit(); 152 | } 153 | 154 | EXPORT(SceDisplayUser, 0x7A410B64, int, sceDisplaySetFrameBuf, const SceDisplayFrameBuf *pParam, 155 | SceDisplaySetBufSync sync) 156 | { 157 | g_vita_conf_fb = *pParam; 158 | 159 | return 0; 160 | } 161 | 162 | EXPORT(SceDisplayUser, 0x42AE6BBC, int, sceDisplayGetFrameBuf, SceDisplayFrameBuf *pParam, 163 | SceDisplaySetBufSync sync) 164 | { 165 | *pParam = g_vita_conf_fb; 166 | 167 | return 0; 168 | } 169 | 170 | EXPORT(SceDisplay, 0x5795E898, int, sceDisplayWaitVblankStart, void) 171 | { 172 | condvarWait(&g_vblank_condvar, &g_vblank_mutex); 173 | return 0; 174 | } 175 | 176 | DECLARE_LIBRARY(SceDisplay, 0x5ed8f994); 177 | DECLARE_LIBRARY(SceDisplayUser, 0x4faacd11); 178 | 179 | int SceDisplay_init(DkDevice dk_device) 180 | { 181 | DkQueueMaker queue_maker; 182 | DkCmdBufMaker cmdbuf_maker; 183 | Result res; 184 | 185 | g_dk_device = dk_device; 186 | 187 | /* Create graphics queue for transfers */ 188 | dkQueueMakerDefaults(&queue_maker, dk_device); 189 | queue_maker.flags = DkQueueFlags_HighPrio; 190 | queue_maker.perWarpScratchMemorySize = 0; 191 | queue_maker.maxConcurrentComputeJobs = 0; 192 | g_transfer_queue = dkQueueCreate(&queue_maker); 193 | 194 | /* Create a memory block which will be used for recording command lists using a command buffer 195 | */ 196 | g_cmdbuf_memblock = dk_alloc_memblock(g_dk_device, CMDBUF_SIZE, 197 | DkMemBlockFlags_CpuUncached | DkMemBlockFlags_GpuCached); 198 | 199 | /* Create a command buffer object */ 200 | dkCmdBufMakerDefaults(&cmdbuf_maker, g_dk_device); 201 | g_cmdbuf = dkCmdBufCreate(&cmdbuf_maker); 202 | 203 | /* Feed our memory to the command buffer so that we can start recording commands */ 204 | dkCmdBufAddMemory(g_cmdbuf, g_cmdbuf_memblock, 0, CMDBUF_SIZE); 205 | 206 | leventInit(&g_presenter_thread_run, true, false); 207 | mutexInit(&g_vblank_mutex); 208 | condvarInit(&g_vblank_condvar); 209 | 210 | g_swapchain_created = false; 211 | 212 | res = threadCreate(&g_presenter_thread, presenter_thread_func, NULL, NULL, 0x10000, 28, -2); 213 | if (R_FAILED(res)) { 214 | LOG("Error creating VSync thread: 0x%" PRIx32, res); 215 | return res; 216 | } 217 | 218 | res = threadStart(&g_presenter_thread); 219 | if (R_FAILED(res)) { 220 | LOG("Error starting VSync thread: 0x%" PRIx32, res); 221 | return res; 222 | } 223 | 224 | return 0; 225 | } 226 | 227 | int SceDisplay_finish(void) 228 | { 229 | Result res; 230 | 231 | leventClear(&g_presenter_thread_run); 232 | condvarWakeAll(&g_vblank_condvar); 233 | 234 | res = threadWaitForExit(&g_presenter_thread); 235 | if (R_FAILED(res)) { 236 | LOG("Error waiting for the presenter thread to finish: 0x%" PRIx32, res); 237 | return res; 238 | } 239 | 240 | dkQueueWaitIdle(g_transfer_queue); 241 | dkCmdBufDestroy(g_cmdbuf); 242 | dkMemBlockDestroy(g_cmdbuf_memblock); 243 | if (g_swapchain_created) { 244 | dkSwapchainDestroy(g_swapchain); 245 | dkMemBlockDestroy(g_framebuffer_memblock); 246 | } 247 | dkQueueDestroy(g_transfer_queue); 248 | 249 | return 0; 250 | } 251 | -------------------------------------------------------------------------------- /include/sce-elf-defs.h: -------------------------------------------------------------------------------- 1 | /* This file gets included multiple times to generate the host-visible and target-visible versions of each struct */ 2 | 3 | #if defined(SCE_ELF_DEFS_HOST) 4 | # define SCE_TYPE(type) type ## _t 5 | # define SCE_PTR(type) type 6 | #elif defined(SCE_ELF_DEFS_TARGET) 7 | # define SCE_TYPE(type) type ## _raw 8 | # define SCE_PTR(type) uint32_t 9 | #else 10 | # error "Do not include sce-elf-defs.h directly! Include sce-elf.h!" 11 | #endif 12 | 13 | #include 14 | 15 | struct SCE_TYPE(sce_module_exports); 16 | struct SCE_TYPE(sce_module_imports); 17 | 18 | typedef struct SCE_TYPE(sce_module_info) { 19 | uint16_t attributes; 20 | uint16_t version; /* Set to 0x0101 */ 21 | char name[27]; /* Name of the library */ 22 | uint8_t type; /* 0x0 for executable, 0x6 for PRX */ 23 | SCE_PTR(const void *) gp_value; 24 | SCE_PTR(struct sce_module_exports_t *) 25 | export_top; /* Offset to start of export table */ 26 | SCE_PTR(struct sce_module_exports_t *) 27 | export_end; /* Offset to end of export table */ 28 | SCE_PTR(struct sce_module_imports_t *) 29 | import_top; /* Offset to start of import table */ 30 | SCE_PTR(struct sce_module_imports_t *) 31 | import_end; /* Offset to end of import table */ 32 | uint32_t module_nid; /* NID of this module */ 33 | uint32_t tls_start; 34 | uint32_t tls_filesz; 35 | uint32_t tls_memsz; 36 | SCE_PTR(const void *) module_start; /* Offset to function to run when library is started, 0 to disable */ 37 | SCE_PTR(const void *) module_stop; /* Offset to function to run when library is exiting, 0 to disable */ 38 | SCE_PTR(const void *) exidx_top; /* Offset to start of ARM EXIDX (optional) */ 39 | SCE_PTR(const void *) exidx_end; /* Offset to end of ARM EXIDX (optional) */ 40 | SCE_PTR(const void *) extab_top; /* Offset to start of ARM EXTAB (optional) */ 41 | SCE_PTR(const void *) extab_end; /* Offset to end of ARM EXTAB (optional */ 42 | 43 | // Included module_sdk_version export in module_info 44 | uint32_t module_sdk_version; /* SDK version */ 45 | } SCE_TYPE(sce_module_info); 46 | 47 | typedef struct SCE_TYPE(sce_module_exports) { 48 | uint16_t size; /* Size of this struct, set to 0x20 */ 49 | uint16_t version; /* 0x1 for normal export, 0x0 for main module export */ 50 | uint16_t flags; /* 0x1 for normal export, 0x8000 for main module export */ 51 | uint16_t num_syms_funcs; /* Number of function exports */ 52 | uint32_t num_syms_vars; /* Number of variable exports */ 53 | uint32_t num_syms_tls_vars; /* Number of TLS variable exports */ 54 | uint32_t library_nid; /* NID of this library */ 55 | SCE_PTR(const char *) library_name; /* Pointer to name of this library */ 56 | SCE_PTR(uint32_t *) nid_table; /* Pointer to array of 32-bit NIDs to export */ 57 | SCE_PTR(const void **) entry_table; /* Pointer to array of data pointers for each NID */ 58 | } SCE_TYPE(sce_module_exports); 59 | 60 | typedef struct SCE_TYPE(sce_module_imports) { 61 | uint16_t size; /* Size of this struct, set to 0x34 */ 62 | uint16_t version; /* Set to 0x1 */ 63 | uint16_t flags; /* Set to 0x0 */ 64 | uint16_t num_syms_funcs; /* Number of function imports */ 65 | uint16_t num_syms_vars; /* Number of variable imports */ 66 | uint16_t num_syms_tls_vars; /* Number of TLS variable imports */ 67 | 68 | uint32_t reserved1; 69 | uint32_t library_nid; /* NID of library to import */ 70 | SCE_PTR(const char *) library_name; /* Pointer to name of imported library, for debugging */ 71 | uint32_t reserved2; 72 | SCE_PTR(uint32_t *) func_nid_table; /* Pointer to array of function NIDs to import */ 73 | SCE_PTR(const void **) func_entry_table;/* Pointer to array of stub functions to fill */ 74 | SCE_PTR(uint32_t *) var_nid_table; /* Pointer to array of variable NIDs to import */ 75 | SCE_PTR(const void **) var_entry_table; /* Pointer to array of data pointers to write to */ 76 | SCE_PTR(uint32_t *) tls_var_nid_table; /* Pointer to array of TLS variable NIDs to import */ 77 | SCE_PTR(const void **) tls_var_entry_table; /* Pointer to array of data pointers to write to */ 78 | } SCE_TYPE(sce_module_imports); 79 | 80 | /* alternative module imports struct with a size of 0x24 */ 81 | typedef struct SCE_TYPE(sce_module_imports_short) { 82 | uint16_t size; /* Size of this struct, set to 0x24 */ 83 | uint16_t version; /* Set to 0x1 */ 84 | uint16_t flags; /* Set to 0x0 */ 85 | uint16_t num_syms_funcs; /* Number of function imports */ 86 | uint16_t num_syms_vars; /* Number of variable imports */ 87 | uint16_t num_syms_tls_vars; /* Number of TLS variable imports */ 88 | 89 | uint32_t library_nid; /* NID of library to import */ 90 | SCE_PTR(const char *) library_name; /* Pointer to name of imported library, for debugging */ 91 | SCE_PTR(uint32_t *) func_nid_table; /* Pointer to array of function NIDs to import */ 92 | SCE_PTR(const void **) func_entry_table; /* Pointer to array of stub functions to fill */ 93 | SCE_PTR(uint32_t *) var_nid_table; /* Pointer to array of variable NIDs to import */ 94 | SCE_PTR(const void **) var_entry_table; /* Pointer to array of data pointers to write to */ 95 | } SCE_TYPE(sce_module_imports_short); 96 | 97 | typedef union SCE_TYPE(sce_module_imports_u) { 98 | uint16_t size; 99 | SCE_TYPE(sce_module_imports) imports; 100 | SCE_TYPE(sce_module_imports_short) imports_short; 101 | } SCE_TYPE(sce_module_imports_u); 102 | 103 | typedef struct SCE_TYPE(sce_process_param) { 104 | uint32_t size; /* 0x34 */ 105 | uint32_t magic; /* PSP2 */ 106 | uint32_t version; /* Unknown, but it could be 6 */ 107 | uint32_t fw_version; /* SDK vsersion */ 108 | SCE_PTR(const char *) main_thread_name; /* Thread name pointer*/ 109 | int32_t main_thread_priority; /* Thread initPriority */ 110 | uint32_t main_thread_stacksize; /* Thread stacksize*/ 111 | uint32_t main_thread_attribute; /* Unknown */ 112 | SCE_PTR(const char *) process_name; /* Process name pointer */ 113 | uint32_t process_preload_disabled; /* Module load inhibit */ 114 | uint32_t main_thread_cpu_affinity_mask; /* Unknown */ 115 | SCE_PTR(const void *) sce_libc_param; /* SceLibc param pointer */ 116 | uint32_t unk; /* Unknown */ 117 | } SCE_TYPE(sce_process_param); 118 | 119 | typedef struct SCE_TYPE(sce_libc_param) { 120 | struct { 121 | uint32_t size; /* 0x34 */ 122 | uint32_t unk_0x4; /* Unknown, set to 1 */ 123 | SCE_PTR(void *) malloc_init; /* Initialize malloc heap */ 124 | SCE_PTR(void *) malloc_term; /* Terminate malloc heap */ 125 | SCE_PTR(void *) malloc; /* malloc replacement */ 126 | SCE_PTR(void *) free; /* free replacement */ 127 | SCE_PTR(void *) calloc; /* calloc replacement */ 128 | SCE_PTR(void *) realloc; /* realloc replacement */ 129 | SCE_PTR(void *) memalign; /* memalign replacement */ 130 | SCE_PTR(void *) reallocalign; /* reallocalign replacement */ 131 | SCE_PTR(void *) malloc_stats; /* malloc_stats replacement */ 132 | SCE_PTR(void *) malloc_stats_fast; /* malloc_stats_fast replacement */ 133 | SCE_PTR(void *) malloc_usable_size; /* malloc_usable_size replacement */ 134 | } _malloc_replace; 135 | 136 | struct { 137 | uint32_t size; /* 0x28 */ 138 | uint32_t unk_0x4; /* Unknown, set to 1 */ 139 | SCE_PTR(void *) operator_new; /* new operator replacement */ 140 | SCE_PTR(void *) operator_new_nothrow; /* new (nothrow) operator replacement */ 141 | SCE_PTR(void *) operator_new_arr; /* new[] operator replacement */ 142 | SCE_PTR(void *) operator_new_arr_nothrow; /* new[] (nothrow) operator replacement */ 143 | SCE_PTR(void *) operator_delete; /* delete operator replacement */ 144 | SCE_PTR(void *) operator_delete_nothrow; /* delete (nothrow) operator replacement */ 145 | SCE_PTR(void *) operator_delete_arr; /* delete[] operator replacement */ 146 | SCE_PTR(void *) operator_delete_arr_nothrow; /* delete[] (nothrow) operator replacement */ 147 | } _new_replace; 148 | 149 | struct { 150 | uint32_t size; /* 0x18 */ 151 | uint32_t unk_0x4; /* Unknown, set to 1 */ 152 | SCE_PTR(void *) malloc_init_for_tls; /* Initialise tls malloc heap */ 153 | SCE_PTR(void *) malloc_term_for_tls; /* Terminate tls malloc heap */ 154 | SCE_PTR(void *) malloc_for_tls; /* malloc_for_tls replacement */ 155 | SCE_PTR(void *) free_for_tls; /* free_for_tls replacement */ 156 | } _malloc_for_tls_replace; 157 | 158 | uint32_t size; /* 0x38 */ 159 | uint32_t unk_0x04; /* Unknown */ 160 | SCE_PTR(uint32_t *) heap_size; /* Heap size variable */ 161 | SCE_PTR(uint32_t *) default_heap_size; /* Default heap size variable */ 162 | SCE_PTR(uint32_t *) heap_extended_alloc; /* Dynamically extend heap size */ 163 | SCE_PTR(uint32_t *) heap_delayed_alloc; /* Allocate heap on first call to malloc */ 164 | uint32_t fw_version; /* SDK version */ 165 | uint32_t unk_0x1C; /* Unknown, set to 9 */ 166 | SCE_PTR(const void *) malloc_replace; /* malloc replacement functions */ 167 | SCE_PTR(const void *) new_replace; /* new replacement functions */ 168 | SCE_PTR(uint32_t *) heap_initial_size; /* Dynamically allocated heap initial size */ 169 | SCE_PTR(uint32_t *) heap_unit_1mb; /* Change alloc unit size from 64k to 1M */ 170 | SCE_PTR(uint32_t *) heap_detect_overrun; /* Detect heap buffer overruns */ 171 | SCE_PTR(const void *) malloc_for_tls_replace; /* malloc_for_tls replacement functions */ 172 | 173 | uint32_t _default_heap_size; /* Default SceLibc heap size - 0x40000 (256KiB) */ 174 | } SCE_TYPE(sce_libc_param); 175 | 176 | #undef SCE_TYPE 177 | #undef SCE_PTR 178 | -------------------------------------------------------------------------------- /source/modules/SceLibKernel.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #undef st_ctime 11 | #undef st_atime 12 | #undef st_mtime 13 | #include 14 | 15 | #include "log.h" 16 | #include "module.h" 17 | #include "util.h" 18 | 19 | #include "modules/SceKernelThreadMgr.h" 20 | #include "modules/SceLibKernel.h" 21 | #include "modules/SceSysmem.h" 22 | 23 | typedef struct { 24 | uint32_t index; 25 | SceUID uid; 26 | FILE *fp; 27 | char *file; 28 | } VitaOpenedFile; 29 | 30 | typedef struct { 31 | uint32_t index; 32 | SceUID uid; 33 | DIR *dir; 34 | char *path; 35 | } VitaOpenedDir; 36 | 37 | static UEvent g_process_exit_event; 38 | static atomic_int g_process_exit_res; 39 | 40 | DICT_DEF2(vita_opened_files_dict, SceUID, M_DEFAULT_OPLIST, VitaOpenedFile *, M_POD_OPLIST) 41 | static vita_opened_files_dict_t g_vita_opened_files; 42 | static RwLock g_vita_opened_files_lock; 43 | 44 | DICT_DEF2(vita_opened_dirs_dict, SceUID, M_DEFAULT_OPLIST, VitaOpenedDir *, M_POD_OPLIST) 45 | static vita_opened_dirs_dict_t g_vita_opened_dirs; 46 | static RwLock g_vita_opened_dirs_lock; 47 | 48 | static VitaOpenedFile *get_opened_file_for_fd(SceUID fd) 49 | { 50 | VitaOpenedFile *vfile; 51 | 52 | rwlockReadLock(&g_vita_opened_files_lock); 53 | vfile = *vita_opened_files_dict_get(g_vita_opened_files, fd); 54 | rwlockReadUnlock(&g_vita_opened_files_lock); 55 | 56 | return vfile; 57 | } 58 | 59 | static VitaOpenedDir *get_opened_dir_for_fd(SceUID fd) 60 | { 61 | VitaOpenedDir *vdir; 62 | 63 | rwlockReadLock(&g_vita_opened_dirs_lock); 64 | vdir = *vita_opened_dirs_dict_get(g_vita_opened_dirs, fd); 65 | rwlockReadUnlock(&g_vita_opened_dirs_lock); 66 | 67 | return vdir; 68 | } 69 | 70 | EXPORT(SceLibKernel, 0xB295EB61, void *, sceKernelGetTLSAddr, int key) 71 | { 72 | VitaThreadInfo *ti; 73 | 74 | if (key >= 0 && key <= 0x100) { 75 | ti = SceKernelThreadMgr_get_thread_info(); 76 | return &ti->vita_tls[key]; 77 | } 78 | 79 | return NULL; 80 | } 81 | 82 | EXPORT(SceLibKernel, 0x7595D9AA, int, sceKernelExitProcess, int res) 83 | { 84 | LOG("sceKernelExitProcess called! Return value %d", res); 85 | 86 | atomic_store(&g_process_exit_res, res); 87 | ueventSignal(&g_process_exit_event); 88 | 89 | threadExit(); 90 | 91 | return 0; 92 | } 93 | 94 | EXPORT(SceLibKernel, 0xDA6EC8EF, int, sceKernelCreateLwMutex, SceKernelLwMutexWork *pWork, 95 | const char *pName, unsigned int attr, int initCount, 96 | const SceKernelLwMutexOptParam *pOptParam) 97 | { 98 | Mutex *mutex = (void *)pWork; 99 | mutexInit(mutex); 100 | if (initCount > 0) 101 | mutexLock(mutex); 102 | return 0; 103 | } 104 | 105 | EXPORT(SceLibKernel, 0x244E76D2, int, sceKernelDeleteLwMutex, SceKernelLwMutexWork *pWork) 106 | { 107 | mutexUnlock((void *)pWork); 108 | return 0; 109 | } 110 | 111 | EXPORT(SceLibKernel, 0x46E7BE7B, int, sceKernelLockLwMutex, SceKernelLwMutexWork *pWork, 112 | int lockCount, unsigned int *pTimeout) 113 | { 114 | mutexLock((void *)pWork); 115 | return 0; 116 | } 117 | 118 | EXPORT(SceLibKernel, 0xA6A2C915, int, sceKernelTryLockLwMutex, SceKernelLwMutexWork *pWork, 119 | int lockCount) 120 | { 121 | if (mutexTryLock((void *)pWork)) 122 | return 0; 123 | else 124 | return SCE_KERNEL_ERROR_LW_MUTEX_FAILED_TO_OWN; 125 | } 126 | 127 | EXPORT(SceLibKernel, 0x91FA6614, int, sceKernelUnlockLwMutex, SceKernelLwMutexWork *pWork, 128 | int unlockCount) 129 | { 130 | mutexUnlock((void *)pWork); 131 | return 0; 132 | } 133 | 134 | static const char *vita_io_open_flags_to_fopen_flags(int flags) 135 | { 136 | if (flags == SCE_O_RDONLY) 137 | return "r"; 138 | else if (flags == (SCE_O_WRONLY | SCE_O_CREAT | SCE_O_TRUNC)) 139 | return "w"; 140 | else if (flags == (SCE_O_WRONLY | SCE_O_CREAT | SCE_O_APPEND)) 141 | return "a"; 142 | else if (flags == SCE_O_RDWR) 143 | return "r+"; 144 | else if (flags == (SCE_O_RDWR | SCE_O_CREAT | SCE_O_TRUNC)) 145 | return "w+"; 146 | else if (flags == (SCE_O_RDWR | SCE_O_CREAT | SCE_O_APPEND)) 147 | return "a+"; 148 | else 149 | return NULL; 150 | } 151 | 152 | EXPORT(SceLibKernel, 0x6C60AC61, SceUID, sceIoOpen, const char *file, int flags, SceMode mode) 153 | { 154 | VitaOpenedFile *vfile; 155 | const char *c; 156 | FILE *fp; 157 | 158 | c = strchr(file, ':'); 159 | if (c) 160 | file = c + 1; 161 | 162 | if (*file == '\0') 163 | return SCE_ERROR_ERRNO_ENODEV; 164 | 165 | fp = fopen(file, vita_io_open_flags_to_fopen_flags(flags)); 166 | if (!fp) 167 | return SCE_ERROR_ERRNO_ENODEV; 168 | 169 | vfile = malloc(sizeof(*vfile)); 170 | if (!vfile) { 171 | fclose(fp); 172 | return SCE_ERROR_ERRNO_EMFILE; 173 | } 174 | 175 | memset(vfile, 0, sizeof(*vfile)); 176 | vfile->uid = SceSysmem_get_next_uid(); 177 | vfile->fp = fp; 178 | vfile->file = strdup(file); 179 | 180 | rwlockWriteLock(&g_vita_opened_files_lock); 181 | vita_opened_files_dict_set_at(g_vita_opened_files, vfile->uid, vfile); 182 | rwlockWriteUnlock(&g_vita_opened_files_lock); 183 | 184 | return vfile->uid; 185 | } 186 | 187 | EXPORT(SceIofilemgr, 0xC70B8886, int, sceIoClose, SceUID fd) 188 | { 189 | VitaOpenedFile *vfile = get_opened_file_for_fd(fd); 190 | int ret; 191 | 192 | if (!vfile) 193 | return SCE_ERROR_ERRNO_EBADF; 194 | 195 | ret = fclose(vfile->fp); 196 | rwlockWriteLock(&g_vita_opened_files_lock); 197 | vita_opened_files_dict_erase(g_vita_opened_files, fd); 198 | rwlockWriteUnlock(&g_vita_opened_files_lock); 199 | free(vfile->file); 200 | free(vfile); 201 | 202 | if (ret) 203 | return SCE_ERROR_ERRNO_EBADF; 204 | 205 | return 0; 206 | } 207 | 208 | EXPORT(SceIofilemgr, 0xFDB32293, SceSSize, sceIoRead, SceUID fd, void *buf, SceSize nbyte) 209 | { 210 | VitaOpenedFile *vfile = get_opened_file_for_fd(fd); 211 | if (!vfile) 212 | return SCE_ERROR_ERRNO_EBADF; 213 | 214 | return fread(buf, 1, nbyte, vfile->fp); 215 | } 216 | 217 | EXPORT(SceIofilemgr, 0x34EFD876, SceSSize, sceIoWrite, SceUID fd, const void *buf, SceSize nbyte) 218 | { 219 | VitaOpenedFile *vfile = get_opened_file_for_fd(fd); 220 | if (!vfile) 221 | return SCE_ERROR_ERRNO_EBADF; 222 | 223 | return fwrite(buf, 1, nbyte, vfile->fp); 224 | } 225 | 226 | EXPORT(SceIofilemgr, 0x49252B9B, long, sceIoLseek32, SceUID fd, long offset, int whence) 227 | { 228 | VitaOpenedFile *vfile = get_opened_file_for_fd(fd); 229 | if (!vfile) 230 | return SCE_ERROR_ERRNO_EBADF; 231 | 232 | return fseek(vfile->fp, offset, whence); 233 | } 234 | 235 | EXPORT(SceIofilemgr, 0x16512F59, int, sceIoSyncByFd, SceUID fd, int flag) 236 | { 237 | VitaOpenedFile *vfile = get_opened_file_for_fd(fd); 238 | if (!vfile) 239 | return SCE_ERROR_ERRNO_EBADF; 240 | 241 | return fflush(vfile->fp); 242 | } 243 | 244 | EXPORT(SceLibKernel, 0xA9283DD0, SceUID, sceIoDopen, const char *dirname) 245 | { 246 | VitaOpenedDir *vdir; 247 | const char *c; 248 | DIR *dir; 249 | 250 | c = strchr(dirname, ':'); 251 | if (c) 252 | dirname = c + 1; 253 | 254 | dir = opendir(dirname); 255 | if (!dir) 256 | return SCE_ERROR_ERRNO_ENODEV; 257 | 258 | vdir = malloc(sizeof(*vdir)); 259 | if (!vdir) { 260 | closedir(dir); 261 | return SCE_ERROR_ERRNO_EMFILE; 262 | } 263 | 264 | memset(vdir, 0, sizeof(*vdir)); 265 | vdir->uid = SceSysmem_get_next_uid(); 266 | vdir->dir = dir; 267 | vdir->path = strdup(dirname); 268 | 269 | rwlockWriteLock(&g_vita_opened_dirs_lock); 270 | vita_opened_dirs_dict_set_at(g_vita_opened_dirs, vdir->uid, vdir); 271 | rwlockWriteUnlock(&g_vita_opened_dirs_lock); 272 | 273 | return vdir->uid; 274 | } 275 | 276 | EXPORT(SceIofilemgr, 0x422A221A, int, sceIoDclose, SceUID fd) 277 | { 278 | VitaOpenedDir *vdir = get_opened_dir_for_fd(fd); 279 | int ret; 280 | 281 | if (!vdir) 282 | return SCE_ERROR_ERRNO_EBADF; 283 | 284 | ret = closedir(vdir->dir); 285 | free(vdir->path); 286 | rwlockWriteLock(&g_vita_opened_dirs_lock); 287 | vita_opened_dirs_dict_erase(g_vita_opened_dirs, fd); 288 | rwlockWriteUnlock(&g_vita_opened_dirs_lock); 289 | free(vdir); 290 | 291 | if (ret) 292 | return SCE_ERROR_ERRNO_EBADF; 293 | 294 | return 0; 295 | } 296 | 297 | static inline void tm_to_sce_datetime(SceDateTime *dt, const struct tm *tm) 298 | { 299 | dt->year = tm->tm_year; 300 | dt->month = tm->tm_mon; 301 | dt->day = tm->tm_mday; 302 | dt->hour = tm->tm_hour; 303 | dt->minute = tm->tm_min; 304 | dt->second = tm->tm_sec; 305 | dt->microsecond = 0; 306 | } 307 | 308 | EXPORT(SceLibKernel, 0x9C8B6624, int, sceIoDread, SceUID fd, SceIoDirent *dirent) 309 | { 310 | VitaOpenedDir *vdir = get_opened_dir_for_fd(fd); 311 | struct dirent *de; 312 | struct stat statbuf; 313 | SceMode st_mode; 314 | unsigned int st_attr; 315 | const char *path; 316 | char full_path[PATH_MAX]; 317 | 318 | if (!vdir) 319 | return SCE_ERROR_ERRNO_EBADF; 320 | 321 | de = readdir(vdir->dir); 322 | if (!de) 323 | return 0; 324 | 325 | if (strcmp(vdir->path, "/") == 0) { 326 | path = de->d_name; 327 | } else { 328 | snprintf(full_path, sizeof(full_path), "%s/%s", vdir->path, de->d_name); 329 | path = full_path; 330 | } 331 | 332 | if (stat(path, &statbuf) != 0) 333 | return SCE_ERROR_ERRNO_EBADF; 334 | 335 | if ((statbuf.st_mode & S_IFMT) == S_IFDIR) { 336 | st_mode = SCE_S_IFDIR | SCE_S_IRWXU | SCE_S_IRWXG | SCE_S_IRWXS; 337 | st_attr = SCE_SO_IFDIR; 338 | } else { 339 | st_mode = SCE_S_IFREG | SCE_S_IRUSR | SCE_S_IWUSR | SCE_S_IRGRP | SCE_S_IWGRP | 340 | SCE_S_IRSYS | SCE_S_IWSYS; 341 | st_attr = SCE_SO_IFREG; 342 | } 343 | 344 | dirent->d_stat.st_mode = st_mode; 345 | dirent->d_stat.st_attr = st_attr; 346 | dirent->d_stat.st_size = statbuf.st_size; 347 | tm_to_sce_datetime(&dirent->d_stat.st_ctime, gmtime(&statbuf.st_ctim.tv_sec)); 348 | tm_to_sce_datetime(&dirent->d_stat.st_atime, gmtime(&statbuf.st_atim.tv_sec)); 349 | tm_to_sce_datetime(&dirent->d_stat.st_mtime, gmtime(&statbuf.st_mtim.tv_sec)); 350 | strncpy(dirent->d_name, de->d_name, sizeof(dirent->d_name)); 351 | dirent->d_private = NULL; 352 | 353 | return 1; 354 | } 355 | 356 | EXPORT_FUNC(SceLibKernel, 0x632980D7, memset) 357 | EXPORT_FUNC(SceLibKernel, 0x14E9DBD7, memcpy) 358 | EXPORT_FUNC(SceLibKernel, 0x736753C8, memmove) 359 | EXPORT_FUNC(SceLibKernel, 0xFA26BC62, printf) 360 | 361 | EXPORT(SceLibKernel, 0x0FB972F9, int, sceKernelGetThreadId) 362 | { 363 | return SceKernelThreadMgr_get_thread_info()->uid; 364 | } 365 | 366 | DECLARE_LIBRARY(SceLibKernel, 0xcae9ace6); 367 | DECLARE_LIBRARY(SceIofilemgr, 0xf2ff276e); 368 | 369 | int SceLibKernel_init(void) 370 | { 371 | ueventCreate(&g_process_exit_event, false); 372 | 373 | return 0; 374 | } 375 | 376 | UEvent *SceLibKernel_get_process_exit_uevent(void) 377 | { 378 | return &g_process_exit_event; 379 | } 380 | 381 | int SceLibKernel_get_process_exit_res(void) 382 | { 383 | return atomic_load(&g_process_exit_res); 384 | } 385 | -------------------------------------------------------------------------------- /include/gxm/gxm_to_dk.h: -------------------------------------------------------------------------------- 1 | #ifndef GXM_TO_DK_H 2 | #define GXM_TO_DK_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "gxm/util.h" 9 | 10 | static inline uint32_t gxm_color_surface_type_to_dk_image_flags(SceGxmColorSurfaceType type) 11 | { 12 | switch (type) { 13 | case SCE_GXM_COLOR_SURFACE_LINEAR: 14 | return DkImageFlags_PitchLinear; 15 | case SCE_GXM_COLOR_SURFACE_TILED: 16 | case SCE_GXM_COLOR_SURFACE_SWIZZLED: 17 | return DkImageFlags_BlockLinear; 18 | default: 19 | UNREACHABLE("Unsupported SceGxmColorSurfaceType"); 20 | } 21 | } 22 | 23 | static inline uint32_t gxm_ds_surface_type_to_dk_image_flags(SceGxmDepthStencilSurfaceType type) 24 | { 25 | switch (type) { 26 | case SCE_GXM_DEPTH_STENCIL_SURFACE_LINEAR: 27 | return DkImageFlags_PitchLinear; 28 | case SCE_GXM_DEPTH_STENCIL_SURFACE_TILED: 29 | /* TODO */ 30 | default: 31 | UNREACHABLE("Unsupported SceGxmDepthStencilSurfaceType"); 32 | } 33 | } 34 | 35 | static inline size_t gxm_color_base_format_bits_per_pixel(SceGxmColorBaseFormat base_format) 36 | { 37 | switch (base_format) { 38 | case SCE_GXM_COLOR_BASE_FORMAT_U8: 39 | case SCE_GXM_COLOR_BASE_FORMAT_S8: 40 | return 8; 41 | case SCE_GXM_COLOR_BASE_FORMAT_U5U6U5: 42 | case SCE_GXM_COLOR_BASE_FORMAT_U1U5U5U5: 43 | case SCE_GXM_COLOR_BASE_FORMAT_U4U4U4U4: 44 | case SCE_GXM_COLOR_BASE_FORMAT_U8U3U3U2: 45 | case SCE_GXM_COLOR_BASE_FORMAT_F16: 46 | case SCE_GXM_COLOR_BASE_FORMAT_S16: 47 | case SCE_GXM_COLOR_BASE_FORMAT_U16: 48 | case SCE_GXM_COLOR_BASE_FORMAT_S5S5U6: 49 | case SCE_GXM_COLOR_BASE_FORMAT_U8U8: 50 | case SCE_GXM_COLOR_BASE_FORMAT_S8S8: 51 | return 16; 52 | case SCE_GXM_COLOR_BASE_FORMAT_U8U8U8: 53 | return 24; 54 | case SCE_GXM_COLOR_BASE_FORMAT_U8U8U8U8: 55 | case SCE_GXM_COLOR_BASE_FORMAT_F16F16: 56 | case SCE_GXM_COLOR_BASE_FORMAT_F32: 57 | case SCE_GXM_COLOR_BASE_FORMAT_S16S16: 58 | case SCE_GXM_COLOR_BASE_FORMAT_U16U16: 59 | case SCE_GXM_COLOR_BASE_FORMAT_U2U10U10U10: 60 | case SCE_GXM_COLOR_BASE_FORMAT_U8S8S8U8: 61 | case SCE_GXM_COLOR_BASE_FORMAT_S8S8S8S8: 62 | case SCE_GXM_COLOR_BASE_FORMAT_F11F11F10: 63 | case SCE_GXM_COLOR_BASE_FORMAT_SE5M9M9M9: 64 | case SCE_GXM_COLOR_BASE_FORMAT_U2F10F10F10: 65 | return 32; 66 | case SCE_GXM_COLOR_BASE_FORMAT_F16F16F16F16: 67 | case SCE_GXM_COLOR_BASE_FORMAT_F32F32: 68 | return 64; 69 | default: 70 | UNREACHABLE("Unsupported SceGxmColorBaseFormat"); 71 | } 72 | } 73 | 74 | static inline size_t gxm_color_base_format_bytes_per_pixel(SceGxmColorBaseFormat base_format) 75 | { 76 | return gxm_color_base_format_bits_per_pixel(base_format) >> 3; 77 | } 78 | 79 | static inline size_t gxm_color_format_bytes_per_pixel(SceGxmColorFormat format) 80 | { 81 | return gxm_color_base_format_bytes_per_pixel(format & SCE_GXM_COLOR_BASE_FORMAT_MASK); 82 | } 83 | 84 | static inline size_t gxm_texture_base_format_bytes_per_pixel(SceGxmTextureBaseFormat base_format) 85 | { 86 | switch (base_format) { 87 | case SCE_GXM_TEXTURE_BASE_FORMAT_U8: 88 | case SCE_GXM_TEXTURE_BASE_FORMAT_S8: 89 | return 8; 90 | case SCE_GXM_TEXTURE_BASE_FORMAT_U4U4U4U4: 91 | case SCE_GXM_TEXTURE_BASE_FORMAT_U8U3U3U2: 92 | case SCE_GXM_TEXTURE_BASE_FORMAT_U1U5U5U5: 93 | case SCE_GXM_TEXTURE_BASE_FORMAT_U5U6U5: 94 | case SCE_GXM_TEXTURE_BASE_FORMAT_S5S5U6: 95 | case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8: 96 | case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8: 97 | case SCE_GXM_TEXTURE_BASE_FORMAT_U16: 98 | case SCE_GXM_TEXTURE_BASE_FORMAT_S16: 99 | case SCE_GXM_TEXTURE_BASE_FORMAT_F16: 100 | return 16; 101 | case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8U8U8: 102 | case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8S8S8: 103 | case SCE_GXM_TEXTURE_BASE_FORMAT_U2U10U10U10: 104 | case SCE_GXM_TEXTURE_BASE_FORMAT_U16U16: 105 | case SCE_GXM_TEXTURE_BASE_FORMAT_S16S16: 106 | case SCE_GXM_TEXTURE_BASE_FORMAT_F16F16: 107 | case SCE_GXM_TEXTURE_BASE_FORMAT_F32: 108 | case SCE_GXM_TEXTURE_BASE_FORMAT_F32M: 109 | case SCE_GXM_TEXTURE_BASE_FORMAT_X8S8S8U8: 110 | case SCE_GXM_TEXTURE_BASE_FORMAT_X8U24: 111 | case SCE_GXM_TEXTURE_BASE_FORMAT_U32: 112 | case SCE_GXM_TEXTURE_BASE_FORMAT_S32: 113 | case SCE_GXM_TEXTURE_BASE_FORMAT_SE5M9M9M9: 114 | case SCE_GXM_TEXTURE_BASE_FORMAT_F11F11F10: 115 | return 32; 116 | case SCE_GXM_TEXTURE_BASE_FORMAT_F16F16F16F16: 117 | case SCE_GXM_TEXTURE_BASE_FORMAT_U16U16U16U16: 118 | case SCE_GXM_TEXTURE_BASE_FORMAT_S16S16S16S16: 119 | case SCE_GXM_TEXTURE_BASE_FORMAT_F32F32: 120 | case SCE_GXM_TEXTURE_BASE_FORMAT_U32U32: 121 | return 64; 122 | case SCE_GXM_TEXTURE_BASE_FORMAT_PVRT2BPP: 123 | return 2; 124 | case SCE_GXM_TEXTURE_BASE_FORMAT_PVRT4BPP: 125 | return 4; 126 | case SCE_GXM_TEXTURE_BASE_FORMAT_PVRTII2BPP: 127 | return 2; 128 | case SCE_GXM_TEXTURE_BASE_FORMAT_PVRTII4BPP: 129 | return 4; 130 | case SCE_GXM_TEXTURE_BASE_FORMAT_UBC1: 131 | return 4; 132 | case SCE_GXM_TEXTURE_BASE_FORMAT_UBC2: 133 | case SCE_GXM_TEXTURE_BASE_FORMAT_UBC3: 134 | return 8; 135 | case SCE_GXM_TEXTURE_BASE_FORMAT_UBC4: 136 | case SCE_GXM_TEXTURE_BASE_FORMAT_SBC4: 137 | return 4; 138 | case SCE_GXM_TEXTURE_BASE_FORMAT_UBC5: 139 | case SCE_GXM_TEXTURE_BASE_FORMAT_SBC5: 140 | return 8; 141 | case SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P2: 142 | case SCE_GXM_TEXTURE_BASE_FORMAT_YUV420P3: 143 | return 12; 144 | case SCE_GXM_TEXTURE_BASE_FORMAT_YUV422: 145 | return 16; 146 | case SCE_GXM_TEXTURE_BASE_FORMAT_P4: 147 | return 4; 148 | case SCE_GXM_TEXTURE_BASE_FORMAT_P8: 149 | return 8; 150 | case SCE_GXM_TEXTURE_BASE_FORMAT_U8U8U8: 151 | case SCE_GXM_TEXTURE_BASE_FORMAT_S8S8S8: 152 | return 24; 153 | case SCE_GXM_TEXTURE_BASE_FORMAT_U2F10F10F10: 154 | return 32; 155 | default: 156 | UNREACHABLE("Unsupported SceGxmTextureBaseFormat"); 157 | } 158 | } 159 | 160 | static inline size_t gxm_texture_format_bytes_per_pixel(SceGxmTextureFormat format) 161 | { 162 | return gxm_texture_base_format_bytes_per_pixel(format & SCE_GXM_TEXTURE_BASE_FORMAT_MASK) >> 3; 163 | } 164 | 165 | static inline DkImageFormat gxm_color_format_to_dk_image_format(SceGxmColorFormat format) 166 | { 167 | switch (format) { 168 | case SCE_GXM_COLOR_FORMAT_U8U8U8U8_ABGR: 169 | return DkImageFormat_RGBA8_Unorm; 170 | default: 171 | UNREACHABLE("Unsupported SceGxmColorFormat"); 172 | } 173 | } 174 | 175 | static inline DkImageFormat gxm_ds_format_to_dk_image_format(SceGxmDepthStencilFormat format) 176 | { 177 | switch (format) { 178 | case SCE_GXM_DEPTH_STENCIL_FORMAT_DF32: 179 | return DkImageFormat_ZF32; 180 | case SCE_GXM_DEPTH_STENCIL_FORMAT_S8: 181 | return DkImageFormat_S8; 182 | case SCE_GXM_DEPTH_STENCIL_FORMAT_S8D24: 183 | return DkImageFormat_Z24S8; 184 | case SCE_GXM_DEPTH_STENCIL_FORMAT_D16: 185 | return DkImageFormat_Z16; 186 | default: 187 | UNREACHABLE("Unsupported SceGxmDepthStencilFormat"); 188 | } 189 | } 190 | 191 | static inline DkVtxAttribType gxm_to_dk_vtx_attrib_type(SceGxmAttributeFormat format) 192 | { 193 | switch (format) { 194 | case SCE_GXM_ATTRIBUTE_FORMAT_U8: 195 | case SCE_GXM_ATTRIBUTE_FORMAT_U16: 196 | return DkVtxAttribType_Uint; 197 | case SCE_GXM_ATTRIBUTE_FORMAT_S8: 198 | case SCE_GXM_ATTRIBUTE_FORMAT_S16: 199 | return DkVtxAttribType_Sint; 200 | case SCE_GXM_ATTRIBUTE_FORMAT_U8N: 201 | case SCE_GXM_ATTRIBUTE_FORMAT_U16N: 202 | return DkVtxAttribType_Unorm; 203 | case SCE_GXM_ATTRIBUTE_FORMAT_S8N: 204 | case SCE_GXM_ATTRIBUTE_FORMAT_S16N: 205 | return DkVtxAttribType_Snorm; 206 | case SCE_GXM_ATTRIBUTE_FORMAT_F16: 207 | case SCE_GXM_ATTRIBUTE_FORMAT_F32: 208 | return DkVtxAttribType_Float; 209 | default: 210 | UNREACHABLE("Unsupported SceGxmAttributeFormat"); 211 | } 212 | } 213 | 214 | static inline DkVtxAttribSize gxm_to_dk_vtx_attrib_size(SceGxmAttributeFormat format, 215 | uint8_t componentCount) 216 | { 217 | switch (format) { 218 | case SCE_GXM_ATTRIBUTE_FORMAT_U8: 219 | case SCE_GXM_ATTRIBUTE_FORMAT_S8: 220 | case SCE_GXM_ATTRIBUTE_FORMAT_U8N: 221 | case SCE_GXM_ATTRIBUTE_FORMAT_S8N: 222 | switch (componentCount) { 223 | case 1: 224 | return DkVtxAttribSize_1x8; 225 | case 2: 226 | return DkVtxAttribSize_2x8; 227 | case 3: 228 | return DkVtxAttribSize_3x8; 229 | case 4: 230 | return DkVtxAttribSize_4x8; 231 | default: 232 | UNREACHABLE("Unsupported SceGxmAttributeFormat component count"); 233 | } 234 | case SCE_GXM_ATTRIBUTE_FORMAT_U16: 235 | case SCE_GXM_ATTRIBUTE_FORMAT_S16: 236 | case SCE_GXM_ATTRIBUTE_FORMAT_U16N: 237 | case SCE_GXM_ATTRIBUTE_FORMAT_S16N: 238 | case SCE_GXM_ATTRIBUTE_FORMAT_F16: 239 | switch (componentCount) { 240 | case 1: 241 | return DkVtxAttribSize_1x16; 242 | case 2: 243 | return DkVtxAttribSize_2x16; 244 | case 3: 245 | return DkVtxAttribSize_3x16; 246 | case 4: 247 | return DkVtxAttribSize_4x16; 248 | default: 249 | UNREACHABLE("Unsupported SceGxmAttributeFormat component count"); 250 | } 251 | case SCE_GXM_ATTRIBUTE_FORMAT_F32: 252 | switch (componentCount) { 253 | case 1: 254 | return DkVtxAttribSize_1x32; 255 | case 2: 256 | return DkVtxAttribSize_2x32; 257 | case 3: 258 | return DkVtxAttribSize_3x32; 259 | case 4: 260 | return DkVtxAttribSize_4x32; 261 | default: 262 | UNREACHABLE("Unsupported SceGxmAttributeFormat component count"); 263 | } 264 | default: 265 | UNREACHABLE("Unsupported SceGxmAttributeFormat"); 266 | } 267 | } 268 | 269 | static inline DkPrimitive gxm_to_dk_primitive(SceGxmPrimitiveType prim) 270 | { 271 | switch (prim) { 272 | case SCE_GXM_PRIMITIVE_TRIANGLES: 273 | return DkPrimitive_Triangles; 274 | case SCE_GXM_PRIMITIVE_LINES: 275 | return DkPrimitive_Lines; 276 | case SCE_GXM_PRIMITIVE_POINTS: 277 | return DkPrimitive_Lines; 278 | case SCE_GXM_PRIMITIVE_TRIANGLE_STRIP: 279 | return DkPrimitive_TriangleStrip; 280 | case SCE_GXM_PRIMITIVE_TRIANGLE_FAN: 281 | return DkPrimitive_TriangleFan; 282 | case SCE_GXM_PRIMITIVE_TRIANGLE_EDGES: 283 | return DkPrimitive_Triangles; 284 | default: 285 | UNREACHABLE("Unsupported SceGxmPrimitiveType"); 286 | } 287 | } 288 | 289 | static inline DkIdxFormat gxm_to_dk_idx_format(SceGxmIndexFormat format) 290 | { 291 | switch (format) { 292 | case SCE_GXM_INDEX_FORMAT_U16: 293 | return DkIdxFormat_Uint16; 294 | case SCE_GXM_INDEX_FORMAT_U32: 295 | return DkIdxFormat_Uint32; 296 | default: 297 | UNREACHABLE("Unsupported SceGxmIndexFormat"); 298 | } 299 | } 300 | 301 | static inline DkCompareOp gxm_depth_func_to_dk_compare_op(SceGxmDepthFunc func) 302 | { 303 | switch (func) { 304 | case SCE_GXM_DEPTH_FUNC_NEVER: 305 | return DkCompareOp_Never; 306 | case SCE_GXM_DEPTH_FUNC_LESS: 307 | return DkCompareOp_Less; 308 | case SCE_GXM_DEPTH_FUNC_EQUAL: 309 | return DkCompareOp_Equal; 310 | case SCE_GXM_DEPTH_FUNC_LESS_EQUAL: 311 | return DkCompareOp_Lequal; 312 | case SCE_GXM_DEPTH_FUNC_GREATER: 313 | return DkCompareOp_Greater; 314 | case SCE_GXM_DEPTH_FUNC_NOT_EQUAL: 315 | return DkCompareOp_NotEqual; 316 | case SCE_GXM_DEPTH_FUNC_GREATER_EQUAL: 317 | return DkCompareOp_Gequal; 318 | case SCE_GXM_DEPTH_FUNC_ALWAYS: 319 | return DkCompareOp_Always; 320 | default: 321 | UNREACHABLE("Unsupported SceGxmDepthFunc"); 322 | } 323 | } 324 | 325 | static inline DkCompareOp gxm_stencil_func_to_dk_compare_op(SceGxmStencilFunc func) 326 | { 327 | switch (func) { 328 | case SCE_GXM_STENCIL_FUNC_NEVER: 329 | return DkCompareOp_Never; 330 | case SCE_GXM_STENCIL_FUNC_LESS: 331 | return DkCompareOp_Less; 332 | case SCE_GXM_STENCIL_FUNC_EQUAL: 333 | return DkCompareOp_Equal; 334 | case SCE_GXM_STENCIL_FUNC_LESS_EQUAL: 335 | return DkCompareOp_Lequal; 336 | case SCE_GXM_STENCIL_FUNC_GREATER: 337 | return DkCompareOp_Greater; 338 | case SCE_GXM_STENCIL_FUNC_NOT_EQUAL: 339 | return DkCompareOp_NotEqual; 340 | case SCE_GXM_STENCIL_FUNC_GREATER_EQUAL: 341 | return DkCompareOp_Gequal; 342 | case SCE_GXM_STENCIL_FUNC_ALWAYS: 343 | return DkCompareOp_Always; 344 | default: 345 | UNREACHABLE("Unsupported SceGxmStencilFunc"); 346 | } 347 | } 348 | 349 | static inline DkStencilOp gxm_stencil_op_to_dk_stencil_op(SceGxmStencilOp op) 350 | { 351 | switch (op) { 352 | case SCE_GXM_STENCIL_OP_KEEP: 353 | return DkStencilOp_Keep; 354 | case SCE_GXM_STENCIL_OP_ZERO: 355 | return DkStencilOp_Zero; 356 | case SCE_GXM_STENCIL_OP_REPLACE: 357 | return DkStencilOp_Replace; 358 | case SCE_GXM_STENCIL_OP_INCR: 359 | return DkStencilOp_Incr; 360 | case SCE_GXM_STENCIL_OP_DECR: 361 | return DkStencilOp_Decr; 362 | case SCE_GXM_STENCIL_OP_INVERT: 363 | return DkStencilOp_Invert; 364 | case SCE_GXM_STENCIL_OP_INCR_WRAP: 365 | return DkStencilOp_IncrWrap; 366 | case SCE_GXM_STENCIL_OP_DECR_WRAP: 367 | return DkStencilOp_DecrWrap; 368 | default: 369 | UNREACHABLE("Unsupported SceGxmStencilOp"); 370 | } 371 | } 372 | 373 | static inline DkWrapMode gxm_texture_addr_mode_to_dk_wrap_mode(SceGxmTextureAddrMode mode) 374 | { 375 | switch (mode) { 376 | case SCE_GXM_TEXTURE_ADDR_REPEAT: 377 | return DkWrapMode_Repeat; 378 | case SCE_GXM_TEXTURE_ADDR_MIRROR: 379 | return DkWrapMode_MirroredRepeat; 380 | case SCE_GXM_TEXTURE_ADDR_CLAMP: 381 | return DkWrapMode_Clamp; 382 | case SCE_GXM_TEXTURE_ADDR_MIRROR_CLAMP: 383 | return DkWrapMode_MirrorClamp; 384 | case SCE_GXM_TEXTURE_ADDR_REPEAT_IGNORE_BORDER: 385 | UNREACHABLE("Unsupported SceGxmTextureAddrMode SCE_GXM_TEXTURE_ADDR_REPEAT_IGNORE_BORDER"); 386 | case SCE_GXM_TEXTURE_ADDR_CLAMP_FULL_BORDER: 387 | return DkWrapMode_ClampToBorder; 388 | case SCE_GXM_TEXTURE_ADDR_CLAMP_IGNORE_BORDER: 389 | case SCE_GXM_TEXTURE_ADDR_CLAMP_HALF_BORDER: 390 | default: 391 | UNREACHABLE("Unsupported SceGxmTextureAddrMode"); 392 | } 393 | } 394 | 395 | static inline DkFilter gxm_texture_filter_to_dk_filter(SceGxmTextureFilter filter) 396 | { 397 | switch (filter) { 398 | case SCE_GXM_TEXTURE_FILTER_POINT: 399 | return DkFilter_Nearest; 400 | case SCE_GXM_TEXTURE_FILTER_LINEAR: 401 | return DkFilter_Linear; 402 | default: 403 | UNREACHABLE("Unsupported SceGxmTextureFilter"); 404 | } 405 | } 406 | 407 | #endif 408 | -------------------------------------------------------------------------------- /licenses/COPYING.txt: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc., 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | Preamble 10 | 11 | The licenses for most software are designed to take away your 12 | freedom to share and change it. By contrast, the GNU General Public 13 | License is intended to guarantee your freedom to share and change free 14 | software--to make sure the software is free for all its users. This 15 | General Public License applies to most of the Free Software 16 | Foundation's software and to any other program whose authors commit to 17 | using it. (Some other Free Software Foundation software is covered by 18 | the GNU Lesser General Public License instead.) You can apply it to 19 | your programs, too. 20 | 21 | When we speak of free software, we are referring to freedom, not 22 | price. Our General Public Licenses are designed to make sure that you 23 | have the freedom to distribute copies of free software (and charge for 24 | this service if you wish), that you receive source code or can get it 25 | if you want it, that you can change the software or use pieces of it 26 | in new free programs; and that you know you can do these things. 27 | 28 | To protect your rights, we need to make restrictions that forbid 29 | anyone to deny you these rights or to ask you to surrender the rights. 30 | These restrictions translate to certain responsibilities for you if you 31 | distribute copies of the software, or if you modify it. 32 | 33 | For example, if you distribute copies of such a program, whether 34 | gratis or for a fee, you must give the recipients all the rights that 35 | you have. You must make sure that they, too, receive or can get the 36 | source code. And you must show them these terms so they know their 37 | rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and 40 | (2) offer you this license which gives you legal permission to copy, 41 | distribute and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain 44 | that everyone understands that there is no warranty for this free 45 | software. If the software is modified by someone else and passed on, we 46 | want its recipients to know that what they have is not the original, so 47 | that any problems introduced by others will not reflect on the original 48 | authors' reputations. 49 | 50 | Finally, any free program is threatened constantly by software 51 | patents. We wish to avoid the danger that redistributors of a free 52 | program will individually obtain patent licenses, in effect making the 53 | program proprietary. To prevent this, we have made it clear that any 54 | patent must be licensed for everyone's free use or not licensed at all. 55 | 56 | The precise terms and conditions for copying, distribution and 57 | modification follow. 58 | 59 | GNU GENERAL PUBLIC LICENSE 60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 61 | 62 | 0. This License applies to any program or other work which contains 63 | a notice placed by the copyright holder saying it may be distributed 64 | under the terms of this General Public License. The "Program", below, 65 | refers to any such program or work, and a "work based on the Program" 66 | means either the Program or any derivative work under copyright law: 67 | that is to say, a work containing the Program or a portion of it, 68 | either verbatim or with modifications and/or translated into another 69 | language. (Hereinafter, translation is included without limitation in 70 | the term "modification".) Each licensee is addressed as "you". 71 | 72 | Activities other than copying, distribution and modification are not 73 | covered by this License; they are outside its scope. The act of 74 | running the Program is not restricted, and the output from the Program 75 | is covered only if its contents constitute a work based on the 76 | Program (independent of having been made by running the Program). 77 | Whether that is true depends on what the Program does. 78 | 79 | 1. You may copy and distribute verbatim copies of the Program's 80 | source code as you receive it, in any medium, provided that you 81 | conspicuously and appropriately publish on each copy an appropriate 82 | copyright notice and disclaimer of warranty; keep intact all the 83 | notices that refer to this License and to the absence of any warranty; 84 | and give any other recipients of the Program a copy of this License 85 | along with the Program. 86 | 87 | You may charge a fee for the physical act of transferring a copy, and 88 | you may at your option offer warranty protection in exchange for a fee. 89 | 90 | 2. You may modify your copy or copies of the Program or any portion 91 | of it, thus forming a work based on the Program, and copy and 92 | distribute such modifications or work under the terms of Section 1 93 | above, provided that you also meet all of these conditions: 94 | 95 | a) You must cause the modified files to carry prominent notices 96 | stating that you changed the files and the date of any change. 97 | 98 | b) You must cause any work that you distribute or publish, that in 99 | whole or in part contains or is derived from the Program or any 100 | part thereof, to be licensed as a whole at no charge to all third 101 | parties under the terms of this License. 102 | 103 | c) If the modified program normally reads commands interactively 104 | when run, you must cause it, when started running for such 105 | interactive use in the most ordinary way, to print or display an 106 | announcement including an appropriate copyright notice and a 107 | notice that there is no warranty (or else, saying that you provide 108 | a warranty) and that users may redistribute the program under 109 | these conditions, and telling the user how to view a copy of this 110 | License. (Exception: if the Program itself is interactive but 111 | does not normally print such an announcement, your work based on 112 | the Program is not required to print an announcement.) 113 | 114 | These requirements apply to the modified work as a whole. If 115 | identifiable sections of that work are not derived from the Program, 116 | and can be reasonably considered independent and separate works in 117 | themselves, then this License, and its terms, do not apply to those 118 | sections when you distribute them as separate works. But when you 119 | distribute the same sections as part of a whole which is a work based 120 | on the Program, the distribution of the whole must be on the terms of 121 | this License, whose permissions for other licensees extend to the 122 | entire whole, and thus to each and every part regardless of who wrote it. 123 | 124 | Thus, it is not the intent of this section to claim rights or contest 125 | your rights to work written entirely by you; rather, the intent is to 126 | exercise the right to control the distribution of derivative or 127 | collective works based on the Program. 128 | 129 | In addition, mere aggregation of another work not based on the Program 130 | with the Program (or with a work based on the Program) on a volume of 131 | a storage or distribution medium does not bring the other work under 132 | the scope of this License. 133 | 134 | 3. You may copy and distribute the Program (or a work based on it, 135 | under Section 2) in object code or executable form under the terms of 136 | Sections 1 and 2 above provided that you also do one of the following: 137 | 138 | a) Accompany it with the complete corresponding machine-readable 139 | source code, which must be distributed under the terms of Sections 140 | 1 and 2 above on a medium customarily used for software interchange; or, 141 | 142 | b) Accompany it with a written offer, valid for at least three 143 | years, to give any third party, for a charge no more than your 144 | cost of physically performing source distribution, a complete 145 | machine-readable copy of the corresponding source code, to be 146 | distributed under the terms of Sections 1 and 2 above on a medium 147 | customarily used for software interchange; or, 148 | 149 | c) Accompany it with the information you received as to the offer 150 | to distribute corresponding source code. (This alternative is 151 | allowed only for noncommercial distribution and only if you 152 | received the program in object code or executable form with such 153 | an offer, in accord with Subsection b above.) 154 | 155 | The source code for a work means the preferred form of the work for 156 | making modifications to it. For an executable work, complete source 157 | code means all the source code for all modules it contains, plus any 158 | associated interface definition files, plus the scripts used to 159 | control compilation and installation of the executable. However, as a 160 | special exception, the source code distributed need not include 161 | anything that is normally distributed (in either source or binary 162 | form) with the major components (compiler, kernel, and so on) of the 163 | operating system on which the executable runs, unless that component 164 | itself accompanies the executable. 165 | 166 | If distribution of executable or object code is made by offering 167 | access to copy from a designated place, then offering equivalent 168 | access to copy the source code from the same place counts as 169 | distribution of the source code, even though third parties are not 170 | compelled to copy the source along with the object code. 171 | 172 | 4. You may not copy, modify, sublicense, or distribute the Program 173 | except as expressly provided under this License. Any attempt 174 | otherwise to copy, modify, sublicense or distribute the Program is 175 | void, and will automatically terminate your rights under this License. 176 | However, parties who have received copies, or rights, from you under 177 | this License will not have their licenses terminated so long as such 178 | parties remain in full compliance. 179 | 180 | 5. You are not required to accept this License, since you have not 181 | signed it. However, nothing else grants you permission to modify or 182 | distribute the Program or its derivative works. These actions are 183 | prohibited by law if you do not accept this License. Therefore, by 184 | modifying or distributing the Program (or any work based on the 185 | Program), you indicate your acceptance of this License to do so, and 186 | all its terms and conditions for copying, distributing or modifying 187 | the Program or works based on it. 188 | 189 | 6. Each time you redistribute the Program (or any work based on the 190 | Program), the recipient automatically receives a license from the 191 | original licensor to copy, distribute or modify the Program subject to 192 | these terms and conditions. You may not impose any further 193 | restrictions on the recipients' exercise of the rights granted herein. 194 | You are not responsible for enforcing compliance by third parties to 195 | this License. 196 | 197 | 7. If, as a consequence of a court judgment or allegation of patent 198 | infringement or for any other reason (not limited to patent issues), 199 | conditions are imposed on you (whether by court order, agreement or 200 | otherwise) that contradict the conditions of this License, they do not 201 | excuse you from the conditions of this License. If you cannot 202 | distribute so as to satisfy simultaneously your obligations under this 203 | License and any other pertinent obligations, then as a consequence you 204 | may not distribute the Program at all. For example, if a patent 205 | license would not permit royalty-free redistribution of the Program by 206 | all those who receive copies directly or indirectly through you, then 207 | the only way you could satisfy both it and this License would be to 208 | refrain entirely from distribution of the Program. 209 | 210 | If any portion of this section is held invalid or unenforceable under 211 | any particular circumstance, the balance of the section is intended to 212 | apply and the section as a whole is intended to apply in other 213 | circumstances. 214 | 215 | It is not the purpose of this section to induce you to infringe any 216 | patents or other property right claims or to contest validity of any 217 | such claims; this section has the sole purpose of protecting the 218 | integrity of the free software distribution system, which is 219 | implemented by public license practices. Many people have made 220 | generous contributions to the wide range of software distributed 221 | through that system in reliance on consistent application of that 222 | system; it is up to the author/donor to decide if he or she is willing 223 | to distribute software through any other system and a licensee cannot 224 | impose that choice. 225 | 226 | This section is intended to make thoroughly clear what is believed to 227 | be a consequence of the rest of this License. 228 | 229 | 8. If the distribution and/or use of the Program is restricted in 230 | certain countries either by patents or by copyrighted interfaces, the 231 | original copyright holder who places the Program under this License 232 | may add an explicit geographical distribution limitation excluding 233 | those countries, so that distribution is permitted only in or among 234 | countries not thus excluded. In such case, this License incorporates 235 | the limitation as if written in the body of this License. 236 | 237 | 9. The Free Software Foundation may publish revised and/or new versions 238 | of the General Public License from time to time. Such new versions will 239 | be similar in spirit to the present version, but may differ in detail to 240 | address new problems or concerns. 241 | 242 | Each version is given a distinguishing version number. If the Program 243 | specifies a version number of this License which applies to it and "any 244 | later version", you have the option of following the terms and conditions 245 | either of that version or of any later version published by the Free 246 | Software Foundation. If the Program does not specify a version number of 247 | this License, you may choose any version ever published by the Free Software 248 | Foundation. 249 | 250 | 10. If you wish to incorporate parts of the Program into other free 251 | programs whose distribution conditions are different, write to the author 252 | to ask for permission. For software which is copyrighted by the Free 253 | Software Foundation, write to the Free Software Foundation; we sometimes 254 | make exceptions for this. Our decision will be guided by the two goals 255 | of preserving the free status of all derivatives of our free software and 256 | of promoting the sharing and reuse of software generally. 257 | 258 | NO WARRANTY 259 | 260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY 261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN 262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES 263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED 264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS 266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE 267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 268 | REPAIR OR CORRECTION. 269 | 270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING 271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR 272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, 273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING 274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED 275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY 276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER 277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE 278 | POSSIBILITY OF SUCH DAMAGES. 279 | 280 | END OF TERMS AND CONDITIONS 281 | 282 | How to Apply These Terms to Your New Programs 283 | 284 | If you develop a new program, and you want it to be of the greatest 285 | possible use to the public, the best way to achieve this is to make it 286 | free software which everyone can redistribute and change under these terms. 287 | 288 | To do so, attach the following notices to the program. It is safest 289 | to attach them to the start of each source file to most effectively 290 | convey the exclusion of warranty; and each file should have at least 291 | the "copyright" line and a pointer to where the full notice is found. 292 | 293 | 294 | Copyright (C) 295 | 296 | This program is free software; you can redistribute it and/or modify 297 | it under the terms of the GNU General Public License as published by 298 | the Free Software Foundation; either version 2 of the License, or 299 | (at your option) any later version. 300 | 301 | This program is distributed in the hope that it will be useful, 302 | but WITHOUT ANY WARRANTY; without even the implied warranty of 303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 304 | GNU General Public License for more details. 305 | 306 | You should have received a copy of the GNU General Public License along 307 | with this program; if not, write to the Free Software Foundation, Inc., 308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 309 | 310 | Also add information on how to contact you by electronic and paper mail. 311 | 312 | If the program is interactive, make it output a short notice like this 313 | when it starts in an interactive mode: 314 | 315 | Gnomovision version 69, Copyright (C) year name of author 316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 317 | This is free software, and you are welcome to redistribute it 318 | under certain conditions; type `show c' for details. 319 | 320 | The hypothetical commands `show w' and `show c' should show the appropriate 321 | parts of the General Public License. Of course, the commands you use may 322 | be called something other than `show w' and `show c'; they could even be 323 | mouse-clicks or menu items--whatever suits your program. 324 | 325 | You should also get your employer (if you work as a programmer) or your 326 | school, if any, to sign a "copyright disclaimer" for the program, if 327 | necessary. Here is a sample; alter the names: 328 | 329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 330 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 331 | 332 | , 1 April 1989 333 | Ty Coon, President of Vice 334 | 335 | This General Public License does not permit incorporating your program into 336 | proprietary programs. If your program is a subroutine library, you may 337 | consider it more useful to permit linking proprietary applications with the 338 | library. If this is what you want to do, use the GNU Lesser General 339 | Public License instead of this License. 340 | -------------------------------------------------------------------------------- /source/load.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "arm-encode.h" 8 | #include "load.h" 9 | #include "log.h" 10 | #include "module.h" 11 | #include "sce-elf.h" 12 | #include "self.h" 13 | #include "util.h" 14 | 15 | #define IMP_GET_NEXT(imp) ((sce_module_imports_u_t *)((char *)imp + imp->size)) 16 | #define IMP_GET_FUNC_COUNT(imp) \ 17 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.num_syms_funcs \ 18 | : imp->imports.num_syms_funcs) 19 | #define IMP_GET_VARS_COUNT(imp) \ 20 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.num_syms_vars \ 21 | : imp->imports.num_syms_vars) 22 | #define IMP_GET_NID(imp) \ 23 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.library_nid \ 24 | : imp->imports.library_nid) 25 | #define IMP_GET_NAME(imp) \ 26 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.library_name \ 27 | : imp->imports.library_name) 28 | #define IMP_GET_FUNC_TABLE(imp) \ 29 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.func_nid_table \ 30 | : imp->imports.func_nid_table) 31 | #define IMP_GET_FUNC_ENTRIES(imp) \ 32 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.func_entry_table \ 33 | : imp->imports.func_entry_table) 34 | #define IMP_GET_VARS_TABLE(imp) \ 35 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.var_nid_table \ 36 | : imp->imports.var_nid_table) 37 | #define IMP_GET_VARS_ENTRIES(imp) \ 38 | (imp->size == sizeof(sce_module_imports_short_raw) ? imp->imports_short.var_entry_table \ 39 | : imp->imports.var_entry_table) 40 | 41 | #define CODE_RX_TO_RW_ADDR(rx_base, rw_base, rx_addr) \ 42 | ((((uintptr_t)rx_addr - (uintptr_t)rx_base)) + (uintptr_t)rw_base) 43 | 44 | typedef struct { 45 | void *src_data; 46 | uintptr_t rw_addr; 47 | uintptr_t rx_addr; /* Only used for executable segments */ 48 | Elf32_Word p_type; 49 | Elf32_Word p_filesz; 50 | Elf32_Word p_memsz; 51 | Elf32_Word p_flags; 52 | Elf32_Word p_align; 53 | bool needs_free; 54 | } segment_info_t; 55 | 56 | static int load_vpk(Jit *jit, const void *data, uint32_t size, void **entry); 57 | static int load_executable(Jit *jit, const uint8_t *data, void **entry); 58 | static int load_elf(Jit *jit, const void *data, void **entry); 59 | static int load_self(Jit *jit, const void *data, void **entry); 60 | static int load_segments(Jit *jit, void **entry, Elf32_Addr e_entry, segment_info_t *segments, 61 | int num_segments); 62 | static int resolve_imports(uintptr_t rx_base, uintptr_t rw_base, sce_module_imports_u_t *import); 63 | static int relocate(const void *reloc, uint32_t size, segment_info_t *segs); 64 | 65 | int elf_check_vita_header(const Elf32_Ehdr *hdr) 66 | { 67 | if (!(hdr->e_ident[EI_MAG0] == ELFMAG0 && hdr->e_ident[EI_MAG1] == ELFMAG1 && 68 | hdr->e_ident[EI_MAG2] == ELFMAG2 && hdr->e_ident[EI_MAG3] == ELFMAG3)) { 69 | LOG("Invalid ELF magic number."); 70 | return -1; 71 | } 72 | 73 | if (hdr->e_ident[EI_CLASS] != ELFCLASS32) { 74 | LOG("Not a 32bit executable."); 75 | return -1; 76 | } 77 | 78 | if (hdr->e_ident[EI_DATA] != ELFDATA2LSB) { 79 | LOG("Not a valid ARM executable."); 80 | return -1; 81 | } 82 | 83 | if (hdr->e_ident[EI_VERSION] != EV_CURRENT) { 84 | LOG("Unsupported ELF version."); 85 | return -1; 86 | } 87 | 88 | if (hdr->e_type != ET_SCE_RELEXEC) { 89 | LOG("Only ET_SCE_RELEXEC files are supported."); 90 | return -1; 91 | } 92 | 93 | if (hdr->e_machine != EM_ARM) { 94 | LOG("Not an ARM executable."); 95 | return -1; 96 | } 97 | 98 | if (hdr->e_version != EV_CURRENT) { 99 | LOG("Unsupported ELF version."); 100 | return -1; 101 | } 102 | 103 | return 0; 104 | } 105 | 106 | static int elf_get_sce_module_info(Elf32_Addr e_entry, const segment_info_t *segments, 107 | sce_module_info_raw **mod_info) 108 | { 109 | uint32_t offset; 110 | uint32_t index; 111 | 112 | index = (e_entry & 0xC0000000) >> 30; 113 | offset = e_entry & 0x3FFFFFFF; 114 | 115 | if (segments[index].src_data == NULL) { 116 | LOG("Invalid segment index %" PRId32, index); 117 | return -1; 118 | } 119 | 120 | *mod_info = (sce_module_info_raw *)((char *)segments[index].src_data + offset); 121 | 122 | return index; 123 | } 124 | 125 | int load_file(Jit *jit, const char *filename, void **entry) 126 | { 127 | void *data; 128 | uint32_t size; 129 | int ret = 0; 130 | 131 | LOG("Opening '%s' for reading.", filename); 132 | if (util_load_file(filename, &data, &size) != 0) { 133 | LOG("Cannot load file."); 134 | return -1; 135 | } 136 | 137 | if (load_executable(jit, data, entry) < 0) { 138 | /* Fallback to vpk (zip file) */ 139 | if (load_vpk(jit, data, size, entry) < 0) { 140 | LOG("Unsupported file."); 141 | ret = -1; 142 | } 143 | } 144 | 145 | free(data); 146 | return ret; 147 | } 148 | 149 | static int load_vpk(Jit *jit, const void *data, uint32_t size, void **entry) 150 | { 151 | mz_bool status; 152 | mz_zip_archive zip_archive; 153 | size_t uncompressed_size; 154 | void *eboot_bin; 155 | int ret; 156 | 157 | mz_zip_zero_struct(&zip_archive); 158 | 159 | status = mz_zip_reader_init_mem(&zip_archive, data, size, 0); 160 | if (status == MZ_FALSE) 161 | return -1; 162 | 163 | LOG("Found a ZIP file (VPK), extracting eboot.bin."); 164 | 165 | eboot_bin = 166 | mz_zip_reader_extract_file_to_heap(&zip_archive, "eboot.bin", &uncompressed_size, 0); 167 | if (!eboot_bin) { 168 | mz_zip_reader_end(&zip_archive); 169 | return -1; 170 | } 171 | 172 | ret = load_executable(jit, eboot_bin, entry); 173 | 174 | mz_free(eboot_bin); 175 | mz_zip_reader_end(&zip_archive); 176 | 177 | return ret; 178 | } 179 | 180 | static int load_executable(Jit *jit, const uint8_t *data, void **entry) 181 | { 182 | if (data[0] == ELFMAG0) { 183 | if (data[1] == ELFMAG1 && data[2] == ELFMAG2 && data[3] == ELFMAG3) { 184 | LOG("Found an ELF, loading."); 185 | if (load_elf(jit, data, entry) < 0) { 186 | LOG("Cannot load ELF."); 187 | return -1; 188 | } 189 | } 190 | } else if (data[0] == SCEMAG0) { 191 | if (data[1] == SCEMAG1 && data[2] == SCEMAG2 && data[3] == SCEMAG3) { 192 | LOG("Found a SELF, loading."); 193 | if (load_self(jit, data, entry) < 0) { 194 | LOG("Cannot load SELF."); 195 | return -1; 196 | } 197 | } 198 | } else { 199 | /* Unsupported executable */ 200 | return -1; 201 | } 202 | 203 | return 0; 204 | } 205 | 206 | static int load_self(Jit *jit, const void *data, void **entry) 207 | { 208 | const SCE_header *self_header = data; 209 | const Elf32_Ehdr *elf_hdr = (void *)((char *)data + self_header->elf_offset); 210 | const Elf32_Phdr *prog_hdrs = (void *)((char *)data + self_header->phdr_offset); 211 | const segment_info *seg_infos = (void *)((char *)data + self_header->section_info_offset); 212 | segment_info_t *segments; 213 | const Elf32_Phdr *seg_header; 214 | uint8_t *seg_bytes; 215 | void *uncompressed; 216 | mz_ulong dest_bytes; 217 | int ret; 218 | 219 | if (self_header->magic != 0x00454353) { 220 | LOG("SELF is corrupt or encrypted. Decryption is not yet supported."); 221 | return -1; 222 | } 223 | 224 | if (self_header->version != 3) { 225 | LOG("SELF version 0x%" PRIx32 " is not supported.", self_header->version); 226 | return -1; 227 | } 228 | 229 | if (self_header->header_type != 1) { 230 | LOG("SELF header type 0x%x is not supported.", self_header->header_type); 231 | return -1; 232 | } 233 | 234 | LOG("Loading SELF: ELF type: 0x%x, header_type: 0x%x, self_filesize: 0x%llx, self_offset: " 235 | "0x%llx", 236 | elf_hdr->e_type, self_header->header_type, self_header->self_filesize, 237 | self_header->self_offset); 238 | 239 | /* Make sure it contains a PSVita ELF */ 240 | if (elf_check_vita_header(elf_hdr) < 0) { 241 | LOG("Check header failed."); 242 | return -1; 243 | } 244 | 245 | segments = calloc(elf_hdr->e_phnum, sizeof(segment_info_t)); 246 | if (!segments) 247 | return -1; 248 | 249 | for (Elf32_Half i = 0; i < elf_hdr->e_phnum; i++) { 250 | seg_header = &prog_hdrs[i]; 251 | seg_bytes = (uint8_t *)data + self_header->header_len + seg_header->p_offset; 252 | 253 | LOG("Segment %i: encryption: %lld, compression: %lld", i, seg_infos[i].encryption, 254 | seg_infos[i].compression); 255 | 256 | segments[i].p_type = seg_header->p_type; 257 | segments[i].p_filesz = seg_header->p_filesz; 258 | segments[i].p_memsz = seg_header->p_memsz; 259 | segments[i].p_flags = seg_header->p_flags; 260 | segments[i].p_align = seg_header->p_align; 261 | 262 | if (seg_header->p_type == PT_LOAD) { 263 | if (seg_header->p_memsz != 0) { 264 | if (seg_infos[i].compression == 2) { 265 | dest_bytes = seg_header->p_filesz; 266 | uncompressed = malloc(seg_header->p_memsz); 267 | if (!uncompressed) { 268 | ret = -1; 269 | goto done; 270 | } 271 | 272 | ret = mz_uncompress(uncompressed, &dest_bytes, 273 | (uint8_t *)data + seg_infos[i].offset, seg_infos[i].length); 274 | if (ret != MZ_OK) { 275 | ret = -1; 276 | goto done; 277 | } 278 | 279 | segments[i].src_data = uncompressed; 280 | segments[i].needs_free = true; 281 | } else { 282 | segments[i].src_data = seg_bytes; 283 | } 284 | } 285 | } else if (seg_header->p_type == PT_LOOS) { 286 | if (seg_infos[i].compression == 2) { 287 | dest_bytes = seg_header->p_filesz; 288 | uncompressed = malloc(seg_header->p_filesz); 289 | if (!uncompressed) { 290 | ret = -1; 291 | goto done; 292 | } 293 | 294 | ret = mz_uncompress(uncompressed, &dest_bytes, 295 | (uint8_t *)data + seg_infos[i].offset, seg_infos[i].length); 296 | if (ret != MZ_OK) { 297 | ret = -1; 298 | goto done; 299 | } 300 | 301 | segments[i].src_data = uncompressed; 302 | segments[i].needs_free = true; 303 | } else { 304 | segments[i].src_data = seg_bytes; 305 | } 306 | } else { 307 | LOG("Unknown segment type 0x%" PRIx32, seg_header->p_type); 308 | } 309 | } 310 | 311 | ret = load_segments(jit, entry, elf_hdr->e_entry, segments, elf_hdr->e_phnum); 312 | 313 | done: 314 | for (Elf32_Half i = 0; i < elf_hdr->e_phnum; i++) { 315 | if (segments[i].needs_free) 316 | free(segments[i].src_data); 317 | } 318 | 319 | free(segments); 320 | 321 | return ret; 322 | } 323 | 324 | static int load_elf(Jit *jit, const void *data, void **entry) 325 | { 326 | const Elf32_Ehdr *elf_hdr = data; 327 | Elf32_Phdr *prog_hdrs; 328 | int ret; 329 | 330 | /* Make sure it's a PSVita ELF */ 331 | if (elf_check_vita_header(elf_hdr) < 0) { 332 | LOG("Check header failed."); 333 | return -1; 334 | } 335 | 336 | LOG("Found %u program segments.", elf_hdr->e_phnum); 337 | if (elf_hdr->e_phnum < 1) { 338 | LOG("No program sections to load!"); 339 | return -1; 340 | } 341 | 342 | /* Read ELF program headers */ 343 | LOG("Reading program headers."); 344 | prog_hdrs = (void *)((uintptr_t)data + elf_hdr->e_phoff); 345 | 346 | segment_info_t *segments = calloc(elf_hdr->e_phnum, sizeof(segment_info_t)); 347 | 348 | for (Elf32_Half i = 0; i < elf_hdr->e_phnum; i++) { 349 | segments[i].src_data = (char *)data + prog_hdrs[i].p_offset; 350 | segments[i].p_type = prog_hdrs[i].p_type; 351 | segments[i].p_filesz = prog_hdrs[i].p_filesz; 352 | segments[i].p_memsz = prog_hdrs[i].p_memsz; 353 | segments[i].p_flags = prog_hdrs[i].p_flags; 354 | segments[i].p_align = prog_hdrs[i].p_align; 355 | } 356 | 357 | ret = load_segments(jit, entry, elf_hdr->e_entry, segments, elf_hdr->e_phnum); 358 | 359 | free(segments); 360 | 361 | return ret; 362 | } 363 | 364 | static int load_segments(Jit *jit, void **entry, Elf32_Addr e_entry, segment_info_t *segments, 365 | int num_segments) 366 | { 367 | uint32_t code_size = 0, data_size = 0; 368 | uint32_t code_offset = 0, data_offset = 0; 369 | uintptr_t data_addr, code_rw_addr, code_rx_addr; 370 | uintptr_t addr_rw, addr_rx; 371 | uint32_t length; 372 | sce_module_info_raw *mod_info; 373 | const char *imp_name; 374 | int mod_info_idx; 375 | Result res; 376 | 377 | /* Calculate total code and data sizes */ 378 | for (int i = 0; i < num_segments; i++) { 379 | if (segments[i].p_type == PT_LOAD) { 380 | length = segments[i].p_memsz + segments[i].p_align; 381 | if ((segments[i].p_flags & PF_X) == PF_X) 382 | code_size += length; 383 | else 384 | data_size += length; 385 | } 386 | } 387 | 388 | LOG("Total needed code size: 0x%" PRIx32, code_size); 389 | LOG("Total needed data size: 0x%" PRIx32, data_size); 390 | 391 | res = jitCreate(jit, code_size); 392 | if (R_FAILED(res)) { 393 | LOG("jitCreate failed: 0x%" PRIx32, res); 394 | return -1; 395 | } 396 | 397 | data_addr = (uintptr_t)malloc(data_size); 398 | if (!data_addr) { 399 | LOG("Could not allocate buffer for the data segment"); 400 | goto err_jit_close; 401 | } 402 | 403 | code_rw_addr = (uintptr_t)jitGetRwAddr(jit); 404 | code_rx_addr = (uintptr_t)jitGetRxAddr(jit); 405 | 406 | LOG("JIT code RW addr: %p", (void *)code_rw_addr); 407 | LOG("JIT code RX addr: %p", (void *)code_rx_addr); 408 | LOG("Data addr: %p", (void *)data_addr); 409 | 410 | res = jitTransitionToWritable(jit); 411 | if (R_FAILED(res)) { 412 | LOG("Could not transition JIT to writable: 0x%" PRIx32, res); 413 | goto err_free_data; 414 | } 415 | 416 | for (int i = 0; i < num_segments; i++) { 417 | if (segments[i].p_type == PT_LOAD) { 418 | LOG("Found loadable segment (%u)", i); 419 | LOG(" p_filesz: 0x%" PRIx32, segments[i].p_filesz); 420 | LOG(" p_memsz: 0x%" PRIx32, segments[i].p_align); 421 | LOG(" p_align: 0x%" PRIx32, segments[i].p_align); 422 | 423 | if ((segments[i].p_flags & PF_X) == PF_X) { 424 | length = ALIGN(code_rx_addr, segments[i].p_align) - code_rx_addr; 425 | addr_rx = code_rx_addr + code_offset; 426 | addr_rw = code_rw_addr + code_offset; 427 | code_offset += segments[i].p_memsz; 428 | } else { 429 | length = ALIGN(data_addr, segments[i].p_align) - data_addr; 430 | addr_rw = addr_rx = data_addr + data_offset; 431 | data_offset += segments[i].p_memsz; 432 | } 433 | 434 | segments[i].rx_addr = addr_rx; 435 | segments[i].rw_addr = addr_rw; 436 | 437 | if ((segments[i].p_flags & PF_X) == PF_X) { 438 | LOG("Code segment loaded at %p for RW, %p for RX", (void *)addr_rw, 439 | (void *)addr_rx); 440 | } else { 441 | LOG("Data segment loaded at %p ", (void *)addr_rw); 442 | } 443 | 444 | memcpy((void *)segments[i].rw_addr, segments[i].src_data, segments[i].p_filesz); 445 | memset((char *)segments[i].rw_addr + segments[i].p_filesz, 0, 446 | segments[i].p_memsz - segments[i].p_filesz); 447 | } else if (segments[i].p_type == PT_SCE_RELA) { 448 | LOG("Found relocations segment (%u)", i); 449 | relocate(segments[i].src_data, segments[i].p_filesz, segments); 450 | } else { 451 | LOG("Segment %u is not loadable. Skipping.", i); 452 | continue; 453 | } 454 | } 455 | 456 | /* Get module info */ 457 | LOG("Getting module info."); 458 | if ((mod_info_idx = elf_get_sce_module_info(e_entry, segments, &mod_info)) < 0) { 459 | LOG("Cannot find module info section."); 460 | goto err_free_data; 461 | } 462 | LOG("Module name: %s", mod_info->name); 463 | LOG(" export table offset: 0x%" PRIx32, mod_info->export_top); 464 | LOG(" import table offset: 0x%" PRIx32, mod_info->import_top); 465 | LOG(" tls start: 0x%" PRIx32, mod_info->tls_start); 466 | LOG(" tls filesz: 0x%" PRIx32, mod_info->tls_filesz); 467 | LOG(" tls memsz: 0x%" PRIx32, mod_info->tls_memsz); 468 | 469 | /* Resolve NIDs */ 470 | sce_module_imports_u_t *import = 471 | (void *)(segments[mod_info_idx].rw_addr + mod_info->import_top); 472 | void *end = (void *)(segments[mod_info_idx].rw_addr + mod_info->import_end); 473 | 474 | for (; (void *)import < end; import = IMP_GET_NEXT(import)) { 475 | imp_name = (void *)CODE_RX_TO_RW_ADDR(code_rx_addr, code_rw_addr, IMP_GET_NAME(import)); 476 | LOG("Resolving imports for %s (NID: 0x%08" PRIx32 ")", imp_name, IMP_GET_NID(import)); 477 | if (resolve_imports(code_rx_addr, code_rw_addr, import) < 0) { 478 | LOG("Failed to resolve imports for %s", imp_name); 479 | goto err_free_data; 480 | } 481 | } 482 | 483 | /* Find the entry point (address belonging to the RX JIT area) */ 484 | *entry = (char *)segments[mod_info_idx].rx_addr + mod_info->module_start; 485 | if (*entry == NULL) { 486 | LOG("Invalid module entry function."); 487 | goto err_free_data; 488 | } 489 | 490 | res = jitTransitionToExecutable(jit); 491 | if (R_FAILED(res)) { 492 | LOG("Could not transition JIT to executable: 0x%" PRIx32, res); 493 | goto err_free_data; 494 | } 495 | 496 | return 0; 497 | 498 | err_free_data: 499 | free((void *)data_addr); 500 | err_jit_close: 501 | jitClose(jit); 502 | 503 | return -1; 504 | } 505 | 506 | static int resolve_imports(uintptr_t rx_base, uintptr_t rw_base, sce_module_imports_u_t *import) 507 | { 508 | const void *addr; 509 | uint32_t nid; 510 | uint32_t *stub; 511 | uint32_t lib_nid = IMP_GET_NID(import); 512 | 513 | /* The module imports struct contains pointers to entries to the RX JIT area because 514 | * it has already been relocated, so we have to convert them to RW addresses */ 515 | 516 | for (uint32_t i = 0; i < IMP_GET_FUNC_COUNT(import); i++) { 517 | nid = ((uint32_t *)CODE_RX_TO_RW_ADDR(rx_base, rw_base, IMP_GET_FUNC_TABLE(import)))[i]; 518 | stub = (void *)CODE_RX_TO_RW_ADDR(rx_base, rw_base, IMP_GET_FUNC_ENTRIES(import)[i]); 519 | addr = module_get_func_export(lib_nid, nid); 520 | 521 | if (addr) { 522 | stub[0] = arm_encode_movw(12, (uint16_t)(uintptr_t)addr); 523 | stub[1] = arm_encode_movt(12, (uint16_t)(((uintptr_t)addr) >> 16)); 524 | stub[2] = arm_encode_bx(12); 525 | } else { 526 | stub[0] = arm_encode_movw(0, (uint16_t)SCE_KERNEL_ERROR_MODULEMGR_NO_FUNC_NID); 527 | stub[1] = arm_encode_movt(0, (uint16_t)(SCE_KERNEL_ERROR_MODULEMGR_NO_FUNC_NID >> 16)); 528 | stub[2] = arm_encode_ret(); 529 | LOG(" Could not resolve NID 0x%08" PRIx32 ", export not found!", nid); 530 | } 531 | } 532 | 533 | for (uint32_t i = 0; i < IMP_GET_VARS_COUNT(import); i++) { 534 | nid = ((uint32_t *)CODE_RX_TO_RW_ADDR(rx_base, rw_base, IMP_GET_VARS_TABLE(import)))[i]; 535 | LOG(" Trying to resolve variable NID 0x%08" PRIx32, nid); 536 | /* TODO */ 537 | LOG(" Variable NID resolving currently not implemented!"); 538 | } 539 | 540 | return 0; 541 | } 542 | 543 | static int relocate(const void *reloc, uint32_t size, segment_info_t *segs) 544 | { 545 | SCE_Rel *entry; 546 | uint32_t pos; 547 | uint16_t r_code = 0; 548 | uint32_t r_offset; 549 | uint32_t r_addend; 550 | uint8_t r_symseg; 551 | uint8_t r_datseg; 552 | int32_t offset; 553 | uint32_t symval, loc_rw, loc_rx; 554 | uint32_t upper, lower, sign, j1, j2; 555 | uint32_t value = 0; 556 | 557 | pos = 0; 558 | while (pos < size) { 559 | // get entry 560 | entry = (SCE_Rel *)((char *)reloc + pos); 561 | if (SCE_REL_IS_SHORT(*entry)) { 562 | r_offset = SCE_REL_SHORT_OFFSET(entry->r_short); 563 | r_addend = SCE_REL_SHORT_ADDEND(entry->r_short); 564 | pos += 8; 565 | } else { 566 | r_offset = SCE_REL_LONG_OFFSET(entry->r_long); 567 | r_addend = SCE_REL_LONG_ADDEND(entry->r_long); 568 | if (SCE_REL_LONG_CODE2(entry->r_long)) 569 | LOG("Code2 ignored for relocation at 0x%" PRIx32, pos); 570 | pos += 12; 571 | } 572 | 573 | // get values 574 | r_symseg = SCE_REL_SYMSEG(*entry); 575 | r_datseg = SCE_REL_DATSEG(*entry); 576 | symval = r_symseg == 15 ? 0 : (uint32_t)segs[r_symseg].rx_addr; 577 | loc_rw = (uint32_t)segs[r_datseg].rw_addr + r_offset; 578 | loc_rx = (uint32_t)segs[r_datseg].rx_addr + r_offset; 579 | 580 | // perform relocation 581 | // taken from linux/arch/arm/kernel/module.c of Linux Kernel 4.0 582 | switch (SCE_REL_CODE(*entry)) { 583 | case R_ARM_V4BX: { 584 | /* Preserve Rm and the condition code. Alter 585 | * other bits to re-code instruction as 586 | * MOV PC,Rm. 587 | */ 588 | value = (*(uint32_t *)loc_rw & 0xf000000f) | 0x01a0f000; 589 | } break; 590 | case R_ARM_ABS32: 591 | case R_ARM_TARGET1: { 592 | value = r_addend + symval; 593 | } break; 594 | case R_ARM_REL32: 595 | case R_ARM_TARGET2: { 596 | value = r_addend + symval - loc_rx; 597 | } break; 598 | case R_ARM_THM_PC22: { 599 | upper = *(uint16_t *)loc_rw; 600 | lower = *(uint16_t *)(loc_rw + 2); 601 | 602 | /* 603 | * 25 bit signed address range (Thumb-2 BL and B.W 604 | * instructions): 605 | * S:I1:I2:imm10:imm11:0 606 | * where: 607 | * S = upper[10] = offset[24] 608 | * I1 = ~(J1 ^ S) = offset[23] 609 | * I2 = ~(J2 ^ S) = offset[22] 610 | * imm10 = upper[9:0] = offset[21:12] 611 | * imm11 = lower[10:0] = offset[11:1] 612 | * J1 = lower[13] 613 | * J2 = lower[11] 614 | */ 615 | sign = (upper >> 10) & 1; 616 | j1 = (lower >> 13) & 1; 617 | j2 = (lower >> 11) & 1; 618 | offset = r_addend + symval - loc_rx; 619 | 620 | if (offset <= (int32_t)0xff000000 || offset >= (int32_t)0x01000000) { 621 | LOG("reloc 0x%" PRIx32 " out of range: 0x%08" PRIx32, pos, symval); 622 | break; 623 | } 624 | 625 | sign = (offset >> 24) & 1; 626 | j1 = sign ^ (~(offset >> 23) & 1); 627 | j2 = sign ^ (~(offset >> 22) & 1); 628 | upper = (uint16_t)((upper & 0xf800) | (sign << 10) | ((offset >> 12) & 0x03ff)); 629 | lower = 630 | (uint16_t)((lower & 0xd000) | (j1 << 13) | (j2 << 11) | ((offset >> 1) & 0x07ff)); 631 | 632 | value = ((uint32_t)lower << 16) | upper; 633 | } break; 634 | case R_ARM_CALL: 635 | case R_ARM_JUMP24: { 636 | offset = r_addend + symval - loc_rx; 637 | if (offset <= (int32_t)0xfe000000 || offset >= (int32_t)0x02000000) { 638 | LOG("reloc 0x%" PRIx32 " out of range: 0x%08" PRIx32, pos, symval); 639 | break; 640 | } 641 | 642 | offset >>= 2; 643 | offset &= 0x00ffffff; 644 | 645 | value = (*(uint32_t *)loc_rw & 0xff000000) | offset; 646 | } break; 647 | case R_ARM_PREL31: { 648 | offset = r_addend + symval - loc_rx; 649 | value = offset & 0x7fffffff; 650 | } break; 651 | case R_ARM_MOVW_ABS_NC: 652 | case R_ARM_MOVT_ABS: { 653 | offset = symval + r_addend; 654 | if (SCE_REL_CODE(*entry) == R_ARM_MOVT_ABS) 655 | offset >>= 16; 656 | 657 | value = *(uint32_t *)loc_rw; 658 | value &= 0xfff0f000; 659 | value |= ((offset & 0xf000) << 4) | (offset & 0x0fff); 660 | } break; 661 | case R_ARM_THM_MOVW_ABS_NC: 662 | case R_ARM_THM_MOVT_ABS: { 663 | upper = *(uint16_t *)loc_rw; 664 | lower = *(uint16_t *)(loc_rw + 2); 665 | 666 | /* 667 | * MOVT/MOVW instructions encoding in Thumb-2: 668 | * 669 | * i = upper[10] 670 | * imm4 = upper[3:0] 671 | * imm3 = lower[14:12] 672 | * imm8 = lower[7:0] 673 | * 674 | * imm16 = imm4:i:imm3:imm8 675 | */ 676 | offset = r_addend + symval; 677 | 678 | if (SCE_REL_CODE(*entry) == R_ARM_THM_MOVT_ABS) 679 | offset >>= 16; 680 | 681 | upper = 682 | (uint16_t)((upper & 0xfbf0) | ((offset & 0xf000) >> 12) | ((offset & 0x0800) >> 1)); 683 | lower = (uint16_t)((lower & 0x8f00) | ((offset & 0x0700) << 4) | (offset & 0x00ff)); 684 | 685 | value = ((uint32_t)lower << 16) | upper; 686 | } break; 687 | default: { 688 | LOG("Unknown relocation code %u at 0x%" PRIx32, r_code, pos); 689 | } 690 | case R_ARM_NONE: 691 | continue; 692 | } 693 | 694 | memcpy((char *)segs[r_datseg].rw_addr + r_offset, &value, sizeof(value)); 695 | } 696 | 697 | return 0; 698 | } 699 | --------------------------------------------------------------------------------