├── .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
--------------------------------------------------------------------------------