├── .gitignore ├── .gitattributes ├── livearea ├── bg0.png ├── icon0.png ├── TROPHY.TRP ├── startup.png └── template.xml ├── screenshots ├── game.png ├── game2.png ├── game3.png └── game4.png ├── loader ├── trophies.h ├── dialog.h ├── main.h ├── config.h ├── sha1.h ├── audio_player.cpp ├── so_util.h ├── trophies.c ├── sha1.c ├── dialog.c ├── so_util.c └── main.c ├── LICENSE ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/* -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /livearea/bg0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/livearea/bg0.png -------------------------------------------------------------------------------- /livearea/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/livearea/icon0.png -------------------------------------------------------------------------------- /livearea/TROPHY.TRP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/livearea/TROPHY.TRP -------------------------------------------------------------------------------- /livearea/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/livearea/startup.png -------------------------------------------------------------------------------- /screenshots/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/screenshots/game.png -------------------------------------------------------------------------------- /screenshots/game2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/screenshots/game2.png -------------------------------------------------------------------------------- /screenshots/game3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/screenshots/game3.png -------------------------------------------------------------------------------- /screenshots/game4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rinnegatamante/smb2se-vita/HEAD/screenshots/game4.png -------------------------------------------------------------------------------- /loader/trophies.h: -------------------------------------------------------------------------------- 1 | #ifndef __TROPHIES_H__ 2 | #define __TROPHIES_H__ 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | int trophies_init(); 9 | void trophies_unlock(uint32_t id); 10 | uint8_t trophies_is_unlocked(uint32_t id); 11 | 12 | #ifdef __cplusplus 13 | } 14 | #endif 15 | #endif 16 | -------------------------------------------------------------------------------- /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 | void warning(const char *msg); 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /loader/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAIN_H__ 2 | #define __MAIN_H__ 3 | 4 | #include 5 | #include "config.h" 6 | #include "so_util.h" 7 | 8 | extern so_module twom_mod; 9 | 10 | int debugPrintf(char *text, ...); 11 | 12 | int ret0(); 13 | 14 | int sceKernelChangeThreadCpuAffinityMask(SceUID thid, int cpuAffinityMask); 15 | 16 | extern SceTouchPanelInfo panelInfoFront, panelInfoBack; 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /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 240 9 | #define MEMORY_VITAGL_THRESHOLD_MB 12 10 | 11 | #define DATA_PATH "ux0:data/smb2" 12 | #define SO_PATH DATA_PATH "/" "libsmb2.so" 13 | #define TROPHIES_FILE "ux0:data/smb2/trophies.chk" 14 | 15 | #define SCREEN_W 960 16 | #define SCREEN_H 544 17 | 18 | #endif 19 | -------------------------------------------------------------------------------- /livearea/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bg0.png 5 | 6 | 7 | startup.png 8 | 9 | 10 | 11 | 12 | psla:analogs 13 | 14 |  Use analogs controls 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /loader/sha1.h: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha1.h 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Defines the API for the corresponding SHA1 implementation. 7 | *********************************************************************/ 8 | 9 | #ifndef SHA1_H 10 | #define SHA1_H 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | 15 | /****************************** MACROS ******************************/ 16 | #define SHA1_BLOCK_SIZE 20 // SHA1 outputs a 20 byte digest 17 | 18 | /**************************** DATA TYPES ****************************/ 19 | typedef unsigned char BYTE; // 8-bit byte 20 | typedef unsigned int WORD; // 32-bit word, change to "long" for 16-bit machines 21 | 22 | typedef struct { 23 | BYTE data[64]; 24 | WORD datalen; 25 | unsigned long long bitlen; 26 | WORD state[5]; 27 | WORD k[4]; 28 | } SHA1_CTX; 29 | 30 | /*********************** FUNCTION DECLARATIONS **********************/ 31 | void sha1_init(SHA1_CTX *ctx); 32 | void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len); 33 | void sha1_final(SHA1_CTX *ctx, BYTE hash[]); 34 | 35 | #endif // SHA1_H 36 | -------------------------------------------------------------------------------- /loader/audio_player.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "soloud.h" 6 | #include "soloud_wav.h" 7 | #include "soloud_wavstream.h" 8 | 9 | SoLoud::Soloud soloud; 10 | SoLoud::WavStream music; 11 | int music_handle = -1; 12 | 13 | extern "C" { 14 | 15 | void audio_player_init() { 16 | soloud.init(); 17 | } 18 | 19 | void *audio_load_sound(char *fname) { 20 | auto *w = new SoLoud::Wav; 21 | w->load(fname); 22 | return w; 23 | } 24 | 25 | void audio_queue_music(char *fname, int loopcount) { 26 | music.load(fname); 27 | music.setLooping(loopcount == -1 ? true : false); 28 | music_handle = soloud.playBackground(music); 29 | } 30 | 31 | void audio_stop_music() { 32 | if (music_handle != -1) 33 | soloud.stop(music_handle); 34 | music_handle = -1; 35 | } 36 | 37 | void audio_set_music_volume(float volume) { 38 | music.setVolume(volume); 39 | if (music_handle != -1) 40 | soloud.setVolume(music_handle, volume); 41 | } 42 | 43 | int audio_play_sound(int id, float volume, float pitch, int loopcount) { 44 | auto *w = (SoLoud::Wav *)-id; 45 | w->setLooping(loopcount ? true : false); 46 | return soloud.play(*w, volume); 47 | } 48 | 49 | int last_removed_id = -1; 50 | void audio_remove_sound(int id) { 51 | if (id != last_removed_id) { 52 | auto *w = (SoLoud::Wav *)-id; 53 | w->stop(); 54 | delete w; 55 | } 56 | last_removed_id = id; 57 | } 58 | 59 | void audio_stop_sound(int id) { 60 | soloud.stop(id); 61 | } 62 | 63 | void audio_set_volume(int id, float volume) { 64 | soloud.setVolume(id, volume); 65 | } 66 | 67 | void audio_set_pitch(int id, float pitch) { 68 | soloud.setRelativePlaySpeed(id, pitch); 69 | } 70 | 71 | }; 72 | -------------------------------------------------------------------------------- /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(smb2se C CXX) 12 | include("${VITASDK}/share/vita.cmake" REQUIRED) 13 | set(VITA_APP_NAME "Super Monkey Ball 2: Sakura Edition") 14 | set(VITA_TITLEID "SMB2SAKED") 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 -D_GNU_SOURCE -Wall -O3 -mfloat-abi=softfp") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11") 20 | 21 | add_executable(smb2se 22 | loader/main.c 23 | loader/dialog.c 24 | loader/so_util.c 25 | loader/sha1.c 26 | loader/trophies.c 27 | loader/audio_player.cpp 28 | ) 29 | 30 | target_link_libraries(smb2se 31 | -Wl,--whole-archive pthread -Wl,--no-whole-archive 32 | m 33 | z 34 | stdc++ 35 | vorbis 36 | vorbisfile 37 | ogg 38 | vitaGL 39 | vitashark 40 | soloud 41 | mathneon 42 | SceShaccCgExt 43 | taihen_stub 44 | kubridge_stub 45 | SceAppMgr_stub 46 | SceAppUtil_stub 47 | SceAudio_stub 48 | SceAudioIn_stub 49 | SceCtrl_stub 50 | SceCommonDialog_stub 51 | SceDisplay_stub 52 | SceKernelDmacMgr_stub 53 | SceRazorHud_stub 54 | SceRazorCapture_stub 55 | SceFios2_stub 56 | SceGxm_stub 57 | SceLibcBridge_stub 58 | SceShaccCg_stub 59 | SceSysmodule_stub 60 | ScePower_stub 61 | SceTouch_stub 62 | SceVshBridge_stub 63 | SceMotion_stub 64 | SceNpTrophy_stub 65 | ) 66 | 67 | vita_create_self(eboot.bin smb2se UNSAFE) 68 | vita_create_vpk(smb2se.vpk ${VITA_TITLEID} eboot.bin 69 | VERSION ${VITA_VERSION} 70 | NAME ${VITA_APP_NAME} 71 | FILE ${CMAKE_SOURCE_DIR}/livearea/icon0.png sce_sys/icon0.png 72 | ${CMAKE_SOURCE_DIR}/livearea/bg0.png sce_sys/livearea/contents/bg0.png 73 | ${CMAKE_SOURCE_DIR}/livearea/startup.png sce_sys/livearea/contents/startup.png 74 | ${CMAKE_SOURCE_DIR}/livearea/template.xml sce_sys/livearea/contents/template.xml 75 | ${CMAKE_SOURCE_DIR}/livearea/TROPHY.TRP sce_sys/trophy/SMBS00001_00/TROPHY.TRP 76 | ) 77 | 78 | add_custom_target(copy 79 | COMMAND cp eboot.bin D:/app/${VITA_TITLEID}/eboot.bin 80 | DEPENDS eboot.bin 81 | ) 82 | -------------------------------------------------------------------------------- /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 | #define MAX_DATA_SEG 4 8 | 9 | typedef struct { 10 | uintptr_t addr; 11 | uintptr_t thumb_addr; 12 | uint32_t orig_instr[2]; 13 | uint32_t patch_instr[2]; 14 | } so_hook; 15 | 16 | typedef struct so_module { 17 | struct so_module *next; 18 | 19 | SceUID patch_blockid, text_blockid, data_blockid[MAX_DATA_SEG]; 20 | uintptr_t patch_base, patch_head, cave_base, cave_head, text_base, data_base[MAX_DATA_SEG]; 21 | size_t patch_size, cave_size, text_size, data_size[MAX_DATA_SEG]; 22 | int n_data; 23 | 24 | Elf32_Ehdr *ehdr; 25 | Elf32_Phdr *phdr; 26 | Elf32_Shdr *shdr; 27 | 28 | Elf32_Dyn *dynamic; 29 | Elf32_Sym *dynsym; 30 | Elf32_Rel *reldyn; 31 | Elf32_Rel *relplt; 32 | 33 | int (** init_array)(void); 34 | uint32_t *hash; 35 | 36 | int num_dynamic; 37 | int num_dynsym; 38 | int num_reldyn; 39 | int num_relplt; 40 | int num_init_array; 41 | 42 | char *soname; 43 | char *shstr; 44 | char *dynstr; 45 | } so_module; 46 | 47 | typedef struct { 48 | char *symbol; 49 | uintptr_t func; 50 | } so_default_dynlib; 51 | 52 | so_hook hook_thumb(uintptr_t addr, uintptr_t dst); 53 | so_hook hook_arm(uintptr_t addr, uintptr_t dst); 54 | so_hook hook_addr(uintptr_t addr, uintptr_t dst); 55 | 56 | void so_flush_caches(so_module *mod); 57 | int so_file_load(so_module *mod, const char *filename, uintptr_t load_addr); 58 | int so_mem_load(so_module *mod, void * buffer, size_t so_size, uintptr_t load_addr); 59 | int so_relocate(so_module *mod); 60 | int so_resolve(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only); 61 | int so_resolve_with_dummy(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only); 62 | void so_symbol_fix_ldmia(so_module *mod, const char *symbol); 63 | void so_initialize(so_module *mod); 64 | uintptr_t so_symbol(so_module *mod, const char *symbol); 65 | 66 | #define SO_CONTINUE(type, h, ...) ({ \ 67 | kuKernelCpuUnrestrictedMemcpy((void *)h.addr, h.orig_instr, sizeof(h.orig_instr)); \ 68 | kuKernelFlushCaches((void *)h.addr, sizeof(h.orig_instr)); \ 69 | type r = h.thumb_addr ? ((type(*)())h.thumb_addr)(__VA_ARGS__) : ((type(*)())h.addr)(__VA_ARGS__); \ 70 | kuKernelCpuUnrestrictedMemcpy((void *)h.addr, h.patch_instr, sizeof(h.patch_instr)); \ 71 | kuKernelFlushCaches((void *)h.addr, sizeof(h.patch_instr)); \ 72 | r; \ 73 | }) 74 | 75 | #endif 76 | -------------------------------------------------------------------------------- /loader/trophies.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | static char comm_id[12] = {0}; 6 | static char signature[160] = {0xb9,0xdd,0xe1,0x3b,0x01,0x00}; 7 | 8 | static int trp_ctx; 9 | static int plat_id = -1; 10 | 11 | typedef struct { 12 | int sdkVersion; 13 | SceCommonDialogParam commonParam; 14 | int context; 15 | int options; 16 | uint8_t reserved[128]; 17 | } SceNpTrophySetupDialogParam; 18 | 19 | typedef struct { 20 | uint32_t unk[4]; 21 | } SceNpTrophyUnlockState; 22 | SceNpTrophyUnlockState trophies_unlocks; 23 | 24 | int sceNpTrophyInit(void *unk); 25 | int sceNpTrophyCreateContext(int *context, char *commId, char *commSign, uint64_t options); 26 | int sceNpTrophySetupDialogInit(SceNpTrophySetupDialogParam *param); 27 | SceCommonDialogStatus sceNpTrophySetupDialogGetStatus(); 28 | int sceNpTrophySetupDialogTerm(); 29 | int sceNpTrophyCreateHandle(int *handle); 30 | int sceNpTrophyDestroyHandle(int handle); 31 | int sceNpTrophyUnlockTrophy(int ctx, int handle, int id, int *plat_id); 32 | int sceNpTrophyGetTrophyUnlockState(int ctx, int handle, SceNpTrophyUnlockState *state, uint32_t *count); 33 | 34 | int trophies_available = 0; 35 | 36 | volatile int trp_id; 37 | SceUID trp_request_mutex, trp_delivered_mutex; 38 | int trophies_unlocker(SceSize args, void *argp) { 39 | for (;;) { 40 | sceKernelWaitSema(trp_request_mutex, 1, NULL); 41 | int local_trp_id = trp_id; 42 | int trp_handle; 43 | sceNpTrophyCreateHandle(&trp_handle); 44 | sceNpTrophyUnlockTrophy(trp_ctx, trp_handle, local_trp_id, &plat_id); 45 | sceKernelSignalSema(trp_delivered_mutex, 1); 46 | sceNpTrophyDestroyHandle(trp_handle); 47 | } 48 | } 49 | 50 | int trophies_init() { 51 | // Starting sceNpTrophy 52 | strcpy(comm_id, "SMBS00001"); 53 | sceSysmoduleLoadModule(SCE_SYSMODULE_NP_TROPHY); 54 | sceNpTrophyInit(NULL); 55 | int res = sceNpTrophyCreateContext(&trp_ctx, comm_id, signature, 0); 56 | if (res < 0) { 57 | #ifdef DEBUG 58 | printf("sceNpTrophyCreateContext returned 0x%08X\n", res); 59 | #endif 60 | return res; 61 | } 62 | SceNpTrophySetupDialogParam setupParam; 63 | sceClibMemset(&setupParam, 0, sizeof(SceNpTrophySetupDialogParam)); 64 | _sceCommonDialogSetMagicNumber(&setupParam.commonParam); 65 | setupParam.sdkVersion = PSP2_SDK_VERSION; 66 | setupParam.options = 0; 67 | setupParam.context = trp_ctx; 68 | sceNpTrophySetupDialogInit(&setupParam); 69 | static int trophy_setup = SCE_COMMON_DIALOG_STATUS_RUNNING; 70 | while (trophy_setup == SCE_COMMON_DIALOG_STATUS_RUNNING) { 71 | trophy_setup = sceNpTrophySetupDialogGetStatus(); 72 | vglSwapBuffers(GL_TRUE); 73 | } 74 | sceNpTrophySetupDialogTerm(); 75 | 76 | // Starting trophy unlocker thread 77 | trp_delivered_mutex = sceKernelCreateSema("trps delivery", 0, 1, 1, NULL); 78 | trp_request_mutex = sceKernelCreateSema("trps request", 0, 0, 1, NULL); 79 | SceUID tropies_unlocker_thd = sceKernelCreateThread("trophies unlocker", &trophies_unlocker, 0x10000100, 0x10000, 0, 0, NULL); 80 | sceKernelStartThread(tropies_unlocker_thd, 0, NULL); 81 | 82 | // Getting current trophy unlocks state 83 | int trp_handle; 84 | uint32_t dummy; 85 | sceNpTrophyCreateHandle(&trp_handle); 86 | sceNpTrophyGetTrophyUnlockState(trp_ctx, trp_handle, &trophies_unlocks, &dummy); 87 | sceNpTrophyDestroyHandle(trp_handle); 88 | 89 | trophies_available = 1; 90 | return res; 91 | } 92 | 93 | uint8_t trophies_is_unlocked(uint32_t id) { 94 | if (trophies_available) { 95 | return (trophies_unlocks.unk[id >> 5] & (1 << (id & 31))) > 0; 96 | } 97 | return 0; 98 | } 99 | 100 | void trophies_unlock(uint32_t id) { 101 | if (trophies_available && !trophies_is_unlocked(id)) { 102 | trophies_unlocks.unk[id >> 5] |= (1 << (id & 31)); 103 | sceKernelWaitSema(trp_delivered_mutex, 1, NULL); 104 | trp_id = id; 105 | sceKernelSignalSema(trp_request_mutex, 1); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Super Monkey Ball 2 Sakura Edition Vita 2 | 3 |

4 | 5 | This is a wrapper/port of Super Monkey Ball 2: Sakura Edition for the *PS Vita*. 6 | 7 | The port works by loading the official Android ARMv7 executables in memory, resolving its imports with native functions and patching it in order to properly run. 8 | By doing so, it's basically as if we emulate a minimalist Android environment in which we run natively the executable as is. 9 | 10 | ## Changelog 11 | 12 | ### v1.22 13 | 14 | - Updated trophies icons to hi-res versions. 15 | 16 | ### v1.21 17 | 18 | - Fixed analogs mode not working. 19 | 20 | ### v1.2 21 | 22 | - Updated to latest vitaGL build. 23 | - Added trophies support (requires NoTrpDrm). 24 | 25 | ### v1.1 26 | 27 | - Reworked Livearea assets. 28 | - Added an option to use left analog in place of accelerometer. 29 | 30 | ### v1.0 31 | 32 | - Initial release. 33 | 34 | ## Note 35 | 36 | This port works only with versions of the game prior it being made free to play (formerly either v.1.0, v.1.1 or v.1.2). 37 | 38 | ## Setup Instructions (For End Users) 39 | 40 | In order to properly install the game, you'll have to follow these steps precisely: 41 | 42 | - 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`: 43 | 44 | ``` 45 | *KERNEL 46 | ux0:tai/kubridge.skprx 47 | ux0:tai/fd_fix.skprx 48 | ``` 49 | 50 | **Note** Don't install fd_fix.skprx if you're using rePatch plugin 51 | 52 | - **Optional**: Install [PSVshell](https://github.com/Electry/PSVshell/releases) to overclock your device to 500Mhz. 53 | - 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). 54 | - Obtain your copy of *Super Monkey Ball 2: Sakura Edition* legally for Android in form of an `.apk` file and cache files. [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. 55 | - Open the apk with your zip explorer and extract the file `libsmb2.so` from the `lib/armeabi-v7a` folder to `ux0:data/smb2`. 56 | - Extract the folder `assets` inside `ux0:data/smb2`. 57 | - Grab cache data (can be obtained by running once the application on your phone and letting it download required game data files) in form of a `assets` folder (usually can be found in `Android/data/com.ooi.android.smb2`). 58 | - Place the `assets` folder from the cache data inside `ux0:data/smb2`. 59 | - **Optional**: For trophies to be unlockable, install [NoTrpDRM](https://github.com/Rinnegatamante/NoTrpDrm). 60 | 61 | ## Build Instructions (For Developers) 62 | 63 | In order to build the loader, you'll need a [vitasdk](https://github.com/vitasdk) build fully compiled with softfp usage. 64 | You can find a precompiled version here: https://github.com/vitasdk/buildscripts/actions/runs/1102643776. 65 | Additionally, you'll need these libraries to be compiled as well with `-mfloat-abi=softfp` added to their CFLAGS: 66 | 67 | - [SoLoud](https://github.com/vitasdk/packages/blob/master/soloud/VITABUILD) 68 | 69 | - [libmathneon](https://github.com/Rinnegatamante/math-neon) 70 | 71 | - ```bash 72 | make install 73 | ``` 74 | 75 | - [vitaShaRK](https://github.com/Rinnegatamante/vitaShaRK) 76 | 77 | - ```bash 78 | make install 79 | ``` 80 | 81 | - [kubridge](https://github.com/TheOfficialFloW/kubridge) 82 | 83 | - ```bash 84 | mkdir build && cd build 85 | cmake .. && make install 86 | ``` 87 | 88 | - [vitaGL](https://github.com/Rinnegatamante/vitaGL) 89 | 90 | - ````bash 91 | make SOFTFP_ABI=1 NO_DEBUG=1 HAVE_GLSL_SUPPORT=1 install 92 | ```` 93 | 94 | After all these requirements are met, you can compile the loader with the following commands: 95 | 96 | ```bash 97 | mkdir build && cd build 98 | cmake .. && make 99 | ``` 100 | 101 | ## Credits 102 | 103 | - TheFloW for the original .so loader. 104 | - CatoTheYounger for testing the homebrew and providing screenshots. 105 | -------------------------------------------------------------------------------- /loader/sha1.c: -------------------------------------------------------------------------------- 1 | /********************************************************************* 2 | * Filename: sha1.c 3 | * Author: Brad Conte (brad AT bradconte.com) 4 | * Copyright: 5 | * Disclaimer: This code is presented "as is" without any guarantees. 6 | * Details: Implementation of the SHA1 hashing algorithm. 7 | Algorithm specification can be found here: 8 | * http://csrc.nist.gov/publications/fips/fips180-2/fips180-2withchangenotice.pdf 9 | This implementation uses little endian byte order. 10 | *********************************************************************/ 11 | 12 | /*************************** HEADER FILES ***************************/ 13 | #include 14 | #include 15 | #include "sha1.h" 16 | 17 | /****************************** MACROS ******************************/ 18 | #define ROTLEFT(a, b) ((a << b) | (a >> (32 - b))) 19 | 20 | /*********************** FUNCTION DEFINITIONS ***********************/ 21 | void sha1_transform(SHA1_CTX *ctx, const BYTE data[]) 22 | { 23 | WORD a, b, c, d, e, i, j, t, m[80]; 24 | 25 | for (i = 0, j = 0; i < 16; ++i, j += 4) 26 | m[i] = (data[j] << 24) + (data[j + 1] << 16) + (data[j + 2] << 8) + (data[j + 3]); 27 | for ( ; i < 80; ++i) { 28 | m[i] = (m[i - 3] ^ m[i - 8] ^ m[i - 14] ^ m[i - 16]); 29 | m[i] = (m[i] << 1) | (m[i] >> 31); 30 | } 31 | 32 | a = ctx->state[0]; 33 | b = ctx->state[1]; 34 | c = ctx->state[2]; 35 | d = ctx->state[3]; 36 | e = ctx->state[4]; 37 | 38 | for (i = 0; i < 20; ++i) { 39 | t = ROTLEFT(a, 5) + ((b & c) ^ (~b & d)) + e + ctx->k[0] + m[i]; 40 | e = d; 41 | d = c; 42 | c = ROTLEFT(b, 30); 43 | b = a; 44 | a = t; 45 | } 46 | for ( ; i < 40; ++i) { 47 | t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[1] + m[i]; 48 | e = d; 49 | d = c; 50 | c = ROTLEFT(b, 30); 51 | b = a; 52 | a = t; 53 | } 54 | for ( ; i < 60; ++i) { 55 | t = ROTLEFT(a, 5) + ((b & c) ^ (b & d) ^ (c & d)) + e + ctx->k[2] + m[i]; 56 | e = d; 57 | d = c; 58 | c = ROTLEFT(b, 30); 59 | b = a; 60 | a = t; 61 | } 62 | for ( ; i < 80; ++i) { 63 | t = ROTLEFT(a, 5) + (b ^ c ^ d) + e + ctx->k[3] + m[i]; 64 | e = d; 65 | d = c; 66 | c = ROTLEFT(b, 30); 67 | b = a; 68 | a = t; 69 | } 70 | 71 | ctx->state[0] += a; 72 | ctx->state[1] += b; 73 | ctx->state[2] += c; 74 | ctx->state[3] += d; 75 | ctx->state[4] += e; 76 | } 77 | 78 | void sha1_init(SHA1_CTX *ctx) 79 | { 80 | ctx->datalen = 0; 81 | ctx->bitlen = 0; 82 | ctx->state[0] = 0x67452301; 83 | ctx->state[1] = 0xEFCDAB89; 84 | ctx->state[2] = 0x98BADCFE; 85 | ctx->state[3] = 0x10325476; 86 | ctx->state[4] = 0xc3d2e1f0; 87 | ctx->k[0] = 0x5a827999; 88 | ctx->k[1] = 0x6ed9eba1; 89 | ctx->k[2] = 0x8f1bbcdc; 90 | ctx->k[3] = 0xca62c1d6; 91 | } 92 | 93 | void sha1_update(SHA1_CTX *ctx, const BYTE data[], size_t len) 94 | { 95 | size_t i; 96 | 97 | for (i = 0; i < len; ++i) { 98 | ctx->data[ctx->datalen] = data[i]; 99 | ctx->datalen++; 100 | if (ctx->datalen == 64) { 101 | sha1_transform(ctx, ctx->data); 102 | ctx->bitlen += 512; 103 | ctx->datalen = 0; 104 | } 105 | } 106 | } 107 | 108 | void sha1_final(SHA1_CTX *ctx, BYTE hash[]) 109 | { 110 | WORD i; 111 | 112 | i = ctx->datalen; 113 | 114 | // Pad whatever data is left in the buffer. 115 | if (ctx->datalen < 56) { 116 | ctx->data[i++] = 0x80; 117 | while (i < 56) 118 | ctx->data[i++] = 0x00; 119 | } 120 | else { 121 | ctx->data[i++] = 0x80; 122 | while (i < 64) 123 | ctx->data[i++] = 0x00; 124 | sha1_transform(ctx, ctx->data); 125 | memset(ctx->data, 0, 56); 126 | } 127 | 128 | // Append to the padding the total message's length in bits and transform. 129 | ctx->bitlen += ctx->datalen * 8; 130 | ctx->data[63] = ctx->bitlen; 131 | ctx->data[62] = ctx->bitlen >> 8; 132 | ctx->data[61] = ctx->bitlen >> 16; 133 | ctx->data[60] = ctx->bitlen >> 24; 134 | ctx->data[59] = ctx->bitlen >> 32; 135 | ctx->data[58] = ctx->bitlen >> 40; 136 | ctx->data[57] = ctx->bitlen >> 48; 137 | ctx->data[56] = ctx->bitlen >> 56; 138 | sha1_transform(ctx, ctx->data); 139 | 140 | // Since this implementation uses little endian byte ordering and MD uses big endian, 141 | // reverse all the bytes when copying the final state to the output hash. 142 | for (i = 0; i < 4; ++i) { 143 | hash[i] = (ctx->state[0] >> (24 - i * 8)) & 0x000000ff; 144 | hash[i + 4] = (ctx->state[1] >> (24 - i * 8)) & 0x000000ff; 145 | hash[i + 8] = (ctx->state[2] >> (24 - i * 8)) & 0x000000ff; 146 | hash[i + 12] = (ctx->state[3] >> (24 - i * 8)) & 0x000000ff; 147 | hash[i + 16] = (ctx->state[4] >> (24 - i * 8)) & 0x000000ff; 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /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 | 146 | void warning(const char *msg) { 147 | SceMsgDialogUserMessageParam msg_param; 148 | sceClibMemset(&msg_param, 0, sizeof(SceMsgDialogUserMessageParam)); 149 | msg_param.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK; 150 | msg_param.msg = (const SceChar8*)msg; 151 | SceMsgDialogParam param; 152 | sceMsgDialogParamInit(¶m); 153 | param.mode = SCE_MSG_DIALOG_MODE_USER_MSG; 154 | param.userMsgParam = &msg_param; 155 | sceMsgDialogInit(¶m); 156 | while (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) { 157 | vglSwapBuffers(GL_TRUE); 158 | } 159 | sceMsgDialogTerm(); 160 | } 161 | -------------------------------------------------------------------------------- /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 | 12 | #include 13 | #include 14 | #include 15 | 16 | #include "main.h" 17 | #include "dialog.h" 18 | #include "so_util.h" 19 | 20 | #ifndef SCE_KERNEL_MEMBLOCK_TYPE_USER_RX 21 | #define SCE_KERNEL_MEMBLOCK_TYPE_USER_RX (0x0C20D050) 22 | #endif 23 | 24 | typedef struct b_enc { 25 | union { 26 | struct __attribute__((__packed__)) { 27 | int imm24: 24; 28 | unsigned int l: 1; // Branch with Link flag 29 | unsigned int enc: 3; // 0b101 30 | unsigned int cond: 4; // 0b1110 31 | } bits; 32 | uint32_t raw; 33 | }; 34 | } b_enc; 35 | 36 | typedef struct ldst_enc { 37 | union { 38 | struct __attribute__((__packed__)) { 39 | int imm12: 12; 40 | unsigned int rt: 4; // Source/Destination register 41 | unsigned int rn: 4; // Base register 42 | unsigned int bit20_1: 1; // 0: store to memory, 1: load from memory 43 | unsigned int w: 1; // 0: no write-back, 1: write address into base 44 | unsigned int b: 1; // 0: word, 1: byte 45 | unsigned int u: 1; // 0: subtract offset from base, 1: add to base 46 | unsigned int p: 1; // 0: post indexing, 1: pre indexing 47 | unsigned int enc: 3; 48 | unsigned int cond: 4; 49 | } bits; 50 | uint32_t raw; 51 | }; 52 | } ldst_enc; 53 | 54 | #define B_RANGE ((1 << 24) - 1) 55 | #define B_OFFSET(x) (x + 8) // branch jumps into addr - 8, so range is biased forward 56 | #define B(PC, DEST) ((b_enc){.bits = {.cond = 0b1110, .enc = 0b101, .l = 0, .imm24 = (((intptr_t)DEST-(intptr_t)PC) / 4) - 2}}) 57 | #define LDR_OFFS(RT, RN, IMM) ((ldst_enc){.bits = {.cond = 0b1110, .enc = 0b010, .p = 1, .u = (IMM >= 0), .b = 0, .w = 0, .bit20_1 = 1, .rn = RN, .rt = RT, .imm12 = (IMM >= 0) ? IMM : -IMM}}) 58 | 59 | #define PATCH_SZ 0x10000 //64 KB-ish arenas 60 | static so_module *head = NULL, *tail = NULL; 61 | 62 | so_hook hook_thumb(uintptr_t addr, uintptr_t dst) { 63 | so_hook h; 64 | printf("THUMB HOOK\n"); 65 | if (addr == 0) 66 | return; 67 | h.thumb_addr = addr; 68 | addr &= ~1; 69 | if (addr & 2) { 70 | uint16_t nop = 0xbf00; 71 | kuKernelCpuUnrestrictedMemcpy((void *)addr, &nop, sizeof(nop)); 72 | addr += 2; 73 | printf("THUMB UNALIGNED\n"); 74 | } 75 | 76 | h.addr = addr; 77 | h.patch_instr[0] = 0xf000f8df; // LDR PC, [PC] 78 | h.patch_instr[1] = dst; 79 | kuKernelCpuUnrestrictedMemcpy(&h.orig_instr, (void *)addr, sizeof(h.orig_instr)); 80 | kuKernelCpuUnrestrictedMemcpy((void *)addr, h.patch_instr, sizeof(h.patch_instr)); 81 | 82 | return h; 83 | } 84 | 85 | so_hook hook_arm(uintptr_t addr, uintptr_t dst) { 86 | printf("ARM HOOK\n"); 87 | if (addr == 0) 88 | return; 89 | uint32_t hook[2]; 90 | so_hook h; 91 | h.thumb_addr = 0; 92 | h.addr = addr; 93 | h.patch_instr[0] = 0xe51ff004; // LDR PC, [PC, #-0x4] 94 | h.patch_instr[1] = dst; 95 | kuKernelCpuUnrestrictedMemcpy(&h.orig_instr, (void *)addr, sizeof(h.orig_instr)); 96 | kuKernelCpuUnrestrictedMemcpy((void *)addr, h.patch_instr, sizeof(h.patch_instr)); 97 | 98 | return h; 99 | } 100 | 101 | so_hook hook_addr(uintptr_t addr, uintptr_t dst) { 102 | if (addr == 0) 103 | return; 104 | if (addr & 1) 105 | return hook_thumb(addr, dst); 106 | else 107 | return hook_arm(addr, dst); 108 | } 109 | 110 | void so_flush_caches(so_module *mod) { 111 | kuKernelFlushCaches((void *)mod->text_base, mod->text_size); 112 | } 113 | 114 | int _so_load(so_module *mod, SceUID so_blockid, void *so_data, uintptr_t load_addr) { 115 | int res = 0; 116 | uintptr_t data_addr = 0; 117 | 118 | if (memcmp(so_data, ELFMAG, SELFMAG) != 0) { 119 | res = -1; 120 | goto err_free_so; 121 | } 122 | 123 | mod->ehdr = (Elf32_Ehdr *)so_data; 124 | mod->phdr = (Elf32_Phdr *)((uintptr_t)so_data + mod->ehdr->e_phoff); 125 | mod->shdr = (Elf32_Shdr *)((uintptr_t)so_data + mod->ehdr->e_shoff); 126 | 127 | mod->shstr = (char *)((uintptr_t)so_data + mod->shdr[mod->ehdr->e_shstrndx].sh_offset); 128 | 129 | for (int i = 0; i < mod->ehdr->e_phnum; i++) { 130 | if (mod->phdr[i].p_type == PT_LOAD) { 131 | void *prog_data; 132 | size_t prog_size; 133 | 134 | if ((mod->phdr[i].p_flags & PF_X) == PF_X) { 135 | // Allocate arena for code patches, trampolines, etc 136 | // Sits exactly under the desired allocation space 137 | mod->patch_size = ALIGN_MEM(PATCH_SZ, mod->phdr[i].p_align); 138 | SceKernelAllocMemBlockKernelOpt opt; 139 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 140 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 141 | opt.attr = 0x1; 142 | opt.field_C = (SceUInt32)load_addr - mod->patch_size; 143 | res = mod->patch_blockid = kuKernelAllocMemBlock("rx_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RX, mod->patch_size, &opt); 144 | if (res < 0) 145 | goto err_free_so; 146 | 147 | sceKernelGetMemBlockBase(mod->patch_blockid, &mod->patch_base); 148 | mod->patch_head = mod->patch_base; 149 | 150 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz, mod->phdr[i].p_align); 151 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 152 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 153 | opt.attr = 0x1; 154 | opt.field_C = (SceUInt32)load_addr; 155 | res = mod->text_blockid = kuKernelAllocMemBlock("rx_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RX, prog_size, &opt); 156 | if (res < 0) 157 | goto err_free_so; 158 | 159 | sceKernelGetMemBlockBase(mod->text_blockid, &prog_data); 160 | 161 | mod->phdr[i].p_vaddr += (Elf32_Addr)prog_data; 162 | 163 | mod->text_base = mod->phdr[i].p_vaddr; 164 | mod->text_size = mod->phdr[i].p_memsz; 165 | 166 | // Use the .text segment padding as a code cave 167 | // Word-align it to make it simpler for instruction arena allocation 168 | mod->cave_size = ALIGN_MEM(prog_size - mod->phdr[i].p_memsz, 0x4); 169 | mod->cave_base = mod->cave_head = prog_data + mod->phdr[i].p_memsz; 170 | mod->cave_base = ALIGN_MEM(mod->cave_base, 0x4); 171 | mod->cave_head = mod->cave_base; 172 | printf("code cave: %d bytes (@0x%08X).\n", mod->cave_size, mod->cave_base); 173 | 174 | data_addr = (uintptr_t)prog_data + prog_size; 175 | } else { 176 | if (data_addr == 0) 177 | goto err_free_so; 178 | 179 | if (mod->n_data >= MAX_DATA_SEG) 180 | goto err_free_data; 181 | 182 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz + mod->phdr[i].p_vaddr - (data_addr - mod->text_base), mod->phdr[i].p_align); 183 | 184 | SceKernelAllocMemBlockKernelOpt opt; 185 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 186 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 187 | opt.attr = 0x1; 188 | opt.field_C = (SceUInt32)data_addr; 189 | res = mod->data_blockid[mod->n_data] = kuKernelAllocMemBlock("rw_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, prog_size, &opt); 190 | if (res < 0) 191 | goto err_free_text; 192 | 193 | sceKernelGetMemBlockBase(mod->data_blockid[mod->n_data], &prog_data); 194 | data_addr = (uintptr_t)prog_data + prog_size; 195 | 196 | mod->phdr[i].p_vaddr += (Elf32_Addr)mod->text_base; 197 | 198 | mod->data_base[mod->n_data] = mod->phdr[i].p_vaddr; 199 | mod->data_size[mod->n_data] = mod->phdr[i].p_memsz; 200 | mod->n_data++; 201 | } 202 | 203 | char *zero = malloc(prog_size - mod->phdr[i].p_filesz); 204 | memset(zero, 0, prog_size - mod->phdr[i].p_filesz); 205 | kuKernelCpuUnrestrictedMemcpy(prog_data + mod->phdr[i].p_filesz, zero, prog_size - mod->phdr[i].p_filesz); 206 | free(zero); 207 | 208 | kuKernelCpuUnrestrictedMemcpy((void *)mod->phdr[i].p_vaddr, (void *)((uintptr_t)so_data + mod->phdr[i].p_offset), mod->phdr[i].p_filesz); 209 | } 210 | } 211 | 212 | for (int i = 0; i < mod->ehdr->e_shnum; i++) { 213 | char *sh_name = mod->shstr + mod->shdr[i].sh_name; 214 | uintptr_t sh_addr = mod->text_base + mod->shdr[i].sh_addr; 215 | size_t sh_size = mod->shdr[i].sh_size; 216 | if (strcmp(sh_name, ".dynamic") == 0) { 217 | mod->dynamic = (Elf32_Dyn *)sh_addr; 218 | mod->num_dynamic = sh_size / sizeof(Elf32_Dyn); 219 | } else if (strcmp(sh_name, ".dynstr") == 0) { 220 | mod->dynstr = (char *)sh_addr; 221 | } else if (strcmp(sh_name, ".dynsym") == 0) { 222 | mod->dynsym = (Elf32_Sym *)sh_addr; 223 | mod->num_dynsym = sh_size / sizeof(Elf32_Sym); 224 | } else if (strcmp(sh_name, ".rel.dyn") == 0) { 225 | mod->reldyn = (Elf32_Rel *)sh_addr; 226 | mod->num_reldyn = sh_size / sizeof(Elf32_Rel); 227 | } else if (strcmp(sh_name, ".rel.plt") == 0) { 228 | mod->relplt = (Elf32_Rel *)sh_addr; 229 | mod->num_relplt = sh_size / sizeof(Elf32_Rel); 230 | } else if (strcmp(sh_name, ".init_array") == 0) { 231 | mod->init_array = (void *)sh_addr; 232 | mod->num_init_array = sh_size / sizeof(void *); 233 | } else if (strcmp(sh_name, ".hash") == 0) { 234 | mod->hash = (void *)sh_addr; 235 | } 236 | } 237 | 238 | if (mod->dynamic == NULL || 239 | mod->dynstr == NULL || 240 | mod->dynsym == NULL || 241 | mod->reldyn == NULL || 242 | mod->relplt == NULL) { 243 | res = -2; 244 | goto err_free_data; 245 | } 246 | 247 | for (int i = 0; i < mod->num_dynamic; i++) { 248 | switch (mod->dynamic[i].d_tag) { 249 | case DT_SONAME: 250 | mod->soname = mod->dynstr + mod->dynamic[i].d_un.d_ptr; 251 | break; 252 | default: 253 | break; 254 | } 255 | } 256 | 257 | sceKernelFreeMemBlock(so_blockid); 258 | 259 | if (!head && !tail) { 260 | head = mod; 261 | tail = mod; 262 | } else { 263 | tail->next = mod; 264 | tail = mod; 265 | } 266 | 267 | return 0; 268 | 269 | err_free_data: 270 | for (int i = 0; i < mod->n_data; i++) 271 | sceKernelFreeMemBlock(mod->data_blockid[i]); 272 | err_free_text: 273 | sceKernelFreeMemBlock(mod->text_blockid); 274 | err_free_so: 275 | sceKernelFreeMemBlock(so_blockid); 276 | 277 | return res; 278 | } 279 | 280 | int so_mem_load(so_module *mod, void *buffer, size_t so_size, uintptr_t load_addr) { 281 | SceUID so_blockid; 282 | void *so_data; 283 | 284 | memset(mod, 0, sizeof(so_module)); 285 | 286 | so_blockid = sceKernelAllocMemBlock("so block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, (so_size + 0xfff) & ~0xfff, NULL); 287 | if (so_blockid < 0) 288 | return so_blockid; 289 | 290 | sceKernelGetMemBlockBase(so_blockid, &so_data); 291 | sceClibMemcpy(so_data, buffer, so_size); 292 | 293 | return _so_load(mod, so_blockid, so_data, load_addr); 294 | } 295 | 296 | int so_file_load(so_module *mod, const char *filename, uintptr_t load_addr) { 297 | SceUID so_blockid; 298 | void *so_data; 299 | 300 | memset(mod, 0, sizeof(so_module)); 301 | 302 | SceUID fd = sceIoOpen(filename, SCE_O_RDONLY, 0); 303 | if (fd < 0) 304 | return fd; 305 | 306 | size_t so_size = sceIoLseek(fd, 0, SCE_SEEK_END); 307 | sceIoLseek(fd, 0, SCE_SEEK_SET); 308 | 309 | so_blockid = sceKernelAllocMemBlock("so block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, (so_size + 0xfff) & ~0xfff, NULL); 310 | if (so_blockid < 0) 311 | return so_blockid; 312 | 313 | sceKernelGetMemBlockBase(so_blockid, &so_data); 314 | 315 | sceIoRead(fd, so_data, so_size); 316 | sceIoClose(fd); 317 | 318 | return _so_load(mod, so_blockid, so_data, load_addr); 319 | } 320 | 321 | int so_relocate(so_module *mod) { 322 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 323 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 324 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 325 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 326 | 327 | int type = ELF32_R_TYPE(rel->r_info); 328 | switch (type) { 329 | case R_ARM_ABS32: 330 | if (sym->st_shndx != SHN_UNDEF) 331 | *ptr += mod->text_base + sym->st_value; 332 | break; 333 | case R_ARM_RELATIVE: 334 | *ptr += mod->text_base; 335 | break; 336 | case R_ARM_GLOB_DAT: 337 | case R_ARM_JUMP_SLOT: 338 | { 339 | if (sym->st_shndx != SHN_UNDEF) 340 | *ptr = mod->text_base + sym->st_value; 341 | break; 342 | } 343 | default: 344 | fatal_error("Error unknown relocation type %x\n", type); 345 | break; 346 | } 347 | } 348 | 349 | return 0; 350 | } 351 | 352 | uintptr_t so_resolve_link(so_module *mod, const char *symbol) { 353 | for (int i = 0; i < mod->num_dynamic; i++) { 354 | switch (mod->dynamic[i].d_tag) { 355 | case DT_NEEDED: 356 | { 357 | so_module *curr = head; 358 | while (curr) { 359 | if (curr != mod && strcmp(curr->soname, mod->dynstr + mod->dynamic[i].d_un.d_ptr) == 0) { 360 | uintptr_t link = so_symbol(curr, symbol); 361 | if (link) 362 | return link; 363 | } 364 | curr = curr->next; 365 | } 366 | 367 | break; 368 | } 369 | default: 370 | break; 371 | } 372 | } 373 | 374 | return 0; 375 | } 376 | 377 | void reloc_err(uintptr_t got0) 378 | { 379 | // Find to which module this missing symbol belongs 380 | int found = 0; 381 | so_module *curr = head; 382 | while (curr && !found) { 383 | for (int i = 0; i < curr->n_data; i++) 384 | if ((got0 >= curr->data_base[i]) && (got0 <= (uintptr_t)(curr->data_base[i] + curr->data_size[i]))) 385 | found = 1; 386 | 387 | if (!found) 388 | curr = curr->next; 389 | } 390 | 391 | if (curr) { 392 | // Attempt to find symbol name and then display error 393 | for (int i = 0; i < curr->num_reldyn + curr->num_relplt; i++) { 394 | Elf32_Rel *rel = i < curr->num_reldyn ? &curr->reldyn[i] : &curr->relplt[i - curr->num_reldyn]; 395 | Elf32_Sym *sym = &curr->dynsym[ELF32_R_SYM(rel->r_info)]; 396 | uintptr_t *ptr = (uintptr_t *)(curr->text_base + rel->r_offset); 397 | 398 | int type = ELF32_R_TYPE(rel->r_info); 399 | switch (type) { 400 | case R_ARM_JUMP_SLOT: 401 | { 402 | if (got0 == (uintptr_t)ptr) { 403 | fatal_error("Unknown symbol \"%s\" (%p).\n", curr->dynstr + sym->st_name, (void*)got0); 404 | } 405 | break; 406 | } 407 | } 408 | } 409 | } 410 | 411 | // Ooops, this shouldn't have happened. 412 | fatal_error("Unknown symbol \"???\" (%p).\n", (void*)got0); 413 | } 414 | 415 | __attribute__((naked)) void plt0_stub() 416 | { 417 | register uintptr_t got0 asm("r12"); 418 | reloc_err(got0); 419 | } 420 | 421 | int so_resolve(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only) { 422 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 423 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 424 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 425 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 426 | 427 | int type = ELF32_R_TYPE(rel->r_info); 428 | switch (type) { 429 | case R_ARM_ABS32: 430 | case R_ARM_GLOB_DAT: 431 | case R_ARM_JUMP_SLOT: 432 | { 433 | if (sym->st_shndx == SHN_UNDEF) { 434 | int resolved = 0; 435 | if (!default_dynlib_only) { 436 | uintptr_t link = so_resolve_link(mod, mod->dynstr + sym->st_name); 437 | if (link) { 438 | // debugPrintf("Resolved from dependencies: %s\n", mod->dynstr + sym->st_name); 439 | if (type == R_ARM_ABS32) 440 | *ptr += link; 441 | else 442 | *ptr = link; 443 | resolved = 1; 444 | } 445 | } 446 | 447 | for (int j = 0; j < size_default_dynlib / sizeof(so_default_dynlib); j++) { 448 | if (strcmp(mod->dynstr + sym->st_name, default_dynlib[j].symbol) == 0) { 449 | *ptr = default_dynlib[j].func; 450 | resolved = 1; 451 | break; 452 | } 453 | } 454 | 455 | if (!resolved) { 456 | void *f = vglGetProcAddress(mod->dynstr + sym->st_name); 457 | if (f) { 458 | *ptr = f; 459 | resolved = 1; 460 | break; 461 | } 462 | } 463 | 464 | if (!resolved) { 465 | if (type == R_ARM_JUMP_SLOT) { 466 | printf("Unresolved import: %s\n", mod->dynstr + sym->st_name); 467 | *ptr = (uintptr_t)&plt0_stub; 468 | } 469 | else { 470 | //printf("Unresolved import: %s\n", mod->dynstr + sym->st_name); 471 | } 472 | } 473 | } 474 | 475 | break; 476 | } 477 | default: 478 | break; 479 | } 480 | } 481 | 482 | return 0; 483 | } 484 | 485 | int so_resolve_with_dummy(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only) { 486 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 487 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 488 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 489 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 490 | 491 | int type = ELF32_R_TYPE(rel->r_info); 492 | switch (type) { 493 | case R_ARM_ABS32: 494 | case R_ARM_GLOB_DAT: 495 | case R_ARM_JUMP_SLOT: 496 | { 497 | if (sym->st_shndx == SHN_UNDEF) { 498 | for (int j = 0; j < size_default_dynlib / sizeof(so_default_dynlib); j++) { 499 | if (strcmp(mod->dynstr + sym->st_name, default_dynlib[j].symbol) == 0) { 500 | *ptr = &ret0; 501 | break; 502 | } 503 | } 504 | } 505 | 506 | break; 507 | } 508 | default: 509 | break; 510 | } 511 | } 512 | 513 | return 0; 514 | } 515 | 516 | void so_initialize(so_module *mod) { 517 | for (int i = 0; i < mod->num_init_array; i++) { 518 | if (mod->init_array[i]) 519 | mod->init_array[i](); 520 | } 521 | } 522 | 523 | uint32_t so_hash(const uint8_t *name) { 524 | uint64_t h = 0, g; 525 | while (*name) { 526 | h = (h << 4) + *name++; 527 | if ((g = (h & 0xf0000000)) != 0) 528 | h ^= g >> 24; 529 | h &= 0x0fffffff; 530 | } 531 | return h; 532 | } 533 | 534 | static int so_symbol_index(so_module *mod, const char *symbol) 535 | { 536 | if (mod->hash) { 537 | uint32_t hash = so_hash((const uint8_t *)symbol); 538 | uint32_t nbucket = mod->hash[0]; 539 | uint32_t *bucket = &mod->hash[2]; 540 | uint32_t *chain = &bucket[nbucket]; 541 | for (int i = bucket[hash % nbucket]; i; i = chain[i]) { 542 | if (mod->dynsym[i].st_shndx == SHN_UNDEF) 543 | continue; 544 | if (mod->dynsym[i].st_info != SHN_UNDEF && strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 545 | return i; 546 | } 547 | } 548 | 549 | for (int i = 0; i < mod->num_dynsym; i++) { 550 | if (mod->dynsym[i].st_shndx == SHN_UNDEF) 551 | continue; 552 | if (mod->dynsym[i].st_info != SHN_UNDEF && strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 553 | return i; 554 | } 555 | 556 | return -1; 557 | } 558 | 559 | /* 560 | * alloc_arena: allocates space on either patch or cave arenas, 561 | * range: maximum range from allocation to dst (ignored if NULL) 562 | * dst: destination address 563 | */ 564 | static uintptr_t so_alloc_arena(so_module *so, uintptr_t range, uintptr_t dst, size_t sz) { 565 | // Is address in range? 566 | #define inrange(lsr, gtr, range) \ 567 | (((uintptr_t)(range) == (uintptr_t)NULL) || ((uintptr_t)(range) >= ((uintptr_t)(gtr) - (uintptr_t)(lsr)))) 568 | // Space left on block 569 | #define blkavail(type) (so->type##_size - (so->type##_head - so->type##_base)) 570 | 571 | // keep allocations 4-byte aligned for simplicity 572 | sz = ALIGN_MEM(sz, 4); 573 | 574 | if (sz <= (blkavail(patch)) && inrange(so->patch_base, dst, range)) { 575 | so->patch_head += sz; 576 | return (so->patch_head - sz); 577 | } else if (sz <= (blkavail(cave)) && inrange(dst, so->cave_base, range)) { 578 | so->cave_head += sz; 579 | return (so->cave_head - sz); 580 | } 581 | 582 | return (uintptr_t)NULL; 583 | } 584 | 585 | static void trampoline_ldm(so_module *mod, uint32_t *dst) { 586 | uint32_t trampoline[1]; 587 | uint32_t funct[20] = {0xFAFAFAFA}; 588 | uint32_t *ptr = funct; 589 | 590 | int cur = 0; 591 | int baseReg = ((*dst) >> 16) & 0xF; 592 | int bitMask = (*dst) & 0xFFFF; 593 | 594 | uint32_t stored = NULL; 595 | for (int i = 0; i < 16; i++) { 596 | if (bitMask & (1 << i)) { 597 | // If the register we're reading the offset from is the same as the one we're writing, 598 | // delay it to the very end so that the base pointer ins't clobbered 599 | if (baseReg == i) 600 | stored = LDR_OFFS(i, baseReg, cur).raw; 601 | else 602 | *ptr++ = LDR_OFFS(i, baseReg, cur).raw; 603 | cur += 4; 604 | } 605 | } 606 | 607 | // Perform the delayed load if needed 608 | if (stored) { 609 | *ptr++ = stored; 610 | } 611 | 612 | *ptr++ = 0xe51ff004; // LDR PC, [PC, -0x4] ; jmp to [dst+0x4] 613 | *ptr++ = dst+1; // .dword <...> ; [dst+0x4] 614 | 615 | size_t trampoline_sz = ((uintptr_t)ptr - (uintptr_t)&funct[0]); 616 | uintptr_t patch_addr = so_alloc_arena(mod, B_RANGE, B_OFFSET(dst), trampoline_sz); 617 | 618 | if (!patch_addr) { 619 | fatal_error("Failed to patch LDMIA at 0x%08X, unable to allocate space.\n", dst); 620 | } 621 | 622 | // Create sign extended relative address rel_addr 623 | trampoline[0] = B(dst, patch_addr).raw; 624 | 625 | kuKernelCpuUnrestrictedMemcpy((void*)patch_addr, funct, trampoline_sz); 626 | kuKernelCpuUnrestrictedMemcpy(dst, trampoline, sizeof(trampoline)); 627 | } 628 | 629 | uintptr_t so_symbol(so_module *mod, const char *symbol) { 630 | int index = so_symbol_index(mod, symbol); 631 | if (index == -1) 632 | return NULL; 633 | 634 | return mod->text_base + mod->dynsym[index].st_value; 635 | } 636 | 637 | void so_symbol_fix_ldmia(so_module *mod, const char *symbol) { 638 | // This is meant to work around crashes due to unaligned accesses (SIGBUS :/) due to certain 639 | // kernels not having the fault trap enabled, e.g. certain RK3326 Odroid Go Advance clone distros. 640 | // TODO:: Maybe enable this only with a config flag? maybe with a list of known broken functions? 641 | // Known to trigger on GM:S's "_Z11Shader_LoadPhjS_" - if it starts happening on other places, 642 | // might be worth enabling it globally. 643 | 644 | int idx = so_symbol_index(mod, symbol); 645 | if (idx == -1) 646 | return; 647 | 648 | uintptr_t st_addr = mod->text_base + mod->dynsym[idx].st_value; 649 | for (uintptr_t addr = st_addr; addr < st_addr + mod->dynsym[idx].st_size; addr+=4) { 650 | uint32_t inst = *(uint32_t*)(addr); 651 | 652 | //Is this an LDMIA instruction with a R0-R12 base register? 653 | if (((inst & 0xFFF00000) == 0xE8900000) && (((inst >> 16) & 0xF) < 13) ) { 654 | debugPrintf("Found possibly misaligned LDMIA on 0x%08X, trying to fix it... (instr: 0x%08X, to 0x%08X)\n", addr, *(uint32_t*)addr, mod->patch_head); 655 | trampoline_ldm(mod, addr); 656 | } 657 | } 658 | } 659 | -------------------------------------------------------------------------------- /loader/main.c: -------------------------------------------------------------------------------- 1 | /* main.c -- Super Monkey Ball 2: Sakura Edition .so loader 2 | * 3 | * Copyright (C) 2021 Andy Nguyen 4 | * Copyright (C) 2023 Rinnegatamante 5 | * 6 | * This software may be modified and distributed under the terms 7 | * of the MIT license. See the LICENSE file for details. 8 | */ 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #define AL_ALEXT_PROTOTYPES 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | #include "main.h" 40 | #include "config.h" 41 | #include "dialog.h" 42 | #include "so_util.h" 43 | #include "sha1.h" 44 | #include "trophies.h" 45 | 46 | //#define ENABLE_DEBUG 47 | 48 | void audio_player_init(); 49 | void *audio_load_sound(char *fname); 50 | void audio_queue_music(char *fname, int loopcount); 51 | void audio_stop_music(); 52 | void audio_set_music_volume(float volume); 53 | void audio_remove_sound(int id); 54 | int audio_play_sound(int id, float volume, float pitch, int loopcount); 55 | void audio_set_volume(int id, float volume); 56 | void audio_set_pitch(int id, float pitch); 57 | 58 | typedef struct { 59 | unsigned char *elements; 60 | int size; 61 | } jni_bytearray; 62 | 63 | static char fake_vm[0x1000]; 64 | static char fake_env[0x1000]; 65 | 66 | int _newlib_heap_size_user = MEMORY_NEWLIB_MB * 1024 * 1024; 67 | 68 | unsigned int _pthread_stack_default_user = 1 * 1024 * 1024; 69 | 70 | so_module smb2_mod; 71 | 72 | void *__wrap_memcpy(void *dest, const void *src, size_t n) { 73 | return sceClibMemcpy(dest, src, n); 74 | } 75 | 76 | void *__wrap_memmove(void *dest, const void *src, size_t n) { 77 | return sceClibMemmove(dest, src, n); 78 | } 79 | 80 | void *__wrap_memset(void *s, int c, size_t n) { 81 | return sceClibMemset(s, c, n); 82 | } 83 | 84 | int debugPrintf(char *fmt, ...) { 85 | #ifdef ENABLE_DEBUG 86 | va_list list; 87 | static char string[0x8000]; 88 | 89 | va_start(list, fmt); 90 | vsprintf(string, fmt, list); 91 | va_end(list); 92 | 93 | printf("[DBG] %s\n", string); 94 | #endif 95 | return 0; 96 | } 97 | 98 | int __android_log_print(int prio, const char *tag, const char *fmt, ...) { 99 | #ifdef ENABLE_DEBUG 100 | va_list list; 101 | static char string[0x8000]; 102 | 103 | va_start(list, fmt); 104 | vsprintf(string, fmt, list); 105 | va_end(list); 106 | 107 | printf("[LOG] %s: %s\n", tag, string); 108 | #endif 109 | return 0; 110 | } 111 | 112 | int __android_log_vprint(int prio, const char *tag, const char *fmt, va_list list) { 113 | #ifdef ENABLE_DEBUG 114 | static char string[0x8000]; 115 | 116 | vsprintf(string, fmt, list); 117 | va_end(list); 118 | 119 | printf("[LOGV] %s: %s\n", tag, string); 120 | #endif 121 | return 0; 122 | } 123 | 124 | int ret0(void) { 125 | return 0; 126 | } 127 | 128 | int ret1(void) { 129 | return 1; 130 | } 131 | 132 | int clock_gettime(int clk_ik, struct timespec *t) { 133 | struct timeval now; 134 | int rv = gettimeofday(&now, NULL); 135 | if (rv) 136 | return rv; 137 | t->tv_sec = now.tv_sec; 138 | t->tv_nsec = now.tv_usec * 1000; 139 | return 0; 140 | } 141 | 142 | int pthread_mutex_init_fake(pthread_mutex_t **uid, 143 | const pthread_mutexattr_t *mutexattr) { 144 | pthread_mutex_t *m = calloc(1, sizeof(pthread_mutex_t)); 145 | if (!m) 146 | return -1; 147 | 148 | const int recursive = (mutexattr && *(const int *)mutexattr == 1); 149 | *m = recursive ? PTHREAD_RECURSIVE_MUTEX_INITIALIZER 150 | : PTHREAD_MUTEX_INITIALIZER; 151 | 152 | int ret = pthread_mutex_init(m, mutexattr); 153 | if (ret < 0) { 154 | free(m); 155 | return -1; 156 | } 157 | 158 | *uid = m; 159 | 160 | return 0; 161 | } 162 | 163 | int pthread_mutex_destroy_fake(pthread_mutex_t **uid) { 164 | if (uid && *uid && (uintptr_t)*uid > 0x8000) { 165 | pthread_mutex_destroy(*uid); 166 | free(*uid); 167 | *uid = NULL; 168 | } 169 | return 0; 170 | } 171 | 172 | int pthread_mutex_lock_fake(pthread_mutex_t **uid) { 173 | int ret = 0; 174 | if (!*uid) { 175 | ret = pthread_mutex_init_fake(uid, NULL); 176 | } else if ((uintptr_t)*uid == 0x4000) { 177 | pthread_mutexattr_t attr; 178 | pthread_mutexattr_init(&attr); 179 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 180 | ret = pthread_mutex_init_fake(uid, &attr); 181 | pthread_mutexattr_destroy(&attr); 182 | } else if ((uintptr_t)*uid == 0x8000) { 183 | pthread_mutexattr_t attr; 184 | pthread_mutexattr_init(&attr); 185 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 186 | ret = pthread_mutex_init_fake(uid, &attr); 187 | pthread_mutexattr_destroy(&attr); 188 | } 189 | if (ret < 0) 190 | return ret; 191 | return pthread_mutex_lock(*uid); 192 | } 193 | 194 | int pthread_mutex_unlock_fake(pthread_mutex_t **uid) { 195 | int ret = 0; 196 | if (!*uid) { 197 | ret = pthread_mutex_init_fake(uid, NULL); 198 | } else if ((uintptr_t)*uid == 0x4000) { 199 | pthread_mutexattr_t attr; 200 | pthread_mutexattr_init(&attr); 201 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 202 | ret = pthread_mutex_init_fake(uid, &attr); 203 | pthread_mutexattr_destroy(&attr); 204 | } else if ((uintptr_t)*uid == 0x8000) { 205 | pthread_mutexattr_t attr; 206 | pthread_mutexattr_init(&attr); 207 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 208 | ret = pthread_mutex_init_fake(uid, &attr); 209 | pthread_mutexattr_destroy(&attr); 210 | } 211 | if (ret < 0) 212 | return ret; 213 | return pthread_mutex_unlock(*uid); 214 | } 215 | 216 | int pthread_cond_init_fake(pthread_cond_t **cnd, const int *condattr) { 217 | pthread_cond_t *c = calloc(1, sizeof(pthread_cond_t)); 218 | if (!c) 219 | return -1; 220 | 221 | *c = PTHREAD_COND_INITIALIZER; 222 | 223 | int ret = pthread_cond_init(c, NULL); 224 | if (ret < 0) { 225 | free(c); 226 | return -1; 227 | } 228 | 229 | *cnd = c; 230 | 231 | return 0; 232 | } 233 | 234 | int pthread_cond_broadcast_fake(pthread_cond_t **cnd) { 235 | if (!*cnd) { 236 | if (pthread_cond_init_fake(cnd, NULL) < 0) 237 | return -1; 238 | } 239 | return pthread_cond_broadcast(*cnd); 240 | } 241 | 242 | int pthread_cond_signal_fake(pthread_cond_t **cnd) { 243 | if (!*cnd) { 244 | if (pthread_cond_init_fake(cnd, NULL) < 0) 245 | return -1; 246 | } 247 | return pthread_cond_signal(*cnd); 248 | } 249 | 250 | int pthread_cond_destroy_fake(pthread_cond_t **cnd) { 251 | if (cnd && *cnd) { 252 | pthread_cond_destroy(*cnd); 253 | free(*cnd); 254 | *cnd = NULL; 255 | } 256 | return 0; 257 | } 258 | 259 | int pthread_cond_wait_fake(pthread_cond_t **cnd, pthread_mutex_t **mtx) { 260 | if (!*cnd) { 261 | if (pthread_cond_init_fake(cnd, NULL) < 0) 262 | return -1; 263 | } 264 | return pthread_cond_wait(*cnd, *mtx); 265 | } 266 | 267 | int pthread_cond_timedwait_fake(pthread_cond_t **cnd, pthread_mutex_t **mtx, 268 | const struct timespec *t) { 269 | if (!*cnd) { 270 | if (pthread_cond_init_fake(cnd, NULL) < 0) 271 | return -1; 272 | } 273 | return pthread_cond_timedwait(*cnd, *mtx, t); 274 | } 275 | 276 | int pthread_create_fake(pthread_t *thread, const void *unused, void *entry, 277 | void *arg) { 278 | return pthread_create(thread, NULL, entry, arg); 279 | } 280 | 281 | int pthread_once_fake(volatile int *once_control, void (*init_routine)(void)) { 282 | if (!once_control || !init_routine) 283 | return -1; 284 | if (__sync_lock_test_and_set(once_control, 1) == 0) 285 | (*init_routine)(); 286 | return 0; 287 | } 288 | 289 | int GetCurrentThreadId(void) { 290 | return sceKernelGetThreadId(); 291 | } 292 | 293 | extern void *__aeabi_ldiv0; 294 | 295 | int GetEnv(void *vm, void **env, int r2) { 296 | *env = fake_env; 297 | return 0; 298 | } 299 | 300 | void *GetJNIEnv(void *this) { 301 | return fake_env; 302 | } 303 | 304 | void *accel_instance = NULL; 305 | so_hook accel_hook; 306 | void EnableAccelerometer(void *this) { 307 | accel_instance = this; 308 | SO_CONTINUE(int, accel_hook, this); 309 | } 310 | 311 | int SetMusicVolume(void *this, float vol) { 312 | audio_set_music_volume(vol); 313 | return 0; 314 | } 315 | 316 | int PlaySound(void *this, int unk, double duration, float volume, float pitch, int loop) { 317 | return audio_play_sound(this, volume, pitch, loop); 318 | } 319 | 320 | void SetVolume(void *this, int unk, float volume) { 321 | audio_set_volume(this, volume); 322 | } 323 | 324 | void SetPitch(void *this, int unk, float pitch) { 325 | audio_set_pitch(this, pitch); 326 | } 327 | 328 | void UnlockAchievement(void *this, int id) { 329 | trophies_unlock(id + 1); 330 | } 331 | 332 | void patch_game(void) { 333 | hook_addr(so_symbol(&smb2_mod, "_ZN5shark19AndroidJNIInterface14FlurryLogEventEPKcSt3mapISsSsSt4lessISsESaISt4pairIKSsSsEEE"), (uintptr_t)&ret0); 334 | hook_addr(so_symbol(&smb2_mod, "_ZN7android9GetJNIEnvEv"), (uintptr_t)&GetJNIEnv); 335 | hook_addr(so_symbol(&smb2_mod, "_ZN7android16LogJavaExceptionEb"), (uintptr_t)&ret0); 336 | hook_addr(so_symbol(&smb2_mod, "_ZN17GameCircleWrapper8IsAmazonEv"), (uintptr_t)&ret0); 337 | hook_addr(so_symbol(&smb2_mod, "_ZN5shark19AndroidJNIInterface14SetMusicVolumeEf"), (uintptr_t)&SetMusicVolume); 338 | hook_addr(so_symbol(&smb2_mod, "_ZN5shark19AndroidJNIInterface9PlaySoundEidffb"), (uintptr_t)&PlaySound); 339 | accel_hook = hook_addr(so_symbol(&smb2_mod, "_ZN2io13Accelerometer6EnableEv"), (uintptr_t)&EnableAccelerometer); 340 | hook_addr(so_symbol(&smb2_mod, "_ZN5shark19AndroidJNIInterface9SetVolumeEif"), (uintptr_t)&SetVolume); 341 | hook_addr(so_symbol(&smb2_mod, "_ZN5shark19AndroidJNIInterface8SetPitchEif"), (uintptr_t)&SetPitch); 342 | hook_addr(so_symbol(&smb2_mod, "_ZN18AchievementManager17UnlockAchievementEi"), (uintptr_t)&UnlockAchievement); 343 | } 344 | 345 | extern void *__aeabi_atexit; 346 | extern void *__aeabi_idiv; 347 | extern void *__aeabi_idivmod; 348 | extern void *__aeabi_ldivmod; 349 | extern void *__aeabi_uidiv; 350 | extern void *__aeabi_uidivmod; 351 | extern void *__aeabi_uldivmod; 352 | extern void *__cxa_atexit; 353 | extern void *__cxa_finalize; 354 | extern void *__gnu_unwind_frame; 355 | extern void *__stack_chk_fail; 356 | int open(const char *pathname, int flags); 357 | 358 | static int __stack_chk_guard_fake = 0x42424242; 359 | 360 | static char *__ctype_ = (char *)&_ctype_; 361 | 362 | static FILE __sF_fake[0x100][3]; 363 | 364 | int stat_hook(const char *pathname, void *statbuf) { 365 | char real_fname[128]; 366 | sprintf(real_fname, "%s.mp3", pathname); 367 | 368 | struct stat st; 369 | int res = stat(real_fname, &st); 370 | if (res == 0) 371 | *(uint64_t *)(statbuf + 0x30) = st.st_size; 372 | return res; 373 | } 374 | 375 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, 376 | off_t offset) { 377 | return malloc(length); 378 | } 379 | 380 | int munmap(void *addr, size_t length) { 381 | free(addr); 382 | return 0; 383 | } 384 | 385 | FILE *fopen_hook(char *fname, char *mode) { 386 | //printf("opening %s\n", fname); 387 | return fopen(fname, mode); 388 | } 389 | 390 | int open_hook(const char *fname, int flags) { 391 | return open(fname, flags); 392 | } 393 | 394 | int fstat_hook(int fd, void *statbuf) { 395 | struct stat st; 396 | int res = fstat(fd, &st); 397 | if (res == 0) 398 | *(uint64_t *)(statbuf + 0x30) = st.st_size; 399 | return res; 400 | } 401 | 402 | int skip_next_attrib_array = GL_FALSE; 403 | void glVertexAttribPointer_hook(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *pointer) { 404 | if (type == GL_FIXED) { 405 | skip_next_attrib_array = GL_TRUE; 406 | glDisableVertexAttribArray(index); 407 | } else 408 | glVertexAttribPointer(index, size, type, normalized, stride, pointer); 409 | } 410 | 411 | void glEnableVertexAttribArray_hook(GLuint index) { 412 | if (!skip_next_attrib_array) 413 | glEnableVertexAttribArray(index); 414 | else 415 | skip_next_attrib_array = GL_FALSE; 416 | } 417 | 418 | static so_default_dynlib default_dynlib[] = { 419 | { "__aeabi_atexit", (uintptr_t)&__aeabi_atexit }, 420 | { "__aeabi_uidiv", (uintptr_t)&__aeabi_uidiv }, 421 | { "__aeabi_uidivmod", (uintptr_t)&__aeabi_uidivmod }, 422 | { "__aeabi_idiv", (uintptr_t)&__aeabi_idiv }, 423 | { "__aeabi_idivmod", (uintptr_t)&__aeabi_idivmod }, 424 | { "__android_log_print", (uintptr_t)&__android_log_print }, 425 | { "__android_log_vprint", (uintptr_t)&__android_log_vprint }, 426 | { "__cxa_atexit", (uintptr_t)&__cxa_atexit }, 427 | { "__cxa_finalize", (uintptr_t)&__cxa_finalize }, 428 | { "__errno", (uintptr_t)&__errno }, 429 | { "__gnu_unwind_frame", (uintptr_t)&__gnu_unwind_frame }, 430 | // { "__google_potentially_blocking_region_begin", (uintptr_t)&__google_potentially_blocking_region_begin }, 431 | // { "__google_potentially_blocking_region_end", (uintptr_t)&__google_potentially_blocking_region_end }, 432 | { "__sF", (uintptr_t)&__sF_fake }, 433 | { "__stack_chk_fail", (uintptr_t)&__stack_chk_fail }, 434 | { "__stack_chk_guard", (uintptr_t)&__stack_chk_guard_fake }, 435 | { "_ctype_", (uintptr_t)&__ctype_ }, 436 | { "abort", (uintptr_t)&abort }, 437 | // { "accept", (uintptr_t)&accept }, 438 | { "acos", (uintptr_t)&acos }, 439 | { "acosf", (uintptr_t)&acosf }, 440 | { "asin", (uintptr_t)&asin }, 441 | { "asinf", (uintptr_t)&asinf }, 442 | { "atan", (uintptr_t)&atan }, 443 | { "atan2", (uintptr_t)&atan2 }, 444 | { "atan2f", (uintptr_t)&atan2f }, 445 | { "atanf", (uintptr_t)&atanf }, 446 | { "atoi", (uintptr_t)&atoi }, 447 | { "atoll", (uintptr_t)&atoll }, 448 | // { "bind", (uintptr_t)&bind }, 449 | { "bsearch", (uintptr_t)&bsearch }, 450 | { "btowc", (uintptr_t)&btowc }, 451 | { "calloc", (uintptr_t)&calloc }, 452 | { "ceil", (uintptr_t)&ceil }, 453 | { "ceilf", (uintptr_t)&ceilf }, 454 | { "clearerr", (uintptr_t)&clearerr }, 455 | { "clock", (uintptr_t)&clock }, 456 | { "clock_gettime", (uintptr_t)&clock_gettime }, 457 | { "close", (uintptr_t)&close }, 458 | { "cos", (uintptr_t)&cos }, 459 | { "cosf", (uintptr_t)&cosf }, 460 | { "cosh", (uintptr_t)&cosh }, 461 | { "crc32", (uintptr_t)&crc32 }, 462 | { "difftime", (uintptr_t)&difftime }, 463 | { "div", (uintptr_t)&div }, 464 | { "dlopen", (uintptr_t)&ret0 }, 465 | { "exit", (uintptr_t)&exit }, 466 | { "exp", (uintptr_t)&exp }, 467 | { "expf", (uintptr_t)&expf }, 468 | { "fclose", (uintptr_t)&fclose }, 469 | { "fcntl", (uintptr_t)&ret0 }, 470 | { "fdopen", (uintptr_t)&fdopen }, 471 | { "ferror", (uintptr_t)&ferror }, 472 | { "fflush", (uintptr_t)&fflush }, 473 | { "fgets", (uintptr_t)&fgets }, 474 | { "floor", (uintptr_t)&floor }, 475 | { "floorf", (uintptr_t)&floorf }, 476 | { "fmod", (uintptr_t)&fmod }, 477 | { "fmodf", (uintptr_t)&fmodf }, 478 | { "fopen", (uintptr_t)&fopen_hook }, 479 | { "fprintf", (uintptr_t)&fprintf }, 480 | { "fputc", (uintptr_t)&fputc }, 481 | { "fputs", (uintptr_t)&fputs }, 482 | { "fread", (uintptr_t)&fread }, 483 | { "free", (uintptr_t)&free }, 484 | { "frexp", (uintptr_t)&frexp }, 485 | { "frexpf", (uintptr_t)&frexpf }, 486 | { "fscanf", (uintptr_t)&fscanf }, 487 | { "fseek", (uintptr_t)&fseek }, 488 | { "fstat", (uintptr_t)&fstat_hook }, 489 | { "ftell", (uintptr_t)&ftell }, 490 | { "fwrite", (uintptr_t)&fwrite }, 491 | { "getc", (uintptr_t)&getc }, 492 | { "getenv", (uintptr_t)&ret0 }, 493 | { "getwc", (uintptr_t)&getwc }, 494 | { "gettimeofday", (uintptr_t)&gettimeofday }, 495 | { "glVertexAttribPointer", (uintptr_t)&glVertexAttribPointer_hook }, 496 | { "glEnableVertexAttribArray", (uintptr_t)&glEnableVertexAttribArray_hook }, 497 | { "glAlphaFunc", (uintptr_t)&glAlphaFunc }, 498 | { "glBindBuffer", (uintptr_t)&glBindBuffer }, 499 | { "glBindTexture", (uintptr_t)&glBindTexture }, 500 | { "glBlendFunc", (uintptr_t)&glBlendFunc }, 501 | { "glBufferData", (uintptr_t)&glBufferData }, 502 | { "glClear", (uintptr_t)&glClear }, 503 | { "glClearColor", (uintptr_t)&glClearColor }, 504 | { "glClearDepthf", (uintptr_t)&glClearDepthf }, 505 | { "glColorPointer", (uintptr_t)&glColorPointer }, 506 | { "glCompressedTexImage2D", (uintptr_t)&glCompressedTexImage2D }, 507 | { "glDeleteBuffers", (uintptr_t)&glDeleteBuffers }, 508 | { "glDeleteTextures", (uintptr_t)&glDeleteTextures }, 509 | { "glDepthFunc", (uintptr_t)&glDepthFunc }, 510 | { "glDepthMask", (uintptr_t)&glDepthMask }, 511 | { "glDisable", (uintptr_t)&glDisable }, 512 | { "glDrawElements", (uintptr_t)&glDrawElements }, 513 | { "glEnable", (uintptr_t)&glEnable }, 514 | { "glEnableClientState", (uintptr_t)&glEnableClientState }, 515 | { "glGenBuffers", (uintptr_t)&glGenBuffers }, 516 | { "glGenTextures", (uintptr_t)&glGenTextures }, 517 | { "glGetError", (uintptr_t)&ret0 }, 518 | { "glLoadIdentity", (uintptr_t)&glLoadIdentity }, 519 | { "glMatrixMode", (uintptr_t)&glMatrixMode }, 520 | { "glMultMatrixx", (uintptr_t)&glMultMatrixx }, 521 | { "glOrthof", (uintptr_t)&glOrthof }, 522 | { "glPixelStorei", (uintptr_t)&ret0 }, 523 | { "glPopMatrix", (uintptr_t)&glPopMatrix }, 524 | { "glPushMatrix", (uintptr_t)&glPushMatrix }, 525 | { "glTexCoordPointer", (uintptr_t)&glTexCoordPointer }, 526 | { "glTexImage2D", (uintptr_t)&glTexImage2D }, 527 | { "glTexParameteri", (uintptr_t)&glTexParameteri }, 528 | { "glTexSubImage2D", (uintptr_t)&glTexSubImage2D }, 529 | { "glTranslatex", (uintptr_t)&glTranslatex }, 530 | { "glVertexPointer", (uintptr_t)&glVertexPointer }, 531 | { "glViewport", (uintptr_t)&glViewport }, 532 | { "gmtime", (uintptr_t)&gmtime }, 533 | { "gzopen", (uintptr_t)&ret0 }, 534 | { "inflate", (uintptr_t)&inflate }, 535 | { "inflateEnd", (uintptr_t)&inflateEnd }, 536 | { "inflateInit_", (uintptr_t)&inflateInit_ }, 537 | { "inflateReset", (uintptr_t)&inflateReset }, 538 | { "isalnum", (uintptr_t)&isalnum }, 539 | { "isalpha", (uintptr_t)&isalpha }, 540 | { "iscntrl", (uintptr_t)&iscntrl }, 541 | { "islower", (uintptr_t)&islower }, 542 | { "ispunct", (uintptr_t)&ispunct }, 543 | { "isprint", (uintptr_t)&isprint }, 544 | { "isspace", (uintptr_t)&isspace }, 545 | { "isupper", (uintptr_t)&isupper }, 546 | { "iswalpha", (uintptr_t)&iswalpha }, 547 | { "iswcntrl", (uintptr_t)&iswcntrl }, 548 | { "iswctype", (uintptr_t)&iswctype }, 549 | { "iswdigit", (uintptr_t)&iswdigit }, 550 | { "iswdigit", (uintptr_t)&iswdigit }, 551 | { "iswlower", (uintptr_t)&iswlower }, 552 | { "iswprint", (uintptr_t)&iswprint }, 553 | { "iswpunct", (uintptr_t)&iswpunct }, 554 | { "iswspace", (uintptr_t)&iswspace }, 555 | { "iswupper", (uintptr_t)&iswupper }, 556 | { "iswxdigit", (uintptr_t)&iswxdigit }, 557 | { "isxdigit", (uintptr_t)&isxdigit }, 558 | { "ldexp", (uintptr_t)&ldexp }, 559 | // { "listen", (uintptr_t)&listen }, 560 | { "localtime", (uintptr_t)&localtime }, 561 | { "localtime_r", (uintptr_t)&localtime_r }, 562 | { "log", (uintptr_t)&log }, 563 | { "log10", (uintptr_t)&log10 }, 564 | { "longjmp", (uintptr_t)&longjmp }, 565 | { "lrand48", (uintptr_t)&lrand48 }, 566 | { "lrint", (uintptr_t)&lrint }, 567 | { "lrintf", (uintptr_t)&lrintf }, 568 | { "lseek", (uintptr_t)&lseek }, 569 | { "malloc", (uintptr_t)&malloc }, 570 | { "mbrtowc", (uintptr_t)&mbrtowc }, 571 | { "memchr", (uintptr_t)&sceClibMemchr }, 572 | { "memcmp", (uintptr_t)&memcmp }, 573 | { "memcpy", (uintptr_t)&sceClibMemcpy }, 574 | { "memmove", (uintptr_t)&sceClibMemmove }, 575 | { "memset", (uintptr_t)&sceClibMemset }, 576 | { "mkdir", (uintptr_t)&mkdir }, 577 | { "mktime", (uintptr_t)&mktime }, 578 | { "mmap", (uintptr_t)&mmap}, 579 | { "munmap", (uintptr_t)&munmap}, 580 | { "modf", (uintptr_t)&modf }, 581 | // { "poll", (uintptr_t)&poll }, 582 | { "open", (uintptr_t)&open_hook }, 583 | { "pow", (uintptr_t)&pow }, 584 | { "powf", (uintptr_t)&powf }, 585 | { "printf", (uintptr_t)&printf }, 586 | { "puts", (uintptr_t)&puts }, 587 | { "pthread_attr_destroy", (uintptr_t)&ret0 }, 588 | { "pthread_attr_init", (uintptr_t)&ret0 }, 589 | { "pthread_attr_setdetachstate", (uintptr_t)&ret0 }, 590 | { "pthread_cond_broadcast", (uintptr_t)&pthread_cond_broadcast_fake}, 591 | { "pthread_cond_wait", (uintptr_t)&pthread_cond_wait_fake}, 592 | { "pthread_create", (uintptr_t)&pthread_create_fake }, 593 | { "pthread_getschedparam", (uintptr_t)&pthread_getschedparam }, 594 | { "pthread_getspecific", (uintptr_t)&pthread_getspecific }, 595 | { "pthread_key_create", (uintptr_t)&pthread_key_create }, 596 | { "pthread_key_delete", (uintptr_t)&pthread_key_delete }, 597 | { "pthread_mutex_destroy", (uintptr_t)&pthread_mutex_destroy_fake }, 598 | { "pthread_mutex_init", (uintptr_t)&pthread_mutex_init_fake }, 599 | { "pthread_mutex_lock", (uintptr_t)&pthread_mutex_lock_fake }, 600 | { "pthread_mutex_unlock", (uintptr_t)&pthread_mutex_unlock_fake }, 601 | { "pthread_once", (uintptr_t)&pthread_once_fake }, 602 | { "pthread_self", (uintptr_t)&pthread_self }, 603 | { "pthread_setschedparam", (uintptr_t)&pthread_setschedparam }, 604 | { "pthread_setspecific", (uintptr_t)&pthread_setspecific }, 605 | { "putc", (uintptr_t)&putc }, 606 | { "putwc", (uintptr_t)&putwc }, 607 | { "qsort", (uintptr_t)&qsort }, 608 | { "read", (uintptr_t)&read }, 609 | { "realloc", (uintptr_t)&realloc }, 610 | { "remove", (uintptr_t)&remove }, 611 | // { "recv", (uintptr_t)&recv }, 612 | { "rint", (uintptr_t)&rint }, 613 | // { "send", (uintptr_t)&send }, 614 | // { "sendto", (uintptr_t)&sendto }, 615 | { "setenv", (uintptr_t)&ret0 }, 616 | { "setjmp", (uintptr_t)&setjmp }, 617 | // { "setlocale", (uintptr_t)&setlocale }, 618 | // { "setsockopt", (uintptr_t)&setsockopt }, 619 | { "setvbuf", (uintptr_t)&setvbuf }, 620 | { "sin", (uintptr_t)&sin }, 621 | { "sinf", (uintptr_t)&sinf }, 622 | { "sinh", (uintptr_t)&sinh }, 623 | { "snprintf", (uintptr_t)&snprintf }, 624 | // { "socket", (uintptr_t)&socket }, 625 | { "sprintf", (uintptr_t)&sprintf }, 626 | { "sqrt", (uintptr_t)&sqrt }, 627 | { "sqrtf", (uintptr_t)&sqrtf }, 628 | { "srand48", (uintptr_t)&srand48 }, 629 | { "sscanf", (uintptr_t)&sscanf }, 630 | { "stat", (uintptr_t)&stat_hook }, 631 | { "strcasecmp", (uintptr_t)&strcasecmp }, 632 | { "strcat", (uintptr_t)&strcat }, 633 | { "strchr", (uintptr_t)&strchr }, 634 | { "strcmp", (uintptr_t)&sceClibStrcmp }, 635 | { "strcoll", (uintptr_t)&strcoll }, 636 | { "strcpy", (uintptr_t)&strcpy }, 637 | { "strcspn", (uintptr_t)&strcspn }, 638 | { "strerror", (uintptr_t)&strerror }, 639 | { "strftime", (uintptr_t)&strftime }, 640 | { "strlen", (uintptr_t)&strlen }, 641 | { "strncasecmp", (uintptr_t)&sceClibStrncasecmp }, 642 | { "strncat", (uintptr_t)&sceClibStrncat }, 643 | { "strncmp", (uintptr_t)&sceClibStrncmp }, 644 | { "strncpy", (uintptr_t)&sceClibStrncpy }, 645 | { "strpbrk", (uintptr_t)&strpbrk }, 646 | { "strrchr", (uintptr_t)&sceClibStrrchr }, 647 | { "strdup", (uintptr_t)&strdup }, 648 | { "strstr", (uintptr_t)&sceClibStrstr }, 649 | { "strtod", (uintptr_t)&strtod }, 650 | { "strtol", (uintptr_t)&strtol }, 651 | { "strtok", (uintptr_t)&strtok }, 652 | { "strtoul", (uintptr_t)&strtoul }, 653 | { "strxfrm", (uintptr_t)&strxfrm }, 654 | { "sysconf", (uintptr_t)&ret0 }, 655 | { "tan", (uintptr_t)&tan }, 656 | { "tanf", (uintptr_t)&tanf }, 657 | { "tanh", (uintptr_t)&tanh }, 658 | { "time", (uintptr_t)&time }, 659 | { "tolower", (uintptr_t)&tolower }, 660 | { "toupper", (uintptr_t)&toupper }, 661 | { "towlower", (uintptr_t)&towlower }, 662 | { "towupper", (uintptr_t)&towupper }, 663 | { "ungetc", (uintptr_t)&ungetc }, 664 | { "ungetwc", (uintptr_t)&ungetwc }, 665 | { "usleep", (uintptr_t)&usleep }, 666 | { "vfprintf", (uintptr_t)&vfprintf }, 667 | { "vprintf", (uintptr_t)&vprintf }, 668 | { "vsnprintf", (uintptr_t)&vsnprintf }, 669 | { "vsprintf", (uintptr_t)&vsprintf }, 670 | { "vswprintf", (uintptr_t)&vswprintf }, 671 | { "wcrtomb", (uintptr_t)&wcrtomb }, 672 | { "wcscoll", (uintptr_t)&wcscoll }, 673 | { "wcscmp", (uintptr_t)&wcscmp }, 674 | { "wcsncpy", (uintptr_t)&wcsncpy }, 675 | { "wcsftime", (uintptr_t)&wcsftime }, 676 | { "wcslen", (uintptr_t)&wcslen }, 677 | { "wcsxfrm", (uintptr_t)&wcsxfrm }, 678 | { "wctob", (uintptr_t)&wctob }, 679 | { "wctype", (uintptr_t)&wctype }, 680 | { "wmemchr", (uintptr_t)&wmemchr }, 681 | { "wmemcmp", (uintptr_t)&wmemcmp }, 682 | { "wmemcpy", (uintptr_t)&wmemcpy }, 683 | { "wmemmove", (uintptr_t)&wmemmove }, 684 | { "wmemset", (uintptr_t)&wmemset }, 685 | { "write", (uintptr_t)&write }, 686 | // { "writev", (uintptr_t)&writev }, 687 | }; 688 | 689 | int check_kubridge(void) { 690 | int search_unk[2]; 691 | return _vshKernelSearchModuleByName("kubridge", search_unk); 692 | } 693 | 694 | int file_exists(const char *path) { 695 | SceIoStat stat; 696 | return sceIoGetstat(path, &stat) >= 0; 697 | } 698 | 699 | enum MethodIDs { 700 | UNKNOWN = 0, 701 | INIT, 702 | GET_SCREEN_WIDTH, 703 | GET_SCREEN_HEIGHT, 704 | GET_DEVICE_LANGUAGE, 705 | IS_AMAZON, 706 | BUFFER_SOUND, 707 | QUEUE_MUSIC, 708 | STOP_MUSIC, 709 | SET_MUSIC_VOLUME, 710 | PLAY_SOUND, 711 | REMOVE_SOUND, 712 | STOP_SOUND 713 | } MethodIDs; 714 | 715 | typedef struct { 716 | char *name; 717 | enum MethodIDs id; 718 | } NameToMethodID; 719 | 720 | static NameToMethodID name_to_method_ids[] = { 721 | { "", INIT }, 722 | { "GetScreenWidth", GET_SCREEN_WIDTH }, 723 | { "GetScreenHeight", GET_SCREEN_HEIGHT }, 724 | { "GetDeviceLanguage", GET_DEVICE_LANGUAGE }, 725 | { "IsAmazon", IS_AMAZON }, 726 | { "BufferSound", BUFFER_SOUND }, 727 | { "QueueMusic", QUEUE_MUSIC }, 728 | { "StopMusic", STOP_MUSIC }, 729 | { "StopSound", STOP_SOUND }, 730 | { "SetMusicVolume", SET_MUSIC_VOLUME }, 731 | { "PlaySound", PLAY_SOUND }, 732 | { "RemoveSound", REMOVE_SOUND }, 733 | }; 734 | 735 | int GetMethodID(void *env, void *class, const char *name, const char *sig) { 736 | printf("%s\n", name); 737 | 738 | for (int i = 0; i < sizeof(name_to_method_ids) / sizeof(NameToMethodID); i++) { 739 | if (strcmp(name, name_to_method_ids[i].name) == 0) { 740 | return name_to_method_ids[i].id; 741 | } 742 | } 743 | 744 | return UNKNOWN; 745 | } 746 | 747 | int GetStaticMethodID(void *env, void *class, const char *name, const char *sig) { 748 | //printf("Static: %s\n", name); 749 | 750 | for (int i = 0; i < sizeof(name_to_method_ids) / sizeof(NameToMethodID); i++) { 751 | if (strcmp(name, name_to_method_ids[i].name) == 0) 752 | return name_to_method_ids[i].id; 753 | } 754 | 755 | return UNKNOWN; 756 | } 757 | 758 | void CallStaticVoidMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 759 | switch (methodID) { 760 | case QUEUE_MUSIC: 761 | audio_queue_music(args[0], args[1]); 762 | break; 763 | case STOP_MUSIC: 764 | audio_stop_music(); 765 | break; 766 | case STOP_SOUND: 767 | audio_stop_sound(args[0]); 768 | break; 769 | case SET_MUSIC_VOLUME: 770 | audio_set_music_volume(args[0]); 771 | break; 772 | case REMOVE_SOUND: 773 | audio_remove_sound(args[0]); 774 | break; 775 | } 776 | } 777 | 778 | int CallStaticBooleanMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 779 | return 0; 780 | } 781 | 782 | int CallStaticIntMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 783 | int ret; 784 | switch (methodID) { 785 | case GET_SCREEN_WIDTH: 786 | return 960; 787 | case GET_SCREEN_HEIGHT: 788 | return 544; 789 | case BUFFER_SOUND: 790 | ret = audio_load_sound(args[0]); 791 | return -ret; 792 | case PLAY_SOUND: 793 | return audio_play_sound(args[0], args[2], args[3], args[4]); 794 | } 795 | return 0; 796 | } 797 | 798 | void *CallStaticObjectMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 799 | int lang = -1; 800 | switch (methodID) { 801 | case GET_DEVICE_LANGUAGE: 802 | sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, &lang); 803 | switch (lang) { 804 | case SCE_SYSTEM_PARAM_LANG_FRENCH: 805 | return "FRENCH"; 806 | case SCE_SYSTEM_PARAM_LANG_SPANISH: 807 | return "SPANISH"; 808 | case SCE_SYSTEM_PARAM_LANG_GERMAN: 809 | return "GERMAN"; 810 | case SCE_SYSTEM_PARAM_LANG_ITALIAN: 811 | return "ITALIAN"; 812 | case SCE_SYSTEM_PARAM_LANG_JAPANESE: 813 | return "JAPANESE"; 814 | default: 815 | return "ENGLISH"; 816 | } 817 | } 818 | return NULL; 819 | } 820 | 821 | uint64_t CallLongMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 822 | return -1; 823 | } 824 | 825 | void *FindClass(void) { 826 | return (void *)0x41414141; 827 | } 828 | 829 | void *NewGlobalRef(void *env, char *str) { 830 | return (void *)0x42424242; 831 | } 832 | 833 | void DeleteGlobalRef(void *env, char *str) { 834 | } 835 | 836 | void *NewObjectV(void *env, void *clazz, int methodID, uintptr_t args) { 837 | return (void *)0x43434343; 838 | } 839 | 840 | void *GetObjectClass(void *env, void *obj) { 841 | return (void *)0x44444444; 842 | } 843 | 844 | char *NewStringUTF(void *env, char *bytes) { 845 | return bytes; 846 | } 847 | 848 | char *GetStringUTFChars(void *env, char *string, int *isCopy) { 849 | return string; 850 | } 851 | 852 | int GetJavaVM(void *env, void **vm) { 853 | *vm = fake_vm; 854 | return 0; 855 | } 856 | 857 | int GetFieldID(void *env, void *clazz, const char *name, const char *sig) { 858 | return 0; 859 | } 860 | 861 | int GetBooleanField(void *env, void *obj, int fieldID) { 862 | return 0; 863 | } 864 | 865 | void *CallObjectMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 866 | return NULL; 867 | } 868 | 869 | int CallBooleanMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 870 | switch (methodID) { 871 | default: 872 | return 0; 873 | } 874 | } 875 | 876 | void CallVoidMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 877 | switch (methodID) { 878 | default: 879 | break; 880 | } 881 | } 882 | 883 | int GetIntField(void *env, void *obj, int fieldID) { return 0; } 884 | 885 | int main(int argc, char *argv[]) { 886 | SceAppUtilInitParam init_param; 887 | SceAppUtilBootParam boot_param; 888 | memset(&init_param, 0, sizeof(SceAppUtilInitParam)); 889 | memset(&boot_param, 0, sizeof(SceAppUtilBootParam)); 890 | sceAppUtilInit(&init_param, &boot_param); 891 | 892 | sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, SCE_TOUCH_SAMPLING_STATE_START); 893 | 894 | scePowerSetArmClockFrequency(444); 895 | scePowerSetBusClockFrequency(222); 896 | scePowerSetGpuClockFrequency(222); 897 | scePowerSetGpuXbarClockFrequency(166); 898 | 899 | uint32_t use_analogs = 0; 900 | if (!use_analogs) { 901 | SceAppUtilAppEventParam eventParam; 902 | sceClibMemset(&eventParam, 0, sizeof(SceAppUtilAppEventParam)); 903 | sceAppUtilReceiveAppEvent(&eventParam); 904 | if (eventParam.type == 0x05) { 905 | char buffer[2048]; 906 | sceAppUtilAppEventParseLiveArea(&eventParam, buffer); 907 | if (strstr(buffer, "analogs")) 908 | use_analogs = 1; 909 | } 910 | } 911 | printf("use_analogs is %u\n", use_analogs); 912 | 913 | if (check_kubridge() < 0) 914 | fatal_error("Error kubridge.skprx is not installed."); 915 | 916 | if (!file_exists("ur0:/data/libshacccg.suprx") && !file_exists("ur0:/data/external/libshacccg.suprx")) 917 | fatal_error("Error libshacccg.suprx is not installed."); 918 | 919 | if (so_file_load(&smb2_mod, SO_PATH, LOAD_ADDRESS) < 0) 920 | fatal_error("Error could not load %s.", SO_PATH); 921 | 922 | so_relocate(&smb2_mod); 923 | so_resolve(&smb2_mod, default_dynlib, sizeof(default_dynlib), 0); 924 | 925 | patch_game(); 926 | so_flush_caches(&smb2_mod); 927 | 928 | so_initialize(&smb2_mod); 929 | 930 | audio_player_init(); 931 | 932 | vglSetSemanticBindingMode(VGL_MODE_SHADER_PAIR); 933 | vglSetupGarbageCollector(127, 0x20000); 934 | vglInitExtended(0, SCREEN_W, SCREEN_H, MEMORY_VITAGL_THRESHOLD_MB * 1024 * 1024, SCE_GXM_MULTISAMPLE_4X); 935 | eglSwapInterval(0, 2); 936 | 937 | // Initing trophy system 938 | SceIoStat st; 939 | int r = trophies_init(); 940 | if (r < 0 && sceIoGetstat(TROPHIES_FILE, &st) < 0) { 941 | FILE *f = fopen(TROPHIES_FILE, "w"); 942 | fclose(f); 943 | warning("This game features unlockable trophies but NoTrpDrm is not installed. If you want to be able to unlock trophies, please install it."); 944 | } 945 | 946 | if (use_analogs) 947 | sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); 948 | else { 949 | sceMotionReset(); 950 | sceMotionStartSampling(); 951 | } 952 | 953 | memset(fake_vm, 'A', sizeof(fake_vm)); 954 | *(uintptr_t *)(fake_vm + 0x00) = (uintptr_t)fake_vm; // just point to itself... 955 | *(uintptr_t *)(fake_vm + 0x10) = (uintptr_t)ret0; 956 | *(uintptr_t *)(fake_vm + 0x14) = (uintptr_t)ret0; 957 | *(uintptr_t *)(fake_vm + 0x18) = (uintptr_t)GetEnv; 958 | 959 | memset(fake_env, 'A', sizeof(fake_env)); 960 | *(uintptr_t *)(fake_env + 0x00) = (uintptr_t)fake_env; // just point to itself... 961 | *(uintptr_t *)(fake_env + 0x18) = (uintptr_t)FindClass; 962 | *(uintptr_t *)(fake_env + 0x54) = (uintptr_t)NewGlobalRef; 963 | *(uintptr_t *)(fake_env + 0x58) = (uintptr_t)DeleteGlobalRef; 964 | *(uintptr_t *)(fake_env + 0x5C) = (uintptr_t)ret0; // DeleteLocalRef 965 | *(uintptr_t *)(fake_env + 0x74) = (uintptr_t)NewObjectV; 966 | *(uintptr_t *)(fake_env + 0x7C) = (uintptr_t)GetObjectClass; 967 | *(uintptr_t *)(fake_env + 0x84) = (uintptr_t)GetMethodID; 968 | *(uintptr_t *)(fake_env + 0x8C) = (uintptr_t)CallObjectMethodV; 969 | *(uintptr_t *)(fake_env + 0x98) = (uintptr_t)CallBooleanMethodV; 970 | *(uintptr_t *)(fake_env + 0xD4) = (uintptr_t)CallLongMethodV; 971 | *(uintptr_t *)(fake_env + 0xF8) = (uintptr_t)CallVoidMethodV; 972 | *(uintptr_t *)(fake_env + 0x178) = (uintptr_t)GetFieldID; 973 | *(uintptr_t *)(fake_env + 0x17C) = (uintptr_t)GetBooleanField; 974 | *(uintptr_t *)(fake_env + 0x190) = (uintptr_t)GetIntField; 975 | *(uintptr_t *)(fake_env + 0x1C4) = (uintptr_t)GetStaticMethodID; 976 | *(uintptr_t *)(fake_env + 0x1CC) = (uintptr_t)CallStaticObjectMethodV; 977 | *(uintptr_t *)(fake_env + 0x1D8) = (uintptr_t)CallStaticBooleanMethodV; 978 | *(uintptr_t *)(fake_env + 0x208) = (uintptr_t)CallStaticIntMethodV; 979 | *(uintptr_t *)(fake_env + 0x238) = (uintptr_t)CallStaticVoidMethodV; 980 | *(uintptr_t *)(fake_env + 0x29C) = (uintptr_t)NewStringUTF; 981 | *(uintptr_t *)(fake_env + 0x2A4) = (uintptr_t)GetStringUTFChars; 982 | *(uintptr_t *)(fake_env + 0x2A8) = (uintptr_t)ret0; 983 | *(uintptr_t *)(fake_env + 0x36C) = (uintptr_t)GetJavaVM; 984 | 985 | audio_player_init(); 986 | 987 | int (* Java_com_ooi_android_SharkInterface_SetAssetPath)(void *env, void *obj, char *path) = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkInterface_SetAssetPath"); 988 | int (* Java_com_ooi_android_SharkInterface_SetUserPath)(void *env, void *obj, char *path) = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkInterface_SetUserPath"); 989 | void (* Java_com_ooi_android_SharkRenderer_nativeRender)() = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkRenderer_nativeRender"); 990 | void (* Java_com_ooi_android_SharkWrapper_nativeInit)() = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkWrapper_nativeInit"); 991 | int (* Java_com_ooi_android_SharkRenderer_nativeOpenGLInit)(void *env, void *obj, int is_gles2) = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkRenderer_nativeOpenGLInit"); 992 | int (* Java_com_ooi_android_SharkInterface_ScreenTouchDown)(void *env, void *obj, float x, float y, int id) = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkInterface_ScreenTouchDown"); 993 | int (* Java_com_ooi_android_SharkInterface_ScreenTouchUp)(void *env, void *obj, float x, float y, int id) = (void *)so_symbol(&smb2_mod, "Java_com_ooi_android_SharkInterface_ScreenTouchUp"); 994 | void (* FeedAccelData)(float x, float y, float z) = (void *)so_symbol(&smb2_mod, "_ZN2io13Accelerometer13FeedAccelDataEfff"); 995 | 996 | Java_com_ooi_android_SharkWrapper_nativeInit(); 997 | Java_com_ooi_android_SharkInterface_SetAssetPath(fake_env, NULL, DATA_PATH "/assets/assets_android/"); 998 | Java_com_ooi_android_SharkInterface_SetUserPath(fake_env, NULL, DATA_PATH); 999 | Java_com_ooi_android_SharkRenderer_nativeOpenGLInit(fake_env, NULL, 1); 1000 | 1001 | 1002 | int lastX[SCE_TOUCH_MAX_REPORT] = {-1, -1, -1, -1, -1, -1, -1, -1}; 1003 | int lastY[SCE_TOUCH_MAX_REPORT] = {-1, -1, -1, -1, -1, -1, -1, -1}; 1004 | 1005 | for (;;) { 1006 | SceTouchData touch; 1007 | sceTouchPeek(SCE_TOUCH_PORT_FRONT, &touch, 1); 1008 | for (int i = 0; i < SCE_TOUCH_MAX_REPORT; i++) { 1009 | if (i < touch.reportNum) { 1010 | float x = (float)touch.report[i].x * ((float)SCREEN_W / 1920.0f); 1011 | float y = (float)touch.report[i].y * ((float)SCREEN_H / 1088.0f); 1012 | 1013 | Java_com_ooi_android_SharkInterface_ScreenTouchDown(fake_env, 0, x, y, i); 1014 | lastX[i] = x; 1015 | lastY[i] = y; 1016 | } else { 1017 | if (lastX[i] != -1 || lastY[i] != -1) { 1018 | Java_com_ooi_android_SharkInterface_ScreenTouchUp(fake_env, 0, lastX[i], lastY[i], i); 1019 | lastX[i] = -1; 1020 | lastY[i] = -1; 1021 | } 1022 | } 1023 | } 1024 | 1025 | if (accel_instance) { 1026 | if (use_analogs) { 1027 | SceCtrlData pad; 1028 | sceCtrlPeekBufferPositiveExt2(0, &pad, 1); 1029 | float lx = (float)pad.lx / 255.0f; 1030 | float ly = (float)pad.ly / 255.0f; 1031 | FeedAccelData(0.2f - 2.0f * ly, 1.0f - 2.0f * lx, -0.676981f); 1032 | } else { 1033 | SceMotionSensorState sensor; 1034 | sceMotionGetSensorState(&sensor, 1); 1035 | FeedAccelData(sensor.accelerometer.y, -sensor.accelerometer.x, sensor.accelerometer.z); 1036 | } 1037 | } 1038 | 1039 | Java_com_ooi_android_SharkRenderer_nativeRender(); 1040 | vglSwapBuffers(GL_FALSE); 1041 | } 1042 | 1043 | return 0; 1044 | } 1045 | --------------------------------------------------------------------------------