├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── loader ├── config.h ├── dialog.c ├── dialog.h ├── elf.h ├── main.c ├── main.h ├── so_util.c └── so_util.h ├── sce_sys ├── icon0.png └── livearea │ └── contents │ ├── bg.png │ ├── startup.png │ └── template.xml └── screenshots └── game.png /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.velf 3 | *.elf 4 | *.vpk 5 | *.sfo 6 | *.bin 7 | *.out 8 | *.o 9 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8) 2 | 3 | if(NOT DEFINED CMAKE_TOOLCHAIN_FILE) 4 | if(DEFINED ENV{VITASDK}) 5 | set(CMAKE_TOOLCHAIN_FILE "$ENV{VITASDK}/share/vita.toolchain.cmake" CACHE PATH "toolchain file") 6 | else() 7 | message(FATAL_ERROR "Please define VITASDK to point to your SDK path!") 8 | endif() 9 | endif() 10 | 11 | project(BC2.elf C CXX) 12 | include("${VITASDK}/share/vita.cmake" REQUIRED) 13 | set(VITA_APP_NAME "Battlefield: Bad Company 2") 14 | set(VITA_TITLEID "BC2000000") 15 | set(VITA_VERSION "01.00") 16 | set(VITA_MKSFOEX_FLAGS "-d ATTRIBUTE2=12") 17 | 18 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wl,-q,--wrap,memcpy,--wrap,memmove,--wrap,memset -Wall -O3 -mfloat-abi=softfp") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11") 20 | 21 | add_executable(BC2.elf 22 | loader/main.c 23 | loader/dialog.c 24 | loader/so_util.c 25 | ) 26 | 27 | target_link_libraries(BC2.elf 28 | m 29 | stdc++ 30 | vitaGL 31 | vitashark 32 | mathneon 33 | kubridge_stub 34 | SceAppMgr_stub 35 | SceAudio_stub 36 | SceCtrl_stub 37 | SceCommonDialog_stub 38 | SceDisplay_stub 39 | SceKernelDmacMgr_stub 40 | SceGxm_stub 41 | SceShaccCg_stub 42 | SceSysmodule_stub 43 | ScePower_stub 44 | SceTouch_stub 45 | SceVshBridge_stub 46 | ) 47 | 48 | vita_create_self(eboot.bin BC2.elf UNSAFE) 49 | vita_create_vpk(BC2.vpk ${VITA_TITLEID} eboot.bin 50 | VERSION ${VITA_VERSION} 51 | NAME ${VITA_APP_NAME} 52 | FILE ${CMAKE_SOURCE_DIR}/sce_sys/icon0.png sce_sys/icon0.png 53 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/bg.png sce_sys/livearea/contents/bg.png 54 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png 55 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml 56 | ) 57 | 58 | add_custom_target(copy 59 | COMMAND cp eboot.bin E:/app/${VITA_TITLEID}/eboot.bin 60 | DEPENDS eboot.bin 61 | ) 62 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (C) 2021 Andy Nguyen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Battlefield: Bad Company 2 Vita 2 | 3 |

4 | 5 | This is a wrapper/port of *Battlefield: Bad Company 2 Android* for the *PS Vita*. 6 | 7 | The port works by loading the official Android ARMv6 executable in memory, resolving its imports with native functions and patching it in order to properly run. 8 | 9 | ## Setup Instructions (For End Users) 10 | 11 | In order to properly install the game, you'll have to follow these steps precisely: 12 | 13 | - Install [kubridge](https://github.com/TheOfficialFloW/kubridge/releases/) and [FdFix](https://github.com/TheOfficialFloW/FdFix/releases/) by copying `kubridge.skprx` and `fd_fix.skprx` to your taiHEN plugins folder (usually `ux0:tai`) and adding two entries to your `config.txt` under `*KERNEL`: 14 | 15 | ``` 16 | *KERNEL 17 | ux0:tai/kubridge.skprx 18 | ux0:tai/fd_fix.skprx 19 | ``` 20 | 21 | **Note** Don't install fd_fix.skprx if you're using repatch plugin 22 | 23 | - **Optional**: Install [PSVshell](https://github.com/Electry/PSVshell/releases) to overclock your device to 500Mhz. 24 | - Install `libshacccg.suprx`, if you don't have it already, by following [this guide](https://samilops2.gitbook.io/vita-troubleshooting-guide/shader-compiler/extract-libshacccg.suprx). 25 | - Obtain your copy of *Battlefield: Bad Company 2* legally from the Amazon store in form of an `.apk` file and one or more `.obb` files (usually located inside the `/sdcard/android/obb/bc2/`) folder. [You can get all the required files directly from your phone](https://stackoverflow.com/questions/11012976/how-do-i-get-the-apk-of-an-installed-app-without-root-access) or by using an apk extractor you can find in the play store. The apk can be extracted with whatever Zip extractor you prefer (eg: WinZip, WinRar, etc...) since apk is basically a zip file. You can rename `.apk` to `.zip` to open them with your default zip extractor. 26 | - Copy the `/sdcard/android/obb/bc2/` folder to `ux0:data/bc2`. 27 | - Open the apk and extract `libbc2.so` from the `lib/armeabi` folder to `ux0:data/bc2`. 28 | - Install [BC2.vpk](https://github.com/TheOfficialFloW/bc2_vita/releases/download/v1.0/BC2.vpk) on your *PS Vita*. 29 | 30 | ## Build Instructions (For Developers) 31 | 32 | In order to build the loader, you'll need a [vitasdk](https://github.com/vitasdk) build fully compiled with softfp usage. 33 | You can find a precompiled version here: [Linux](https://github.com/vitasdk/buildscripts/suites/2943994805/artifacts/66184169) / [Windows](https://github.com/vitasdk/buildscripts/suites/2943994805/artifacts/66184170). 34 | Additionally, you'll need these libraries to be compiled as well with `-mfloat-abi=softfp` added to their CFLAGS: 35 | 36 | - [libmathneon](https://github.com/Rinnegatamante/math-neon) 37 | 38 | - ```bash 39 | make install 40 | ``` 41 | 42 | - [vitaShaRK](https://github.com/Rinnegatamante/vitaShaRK) 43 | 44 | - ```bash 45 | make install 46 | ``` 47 | 48 | - [kubridge](https://github.com/TheOfficialFloW/kubridge) 49 | 50 | - ```bash 51 | mkdir build && cd build 52 | cmake .. && make install 53 | ``` 54 | 55 | - [vitaGL](https://github.com/Rinnegatamante/vitaGL) 56 | 57 | - ````bash 58 | make SOFTFP_ABI=1 DRAW_SPEEDHACK=1 install 59 | ```` 60 | 61 | After all these requirements are met, you can compile the loader with the following commands: 62 | 63 | ```bash 64 | mkdir build && cd build 65 | cmake .. && make 66 | ``` 67 | 68 | ## Credits 69 | 70 | - Rinnegatamante for vitaGL. 71 | - Once13One for providing LiveArea assets. 72 | -------------------------------------------------------------------------------- /loader/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | // #define DEBUG 5 | 6 | #define LOAD_ADDRESS 0x98000000 7 | 8 | #define MEMORY_NEWLIB_MB 192 9 | #define MEMORY_VITAGL_THRESHOLD_MB 8 10 | #define AUDIO_SAMPLE_RATE 44100 11 | #define AUDIO_SAMPLES_PER_BUF 8192 12 | 13 | #define DATA_PATH "ux0:data/bc2" 14 | #define SO_PATH DATA_PATH "/" "libbc2.so" 15 | 16 | #define SCREEN_W 960 17 | #define SCREEN_H 544 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /loader/dialog.c: -------------------------------------------------------------------------------- 1 | /* dialog.c -- common dialog for error messages and cheats input 2 | * 3 | * Copyright (C) 2021 fgsfds, Andy Nguyen 4 | * 5 | * This software may be modified and distributed under the terms 6 | * of the MIT license. See the LICENSE file for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "main.h" 19 | #include "dialog.h" 20 | 21 | static uint16_t ime_title_utf16[SCE_IME_DIALOG_MAX_TITLE_LENGTH]; 22 | static uint16_t ime_initial_text_utf16[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; 23 | static uint16_t ime_input_text_utf16[SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1]; 24 | static uint8_t ime_input_text_utf8[SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1]; 25 | 26 | void utf16_to_utf8(const uint16_t *src, uint8_t *dst) { 27 | for (int i = 0; src[i]; i++) { 28 | if ((src[i] & 0xFF80) == 0) { 29 | *(dst++) = src[i] & 0xFF; 30 | } else if((src[i] & 0xF800) == 0) { 31 | *(dst++) = ((src[i] >> 6) & 0xFF) | 0xC0; 32 | *(dst++) = (src[i] & 0x3F) | 0x80; 33 | } else if((src[i] & 0xFC00) == 0xD800 && (src[i + 1] & 0xFC00) == 0xDC00) { 34 | *(dst++) = (((src[i] + 64) >> 8) & 0x3) | 0xF0; 35 | *(dst++) = (((src[i] >> 2) + 16) & 0x3F) | 0x80; 36 | *(dst++) = ((src[i] >> 4) & 0x30) | 0x80 | ((src[i + 1] << 2) & 0xF); 37 | *(dst++) = (src[i + 1] & 0x3F) | 0x80; 38 | i += 1; 39 | } else { 40 | *(dst++) = ((src[i] >> 12) & 0xF) | 0xE0; 41 | *(dst++) = ((src[i] >> 6) & 0x3F) | 0x80; 42 | *(dst++) = (src[i] & 0x3F) | 0x80; 43 | } 44 | } 45 | 46 | *dst = '\0'; 47 | } 48 | 49 | void utf8_to_utf16(const uint8_t *src, uint16_t *dst) { 50 | for (int i = 0; src[i];) { 51 | if ((src[i] & 0xE0) == 0xE0) { 52 | *(dst++) = ((src[i] & 0x0F) << 12) | ((src[i + 1] & 0x3F) << 6) | (src[i + 2] & 0x3F); 53 | i += 3; 54 | } else if ((src[i] & 0xC0) == 0xC0) { 55 | *(dst++) = ((src[i] & 0x1F) << 6) | (src[i + 1] & 0x3F); 56 | i += 2; 57 | } else { 58 | *(dst++) = src[i]; 59 | i += 1; 60 | } 61 | } 62 | 63 | *dst = '\0'; 64 | } 65 | 66 | int init_ime_dialog(const char *title, const char *initial_text) { 67 | memset(ime_title_utf16, 0, sizeof(ime_title_utf16)); 68 | memset(ime_initial_text_utf16, 0, sizeof(ime_initial_text_utf16)); 69 | memset(ime_input_text_utf16, 0, sizeof(ime_input_text_utf16)); 70 | memset(ime_input_text_utf8, 0, sizeof(ime_input_text_utf8)); 71 | 72 | utf8_to_utf16((uint8_t *)title, ime_title_utf16); 73 | utf8_to_utf16((uint8_t *)initial_text, ime_initial_text_utf16); 74 | 75 | SceImeDialogParam param; 76 | sceImeDialogParamInit(¶m); 77 | 78 | param.supportedLanguages = 0x0001FFFF; 79 | param.languagesForced = SCE_TRUE; 80 | param.type = SCE_IME_TYPE_BASIC_LATIN; 81 | param.title = ime_title_utf16; 82 | param.maxTextLength = SCE_IME_DIALOG_MAX_TEXT_LENGTH; 83 | param.initialText = ime_initial_text_utf16; 84 | param.inputTextBuffer = ime_input_text_utf16; 85 | 86 | return sceImeDialogInit(¶m); 87 | } 88 | 89 | char *get_ime_dialog_result(void) { 90 | if (sceImeDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) 91 | return NULL; 92 | 93 | SceImeDialogResult result; 94 | memset(&result, 0, sizeof(SceImeDialogResult)); 95 | sceImeDialogGetResult(&result); 96 | if (result.button == SCE_IME_DIALOG_BUTTON_ENTER) 97 | utf16_to_utf8(ime_input_text_utf16, ime_input_text_utf8); 98 | sceImeDialogTerm(); 99 | // For some reason analog stick stops working after ime 100 | sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); 101 | 102 | return (char *)ime_input_text_utf8; 103 | } 104 | 105 | int init_msg_dialog(const char *msg) { 106 | SceMsgDialogUserMessageParam msg_param; 107 | memset(&msg_param, 0, sizeof(msg_param)); 108 | msg_param.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK; 109 | msg_param.msg = (SceChar8 *)msg; 110 | 111 | SceMsgDialogParam param; 112 | sceMsgDialogParamInit(¶m); 113 | _sceCommonDialogSetMagicNumber(¶m.commonParam); 114 | param.mode = SCE_MSG_DIALOG_MODE_USER_MSG; 115 | param.userMsgParam = &msg_param; 116 | 117 | return sceMsgDialogInit(¶m); 118 | } 119 | 120 | int get_msg_dialog_result(void) { 121 | if (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) 122 | return 0; 123 | sceMsgDialogTerm(); 124 | return 1; 125 | } 126 | 127 | void fatal_error(const char *fmt, ...) { 128 | va_list list; 129 | char string[512]; 130 | 131 | va_start(list, fmt); 132 | vsnprintf(string, sizeof(string), fmt, list); 133 | va_end(list); 134 | 135 | vglInit(0); 136 | 137 | init_msg_dialog(string); 138 | 139 | while (!get_msg_dialog_result()) 140 | vglSwapBuffers(GL_TRUE); 141 | 142 | sceKernelExitProcess(0); 143 | while (1); 144 | } 145 | -------------------------------------------------------------------------------- /loader/dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __DIALOG_H__ 2 | #define __DIALOG_H__ 3 | 4 | int init_ime_dialog(const char *title, const char *initial_text); 5 | char *get_ime_dialog_result(void); 6 | 7 | int init_msg_dialog(const char *msg); 8 | int get_msg_dialog_result(void); 9 | 10 | void fatal_error(const char *fmt, ...) __attribute__((noreturn)); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /loader/main.c: -------------------------------------------------------------------------------- 1 | /* main.c -- Battlefield: Bad Company 2 .so loader 2 | * 3 | * Copyright (C) 2021 Andy Nguyen 4 | * 5 | * This software may be modified and distributed under the terms 6 | * of the MIT license. See the LICENSE file for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | #include "main.h" 36 | #include "config.h" 37 | #include "dialog.h" 38 | #include "so_util.h" 39 | 40 | int _newlib_heap_size_user = MEMORY_NEWLIB_MB * 1024 * 1024; 41 | 42 | static so_module bc2_mod; 43 | 44 | void *__wrap_memcpy(void *dest, const void *src, size_t n) { 45 | return sceClibMemcpy(dest, src, n); 46 | } 47 | 48 | void *__wrap_memmove(void *dest, const void *src, size_t n) { 49 | return sceClibMemmove(dest, src, n); 50 | } 51 | 52 | void *__wrap_memset(void *s, int c, size_t n) { 53 | return sceClibMemset(s, c, n); 54 | } 55 | 56 | int debugPrintf(char *text, ...) { 57 | #ifdef DEBUG 58 | va_list list; 59 | char string[512]; 60 | 61 | va_start(list, text); 62 | vsprintf(string, text, list); 63 | va_end(list); 64 | 65 | SceUID fd = sceIoOpen("ux0:data/bc2_log.txt", SCE_O_WRONLY | SCE_O_CREAT | SCE_O_APPEND, 0777); 66 | if (fd >= 0) { 67 | sceIoWrite(fd, string, strlen(string)); 68 | sceIoClose(fd); 69 | } 70 | #endif 71 | return 0; 72 | } 73 | 74 | int __android_log_print(int prio, const char *tag, const char *fmt, ...) { 75 | #ifdef DEBUG 76 | va_list list; 77 | char string[512]; 78 | 79 | va_start(list, fmt); 80 | vsprintf(string, fmt, list); 81 | va_end(list); 82 | 83 | debugPrintf("%s", string); 84 | #endif 85 | return 0; 86 | } 87 | 88 | int ret0(void) { 89 | return 0; 90 | } 91 | 92 | int ret1(void) { 93 | return 1; 94 | } 95 | 96 | char *getcwd(char *buf, size_t size) { 97 | if (buf) { 98 | buf[0] = '\0'; 99 | return buf; 100 | } 101 | return NULL; 102 | } 103 | 104 | enum { 105 | ACTION_DOWN = 1, 106 | ACTION_UP = 2, 107 | ACTION_MOVE = 3, 108 | }; 109 | 110 | enum { 111 | AKEYCODE_DPAD_UP = 19, 112 | AKEYCODE_DPAD_DOWN = 20, 113 | AKEYCODE_DPAD_LEFT = 21, 114 | AKEYCODE_DPAD_RIGHT = 22, 115 | AKEYCODE_A = 29, 116 | AKEYCODE_B = 30, 117 | AKEYCODE_BUTTON_X = 99, 118 | AKEYCODE_BUTTON_Y = 100, 119 | AKEYCODE_BUTTON_L1 = 102, 120 | AKEYCODE_BUTTON_R1 = 103, 121 | AKEYCODE_BUTTON_START = 108, 122 | AKEYCODE_BUTTON_SELECT = 109, 123 | }; 124 | 125 | typedef struct { 126 | uint32_t sce_button; 127 | uint32_t android_button; 128 | } ButtonMapping; 129 | 130 | static ButtonMapping mapping[] = { 131 | { SCE_CTRL_UP, AKEYCODE_DPAD_UP }, 132 | { SCE_CTRL_DOWN, AKEYCODE_DPAD_DOWN }, 133 | { SCE_CTRL_LEFT, AKEYCODE_DPAD_LEFT }, 134 | { SCE_CTRL_RIGHT, AKEYCODE_DPAD_RIGHT }, 135 | { SCE_CTRL_CROSS, AKEYCODE_A }, 136 | { SCE_CTRL_CIRCLE, AKEYCODE_B }, 137 | { SCE_CTRL_SQUARE, AKEYCODE_BUTTON_X }, 138 | { SCE_CTRL_TRIANGLE, AKEYCODE_BUTTON_Y }, 139 | { SCE_CTRL_L1, AKEYCODE_BUTTON_L1 }, 140 | { SCE_CTRL_R1, AKEYCODE_BUTTON_R1 }, 141 | { SCE_CTRL_START, AKEYCODE_BUTTON_START }, 142 | { SCE_CTRL_SELECT, AKEYCODE_BUTTON_SELECT }, 143 | }; 144 | 145 | int ctrl_thread(SceSize args, void *argp) { 146 | int (* Android_Karisma_AppOnTouchEvent)(int type, int x, int y, int id) = (void *)so_symbol(&bc2_mod, "Android_Karisma_AppOnTouchEvent"); 147 | int (* Android_Karisma_AppOnJoystickEvent)(int type, float x, float y, int id) = (void *)so_symbol(&bc2_mod, "Android_Karisma_AppOnJoystickEvent"); 148 | int (* Android_Karisma_AppOnKeyEvent)(int type, int keycode) = (void *)so_symbol(&bc2_mod, "Android_Karisma_AppOnKeyEvent"); 149 | 150 | int lastX[2] = { -1, -1 }; 151 | int lastY[2] = { -1, -1 }; 152 | 153 | float lastLx = 0.0f, lastLy = 0.0f, lastRx = 0.0f, lastRy = 0.0f; 154 | float lx = 0.0f, ly = 0.0f, rx = 0.0f, ry = 0.0f; 155 | uint32_t old_buttons = 0, current_buttons = 0, pressed_buttons = 0, released_buttons = 0; 156 | 157 | while (1) { 158 | SceTouchData touch; 159 | sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); 160 | 161 | for (int i = 0; i < 2; i++) { 162 | if (i < touch.reportNum) { 163 | int x = (int)((float)touch.report[i].x * (float)SCREEN_W / 1920.0f); 164 | int y = (int)((float)touch.report[i].y * (float)SCREEN_H / 1088.0f); 165 | 166 | if (lastX[i] == -1 || lastY[i] == -1) 167 | Android_Karisma_AppOnTouchEvent(ACTION_DOWN, x, y, i); 168 | else 169 | Android_Karisma_AppOnTouchEvent(ACTION_MOVE, x, y, i); 170 | lastX[i] = x; 171 | lastY[i] = y; 172 | } else { 173 | if (lastX[i] != -1 || lastY[i] != -1) 174 | Android_Karisma_AppOnTouchEvent(ACTION_UP, lastX[i], lastY[i], i); 175 | lastX[i] = -1; 176 | lastY[i] = -1; 177 | } 178 | } 179 | 180 | SceCtrlData pad; 181 | sceCtrlPeekBufferPositiveExt2(0, &pad, 1); 182 | 183 | old_buttons = current_buttons; 184 | current_buttons = pad.buttons; 185 | pressed_buttons = current_buttons & ~old_buttons; 186 | released_buttons = ~current_buttons & old_buttons; 187 | 188 | for (int i = 0; i < sizeof(mapping) / sizeof(ButtonMapping); i++) { 189 | if (pressed_buttons & mapping[i].sce_button) 190 | Android_Karisma_AppOnKeyEvent(0, mapping[i].android_button); 191 | if (released_buttons & mapping[i].sce_button) 192 | Android_Karisma_AppOnKeyEvent(1, mapping[i].android_button); 193 | } 194 | 195 | lx = ((float)pad.lx - 128.0f) / 128.0f; 196 | ly = ((float)pad.ly - 128.0f) / 128.0f; 197 | rx = ((float)pad.rx - 128.0f) / 128.0f; 198 | ry = ((float)pad.ry - 128.0f) / 128.0f; 199 | 200 | if (fabsf(lx) < 0.25f) 201 | lx = 0.0f; 202 | if (fabsf(ly) < 0.25f) 203 | ly = 0.0f; 204 | if (fabsf(rx) < 0.25f) 205 | rx = 0.0f; 206 | if (fabsf(ry) < 0.25f) 207 | ry = 0.0f; 208 | 209 | if (lastLx != lx || lastLy != ly || lastRx != rx || lastRy != ry) { 210 | lastLx = lx; 211 | lastLy = ly; 212 | lastRx = rx; 213 | lastRy = ry; 214 | Android_Karisma_AppOnJoystickEvent(3, lx, ly, 0); 215 | Android_Karisma_AppOnJoystickEvent(3, rx, ry, 1); 216 | } 217 | 218 | sceKernelDelayThread(1000); 219 | } 220 | 221 | return 0; 222 | } 223 | 224 | static int audio_port = 0; 225 | static int disable_sound = 0; 226 | 227 | void SetShortArrayRegion(void *env, int array, size_t start, size_t len, const uint8_t *buf) { 228 | sceAudioOutOutput(audio_port, buf); 229 | } 230 | 231 | int sound_thread(SceSize args, void *argp) { 232 | int (* Java_com_dle_bc2_KarismaBridge_nativeUpdateSound)(void *env, int unused, int type, size_t length) = (void *)so_symbol(&bc2_mod, "Java_com_dle_bc2_KarismaBridge_nativeUpdateSound"); 233 | 234 | audio_port = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_BGM, AUDIO_SAMPLES_PER_BUF / 2, AUDIO_SAMPLE_RATE, SCE_AUDIO_OUT_MODE_STEREO); 235 | 236 | static char fake_env[0x1000]; 237 | memset(fake_env, 'A', sizeof(fake_env)); 238 | *(uintptr_t *)(fake_env + 0x00) = (uintptr_t)fake_env; // just point to itself... 239 | *(uintptr_t *)(fake_env + 0x348) = (uintptr_t)SetShortArrayRegion; 240 | 241 | while (1) { 242 | if (disable_sound) 243 | sceKernelDelayThread(1000); 244 | else 245 | Java_com_dle_bc2_KarismaBridge_nativeUpdateSound(fake_env, 0, 0, AUDIO_SAMPLES_PER_BUF); 246 | } 247 | 248 | return 0; 249 | } 250 | 251 | int main_thread(SceSize args, void *argp) { 252 | vglSetupRuntimeShaderCompiler(SHARK_OPT_UNSAFE, SHARK_ENABLE, SHARK_ENABLE, SHARK_ENABLE); 253 | vglUseVram(GL_TRUE); 254 | vglInitExtended(0, SCREEN_W, SCREEN_H, MEMORY_VITAGL_THRESHOLD_MB * 1024 * 1024, SCE_GXM_MULTISAMPLE_4X); 255 | 256 | int (* Android_Karisma_AppInit)(void) = (void *)so_symbol(&bc2_mod, "Android_Karisma_AppInit"); 257 | int (* Android_Karisma_AppUpdate)(void) = (void *)so_symbol(&bc2_mod, "Android_Karisma_AppUpdate"); 258 | 259 | Android_Karisma_AppInit(); 260 | 261 | SceUID ctrl_thid = sceKernelCreateThread("ctrl_thread", (SceKernelThreadEntry)ctrl_thread, 0x10000100, 128 * 1024, 0, 0, NULL); 262 | sceKernelStartThread(ctrl_thid, 0, NULL); 263 | 264 | SceUID sound_thid = sceKernelCreateThread("sound_thread", (SceKernelThreadEntry)sound_thread, 0x10000100, 128 * 1024, 0, 0, NULL); 265 | sceKernelStartThread(sound_thid, 0, NULL); 266 | 267 | while (1) { 268 | Android_Karisma_AppUpdate(); 269 | vglSwapBuffers(GL_FALSE); 270 | } 271 | 272 | return 0; 273 | } 274 | 275 | char *Android_KarismaBridge_GetAppReadPath(void) { 276 | return DATA_PATH; 277 | } 278 | 279 | char *Android_KarismaBridge_GetAppWritePath(void) { 280 | return DATA_PATH; 281 | } 282 | 283 | void Android_KarismaBridge_EnableSound(void) { 284 | disable_sound = 0; 285 | } 286 | 287 | void Android_KarismaBridge_DisableSound(void) { 288 | disable_sound = 1; 289 | } 290 | 291 | typedef struct { 292 | void *vtable; 293 | char *path; 294 | size_t pathLen; 295 | } CPath; 296 | 297 | int krm__krt__io__CPath__IsRoot(CPath **this) { 298 | char *path = (*this)->path; 299 | if (strcmp(path, "ux0:") == 0) 300 | return 1; 301 | else 302 | return 0; 303 | } 304 | 305 | void patch_game(void) { 306 | *(int *)so_symbol(&bc2_mod, "_ZN3krm3sal12SCREEN_WIDTHE") = SCREEN_W; 307 | *(int *)so_symbol(&bc2_mod, "_ZN3krm3sal13SCREEN_HEIGHTE") = SCREEN_H; 308 | 309 | hook_arm(so_symbol(&bc2_mod, "_ZN3krm10krtNetInitEv"), (uintptr_t)&ret0); 310 | hook_arm(so_symbol(&bc2_mod, "_ZN3krm3krt3dbg15krtDebugMgrInitEPNS0_16CApplicationBaseE"), (uintptr_t)&ret0); 311 | 312 | hook_arm(so_symbol(&bc2_mod, "_ZNK3krm3krt2io5CPath6IsRootEv"), (uintptr_t)&krm__krt__io__CPath__IsRoot); 313 | 314 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_GetAppReadPath"), (uintptr_t)&Android_KarismaBridge_GetAppReadPath); 315 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_GetAppWritePath"), (uintptr_t)&Android_KarismaBridge_GetAppWritePath); 316 | 317 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_GetKeyboardOpened"), (uintptr_t)&ret0); 318 | 319 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_EnableSound"), (uintptr_t)&Android_KarismaBridge_EnableSound); 320 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_DisableSound"), (uintptr_t)&Android_KarismaBridge_DisableSound); 321 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_LockSound"), (uintptr_t)&ret0); 322 | hook_thumb(so_symbol(&bc2_mod, "Android_KarismaBridge_UnlockSound"), (uintptr_t)&ret0); 323 | } 324 | 325 | extern void *_ZdaPv; 326 | extern void *_ZdlPv; 327 | extern void *_Znaj; 328 | extern void *_Znwj; 329 | 330 | extern void *__aeabi_atexit; 331 | extern void *__aeabi_d2f; 332 | extern void *__aeabi_d2ulz; 333 | extern void *__aeabi_dcmpgt; 334 | extern void *__aeabi_dmul; 335 | extern void *__aeabi_f2d; 336 | extern void *__aeabi_f2iz; 337 | extern void *__aeabi_f2ulz; 338 | extern void *__aeabi_fadd; 339 | extern void *__aeabi_fcmpge; 340 | extern void *__aeabi_fcmpgt; 341 | extern void *__aeabi_fcmple; 342 | extern void *__aeabi_fcmplt; 343 | extern void *__aeabi_fdiv; 344 | extern void *__aeabi_fsub; 345 | extern void *__aeabi_idiv; 346 | extern void *__aeabi_idivmod; 347 | extern void *__aeabi_l2d; 348 | extern void *__aeabi_l2f; 349 | extern void *__aeabi_ldivmod; 350 | extern void *__aeabi_uidiv; 351 | extern void *__aeabi_uidivmod; 352 | extern void *__aeabi_uldivmod; 353 | extern void *__cxa_guard_acquire; 354 | extern void *__cxa_guard_release; 355 | extern void *__cxa_pure_virtual; 356 | extern void *__dso_handle; 357 | extern void *__sF; 358 | extern void *__stack_chk_fail; 359 | extern void *__stack_chk_guard; 360 | 361 | static int __stack_chk_guard_fake = 0x42424242; 362 | static FILE __sF_fake[0x100][3]; 363 | 364 | struct tm *localtime_hook(time_t *timer) { 365 | struct tm *res = localtime(timer); 366 | if (res) 367 | return res; 368 | // Fix an uninitialized variable bug. 369 | time(timer); 370 | return localtime(timer); 371 | } 372 | 373 | static DynLibFunction dynlib_functions[] = { 374 | { "_ZdaPv", (uintptr_t)&_ZdaPv }, 375 | { "_ZdlPv", (uintptr_t)&_ZdlPv }, 376 | { "_Znaj", (uintptr_t)&_Znaj }, 377 | { "_Znwj", (uintptr_t)&_Znwj }, 378 | { "__aeabi_atexit", (uintptr_t)&__aeabi_atexit }, 379 | { "__aeabi_d2f", (uintptr_t)&__aeabi_d2f }, 380 | { "__aeabi_d2ulz", (uintptr_t)&__aeabi_d2ulz }, 381 | { "__aeabi_dcmpgt", (uintptr_t)&__aeabi_dcmpgt }, 382 | { "__aeabi_dmul", (uintptr_t)&__aeabi_dmul }, 383 | { "__aeabi_f2d", (uintptr_t)&__aeabi_f2d }, 384 | { "__aeabi_f2iz", (uintptr_t)&__aeabi_f2iz }, 385 | { "__aeabi_f2ulz", (uintptr_t)&__aeabi_f2ulz }, 386 | { "__aeabi_fadd", (uintptr_t)&__aeabi_fadd }, 387 | { "__aeabi_fcmpge", (uintptr_t)&__aeabi_fcmpge }, 388 | { "__aeabi_fcmpgt", (uintptr_t)&__aeabi_fcmpgt }, 389 | { "__aeabi_fcmple", (uintptr_t)&__aeabi_fcmple }, 390 | { "__aeabi_fcmplt", (uintptr_t)&__aeabi_fcmplt }, 391 | { "__aeabi_fdiv", (uintptr_t)&__aeabi_fdiv }, 392 | { "__aeabi_fsub", (uintptr_t)&__aeabi_fsub }, 393 | { "__aeabi_idiv", (uintptr_t)&__aeabi_idiv }, 394 | { "__aeabi_idivmod", (uintptr_t)&__aeabi_idivmod }, 395 | { "__aeabi_l2d", (uintptr_t)&__aeabi_l2d }, 396 | { "__aeabi_l2f", (uintptr_t)&__aeabi_l2f }, 397 | { "__aeabi_ldivmod", (uintptr_t)&__aeabi_ldivmod }, 398 | { "__aeabi_uidiv", (uintptr_t)&__aeabi_uidiv }, 399 | { "__aeabi_uidivmod", (uintptr_t)&__aeabi_uidivmod }, 400 | { "__aeabi_uldivmod", (uintptr_t)&__aeabi_uldivmod }, 401 | { "__android_log_print", (uintptr_t)&__android_log_print }, 402 | { "__cxa_guard_acquire", (uintptr_t)&__cxa_guard_acquire }, 403 | { "__cxa_guard_release", (uintptr_t)&__cxa_guard_release }, 404 | { "__cxa_pure_virtual", (uintptr_t)&__cxa_pure_virtual }, 405 | { "__dso_handle", (uintptr_t)&__dso_handle }, 406 | { "__errno", (uintptr_t)&__errno }, 407 | { "__sF", (uintptr_t)&__sF_fake }, 408 | { "__stack_chk_fail", (uintptr_t)&__stack_chk_fail }, 409 | { "__stack_chk_guard", (uintptr_t)&__stack_chk_guard_fake }, 410 | { "acos", (uintptr_t)&acos }, 411 | { "asin", (uintptr_t)&asin }, 412 | { "atan", (uintptr_t)&atan }, 413 | { "atan2", (uintptr_t)&atan2 }, 414 | { "atoi", (uintptr_t)&atoi }, 415 | { "ceil", (uintptr_t)&ceil }, 416 | { "close", (uintptr_t)&close }, 417 | { "cos", (uintptr_t)&cos }, 418 | { "difftime", (uintptr_t)&difftime }, 419 | { "eglSwapBuffers", (uintptr_t)&eglSwapBuffers }, 420 | { "fclose", (uintptr_t)&fclose }, 421 | { "fflush", (uintptr_t)&fflush }, 422 | { "fgets", (uintptr_t)&fgets }, 423 | { "fileno", (uintptr_t)&fileno }, 424 | { "floor", (uintptr_t)&floor }, 425 | { "fmod", (uintptr_t)&fmod }, 426 | { "fopen", (uintptr_t)&fopen }, 427 | { "fprintf", (uintptr_t)&fprintf }, 428 | { "fread", (uintptr_t)&fread }, 429 | { "free", (uintptr_t)&free }, 430 | { "fseek", (uintptr_t)&fseek }, 431 | { "fstat", (uintptr_t)&fstat }, 432 | { "ftell", (uintptr_t)&ftell }, 433 | { "fwrite", (uintptr_t)&fwrite }, 434 | { "getcwd", (uintptr_t)&getcwd }, 435 | { "gettimeofday", (uintptr_t)&gettimeofday }, 436 | { "glActiveTexture", (uintptr_t)&glActiveTexture }, 437 | { "glAlphaFunc", (uintptr_t)&glAlphaFunc }, 438 | { "glBindBuffer", (uintptr_t)&glBindBuffer }, 439 | { "glBindFramebufferOES", (uintptr_t)&glBindFramebuffer }, 440 | { "glBindTexture", (uintptr_t)&glBindTexture }, 441 | { "glBlendFunc", (uintptr_t)&glBlendFunc }, 442 | { "glClear", (uintptr_t)&glClear }, 443 | { "glClearColor", (uintptr_t)&glClearColor }, 444 | { "glClearDepthf", (uintptr_t)&glClearDepthf }, 445 | { "glClearStencil", (uintptr_t)&glClearStencil }, 446 | { "glClientActiveTexture", (uintptr_t)&glClientActiveTexture }, 447 | { "glColorMask", (uintptr_t)&glColorMask }, 448 | { "glColorPointer", (uintptr_t)&glColorPointer }, 449 | { "glCompressedTexImage2D", (uintptr_t)&glCompressedTexImage2D }, 450 | { "glCullFace", (uintptr_t)&glCullFace }, 451 | { "glDeleteBuffers", (uintptr_t)&glDeleteBuffers }, 452 | { "glDeleteTextures", (uintptr_t)&glDeleteTextures }, 453 | { "glDepthFunc", (uintptr_t)&glDepthFunc }, 454 | { "glDepthMask", (uintptr_t)&glDepthMask }, 455 | { "glDepthRangef", (uintptr_t)&glDepthRangef }, 456 | { "glDisable", (uintptr_t)&glDisable }, 457 | { "glDisableClientState", (uintptr_t)&glDisableClientState }, 458 | { "glDrawArrays", (uintptr_t)&glDrawArrays }, 459 | { "glDrawElements", (uintptr_t)&glDrawElements }, 460 | { "glEnable", (uintptr_t)&glEnable }, 461 | { "glEnableClientState", (uintptr_t)&glEnableClientState }, 462 | { "glFogf", (uintptr_t)&glFogf }, 463 | { "glFogfv", (uintptr_t)&glFogfv }, 464 | { "glFrontFace", (uintptr_t)&glFrontFace }, 465 | { "glGenTextures", (uintptr_t)&glGenTextures }, 466 | { "glGetError", (uintptr_t)&glGetError }, 467 | { "glGetIntegerv", (uintptr_t)&glGetIntegerv }, 468 | { "glGetString", (uintptr_t)&glGetString }, 469 | { "glLoadIdentity", (uintptr_t)&glLoadIdentity }, 470 | { "glLoadMatrixf", (uintptr_t)&glLoadMatrixf }, 471 | { "glMatrixMode", (uintptr_t)&glMatrixMode }, 472 | { "glNormalPointer", (uintptr_t)&ret0 }, 473 | { "glReadPixels", (uintptr_t)&glReadPixels }, 474 | { "glScissor", (uintptr_t)&glScissor }, 475 | { "glStencilFunc", (uintptr_t)&glStencilFunc }, 476 | { "glStencilOp", (uintptr_t)&glStencilOp }, 477 | { "glTexCoordPointer", (uintptr_t)&glTexCoordPointer }, 478 | { "glTexEnvf", (uintptr_t)&glTexEnvf }, 479 | { "glTexEnvfv", (uintptr_t)&glTexEnvfv }, 480 | { "glTexImage2D", (uintptr_t)&glTexImage2D }, 481 | { "glTexParameteri", (uintptr_t)&glTexParameteri }, 482 | { "glVertexPointer", (uintptr_t)&glVertexPointer }, 483 | { "glViewport", (uintptr_t)&glViewport }, 484 | { "ldexp", (uintptr_t)&ldexp }, 485 | { "localtime", (uintptr_t)&localtime_hook }, 486 | { "log", (uintptr_t)&log }, 487 | { "lrand48", (uintptr_t)&lrand48 }, 488 | { "malloc", (uintptr_t)&malloc }, 489 | { "memcmp", (uintptr_t)&memcmp }, 490 | { "memcpy", (uintptr_t)&memcpy }, 491 | { "memmove", (uintptr_t)&memmove }, 492 | { "memset", (uintptr_t)&memset }, 493 | { "mkdir", (uintptr_t)&mkdir }, 494 | { "pow", (uintptr_t)&pow }, 495 | { "printf", (uintptr_t)&ret0 }, 496 | { "read", (uintptr_t)&read }, 497 | { "realloc", (uintptr_t)&realloc }, 498 | { "rmdir", (uintptr_t)&rmdir }, 499 | { "sin", (uintptr_t)&sin }, 500 | { "snprintf", (uintptr_t)&snprintf }, 501 | { "sqrt", (uintptr_t)&sqrt }, 502 | { "sscanf", (uintptr_t)&sscanf }, 503 | { "strchr", (uintptr_t)&strchr }, 504 | { "strcmp", (uintptr_t)&strcmp }, 505 | { "strerror", (uintptr_t)&strerror }, 506 | { "strlen", (uintptr_t)&strlen }, 507 | { "strncat", (uintptr_t)&strncat }, 508 | { "strncmp", (uintptr_t)&strncmp }, 509 | { "strncpy", (uintptr_t)&strncpy }, 510 | { "strrchr", (uintptr_t)&strrchr }, 511 | { "strstr", (uintptr_t)&strstr }, 512 | { "strtoll", (uintptr_t)&strtoll }, 513 | { "tan", (uintptr_t)&tan }, 514 | { "time", (uintptr_t)&time }, 515 | { "tolower", (uintptr_t)&tolower }, 516 | { "toupper", (uintptr_t)&toupper }, 517 | { "unlink", (uintptr_t)&unlink }, 518 | { "vsnprintf", (uintptr_t)&vsnprintf }, 519 | { "write", (uintptr_t)&write }, 520 | }; 521 | 522 | int check_kubridge(void) { 523 | int search_unk[2]; 524 | return _vshKernelSearchModuleByName("kubridge", search_unk); 525 | } 526 | 527 | int file_exists(const char *path) { 528 | SceIoStat stat; 529 | return sceIoGetstat(path, &stat) >= 0; 530 | } 531 | 532 | int main(int argc, char *argv[]) { 533 | sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); 534 | sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); 535 | 536 | scePowerSetArmClockFrequency(444); 537 | scePowerSetBusClockFrequency(222); 538 | scePowerSetGpuClockFrequency(222); 539 | scePowerSetGpuXbarClockFrequency(166); 540 | 541 | if (check_kubridge() < 0) 542 | fatal_error("Error kubridge.skprx is not installed."); 543 | 544 | if (!file_exists("ur0:/data/libshacccg.suprx") && !file_exists("ur0:/data/external/libshacccg.suprx")) 545 | fatal_error("Error libshacccg.suprx is not installed."); 546 | 547 | if (so_load(&bc2_mod, SO_PATH) < 0) 548 | fatal_error("Error could not load %s.", SO_PATH); 549 | 550 | so_relocate(&bc2_mod); 551 | so_resolve(&bc2_mod, dynlib_functions, sizeof(dynlib_functions) / sizeof(DynLibFunction), 1); 552 | 553 | patch_game(); 554 | so_flush_caches(&bc2_mod); 555 | 556 | so_initialize(&bc2_mod); 557 | 558 | SceUID thid = sceKernelCreateThread("main_thread", (SceKernelThreadEntry)main_thread, 0x40, 128 * 1024, 0, 0, NULL); 559 | sceKernelStartThread(thid, 0, NULL); 560 | return sceKernelExitDeleteThread(0); 561 | } 562 | -------------------------------------------------------------------------------- /loader/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAIN_H__ 2 | #define __MAIN_H__ 3 | 4 | SceUID _vshKernelSearchModuleByName(const char *, int *); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /loader/so_util.c: -------------------------------------------------------------------------------- 1 | /* so_util.c -- utils to load and hook .so modules 2 | * 3 | * Copyright (C) 2021 Andy Nguyen 4 | * 5 | * This software may be modified and distributed under the terms 6 | * of the MIT license. See the LICENSE file for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "main.h" 19 | #include "dialog.h" 20 | #include "so_util.h" 21 | 22 | void hook_thumb(uintptr_t addr, uintptr_t dst) { 23 | if (addr == 0) 24 | return; 25 | addr &= ~1; 26 | if (addr & 2) { 27 | uint16_t nop = 0xbf00; 28 | kuKernelCpuUnrestrictedMemcpy((void *)addr, &nop, sizeof(nop)); 29 | addr += 2; 30 | } 31 | uint32_t hook[2]; 32 | hook[0] = 0xf000f8df; // LDR PC, [PC] 33 | hook[1] = dst; 34 | kuKernelCpuUnrestrictedMemcpy((void *)addr, hook, sizeof(hook)); 35 | } 36 | 37 | void hook_arm(uintptr_t addr, uintptr_t dst) { 38 | if (addr == 0) 39 | return; 40 | uint32_t hook[2]; 41 | hook[0] = 0xe51ff004; // LDR PC, [PC, #-0x4] 42 | hook[1] = dst; 43 | kuKernelCpuUnrestrictedMemcpy((void *)addr, hook, sizeof(hook)); 44 | } 45 | 46 | void so_flush_caches(so_module *mod) { 47 | kuKernelFlushCaches((void *)mod->text_base, mod->text_size); 48 | } 49 | 50 | int so_load(so_module *mod, const char *filename) { 51 | int res = 0; 52 | uintptr_t data_addr = 0; 53 | SceUID so_blockid; 54 | void *so_data; 55 | size_t so_size; 56 | 57 | memset(mod, 0, sizeof(so_module)); 58 | 59 | SceUID fd = sceIoOpen(filename, SCE_O_RDONLY, 0); 60 | if (fd < 0) 61 | return fd; 62 | 63 | so_size = sceIoLseek(fd, 0, SCE_SEEK_END); 64 | sceIoLseek(fd, 0, SCE_SEEK_SET); 65 | 66 | so_blockid = sceKernelAllocMemBlock("file", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, (so_size + 0xfff) & ~0xfff, NULL); 67 | if (so_blockid < 0) 68 | return so_blockid; 69 | 70 | sceKernelGetMemBlockBase(so_blockid, &so_data); 71 | 72 | sceIoRead(fd, so_data, so_size); 73 | sceIoClose(fd); 74 | 75 | if (memcmp(so_data, ELFMAG, SELFMAG) != 0) { 76 | res = -1; 77 | goto err_free_so; 78 | } 79 | 80 | mod->ehdr = (Elf32_Ehdr *)so_data; 81 | mod->phdr = (Elf32_Phdr *)((uintptr_t)so_data + mod->ehdr->e_phoff); 82 | mod->shdr = (Elf32_Shdr *)((uintptr_t)so_data + mod->ehdr->e_shoff); 83 | 84 | mod->shstr = (char *)((uintptr_t)so_data + mod->shdr[mod->ehdr->e_shstrndx].sh_offset); 85 | 86 | for (int i = 0; i < mod->ehdr->e_phnum; i++) { 87 | if (mod->phdr[i].p_type == PT_LOAD) { 88 | void *prog_data; 89 | size_t prog_size; 90 | 91 | if ((mod->phdr[i].p_flags & PF_X) == PF_X) { 92 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz, mod->phdr[i].p_align); 93 | 94 | SceKernelAllocMemBlockKernelOpt opt; 95 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 96 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 97 | #ifdef LOAD_ADDRESS 98 | opt.attr = 0x1; 99 | opt.field_C = (SceUInt32)LOAD_ADDRESS; 100 | #endif 101 | res = mod->text_blockid = kuKernelAllocMemBlock("rx_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RX, prog_size, &opt); 102 | if (res < 0) 103 | goto err_free_so; 104 | 105 | sceKernelGetMemBlockBase(mod->text_blockid, &prog_data); 106 | 107 | mod->phdr[i].p_vaddr += (Elf32_Addr)prog_data; 108 | 109 | mod->text_base = mod->phdr[i].p_vaddr; 110 | mod->text_size = mod->phdr[i].p_memsz; 111 | 112 | data_addr = (uintptr_t)prog_data + prog_size; 113 | } else { 114 | if (data_addr == 0) 115 | goto err_free_so; 116 | 117 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz + mod->phdr[i].p_vaddr - (data_addr - mod->text_base), mod->phdr[i].p_align); 118 | 119 | SceKernelAllocMemBlockKernelOpt opt; 120 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 121 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 122 | opt.attr = 0x1; 123 | opt.field_C = (SceUInt32)data_addr; 124 | res = mod->data_blockid = kuKernelAllocMemBlock("rw_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, prog_size, &opt); 125 | if (res < 0) 126 | goto err_free_text; 127 | 128 | sceKernelGetMemBlockBase(mod->data_blockid, &prog_data); 129 | 130 | mod->phdr[i].p_vaddr += (Elf32_Addr)mod->text_base; 131 | 132 | mod->data_base = mod->phdr[i].p_vaddr; 133 | mod->data_size = mod->phdr[i].p_memsz; 134 | } 135 | 136 | char *zero = malloc(prog_size); 137 | memset(zero, 0, prog_size); 138 | kuKernelCpuUnrestrictedMemcpy(prog_data, zero, prog_size); 139 | free(zero); 140 | 141 | kuKernelCpuUnrestrictedMemcpy((void *)mod->phdr[i].p_vaddr, (void *)((uintptr_t)so_data + mod->phdr[i].p_offset), mod->phdr[i].p_filesz); 142 | } 143 | } 144 | 145 | for (int i = 0; i < mod->ehdr->e_shnum; i++) { 146 | char *sh_name = mod->shstr + mod->shdr[i].sh_name; 147 | uintptr_t sh_addr = mod->text_base + mod->shdr[i].sh_addr; 148 | size_t sh_size = mod->shdr[i].sh_size; 149 | if (strcmp(sh_name, ".dynamic") == 0) { 150 | mod->dynamic = (Elf32_Dyn *)sh_addr; 151 | mod->num_dynamic = sh_size / sizeof(Elf32_Dyn); 152 | } else if (strcmp(sh_name, ".dynstr") == 0) { 153 | mod->dynstr = (char *)sh_addr; 154 | } else if (strcmp(sh_name, ".dynsym") == 0) { 155 | mod->dynsym = (Elf32_Sym *)sh_addr; 156 | mod->num_dynsym = sh_size / sizeof(Elf32_Sym); 157 | } else if (strcmp(sh_name, ".rel.dyn") == 0) { 158 | mod->reldyn = (Elf32_Rel *)sh_addr; 159 | mod->num_reldyn = sh_size / sizeof(Elf32_Rel); 160 | } else if (strcmp(sh_name, ".rel.plt") == 0) { 161 | mod->relplt = (Elf32_Rel *)sh_addr; 162 | mod->num_relplt = sh_size / sizeof(Elf32_Rel); 163 | } else if (strcmp(sh_name, ".init_array") == 0) { 164 | mod->init_array = (void *)sh_addr; 165 | mod->num_init_array = sh_size / sizeof(void *); 166 | } else if (strcmp(sh_name, ".hash") == 0) { 167 | mod->hash = (void *)sh_addr; 168 | } 169 | } 170 | 171 | for (int i = 0; i < mod->num_dynamic; i++) { 172 | switch (mod->dynamic[i].d_tag) { 173 | case DT_SONAME: 174 | mod->soname = mod->dynstr + mod->dynamic[i].d_un.d_ptr; 175 | break; 176 | default: 177 | break; 178 | } 179 | } 180 | 181 | if (mod->dynamic == NULL || 182 | mod->dynstr == NULL || 183 | mod->dynsym == NULL || 184 | mod->reldyn == NULL || 185 | mod->relplt == NULL) { 186 | res = -2; 187 | goto err_free_data; 188 | } 189 | 190 | sceKernelFreeMemBlock(so_blockid); 191 | 192 | return 0; 193 | 194 | err_free_data: 195 | sceKernelFreeMemBlock(mod->data_blockid); 196 | err_free_text: 197 | sceKernelFreeMemBlock(mod->text_blockid); 198 | err_free_so: 199 | sceKernelFreeMemBlock(so_blockid); 200 | 201 | return res; 202 | } 203 | 204 | int so_relocate(so_module *mod) { 205 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 206 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 207 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 208 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 209 | 210 | int type = ELF32_R_TYPE(rel->r_info); 211 | switch (type) { 212 | case R_ARM_ABS32: 213 | *ptr += mod->text_base + sym->st_value; 214 | break; 215 | 216 | case R_ARM_RELATIVE: 217 | *ptr += mod->text_base; 218 | break; 219 | 220 | case R_ARM_GLOB_DAT: 221 | case R_ARM_JUMP_SLOT: 222 | { 223 | if (sym->st_shndx != SHN_UNDEF) 224 | *ptr = mod->text_base + sym->st_value; 225 | break; 226 | } 227 | 228 | default: 229 | fatal_error("Error unknown relocation type %x\n", type); 230 | break; 231 | } 232 | } 233 | 234 | return 0; 235 | } 236 | 237 | int so_resolve(so_module *mod, DynLibFunction *funcs, int num_funcs, int taint_missing_imports) { 238 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 239 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 240 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 241 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 242 | 243 | int type = ELF32_R_TYPE(rel->r_info); 244 | switch (type) { 245 | case R_ARM_GLOB_DAT: 246 | case R_ARM_JUMP_SLOT: 247 | { 248 | if (sym->st_shndx == SHN_UNDEF) { 249 | // make it crash for debugging 250 | if (taint_missing_imports) 251 | *ptr = rel->r_offset; 252 | 253 | // debugPrintf(" { \"%s\", (uintptr_t)&%s },\n", mod->dynstr + sym->st_name, mod->dynstr + sym->st_name); 254 | 255 | for (int j = 0; j < num_funcs; j++) { 256 | if (strcmp(mod->dynstr + sym->st_name, funcs[j].symbol) == 0) { 257 | *ptr = funcs[j].func; 258 | break; 259 | } 260 | } 261 | } 262 | 263 | break; 264 | } 265 | 266 | default: 267 | break; 268 | } 269 | } 270 | 271 | return 0; 272 | } 273 | 274 | void so_initialize(so_module *mod) { 275 | for (int i = 0; i < mod->num_init_array; i++) { 276 | if (mod->init_array[i]) 277 | mod->init_array[i](); 278 | } 279 | } 280 | 281 | uint32_t so_hash(const uint8_t *name) { 282 | uint64_t h = 0, g; 283 | while (*name) { 284 | h = (h << 4) + *name++; 285 | if ((g = (h & 0xf0000000)) != 0) 286 | h ^= g >> 24; 287 | h &= 0x0fffffff; 288 | } 289 | return h; 290 | } 291 | 292 | uintptr_t so_symbol(so_module *mod, const char *symbol) { 293 | if (mod->hash) { 294 | uint32_t hash = so_hash((const uint8_t *)symbol); 295 | uint32_t nbucket = mod->hash[0]; 296 | uint32_t *bucket = &mod->hash[2]; 297 | uint32_t *chain = &bucket[nbucket]; 298 | for (int i = bucket[hash % nbucket]; i; i = chain[i]) { 299 | if (strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 300 | return mod->text_base + mod->dynsym[i].st_value; 301 | } 302 | } else { 303 | for (int i = 0; i < mod->num_dynsym; i++) { 304 | if (strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 305 | return mod->text_base + mod->dynsym[i].st_value; 306 | } 307 | } 308 | 309 | fatal_error("Error could not find symbol %s\n", symbol); 310 | return 0; 311 | } 312 | -------------------------------------------------------------------------------- /loader/so_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __SO_UTIL_H__ 2 | #define __SO_UTIL_H__ 3 | 4 | #include "elf.h" 5 | 6 | #define ALIGN_MEM(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 7 | 8 | typedef struct { 9 | SceUID text_blockid, data_blockid; 10 | uintptr_t text_base, data_base; 11 | size_t text_size, data_size; 12 | 13 | Elf32_Ehdr *ehdr; 14 | Elf32_Phdr *phdr; 15 | Elf32_Shdr *shdr; 16 | 17 | Elf32_Dyn *dynamic; 18 | Elf32_Sym *dynsym; 19 | Elf32_Rel *reldyn; 20 | Elf32_Rel *relplt; 21 | 22 | int (** init_array)(void); 23 | uint32_t *hash; 24 | 25 | int num_dynamic; 26 | int num_dynsym; 27 | int num_reldyn; 28 | int num_relplt; 29 | int num_init_array; 30 | 31 | char *soname; 32 | char *shstr; 33 | char *dynstr; 34 | } so_module; 35 | 36 | typedef struct { 37 | char *symbol; 38 | uintptr_t func; 39 | } DynLibFunction; 40 | 41 | void hook_thumb(uintptr_t addr, uintptr_t dst); 42 | void hook_arm(uintptr_t addr, uintptr_t dst); 43 | 44 | void so_flush_caches(so_module *mod); 45 | int so_load(so_module *mod, const char *filename); 46 | int so_relocate(so_module *mod); 47 | int so_resolve(so_module *mod, DynLibFunction *funcs, int num_funcs, int taint_missing_imports); 48 | void so_initialize(so_module *mod); 49 | uintptr_t so_symbol(so_module *mod, const char *symbol); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /sce_sys/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheOfficialFloW/bc2_vita/8b2b3d7d9f0d9e76024017702760ea37da22880c/sce_sys/icon0.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/bg.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheOfficialFloW/bc2_vita/8b2b3d7d9f0d9e76024017702760ea37da22880c/sce_sys/livearea/contents/bg.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheOfficialFloW/bc2_vita/8b2b3d7d9f0d9e76024017702760ea37da22880c/sce_sys/livearea/contents/startup.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bg.png 6 | 7 | 8 | 9 | startup.png 10 | 11 | 12 | -------------------------------------------------------------------------------- /screenshots/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TheOfficialFloW/bc2_vita/8b2b3d7d9f0d9e76024017702760ea37da22880c/screenshots/game.png --------------------------------------------------------------------------------