├── .github ├── FUNDING.yml └── workflows │ └── compilation.yml ├── .gitignore ├── .vscode └── settings.json ├── CMakeLists.txt ├── LICENSE ├── NotoSansJP-Regular.ttf ├── NotoSansKR-Regular.ttf ├── NotoSansSC-Regular.ttf ├── README.md ├── companion └── main.cpp ├── icudt59l ├── cnvalias.icu ├── confusables.cfu ├── ibm-5348_P100-1997.cnv ├── ibm-943_P15A-2003.cnv └── unames.icu ├── loader ├── bridge.c ├── bridge.h ├── config.h ├── dialog.c ├── dialog.h ├── elf.h ├── main.c ├── main.h ├── shaders │ ├── movie_f.h │ └── movie_v.h ├── so_util.c ├── so_util.h ├── stb_image.c ├── stb_image.h ├── stb_truetype.c └── stb_truetype.h ├── sce_sys ├── icon0.png └── livearea │ └── contents │ ├── bg0.png │ ├── config.png │ ├── startup.png │ └── template.xml ├── screenshots └── game.png └── shaders ├── 1_Negative_f.cg ├── 1_Negative_v.cg ├── 2_FXAA_f.cg ├── 2_FXAA_v.cg ├── 3_Sepia_f.cg ├── 3_Sepia_v.cg ├── 4_Greyscale_f.cg └── 4_Greyscale_v.cg /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | ko_fi: frangar 2 | -------------------------------------------------------------------------------- /.github/workflows/compilation.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | pull_request: 6 | repository_dispatch: 7 | types: [run_build] 8 | 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | container: vitasdk/vitasdk-softfp:latest 13 | steps: 14 | - uses: actions/checkout@v2 15 | 16 | - name: Install dependencies 17 | run: | 18 | git clone https://github.com/frangarcj/opensles 19 | make -C opensles/libopensles -j$(nproc) install 20 | git clone https://github.com/Rinnegatamante/vitaGL.git 21 | make -C vitaGL SOFTFP_ABI=1 NO_DEBUG=1 NO_TEX_COMBINER=1 MATH_SPEEDHACK=1 PHYCONT_ON_DEMAND=1 -j$(nproc) install 22 | - name: Get Information Variables 23 | id: core 24 | run: | 25 | echo "::set-output name=sha8::$(echo ${GITHUB_SHA} | cut -c1-8)" 26 | - name: Compile project 27 | run: | 28 | mkdir build 29 | cd build 30 | cmake .. 31 | make -j$(nproc) 32 | - name: Upload artifacts 33 | if: ${{ success() }} 34 | uses: actions/upload-artifact@v2 35 | with: 36 | name: FF3-${{ steps.core.outputs.sha8 }}.vpk 37 | path: build/FF3.vpk 38 | - uses: svenstaro/upload-release-action@v2 39 | if: contains(github.ref,'refs/tags') 40 | with: 41 | repo_token: ${{ secrets.GITHUB_TOKEN }} 42 | file: build/FF3.vpk 43 | overwrite: true 44 | tag: ${{ github.ref }} 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/* 2 | *.velf 3 | *.elf 4 | *.vpk 5 | *.sfo 6 | *.bin 7 | *.out 8 | *.o 9 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "typeinfo": "c", 4 | "cmath": "c", 5 | "limits.h": "c" 6 | } 7 | } -------------------------------------------------------------------------------- /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(FF3.elf C CXX) 12 | include("${VITASDK}/share/vita.cmake" REQUIRED) 13 | set(VITA_APP_NAME "Final Fantasy III") 14 | set(VITA_TITLEID "FF3000000") 15 | set(VITA_VERSION "01.10") 16 | set(VITA_MKSFOEX_FLAGS "-d ATTRIBUTE2=12") 17 | 18 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wl,-q,--wrap,memcpy,--wrap,memmove,--wrap,memset -Wall -O3 -mfloat-abi=softfp") 19 | set(CMAKE_CXX_FLAGS "${CMAKE_C_FLAGS} -std=c++11") 20 | 21 | add_executable(FF3.elf 22 | loader/main.c 23 | loader/dialog.c 24 | loader/so_util.c 25 | loader/bridge.c 26 | loader/stb_image.c 27 | loader/stb_truetype.c 28 | ) 29 | 30 | target_link_libraries(FF3.elf 31 | m 32 | stdc++ 33 | vitaGL 34 | vitashark 35 | mathneon 36 | kubridge_stub 37 | SceAppMgr_stub 38 | SceAppUtil_stub 39 | SceAudio_stub 40 | SceCtrl_stub 41 | SceCommonDialog_stub 42 | SceDisplay_stub 43 | SceKernelDmacMgr_stub 44 | SceGxm_stub 45 | SceShaccCg_stub 46 | SceSysmodule_stub 47 | ScePower_stub 48 | SceTouch_stub 49 | SceVshBridge_stub 50 | SceAvPlayer_stub 51 | OpenSLES 52 | sndfile 53 | SDL 54 | SceHid_stub 55 | icudata 56 | icuuc 57 | z 58 | -Wl,--whole-archive 59 | pthread 60 | -Wl,--no-whole-archive 61 | ) 62 | 63 | vita_create_self(eboot.bin FF3.elf UNSAFE) 64 | 65 | add_executable(companion 66 | companion/main.cpp 67 | ) 68 | 69 | target_link_libraries(companion 70 | imgui 71 | vitaGL 72 | vitashark 73 | mathneon 74 | SceAppMgr_stub 75 | SceAudio_stub 76 | SceCtrl_stub 77 | SceCommonDialog_stub 78 | SceDisplay_stub 79 | SceKernelDmacMgr_stub 80 | SceGxm_stub 81 | SceShaccCg_stub 82 | SceSysmodule_stub 83 | SceTouch_stub 84 | SceVshBridge_stub 85 | ) 86 | 87 | vita_create_self(companion.bin companion UNSAFE) 88 | 89 | vita_create_vpk(FF3.vpk ${VITA_TITLEID} eboot.bin 90 | VERSION ${VITA_VERSION} 91 | NAME ${VITA_APP_NAME} 92 | FILE ${CMAKE_SOURCE_DIR}/sce_sys/icon0.png sce_sys/icon0.png 93 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/bg0.png sce_sys/livearea/contents/bg0.png 94 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/startup.png sce_sys/livearea/contents/startup.png 95 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/config.png sce_sys/livearea/contents/config.png 96 | ${CMAKE_SOURCE_DIR}/sce_sys/livearea/contents/template.xml sce_sys/livearea/contents/template.xml 97 | ${CMAKE_SOURCE_DIR}/NotoSansJP-Regular.ttf NotoSansJP-Regular.ttf 98 | ${CMAKE_SOURCE_DIR}/NotoSansSC-Regular.ttf NotoSansSC-Regular.ttf 99 | ${CMAKE_SOURCE_DIR}/NotoSansKR-Regular.ttf NotoSansKR-Regular.ttf 100 | ${CMAKE_BINARY_DIR}/companion.bin companion.bin 101 | ${CMAKE_SOURCE_DIR}/icudt59l/cnvalias.icu icudt59l/cnvalias.icu 102 | ${CMAKE_SOURCE_DIR}/icudt59l/confusables.cfu icudt59l/confusables.cfu 103 | ${CMAKE_SOURCE_DIR}/icudt59l/unames.icu icudt59l/unames.icu 104 | ${CMAKE_SOURCE_DIR}/icudt59l/ibm-943_P15A-2003.cnv icudt59l/ibm-943_P15A-2003.cnv 105 | ${CMAKE_SOURCE_DIR}/icudt59l/ibm-5348_P100-1997.cnv icudt59l/ibm-5348_P100-1997.cnv 106 | ${CMAKE_SOURCE_DIR}/shaders/1_Negative_f.cg shaders/1_Negative_f.cg 107 | ${CMAKE_SOURCE_DIR}/shaders/1_Negative_v.cg shaders/1_Negative_v.cg 108 | ${CMAKE_SOURCE_DIR}/shaders/2_FXAA_f.cg shaders/2_FXAA_f.cg 109 | ${CMAKE_SOURCE_DIR}/shaders/2_FXAA_v.cg shaders/2_FXAA_v.cg 110 | ${CMAKE_SOURCE_DIR}/shaders/3_Sepia_f.cg shaders/3_Sepia_f.cg 111 | ${CMAKE_SOURCE_DIR}/shaders/3_Sepia_v.cg shaders/3_Sepia_v.cg 112 | ${CMAKE_SOURCE_DIR}/shaders/4_Greyscale_f.cg shaders/4_Greyscale_f.cg 113 | ${CMAKE_SOURCE_DIR}/shaders/4_Greyscale_v.cg shaders/4_Greyscale_v.cg 114 | ) 115 | 116 | add_custom_target(copy 117 | COMMAND echo "destroy" | nc $(PSVITAIP) 1338 118 | COMMAND curl -T eboot.bin ftp://$(PSVITAIP):1337/ux0:/app/${VITA_TITLEID}/eboot.bin DEPENDS eboot.bin 119 | COMMAND curl -T companion.bin ftp://$(PSVITAIP):1337/ux0:/app/${VITA_TITLEID}/companion.bin DEPENDS companion.bin 120 | ) 121 | 122 | add_custom_target(launch 123 | COMMAND echo "screen on" | nc $(PSVITAIP) 1338 124 | COMMAND echo "destroy" | nc $(PSVITAIP) 1338 125 | COMMAND curl -T eboot.bin ftp://$(PSVITAIP):1337/ux0:/app/${VITA_TITLEID}/eboot.bin DEPENDS eboot.bin 126 | COMMAND curl -T companion.bin ftp://$(PSVITAIP):1337/ux0:/app/${VITA_TITLEID}/companion.bin DEPENDS companion.bin 127 | COMMAND echo "launch ${VITA_TITLEID}" | nc $(PSVITAIP) 1338 128 | ) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /NotoSansJP-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/NotoSansJP-Regular.ttf -------------------------------------------------------------------------------- /NotoSansKR-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/NotoSansKR-Regular.ttf -------------------------------------------------------------------------------- /NotoSansSC-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/NotoSansSC-Regular.ttf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Final Fantasy 3 Vita 2 | 3 |

4 | 5 | This is a wrapper/port of *Final Fantasy 3 Android* for the *PS Vita*. 6 | 7 | The port works by loading the official Android ARMv7 executable in memory, resolving its imports with native functions and patching it in order to properly run. 8 | 9 | ## Setup Instructions (For End Users) 10 | 11 | In order to properly install the game, you'll have to follow these steps precisely: 12 | 13 | - Install [kubridge](https://github.com/TheOfficialFloW/kubridge/releases/) and [FdFix](https://github.com/TheOfficialFloW/FdFix/releases/) by copying `kubridge.skprx` and `fd_fix.skprx` to your taiHEN plugins folder (usually `ux0:tai`) and adding two entries to your `config.txt` under `*KERNEL`: 14 | 15 | ``` 16 | *KERNEL 17 | ux0:tai/kubridge.skprx 18 | ux0:tai/fd_fix.skprx 19 | ``` 20 | 21 | **Note** Don't install fd_fix.skprx if you're using repatch plugin 22 | 23 | - **Optional**: Install [PSVshell](https://github.com/Electry/PSVshell/releases) to overclock your device to 500Mhz. 24 | - Install `libshacccg.suprx`, if you don't have it already, by following [this guide](https://samilops2.gitbook.io/vita-troubleshooting-guide/shader-compiler/extract-libshacccg.suprx). 25 | - Obtain your copy of *Final Fantasy 3* legally from the Google Play store in form of an `.apk` file and one or more `.obb` files (usually located inside the `/sdcard/android/obb/com.square_enix.android_googleplay.FFIII_GP/`) folder. [You can get all the required files directly from your phone](https://stackoverflow.com/questions/11012976/how-do-i-get-the-apk-of-an-installed-app-without-root-access) or by using an apk extractor you can find in the play store. The apk can be extracted with whatever Zip extractor you prefer (eg: WinZip, WinRar, etc...) since apk is basically a zip file. You can rename `.apk` to `.zip` to open them with your default zip extractor. 26 | - Copy the `.obb` file to `ux0:data/ff3` and rename it to `main.obb` 27 | - Open the apk and extract `libff3.so` from the `lib/armeabi-v7a` folder to `ux0:data/ff3`. 28 | - Install [FF3.vpk](https://github.com/frangarcj/ff3_vita/releases/download/v1.0/FF3.vpk) on your *PS Vita*. 29 | - **Optional (Opening Video Playback)**: Extract from the apk, the file `res/raw/opening.mp4` and convert it to 1280x720 (ffmpeg can be used for this task with the command `ffmpeg -i opening.mp4 -vf scale=1280x720 output.mp4`). Once converted, copy it to `ux0:data/ff3` named as `opening.mp4`. 30 | 31 | ## Build Instructions (For Developers) 32 | 33 | In order to build the loader, you'll need a [vitasdk](https://github.com/vitasdk) build fully compiled with softfp usage. 34 | You can find a precompiled version here: [Linux](https://github.com/vitasdk/buildscripts/suites/2943994805/artifacts/66184169) / [Windows](https://github.com/vitasdk/buildscripts/suites/2943994805/artifacts/66184170). 35 | Additionally, you'll need these libraries to be compiled as well with `-mfloat-abi=softfp` added to their CFLAGS: 36 | 37 | - [libmathneon](https://github.com/Rinnegatamante/math-neon) 38 | 39 | - ```bash 40 | make install 41 | ``` 42 | 43 | - [vitaShaRK](https://github.com/Rinnegatamante/vitaShaRK) 44 | 45 | - ```bash 46 | make install 47 | ``` 48 | 49 | - [kubridge](https://github.com/TheOfficialFloW/kubridge) 50 | 51 | - ```bash 52 | mkdir build && cd build 53 | cmake .. && make install 54 | ``` 55 | 56 | - [vitaGL](https://github.com/Rinnegatamante/vitaGL) 57 | 58 | - ````bash 59 | make SOFTFP_ABI=1 NO_DEBUG=1 NO_TEX_COMBINER=1 MATH_SPEEDHACK=1 PHYCONT_ON_DEMAND=1 install 60 | ```` 61 | 62 | - [opensles](https://github.com/frangarcj/opensles) 63 | 64 | - ````bash 65 | cd libopensles 66 | make install 67 | ```` 68 | Finally, you'll need these libraries recompiled with `-mfloat-abi=softfp` from [vitasdk/packages](https://github.com/vitasdk/packages): 69 | - sndfile 70 | - SDL 71 | - icu4c 72 | 73 | After all these requirements are met, you can compile the loader with the following commands: 74 | 75 | ```bash 76 | mkdir build && cd build 77 | cmake .. && make 78 | ``` 79 | 80 | You can also use [vitasdk/vitasdk-softfp](https://hub.docker.com/r/vitasdk/vitasdk-softfp) with Docker. See [compilation.yml](https://github.com/frangarcj/ff3_vita/blob/master/.github/workflows/compilation.yml). 81 | 82 | ## Credits 83 | 84 | - TheFloW for the initial arm elf loader. 85 | - Rinnegatamante for vitaGL, companion app and fixes. 86 | - Darthbellic for providing LiveArea assets. 87 | -------------------------------------------------------------------------------- /companion/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | extern "C" { 8 | void *__wrap_memcpy(void *dest, const void *src, size_t n) { 9 | return sceClibMemcpy(dest, src, n); 10 | } 11 | 12 | void *__wrap_memmove(void *dest, const void *src, size_t n) { 13 | return sceClibMemmove(dest, src, n); 14 | } 15 | 16 | void *__wrap_memset(void *s, int c, size_t n) { 17 | return sceClibMemset(s, c, n); 18 | } 19 | } 20 | 21 | #define CONFIG_FILE_PATH "ux0:data/ff3/options.cfg" 22 | 23 | #define RESOLUTION_NUM 3 24 | const char *ResolutionName[RESOLUTION_NUM] = { 25 | "544p", 26 | "720p", 27 | "1080i" 28 | }; 29 | 30 | #define ANTI_ALIASING_NUM 3 31 | const char *AntiAliasingName[ANTI_ALIASING_NUM] = { 32 | "Disabled", 33 | "MSAA 2x", 34 | "MSAA 4x" 35 | }; 36 | 37 | #define LANGUAGES_NUM 10 38 | const char *LanguageName[LANGUAGES_NUM] = { 39 | "Auto", 40 | "Japanese", 41 | "English", 42 | "French", 43 | "German", 44 | "Italian", 45 | "Spanish", 46 | "Simplified Chinese", 47 | "Traditional Chinese", 48 | "Korean" 49 | }; 50 | 51 | typedef struct { 52 | int res; 53 | int bilinear; 54 | int lang; 55 | int msaa; 56 | int postfx; 57 | } config_opts; 58 | config_opts options; 59 | 60 | bool bilinear_filter; 61 | 62 | void loadOptions() { 63 | char buffer[30]; 64 | int value; 65 | 66 | FILE *f = fopen(CONFIG_FILE_PATH, "rb"); 67 | if (f) { 68 | while (EOF != fscanf(f, "%[^=]=%d\n", buffer, &value)) { 69 | if (strcmp("resolution", buffer) == 0) options.res = value; 70 | else if (strcmp("bilinear", buffer) == 0) options.bilinear = value; 71 | else if (strcmp("language", buffer) == 0) options.lang = value; 72 | else if (strcmp("antialiasing", buffer) == 0) options.msaa = value; 73 | else if (strcmp("postfx", buffer) == 0) options.postfx = value; 74 | } 75 | } else { 76 | options.res = 0; 77 | options.bilinear = 0; 78 | options.lang = 0; 79 | options.msaa = 2; 80 | options.postfx = 0; 81 | } 82 | 83 | bilinear_filter = options.bilinear ? true : false; 84 | } 85 | 86 | void saveOptions(void) { 87 | options.bilinear = bilinear_filter ? 1 : 0; 88 | 89 | FILE *config = fopen(CONFIG_FILE_PATH, "w+"); 90 | 91 | if (config) { 92 | fprintf(config, "%s=%d\n", "resolution", options.res); 93 | fprintf(config, "%s=%d\n", "bilinear", options.bilinear); 94 | fprintf(config, "%s=%d\n", "language", options.lang); 95 | fprintf(config, "%s=%d\n", "antialiasing", options.msaa); 96 | fprintf(config, "%s=%d\n", "postfx", options.postfx); 97 | fclose(config); 98 | } 99 | } 100 | 101 | const char *options_descs[] = { 102 | "Internal resolution to use. When a resolution higher than 544p is used on a PSVita, Sharpscale plugin is required.\nThe default value is: 544p.", // resolution 103 | "When enabled, forces bilinear filtering for all game's textures.\nThe default value is: Disabled.", // bilinear 104 | "Anti-Aliasing is a technique used to reduce graphical artifacts surrounding 3D models. Greatly improves graphics quality at the cost of some GPU power.\nThe default value is: MSAA 4x.", // antialiasing 105 | "Language to use for the game. When Auto is used, language will be decided based on system language.\nThe default value is: Auto.", // language 106 | "Enables usage of a post processing effect through shaders. May impact performances.\nThe default value is: Disabled.", // postfx 107 | }; 108 | 109 | enum { 110 | OPT_RESOLUTION, 111 | OPT_BILINEAR, 112 | OPT_ANTIALIASING, 113 | OPT_LANGUAGE, 114 | OPT_POSTFX 115 | }; 116 | 117 | const char *desc = nullptr; 118 | 119 | void SetDescription(int i) { 120 | if (ImGui::IsItemHovered()) 121 | desc = options_descs[i]; 122 | } 123 | 124 | int main(int argc, char *argv[]) { 125 | loadOptions(); 126 | int exit_code = 0xDEAD; 127 | 128 | vglInitExtended(0, 960, 544, 0x1800000, SCE_GXM_MULTISAMPLE_4X); 129 | ImGui::CreateContext(); 130 | ImGui_ImplVitaGL_Init(); 131 | ImGui_ImplVitaGL_TouchUsage(false); 132 | ImGui_ImplVitaGL_GamepadUsage(true); 133 | ImGui::StyleColorsDark(); 134 | ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 0.0f); 135 | 136 | ImGui::GetIO().MouseDrawCursor = false; 137 | 138 | // Generating PostFX array 139 | char none_str[5]; 140 | strcpy(none_str, "None"); 141 | char *PostFxName[64] = {0}; 142 | PostFxName[0] = none_str; 143 | int PostFxNum = 1; 144 | SceIoDirent d; 145 | SceUID fd = sceIoDopen("app0:shaders"); 146 | while (sceIoDread(fd, &d) > 0) { 147 | int n; 148 | char name[64]; 149 | sscanf(d.d_name, "%d_%s", &n, name); 150 | strcpy(name, strchr(d.d_name, '_') + 1); 151 | char *end_of_name = strchr(name, '_'); 152 | end_of_name[0] = 0; 153 | if (PostFxName[n] == NULL) { 154 | PostFxName[n] = (char *)malloc(strlen(name) + 1); 155 | strcpy(PostFxName[n], name); 156 | if (PostFxNum <= n) 157 | PostFxNum = n + 1; 158 | } 159 | } 160 | sceIoDclose(fd); 161 | 162 | while (exit_code == 0xDEAD) { 163 | desc = nullptr; 164 | ImGui_ImplVitaGL_NewFrame(); 165 | 166 | ImGui::SetNextWindowPos(ImVec2(0, 0), ImGuiSetCond_Always); 167 | ImGui::SetNextWindowSize(ImVec2(960, 544), ImGuiSetCond_Always); 168 | ImGui::Begin("##main", nullptr, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoBringToFrontOnFocus); 169 | 170 | ImGui::TextColored(ImVec4(255, 255, 0, 255), "Graphics"); 171 | 172 | ImGui::Text("Resolution:"); ImGui::SameLine(); 173 | if (ImGui::BeginCombo("##combo0", ResolutionName[options.res])) { 174 | for (int n = 0; n < RESOLUTION_NUM; n++) { 175 | bool is_selected = options.res == n; 176 | if (ImGui::Selectable(ResolutionName[n], is_selected)) 177 | options.res = n; 178 | SetDescription(OPT_RESOLUTION); 179 | if (is_selected) 180 | ImGui::SetItemDefaultFocus(); 181 | } 182 | ImGui::EndCombo(); 183 | } 184 | SetDescription(OPT_RESOLUTION); 185 | 186 | ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0.0f, 0.0f)); 187 | ImGui::Text("Bilinear Filter:"); ImGui::SameLine(); 188 | ImGui::Checkbox("##check0", &bilinear_filter); 189 | SetDescription(OPT_BILINEAR); 190 | ImGui::PopStyleVar(); 191 | 192 | ImGui::Text("Anti-Aliasing:"); ImGui::SameLine(); 193 | if (ImGui::BeginCombo("##combo1", AntiAliasingName[options.msaa])) { 194 | for (int n = 0; n < ANTI_ALIASING_NUM; n++) { 195 | bool is_selected = options.msaa == n; 196 | if (ImGui::Selectable(AntiAliasingName[n], is_selected)) 197 | options.msaa = n; 198 | SetDescription(OPT_ANTIALIASING); 199 | if (is_selected) 200 | ImGui::SetItemDefaultFocus(); 201 | } 202 | ImGui::EndCombo(); 203 | } 204 | SetDescription(OPT_ANTIALIASING); 205 | 206 | ImGui::Text("PostFX Effect:"); ImGui::SameLine(); 207 | if (ImGui::BeginCombo("##combo2", PostFxName[options.postfx])) { 208 | for (int n = 0; n < PostFxNum; n++) { 209 | bool is_selected = options.postfx == n; 210 | if (ImGui::Selectable(PostFxName[n], is_selected)) 211 | options.postfx = n; 212 | SetDescription(OPT_POSTFX); 213 | if (is_selected) 214 | ImGui::SetItemDefaultFocus(); 215 | } 216 | ImGui::EndCombo(); 217 | } 218 | SetDescription(OPT_POSTFX); 219 | 220 | ImGui::Separator(); 221 | ImGui::TextColored(ImVec4(255, 255, 0, 255), "Misc"); 222 | 223 | ImGui::Text("Language:"); ImGui::SameLine(); 224 | if (ImGui::BeginCombo("##combo3", LanguageName[options.lang])) { 225 | for (int n = 0; n < LANGUAGES_NUM; n++) { 226 | bool is_selected = options.lang == n; 227 | if (ImGui::Selectable(LanguageName[n], is_selected)) 228 | options.lang = n; 229 | SetDescription(OPT_LANGUAGE); 230 | if (is_selected) 231 | ImGui::SetItemDefaultFocus(); 232 | } 233 | ImGui::EndCombo(); 234 | } 235 | SetDescription(OPT_LANGUAGE); 236 | 237 | ImGui::Separator(); 238 | 239 | if (ImGui::Button("Save and Exit")) 240 | exit_code = 0; 241 | ImGui::SameLine(); 242 | if (ImGui::Button("Save and Launch the game")) 243 | exit_code = 1; 244 | ImGui::SameLine(); 245 | if (ImGui::Button("Discard and Exit")) 246 | exit_code = 2; 247 | ImGui::SameLine(); 248 | if (ImGui::Button("Discard and Launch the game")) 249 | exit_code = 3; 250 | ImGui::Separator(); 251 | 252 | if (desc) { 253 | ImGui::Spacing(); 254 | ImGui::Spacing(); 255 | ImGui::TextWrapped(desc); 256 | } 257 | ImGui::End(); 258 | 259 | glViewport(0, 0, static_cast(ImGui::GetIO().DisplaySize.x), static_cast(ImGui::GetIO().DisplaySize.y)); 260 | ImGui::Render(); 261 | ImGui_ImplVitaGL_RenderDrawData(ImGui::GetDrawData()); 262 | vglSwapBuffers(GL_FALSE); 263 | } 264 | 265 | if (exit_code < 2) // Save 266 | saveOptions(); 267 | 268 | if (exit_code % 2 == 1) // Launch 269 | sceAppMgrLoadExec("app0:/eboot.bin", NULL, NULL); 270 | 271 | return 0; 272 | } -------------------------------------------------------------------------------- /icudt59l/cnvalias.icu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/icudt59l/cnvalias.icu -------------------------------------------------------------------------------- /icudt59l/confusables.cfu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/icudt59l/confusables.cfu -------------------------------------------------------------------------------- /icudt59l/ibm-5348_P100-1997.cnv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/icudt59l/ibm-5348_P100-1997.cnv -------------------------------------------------------------------------------- /icudt59l/ibm-943_P15A-2003.cnv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/icudt59l/ibm-943_P15A-2003.cnv -------------------------------------------------------------------------------- /icudt59l/unames.icu: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/icudt59l/unames.icu -------------------------------------------------------------------------------- /loader/bridge.c: -------------------------------------------------------------------------------- 1 | #include "bridge.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "stb_image.h" 11 | #include "stb_truetype.h" 12 | 13 | #include "zlib.h" 14 | #include 15 | #include 16 | #include 17 | 18 | #include "config.h" 19 | #include "dialog.h" 20 | 21 | #include "shaders/movie_f.h" 22 | #include "shaders/movie_v.h" 23 | 24 | #define SAVE_FILENAME "ux0:/data/ff3" 25 | #define OBB_FILE "ux0:/data/ff3/main.obb" 26 | 27 | #define FB_ALIGNMENT 0x40000 28 | #define ALIGN_MEM(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 29 | 30 | extern float postfx_pos[8]; 31 | extern float postfx_texcoord[8]; 32 | extern uint8_t in_movie; 33 | 34 | SceAvPlayerHandle movie_player; 35 | 36 | GLuint movie_frame[2]; 37 | uint8_t movie_frame_idx = 0; 38 | SceGxmTexture *movie_tex[2]; 39 | GLuint movie_fs; 40 | GLuint movie_vs; 41 | GLuint movie_prog; 42 | 43 | SceUID audio_thid; 44 | int audio_new; 45 | int audio_port; 46 | int audio_len; 47 | int audio_freq; 48 | int audio_mode; 49 | 50 | enum { 51 | PLAYER_INACTIVE, 52 | PLAYER_ACTIVE, 53 | PLAYER_STOP, 54 | }; 55 | 56 | int player_state = PLAYER_INACTIVE; 57 | 58 | void *mem_alloc(void *p, uint32_t align, uint32_t size) { 59 | return memalign(align, size); 60 | } 61 | 62 | void mem_free(void *p, void *ptr) { 63 | free(ptr); 64 | } 65 | 66 | void *gpu_alloc(void *p, uint32_t align, uint32_t size) { 67 | if (align < FB_ALIGNMENT) { 68 | align = FB_ALIGNMENT; 69 | } 70 | size = ALIGN_MEM(size, align); 71 | return vglAlloc(size, VGL_MEM_SLOW); 72 | } 73 | 74 | void gpu_free(void *p, void *ptr) { 75 | glFinish(); 76 | vglFree(ptr); 77 | } 78 | 79 | void movie_player_audio_init(void) { 80 | audio_port = -1; 81 | for (int i = 0; i < 8; i++) { 82 | if (sceAudioOutGetConfig(i, SCE_AUDIO_OUT_CONFIG_TYPE_LEN) >= 0) { 83 | audio_port = i; 84 | break; 85 | } 86 | } 87 | 88 | if (audio_port == -1) { 89 | audio_port = sceAudioOutOpenPort(SCE_AUDIO_OUT_PORT_TYPE_MAIN, 1024, 48000, SCE_AUDIO_OUT_MODE_STEREO); 90 | audio_new = 1; 91 | } else { 92 | audio_len = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_LEN); 93 | audio_freq = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_FREQ); 94 | audio_mode = sceAudioOutGetConfig(audio_port, SCE_AUDIO_OUT_CONFIG_TYPE_MODE); 95 | audio_new = 0; 96 | } 97 | } 98 | 99 | void movie_player_audio_shutdown(void) { 100 | if (audio_new) { 101 | sceAudioOutReleasePort(audio_port); 102 | } else { 103 | sceAudioOutSetConfig(audio_port, audio_len, audio_freq, (SceAudioOutMode)audio_mode); 104 | } 105 | } 106 | 107 | int movie_player_audio_thread(SceSize args, void *argp) { 108 | SceAvPlayerFrameInfo frame; 109 | memset(&frame, 0, sizeof(SceAvPlayerFrameInfo)); 110 | 111 | while (player_state == PLAYER_ACTIVE && sceAvPlayerIsActive(movie_player)) { 112 | if (sceAvPlayerGetAudioData(movie_player, &frame)) { 113 | sceAudioOutSetConfig(audio_port, 1024, frame.details.audio.sampleRate, frame.details.audio.channelCount == 1 ? SCE_AUDIO_OUT_MODE_MONO : SCE_AUDIO_OUT_MODE_STEREO); 114 | sceAudioOutOutput(audio_port, frame.pData); 115 | } else { 116 | sceKernelDelayThread(1000); 117 | } 118 | } 119 | 120 | return sceKernelExitDeleteThread(0); 121 | } 122 | 123 | int movie_first_frame_drawn; 124 | void movie_player_draw(void) { 125 | if (player_state == PLAYER_ACTIVE) { 126 | if (sceAvPlayerIsActive(movie_player)) { 127 | SceAvPlayerFrameInfo frame; 128 | if (sceAvPlayerGetVideoData(movie_player, &frame)) { 129 | movie_first_frame_drawn = 1; 130 | movie_frame_idx = (movie_frame_idx + 1) % 2; 131 | sceGxmTextureInitLinear( 132 | movie_tex[movie_frame_idx], 133 | frame.pData, 134 | SCE_GXM_TEXTURE_FORMAT_YVU420P2_CSC1, 135 | frame.details.video.width, 136 | frame.details.video.height, 0); 137 | sceGxmTextureSetMinFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR); 138 | sceGxmTextureSetMagFilter(movie_tex[movie_frame_idx], SCE_GXM_TEXTURE_FILTER_LINEAR); 139 | } 140 | if (movie_first_frame_drawn) { 141 | glUseProgram(movie_prog); 142 | glBindTexture(GL_TEXTURE_2D, movie_frame[movie_frame_idx]); 143 | glBindBuffer(GL_ARRAY_BUFFER, 0); 144 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 145 | glEnableVertexAttribArray(0); 146 | glEnableVertexAttribArray(1); 147 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &postfx_pos[0]); 148 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &postfx_texcoord[0]); 149 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 150 | glUseProgram(0); 151 | } 152 | } else { 153 | player_state = PLAYER_STOP; 154 | } 155 | } 156 | 157 | if (player_state == PLAYER_STOP) { 158 | sceAvPlayerStop(movie_player); 159 | sceKernelWaitThreadEnd(audio_thid, NULL, NULL); 160 | sceAvPlayerClose(movie_player); 161 | movie_player_audio_shutdown(); 162 | player_state = PLAYER_INACTIVE; 163 | glClear(GL_COLOR_BUFFER_BIT); 164 | } 165 | } 166 | 167 | int movie_player_inited = 0; 168 | void movie_player_init() { 169 | if (movie_player_inited) 170 | return; 171 | 172 | sceSysmoduleLoadModule(SCE_SYSMODULE_AVPLAYER); 173 | 174 | glGenTextures(2, movie_frame); 175 | for (int i = 0; i < 2; i++) { 176 | glBindTexture(GL_TEXTURE_2D, movie_frame[i]); 177 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 960, 544, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 178 | movie_tex[i] = vglGetGxmTexture(GL_TEXTURE_2D); 179 | vglFree(vglGetTexDataPointer(GL_TEXTURE_2D)); 180 | } 181 | 182 | movie_vs = glCreateShader(GL_VERTEX_SHADER); 183 | glShaderBinary(1, &movie_vs, 0, movie_v, size_movie_v); 184 | 185 | movie_fs = glCreateShader(GL_FRAGMENT_SHADER); 186 | glShaderBinary(1, &movie_fs, 0, movie_f, size_movie_f); 187 | 188 | movie_prog = glCreateProgram(); 189 | glAttachShader(movie_prog, movie_vs); 190 | glAttachShader(movie_prog, movie_fs); 191 | glBindAttribLocation(movie_prog, 0, "inPos"); 192 | glBindAttribLocation(movie_prog, 1, "inTex"); 193 | glLinkProgram(movie_prog); 194 | 195 | movie_player_inited = 1; 196 | } 197 | 198 | 199 | void playMovie() { 200 | SceIoStat st; 201 | if (sceIoGetstat("ux0:data/ff3/opening.mp4", &st) < 0) 202 | return; 203 | 204 | movie_player_init(); 205 | movie_player_audio_init(); 206 | 207 | SceAvPlayerInitData playerInit; 208 | sceClibMemset(&playerInit, 0, sizeof(SceAvPlayerInitData)); 209 | 210 | playerInit.memoryReplacement.allocate = mem_alloc; 211 | playerInit.memoryReplacement.deallocate = mem_free; 212 | playerInit.memoryReplacement.allocateTexture = gpu_alloc; 213 | playerInit.memoryReplacement.deallocateTexture = gpu_free; 214 | 215 | playerInit.basePriority = 0xA0; 216 | playerInit.numOutputVideoFrameBuffers = 2; 217 | playerInit.autoStart = GL_TRUE; 218 | 219 | movie_player = sceAvPlayerInit(&playerInit); 220 | 221 | sceAvPlayerAddSource(movie_player, "ux0:data/ff3/opening.mp4"); 222 | 223 | audio_thid = sceKernelCreateThread("movie_audio_thread", movie_player_audio_thread, 0x10000100 - 10, 0x4000, 0, 0, NULL); 224 | sceKernelStartThread(audio_thid, 0, NULL); 225 | 226 | player_state = PLAYER_ACTIVE; 227 | movie_first_frame_drawn = 0; 228 | } 229 | 230 | void stopMovie() { 231 | player_state = PLAYER_STOP; 232 | } 233 | 234 | uint8_t getMovieState() { 235 | movie_player_draw(); 236 | if (!(player_state == PLAYER_ACTIVE && sceAvPlayerIsActive(movie_player))) 237 | return 0; 238 | return 1; 239 | } 240 | 241 | unsigned char *header = NULL; 242 | int header_length = 0; 243 | 244 | void decodeArray(unsigned char *bArr, int size, uint key) { 245 | for (int n = 0; n < size; n++) { 246 | key = key * 0x41c64e6d + 0x3039; 247 | bArr[n] = bArr[n] ^ (unsigned char)(key >> 0x18); 248 | } 249 | } 250 | 251 | static int getInt(unsigned char *bArr, int i) { 252 | return *(unsigned int *)(&bArr[i]); 253 | } 254 | 255 | unsigned char *gzipRead(unsigned char *bArr, int *bArr_length) { 256 | unsigned int readInt = __builtin_bswap32(getInt(bArr, 0)); 257 | unsigned char *bArr2 = calloc(readInt, sizeof(unsigned char)); 258 | unsigned char *bArr3 = &bArr[4]; 259 | 260 | z_stream infstream; 261 | infstream.zalloc = Z_NULL; 262 | infstream.zfree = Z_NULL; 263 | infstream.opaque = Z_NULL; 264 | // setup "b" as the input and "c" as the compressed output 265 | infstream.avail_in = *bArr_length - 4; // size of input 266 | infstream.next_in = bArr3; // input char array 267 | infstream.avail_out = readInt; // size of output 268 | infstream.next_out = bArr2; // output char array 269 | 270 | // the actual DE-compression work. 271 | inflateInit2(&infstream, MAX_WBITS | 16); 272 | inflate(&infstream, Z_FULL_FLUSH); 273 | inflateEnd(&infstream); 274 | 275 | *bArr_length = readInt; 276 | return bArr2; 277 | } 278 | 279 | unsigned char *m476a(char *str, int *file_length) { 280 | int i; 281 | 282 | unsigned char *bArr = header; 283 | if (bArr != NULL) { 284 | int a = getInt(bArr, 0); 285 | int i2 = 0; 286 | i = 0; 287 | while (a > i2) { 288 | int i3 = (i2 + a) / 2; 289 | int i4 = i3 * 12; 290 | int a2 = getInt(header, i4 + 4); 291 | int i5 = 0; 292 | for (int i6 = 0; i6 < strlen(str) && i5 == 0; i6++) { 293 | i5 = (header[a2 + i6] & 0xFF) - (str[i6] & 0xFF); 294 | } 295 | if (i5 == 0) { 296 | i5 = header[a2 + strlen(str)] & 0xFF; 297 | } 298 | if (i5 == 0) { 299 | i = i4 + 8; 300 | a = i3; 301 | i2 = a; 302 | } else if (i5 > 0) { 303 | a = i3; 304 | } else { 305 | i2 = i3 + 1; 306 | } 307 | } 308 | } else { 309 | i = 0; 310 | } 311 | if (i == 0) { 312 | return NULL; 313 | } 314 | 315 | const char *a = OBB_FILE; 316 | FILE *fp = fopen(a, "r"); 317 | int a3 = getInt(header, i + 0); 318 | fseek(fp, a3, SEEK_SET); 319 | 320 | *file_length = getInt(header, i + 4); 321 | unsigned char *bArr2 = malloc(*file_length); 322 | 323 | for (int i7 = 0; i7 < *file_length; 324 | i7 += fread(&bArr2[i7], sizeof(unsigned char), *file_length - i7, fp)) { 325 | } 326 | 327 | fclose(fp); 328 | 329 | decodeArray(bArr2, *file_length, a3 + 84861466u); 330 | 331 | unsigned char *a4 = gzipRead(bArr2, file_length); 332 | 333 | free(bArr2); 334 | 335 | return a4; 336 | } 337 | 338 | int readHeader() { 339 | const char *a = OBB_FILE; 340 | FILE *fp = fopen(a, "r"); 341 | fseek(fp, 0L, SEEK_END); 342 | int length = ftell(fp); 343 | fseek(fp, 0L, SEEK_SET); 344 | 345 | unsigned char bArr[16]; 346 | 347 | for (int i = 0; i < 16; 348 | i += fread(&bArr[i], sizeof(unsigned char), 16 - i, fp)) { 349 | } 350 | 351 | decodeArray(bArr, 16, 84861466u); 352 | 353 | if (getInt(bArr, 0) != 826495553) { 354 | printf("initFileTable: Header Error\n"); 355 | return 0; 356 | } else if (length != getInt(bArr, 4)) { 357 | printf("initFileTable: Size Error\n"); 358 | return 0; 359 | } else { 360 | unsigned int a2 = getInt(bArr, 8); 361 | header_length = getInt(bArr, 12); 362 | header = malloc(header_length); 363 | 364 | fseek(fp, (long)(a2 - 16), SEEK_CUR); 365 | 366 | for (int i = 0; i < header_length; 367 | i += fread(&header[i], sizeof(unsigned char), header_length - i, fp)) { 368 | } 369 | 370 | decodeArray(header, header_length, a2 + 84861466u); 371 | 372 | unsigned char *header2 = gzipRead(header, &header_length); 373 | 374 | free(header); 375 | 376 | header = header2; 377 | 378 | fclose(fp); 379 | } 380 | return 1; 381 | } 382 | 383 | void toUtf8(const char *src, size_t length, char *dst, const char *src_encoding, 384 | size_t *dst_length_p) { 385 | 386 | UErrorCode status = U_ZERO_ERROR; 387 | UConverter *conv; 388 | int32_t len; 389 | 390 | uint16_t *temp = malloc(length * 3); 391 | u_setDataDirectory("app0:/"); 392 | conv = ucnv_open(src_encoding, &status); 393 | 394 | len = ucnv_toUChars(conv, temp, length * 3, src, length, &status); 395 | ucnv_close(conv); 396 | 397 | conv = ucnv_open("utf-8", &status); 398 | *dst_length_p = ucnv_fromUChars(conv, dst, length * 3, temp, len, &status); 399 | ucnv_close(conv); 400 | 401 | free(temp); 402 | } 403 | 404 | unsigned char *decodeString(unsigned char *bArr, int *bArr_length) { 405 | 406 | int lang = getCurrentLanguage(); 407 | if (lang >= 6) { 408 | return bArr; 409 | } 410 | 411 | unsigned char *utf8_strings = malloc(*bArr_length * 3); 412 | 413 | int entry_number = *(int *)&bArr[8]; 414 | int header_length = (entry_number * 12) + 16; 415 | 416 | memcpy(utf8_strings, bArr, header_length); 417 | 418 | int utf8_offset = header_length; 419 | int entry_index = 0; 420 | int entry_offset = 16; 421 | 422 | while (entry_index < entry_number) { 423 | *(int *)&utf8_strings[entry_offset + 8] = utf8_offset; 424 | 425 | unsigned char string_number = bArr[entry_offset + 4]; 426 | int string_offset = *(int *)&bArr[entry_offset + 8]; 427 | for (int string_index = 0; string_index < string_number; string_index++) { 428 | int string_length = 0; 429 | while (bArr[string_offset + string_length] != 0) { 430 | string_length++; 431 | } 432 | unsigned int utf8_length = 0; 433 | toUtf8((const char *)&bArr[string_offset], string_length, 434 | (char *)&utf8_strings[utf8_offset], 435 | lang == 0 ? "shift_jis" : "windows-1252", &utf8_length); 436 | 437 | utf8_strings[utf8_offset + utf8_length] = 0; 438 | 439 | string_offset += string_length + 1; 440 | utf8_offset += utf8_length + 1; 441 | } 442 | utf8_offset = utf8_offset + 1; 443 | utf8_strings[utf8_offset] = 0; 444 | entry_offset += 12; 445 | entry_index++; 446 | } 447 | 448 | unsigned char *final_strings = malloc(utf8_offset); 449 | memcpy(final_strings, utf8_strings, utf8_offset); 450 | free(utf8_strings); 451 | *bArr_length = utf8_offset; 452 | return final_strings; 453 | } 454 | 455 | jni_bytearray *loadFile(char *str) { 456 | char *lang[] = {"ja", "en", "fr", "de", "it", 457 | "es", "zh_CN", "zh_TW", "ko", "th"}; 458 | 459 | char *substring = strrchr(str, 46); 460 | 461 | substring = substring == NULL ? str : substring; 462 | char temp_path[512]; 463 | int file_length; 464 | sprintf(temp_path, "%s.lproj/%s", lang[getCurrentLanguage()], str); 465 | unsigned char *a = m476a(temp_path, &file_length); 466 | if (a == NULL) { 467 | sprintf(temp_path, "files/%s", str); 468 | a = m476a(temp_path, &file_length); 469 | } 470 | if (a == NULL) { 471 | return NULL; 472 | } 473 | 474 | jni_bytearray *result = malloc(sizeof(jni_bytearray)); 475 | result->elements = a; 476 | result->size = file_length; 477 | 478 | if (strcmp(substring, ".msd") || str[0] == 'e') 479 | return result; 480 | if (str[0] == 'n' && str[1] == 'o' && str[2] == 'a') 481 | return result; 482 | 483 | unsigned char *b = decodeString(a, &file_length); 484 | 485 | if (b != a) 486 | free(a); 487 | 488 | result->elements = b; 489 | result->size = file_length; 490 | 491 | return result; 492 | } 493 | 494 | jni_bytearray *loadRawFile(char *str) { 495 | 496 | int file_length; 497 | unsigned char *a = m476a(str, &file_length); 498 | 499 | if (a == NULL) { 500 | return NULL; 501 | } 502 | 503 | jni_bytearray *result = malloc(sizeof(jni_bytearray)); 504 | result->elements = a; 505 | result->size = file_length; 506 | 507 | return result; 508 | } 509 | 510 | jni_bytearray *getSaveFileName() { 511 | 512 | char *buffer = SAVE_FILENAME; 513 | jni_bytearray *result = malloc(sizeof(jni_bytearray)); 514 | result->elements = malloc(strlen(buffer) + 1); 515 | // Sets the value 516 | strcpy((char *)result->elements, buffer); 517 | result->size = strlen(buffer) + 1; 518 | 519 | return result; 520 | } 521 | 522 | void createSaveFile(size_t size) { 523 | char *buffer = malloc(size); 524 | FILE *fd = fopen(SAVE_FILENAME "/save.bin", "wb"); 525 | fwrite(buffer, sizeof(char), size, fd); 526 | fclose(fd); 527 | free(buffer); 528 | } 529 | 530 | uint64_t j3 = 0; 531 | 532 | uint64_t getCurrentFrame(uint64_t j) { 533 | 534 | while (1) { 535 | uint64_t j2 = sceKernelGetProcessTimeWide() * 3 / 100000; 536 | if (j2 != j3) { 537 | j3 = j2; 538 | return j2; 539 | } 540 | sceKernelDelayThread(1000); 541 | } 542 | } 543 | 544 | #define RGBA8(r, g, b, a) \ 545 | ((((a)&0xFF) << 24) | (((b)&0xFF) << 16) | (((g)&0xFF) << 8) | \ 546 | (((r)&0xFF) << 0)) 547 | 548 | jni_intarray *loadTexture(jni_bytearray *bArr) { 549 | 550 | jni_intarray *texture = malloc(sizeof(jni_intarray)); 551 | 552 | int x, y, channels_in_file; 553 | unsigned char *temp = stbi_load_from_memory(bArr->elements, bArr->size, &x, 554 | &y, &channels_in_file, 4); 555 | 556 | texture->size = x * y + 2; 557 | texture->elements = malloc(texture->size * sizeof(int)); 558 | texture->elements[0] = x; 559 | texture->elements[1] = y; 560 | 561 | for (int n = 0; n < y; n++) { 562 | for (int m = 0; m < x; m++) { 563 | unsigned char *color = (unsigned char *)&(((uint32_t *)temp)[n * x + m]); 564 | texture->elements[2 + n * x + m] = 565 | RGBA8(color[2], color[1], color[0], color[3]); 566 | } 567 | } 568 | 569 | free(temp); 570 | 571 | return texture; 572 | } 573 | 574 | int isDeviceAndroidTV() { return 1; } 575 | 576 | stbtt_fontinfo *info = NULL; 577 | unsigned char *fontBuffer = NULL; 578 | 579 | void initFont() { 580 | 581 | long size; 582 | 583 | if (info != NULL) 584 | return; 585 | 586 | char font_path[256]; 587 | switch (getCurrentLanguage()) { 588 | case 6: 589 | case 7: 590 | strcpy(font_path, "app0:/NotoSansSC-Regular.ttf"); 591 | break; 592 | case 8: 593 | strcpy(font_path, "app0:/NotoSansKR-Regular.ttf"); 594 | break; 595 | default: 596 | strcpy(font_path, "app0:/NotoSansJP-Regular.ttf"); 597 | break; 598 | } 599 | FILE *fontFile = fopen(font_path, "rb"); 600 | fseek(fontFile, 0, SEEK_END); 601 | size = ftell(fontFile); /* how long is the file ? */ 602 | fseek(fontFile, 0, SEEK_SET); /* reset */ 603 | 604 | fontBuffer = malloc(size); 605 | 606 | fread(fontBuffer, size, 1, fontFile); 607 | fclose(fontFile); 608 | 609 | info = malloc(sizeof(stbtt_fontinfo)); 610 | 611 | /* prepare font */ 612 | if (!stbtt_InitFont(info, fontBuffer, 0)) { 613 | printf("failed\n"); 614 | } 615 | } 616 | 617 | static inline uint32_t utf8_decode_unsafe_2(const char *data) { 618 | uint32_t codepoint; 619 | codepoint = ((data[0] & 0x1F) << 6); 620 | codepoint |= (data[1] & 0x3F); 621 | return codepoint; 622 | } 623 | 624 | static inline uint32_t utf8_decode_unsafe_3(const char *data) { 625 | uint32_t codepoint; 626 | codepoint = ((data[0] & 0x0F) << 12); 627 | codepoint |= (data[1] & 0x3F) << 6; 628 | codepoint |= (data[2] & 0x3F); 629 | return codepoint; 630 | } 631 | 632 | static inline uint32_t utf8_decode_unsafe_4(const char *data) { 633 | uint32_t codepoint; 634 | codepoint = ((data[0] & 0x07) << 18); 635 | codepoint |= (data[1] & 0x3F) << 12; 636 | codepoint |= (data[2] & 0x3F) << 6; 637 | codepoint |= (data[3] & 0x3F); 638 | return codepoint; 639 | } 640 | 641 | jni_intarray *drawFont(char *word, int size, int i2, int i3) { 642 | initFont(); 643 | 644 | jni_intarray *texture = malloc(sizeof(jni_intarray)); 645 | texture->size = size * size + 5; 646 | texture->elements = malloc(texture->size * sizeof(int)); 647 | 648 | int b_w = size; /* bitmap width */ 649 | int b_h = size; /* bitmap height */ 650 | 651 | /* calculate font scaling */ 652 | float scale = stbtt_ScaleForPixelHeight(info, size); 653 | 654 | int ascent, descent, lineGap; 655 | stbtt_GetFontVMetrics(info, &ascent, &descent, &lineGap); 656 | 657 | int i = 0; 658 | while (word[i]) { 659 | i++; 660 | if (i == 4) 661 | break; 662 | } 663 | 664 | int codepoint; 665 | switch (i) { 666 | case 0: // This should never happen 667 | codepoint = 32; 668 | break; 669 | case 2: 670 | codepoint = utf8_decode_unsafe_2(word); 671 | break; 672 | case 3: 673 | codepoint = utf8_decode_unsafe_3(word); 674 | break; 675 | case 4: 676 | codepoint = utf8_decode_unsafe_4(word); 677 | break; 678 | default: 679 | codepoint = word[0]; 680 | break; 681 | } 682 | 683 | int ax; 684 | int lsb; 685 | stbtt_GetCodepointHMetrics(info, codepoint, &ax, &lsb); 686 | 687 | if (codepoint == 32) { 688 | texture->elements[0] = roundf(ax * scale); 689 | return texture; 690 | } 691 | 692 | /* create a bitmap for the phrase */ 693 | unsigned char *bitmap = calloc(b_w * b_h, sizeof(unsigned char)); 694 | 695 | /* get bounding box for character (may be offset to account for chars that dip 696 | * above or below the line) */ 697 | int c_x1, c_y1, c_x2, c_y2; 698 | stbtt_GetCodepointBitmapBox(info, codepoint, scale, scale, &c_x1, &c_y1, 699 | &c_x2, &c_y2); 700 | 701 | /* compute y (different characters have different heights) */ 702 | int y = roundf(ascent * scale) + c_y1; 703 | 704 | /* render character (stride and offset is important here) */ 705 | int byteOffset = roundf(lsb * scale) + (y * b_w); 706 | 707 | stbtt_MakeCodepointBitmap(info, bitmap + byteOffset, c_x2 - c_x1, c_y2 - c_y1, 708 | b_w, scale, scale, codepoint); 709 | 710 | texture->elements[0] = (c_x2 - c_x1 + roundf(lsb * scale)); 711 | texture->elements[1] = 0; 712 | texture->elements[2] = 0; 713 | 714 | for (int n = 0; n < size * size; n++) { 715 | texture->elements[5 + n] = 716 | RGBA8(bitmap[n], bitmap[n], bitmap[n], bitmap[n]); 717 | } 718 | 719 | free(bitmap); 720 | return texture; 721 | } 722 | 723 | int editText = -1; 724 | 725 | void createEditText(char *str) { editText = init_ime_dialog("", str); } 726 | 727 | extern GLuint fb; 728 | char *getEditText() { 729 | char *result = NULL; 730 | if (!result && editText != -1) { 731 | result = get_ime_dialog_result(); 732 | } 733 | if (result) { 734 | editText = -1; 735 | } 736 | return result; 737 | } 738 | 739 | int getCurrentLanguage() { 740 | 741 | if (options.lang) 742 | return (options.lang - 1); 743 | 744 | int lang = -1; 745 | sceAppUtilSystemParamGetInt(SCE_SYSTEM_PARAM_ID_LANG, &lang); 746 | switch (lang) { 747 | case SCE_SYSTEM_PARAM_LANG_JAPANESE: 748 | return 0; 749 | case SCE_SYSTEM_PARAM_LANG_FRENCH: 750 | return 2; 751 | case SCE_SYSTEM_PARAM_LANG_GERMAN: 752 | return 3; 753 | case SCE_SYSTEM_PARAM_LANG_ITALIAN: 754 | return 4; 755 | case SCE_SYSTEM_PARAM_LANG_SPANISH: 756 | return 5; 757 | case SCE_SYSTEM_PARAM_LANG_CHINESE_S: 758 | return 6; 759 | case SCE_SYSTEM_PARAM_LANG_CHINESE_T: 760 | return 7; 761 | case SCE_SYSTEM_PARAM_LANG_KOREAN: 762 | return 8; 763 | default: 764 | return 1; 765 | } 766 | } 767 | 768 | void loadCompanionApp() { 769 | sceAppMgrLoadExec("app0:/companion.bin", NULL, NULL); 770 | } 771 | -------------------------------------------------------------------------------- /loader/bridge.h: -------------------------------------------------------------------------------- 1 | #ifndef __BRIDGE_H__ 2 | #define __BRIDGE_H__ 3 | 4 | #include 5 | #include 6 | 7 | typedef struct { 8 | unsigned char *elements; 9 | int size; 10 | } jni_bytearray; 11 | 12 | jni_bytearray *loadFile(char *str); 13 | jni_bytearray *loadRawFile(char *str); 14 | jni_bytearray *getSaveFileName(); 15 | void createSaveFile(size_t size); 16 | uint64_t getCurrentFrame(uint64_t j); 17 | int readHeader(); 18 | 19 | typedef struct { 20 | int *elements; 21 | int size; 22 | } jni_intarray; 23 | 24 | jni_intarray *loadTexture(jni_bytearray *bArr); 25 | int isDeviceAndroidTV(); 26 | jni_intarray *drawFont(char *str, int i, int i2, int i3); 27 | 28 | void createEditText(char *str); 29 | char *getEditText(); 30 | 31 | int getCurrentLanguage(); 32 | 33 | void loadCompanionApp(); 34 | 35 | void playMovie(); 36 | uint8_t getMovieState(); 37 | void stopMovie(); 38 | 39 | #endif -------------------------------------------------------------------------------- /loader/config.h: -------------------------------------------------------------------------------- 1 | #ifndef __CONFIG_H__ 2 | #define __CONFIG_H__ 3 | 4 | #define LOAD_ADDRESS 0x98000000 5 | 6 | #define MEMORY_NEWLIB_MB 280 7 | #define MEMORY_VITAGL_THRESHOLD_MB 8 8 | #define AUDIO_SAMPLE_RATE 44100 9 | #define AUDIO_SAMPLES_PER_BUF 8192 10 | 11 | #define DATA_PATH "ux0:data/ff3" 12 | #define SO_PATH DATA_PATH "/" "libff3.so" 13 | #define CONFIG_FILE_PATH DATA_PATH "/" "options.cfg" 14 | 15 | #define DEF_SCREEN_W 960 16 | #define DEF_SCREEN_H 544 17 | 18 | typedef struct { 19 | int res; 20 | int bilinear; 21 | int lang; 22 | int msaa; 23 | int postfx; 24 | } config_opts; 25 | extern config_opts options; 26 | 27 | #endif 28 | -------------------------------------------------------------------------------- /loader/dialog.c: -------------------------------------------------------------------------------- 1 | /* dialog.c -- common dialog for error messages and cheats input 2 | * 3 | * Copyright (C) 2021 fgsfds, Andy Nguyen 4 | * 5 | * This software may be modified and distributed under the terms 6 | * of the MIT license. See the LICENSE file for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include "main.h" 19 | #include "dialog.h" 20 | 21 | static uint16_t ime_title_utf16[SCE_IME_DIALOG_MAX_TITLE_LENGTH]; 22 | static uint16_t ime_initial_text_utf16[SCE_IME_DIALOG_MAX_TEXT_LENGTH]; 23 | static uint16_t ime_input_text_utf16[SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1]; 24 | static uint8_t ime_input_text_utf8[SCE_IME_DIALOG_MAX_TEXT_LENGTH + 1]; 25 | 26 | void utf16_to_utf8(const uint16_t *src, uint8_t *dst) { 27 | for (int i = 0; src[i]; i++) { 28 | if ((src[i] & 0xFF80) == 0) { 29 | *(dst++) = src[i] & 0xFF; 30 | } else if((src[i] & 0xF800) == 0) { 31 | *(dst++) = ((src[i] >> 6) & 0xFF) | 0xC0; 32 | *(dst++) = (src[i] & 0x3F) | 0x80; 33 | } else if((src[i] & 0xFC00) == 0xD800 && (src[i + 1] & 0xFC00) == 0xDC00) { 34 | *(dst++) = (((src[i] + 64) >> 8) & 0x3) | 0xF0; 35 | *(dst++) = (((src[i] >> 2) + 16) & 0x3F) | 0x80; 36 | *(dst++) = ((src[i] >> 4) & 0x30) | 0x80 | ((src[i + 1] << 2) & 0xF); 37 | *(dst++) = (src[i + 1] & 0x3F) | 0x80; 38 | i += 1; 39 | } else { 40 | *(dst++) = ((src[i] >> 12) & 0xF) | 0xE0; 41 | *(dst++) = ((src[i] >> 6) & 0x3F) | 0x80; 42 | *(dst++) = (src[i] & 0x3F) | 0x80; 43 | } 44 | } 45 | 46 | *dst = '\0'; 47 | } 48 | 49 | void utf8_to_utf16(const uint8_t *src, uint16_t *dst) { 50 | for (int i = 0; src[i];) { 51 | if ((src[i] & 0xE0) == 0xE0) { 52 | *(dst++) = ((src[i] & 0x0F) << 12) | ((src[i + 1] & 0x3F) << 6) | (src[i + 2] & 0x3F); 53 | i += 3; 54 | } else if ((src[i] & 0xC0) == 0xC0) { 55 | *(dst++) = ((src[i] & 0x1F) << 6) | (src[i + 1] & 0x3F); 56 | i += 2; 57 | } else { 58 | *(dst++) = src[i]; 59 | i += 1; 60 | } 61 | } 62 | 63 | *dst = '\0'; 64 | } 65 | 66 | int init_ime_dialog(const char *title, const char *initial_text) { 67 | memset(ime_title_utf16, 0, sizeof(ime_title_utf16)); 68 | memset(ime_initial_text_utf16, 0, sizeof(ime_initial_text_utf16)); 69 | memset(ime_input_text_utf16, 0, sizeof(ime_input_text_utf16)); 70 | memset(ime_input_text_utf8, 0, sizeof(ime_input_text_utf8)); 71 | 72 | utf8_to_utf16((uint8_t *)title, ime_title_utf16); 73 | utf8_to_utf16((uint8_t *)initial_text, ime_initial_text_utf16); 74 | 75 | SceImeDialogParam param; 76 | sceImeDialogParamInit(¶m); 77 | 78 | param.supportedLanguages = 0x0001FFFF; 79 | param.languagesForced = SCE_TRUE; 80 | param.type = SCE_IME_TYPE_BASIC_LATIN; 81 | param.title = ime_title_utf16; 82 | param.maxTextLength = SCE_IME_DIALOG_MAX_TEXT_LENGTH; 83 | param.initialText = ime_initial_text_utf16; 84 | param.inputTextBuffer = ime_input_text_utf16; 85 | 86 | return sceImeDialogInit(¶m); 87 | } 88 | 89 | char *get_ime_dialog_result(void) { 90 | if (sceImeDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) 91 | return NULL; 92 | 93 | SceImeDialogResult result; 94 | memset(&result, 0, sizeof(SceImeDialogResult)); 95 | sceImeDialogGetResult(&result); 96 | if (result.button == SCE_IME_DIALOG_BUTTON_ENTER) 97 | utf16_to_utf8(ime_input_text_utf16, ime_input_text_utf8); 98 | sceImeDialogTerm(); 99 | // For some reason analog stick stops working after ime 100 | sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); 101 | 102 | return (char *)ime_input_text_utf8; 103 | } 104 | 105 | int init_msg_dialog(const char *msg) { 106 | SceMsgDialogUserMessageParam msg_param; 107 | memset(&msg_param, 0, sizeof(msg_param)); 108 | msg_param.buttonType = SCE_MSG_DIALOG_BUTTON_TYPE_OK; 109 | msg_param.msg = (SceChar8 *)msg; 110 | 111 | SceMsgDialogParam param; 112 | sceMsgDialogParamInit(¶m); 113 | _sceCommonDialogSetMagicNumber(¶m.commonParam); 114 | param.mode = SCE_MSG_DIALOG_MODE_USER_MSG; 115 | param.userMsgParam = &msg_param; 116 | 117 | return sceMsgDialogInit(¶m); 118 | } 119 | 120 | int get_msg_dialog_result(void) { 121 | if (sceMsgDialogGetStatus() != SCE_COMMON_DIALOG_STATUS_FINISHED) 122 | return 0; 123 | sceMsgDialogTerm(); 124 | return 1; 125 | } 126 | 127 | void fatal_error(const char *fmt, ...) { 128 | va_list list; 129 | char string[512]; 130 | 131 | va_start(list, fmt); 132 | vsnprintf(string, sizeof(string), fmt, list); 133 | va_end(list); 134 | 135 | vglInit(0); 136 | 137 | init_msg_dialog(string); 138 | 139 | while (!get_msg_dialog_result()) 140 | vglSwapBuffers(GL_TRUE); 141 | 142 | sceKernelExitProcess(0); 143 | while (1); 144 | } 145 | -------------------------------------------------------------------------------- /loader/dialog.h: -------------------------------------------------------------------------------- 1 | #ifndef __DIALOG_H__ 2 | #define __DIALOG_H__ 3 | 4 | int init_ime_dialog(const char *title, const char *initial_text); 5 | char *get_ime_dialog_result(void); 6 | 7 | int init_msg_dialog(const char *msg); 8 | int get_msg_dialog_result(void); 9 | 10 | void fatal_error(const char *fmt, ...) __attribute__((noreturn)); 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /loader/main.c: -------------------------------------------------------------------------------- 1 | /* main.c -- Final Fantasy 3 .so loader 2 | * 3 | * Copyright (C) 2021 Andy Nguyen 4 | * Copyright (C) 2021 Francisco José García García 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 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | #include 24 | #include 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | #include 35 | 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | #include "bridge.h" 42 | #include "config.h" 43 | #include "dialog.h" 44 | #include "main.h" 45 | #include "so_util.h" 46 | 47 | float postfx_pos[8] = { 48 | -1.0f, 1.0f, 49 | -1.0f, -1.0f, 50 | 1.0f, 1.0f, 51 | 1.0f, -1.0f 52 | }; 53 | 54 | float postfx_texcoord[8] = { 55 | 0.0f, 0.0f, 56 | 0.0f, 1.0f, 57 | 1.0f, 0.0f, 58 | 1.0f, 1.0f 59 | }; 60 | 61 | int SCREEN_W = DEF_SCREEN_W; 62 | int SCREEN_H = DEF_SCREEN_H; 63 | 64 | config_opts options; 65 | void loadOptions() { 66 | char buffer[30]; 67 | int value; 68 | 69 | FILE *f = fopen(CONFIG_FILE_PATH, "rb"); 70 | if (f) { 71 | while (EOF != fscanf(f, "%[^=]=%d\n", buffer, &value)) { 72 | if (strcmp("resolution", buffer) == 0) 73 | options.res = value; 74 | else if (strcmp("bilinear", buffer) == 0) 75 | options.bilinear = value; 76 | else if (strcmp("language", buffer) == 0) 77 | options.lang = value; 78 | else if (strcmp("antialiasing", buffer) == 0) 79 | options.msaa = value; 80 | else if (strcmp("postfx", buffer) == 0) 81 | options.postfx = value; 82 | } 83 | } else { 84 | options.res = 0; 85 | options.bilinear = 0; 86 | options.lang = 0; 87 | options.msaa = 2; 88 | options.postfx = 0; 89 | } 90 | 91 | switch (options.res) { 92 | case 0: 93 | SCREEN_W = 960; 94 | SCREEN_H = 544; 95 | break; 96 | case 1: 97 | SCREEN_W = 1280; 98 | SCREEN_H = 725; 99 | break; 100 | case 2: 101 | SCREEN_W = 1920; 102 | SCREEN_H = 1088; 103 | break; 104 | } 105 | } 106 | 107 | int _newlib_heap_size_user = MEMORY_NEWLIB_MB * 1024 * 1024; 108 | 109 | int _opensles_user_freq = 32000; 110 | 111 | static so_module ff3_mod; 112 | 113 | void *__wrap_memcpy(void *dest, const void *src, size_t n) { 114 | return sceClibMemcpy(dest, src, n); 115 | } 116 | 117 | void *__wrap_memmove(void *dest, const void *src, size_t n) { 118 | return sceClibMemmove(dest, src, n); 119 | } 120 | 121 | void *__wrap_memset(void *s, int c, size_t n) { return sceClibMemset(s, c, n); } 122 | 123 | int ret0(void) { return 0; } 124 | 125 | int ret1(void) { return 1; } 126 | 127 | int __android_log_print(int prio, const char *tag, const char *fmt, ...) { 128 | #ifdef DEBUG 129 | va_list list; 130 | char string[512]; 131 | 132 | va_start(list, fmt); 133 | vsprintf(string, fmt, list); 134 | va_end(list); 135 | 136 | printf("%s\n", string); 137 | #endif 138 | return 0; 139 | } 140 | 141 | void __assert2(const char *file, int line, const char *func, const char *expr) { 142 | printf("assertion failed:\n%s:%d (%s): %s\n", file, line, func, expr); 143 | } 144 | 145 | static const short _C_toupper_[] = { 146 | -1, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 147 | 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 148 | 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 149 | 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 150 | 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 151 | 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 152 | 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 153 | 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 154 | 0x5f, 0x60, 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 155 | 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 156 | 'W', 'X', 'Y', 'Z', 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 157 | 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 158 | 0x8f, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 159 | 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 160 | 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb0, 0xb1, 0xb2, 161 | 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 162 | 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 163 | 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 164 | 0xd7, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 165 | 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 166 | 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 167 | 0xfb, 0xfc, 0xfd, 0xfe, 0xff}; 168 | 169 | const short *_toupper_tab_ = _C_toupper_; 170 | 171 | enum MethodIDs { 172 | UNKNOWN = 0, 173 | GET_CURRENT_FRAME, /**/ 174 | LOAD_FILE, /**/ 175 | LOAD_RAW_FILE, 176 | GET_LANGUAGE, 177 | GET_SAVEFILENAME, 178 | CREATE_SAVEFILE, 179 | LOAD_TEXTURE, 180 | IS_DEVICE_ANDROID_TV, 181 | DRAW_FONT, 182 | CREATE_EDIT_TEXT, 183 | GET_EDIT_TEXT, 184 | START_DOWNLOAD, 185 | PLAY_MOVIE, 186 | GET_MOVIE_STATE, 187 | STOP_MOVIE 188 | } MethodIDs; 189 | 190 | typedef struct { 191 | char *name; 192 | enum MethodIDs id; 193 | } NameToMethodID; 194 | 195 | static NameToMethodID name_to_method_ids[] = { 196 | {"getCurrentFrame", GET_CURRENT_FRAME}, 197 | {"loadFile", LOAD_FILE}, 198 | {"loadRawFile", LOAD_RAW_FILE}, 199 | {"getLanguage", GET_LANGUAGE}, 200 | {"getSaveFileName", GET_SAVEFILENAME}, 201 | {"createSaveFile", CREATE_SAVEFILE}, 202 | {"loadTexture", LOAD_TEXTURE}, 203 | {"isDeviceAndroidTV", IS_DEVICE_ANDROID_TV}, 204 | {"drawFont", DRAW_FONT}, 205 | {"createEditText", CREATE_EDIT_TEXT}, 206 | {"getEditText", GET_EDIT_TEXT}, 207 | {"startDownload", START_DOWNLOAD}, 208 | {"playMovie", PLAY_MOVIE}, 209 | {"getMovieState", GET_MOVIE_STATE}, 210 | {"stopMovie", STOP_MOVIE}, 211 | }; 212 | 213 | int GetMethodID(void *env, void *class, const char *name, const char *sig) { 214 | 215 | for (int i = 0; i < sizeof(name_to_method_ids) / sizeof(NameToMethodID); 216 | i++) { 217 | if (strcmp(name, name_to_method_ids[i].name) == 0) 218 | return name_to_method_ids[i].id; 219 | } 220 | printf("GetMethodID %s\n", name); 221 | return UNKNOWN; 222 | } 223 | 224 | int GetStaticMethodID(void *env, void *class, const char *name, 225 | const char *sig) { 226 | 227 | for (int i = 0; i < sizeof(name_to_method_ids) / sizeof(NameToMethodID); 228 | i++) { 229 | if (strcmp(name, name_to_method_ids[i].name) == 0) 230 | return name_to_method_ids[i].id; 231 | } 232 | printf("GetMethodID %s\n", name); 233 | 234 | return UNKNOWN; 235 | } 236 | 237 | int CallBooleanMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 238 | switch (methodID) { 239 | default: 240 | return 0; 241 | } 242 | } 243 | 244 | float CallFloatMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 245 | return 0.0f; 246 | } 247 | 248 | int CallIntMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 249 | return 0; 250 | } 251 | 252 | void *CallObjectMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 253 | return NULL; 254 | } 255 | 256 | void CallVoidMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 257 | return; 258 | } 259 | 260 | void *CallStaticObjectMethodV(void *env, void *obj, int methodID, 261 | uintptr_t *args) { 262 | switch (methodID) { 263 | case LOAD_FILE: 264 | return loadFile((char *)args[0]); 265 | case LOAD_RAW_FILE: 266 | return loadRawFile((char *)args[0]); 267 | case GET_SAVEFILENAME: 268 | return getSaveFileName(); 269 | case LOAD_TEXTURE: 270 | return loadTexture((jni_bytearray *)args[0]); 271 | case DRAW_FONT: 272 | return drawFont((char *)args[0], args[1], args[2], args[3]); 273 | case GET_EDIT_TEXT: 274 | return getEditText(); 275 | default: 276 | return NULL; 277 | } 278 | } 279 | 280 | void CallStaticVoidMethodV(void *env, void *obj, int methodID, 281 | uintptr_t *args) { 282 | switch (methodID) { 283 | case CREATE_SAVEFILE: 284 | createSaveFile((size_t)args[0]); 285 | break; 286 | case CREATE_EDIT_TEXT: 287 | createEditText((char *)args[0]); 288 | break; 289 | case START_DOWNLOAD: 290 | loadCompanionApp(); 291 | break; 292 | case PLAY_MOVIE: 293 | playMovie(); 294 | break; 295 | case STOP_MOVIE: 296 | stopMovie(); 297 | break; 298 | default: 299 | return; 300 | } 301 | } 302 | 303 | int CallStaticBooleanMethodV(void *env, void *obj, int methodID, 304 | uintptr_t *args) { 305 | switch (methodID) { 306 | case IS_DEVICE_ANDROID_TV: 307 | return isDeviceAndroidTV(); 308 | case GET_MOVIE_STATE: 309 | return getMovieState(); 310 | default: 311 | return 0; 312 | } 313 | } 314 | 315 | uint64_t CallStaticLongMethodV(void *env, void *obj, int methodID, 316 | uintptr_t *args) { 317 | switch (methodID) { 318 | case GET_CURRENT_FRAME: 319 | return getCurrentFrame((uint64_t)args[0]); 320 | default: 321 | return 0; 322 | } 323 | } 324 | 325 | int CallStaticIntMethodV(void *env, void *obj, int methodID, uintptr_t *args) { 326 | switch (methodID) { 327 | case GET_LANGUAGE: 328 | return getCurrentLanguage(); 329 | default: 330 | return 0; 331 | } 332 | } 333 | 334 | float CallStaticFloatMethodV(void *env, void *obj, int methodID, 335 | uintptr_t *args) { 336 | switch (methodID) { 337 | default: 338 | return 0.0f; 339 | } 340 | } 341 | 342 | void *FindClass(void) { return (void *)0x41414141; } 343 | 344 | void DeleteLocalRef(void *env, void *localRef) { return; } 345 | 346 | void *NewObjectV(void *env, void *clazz, int methodID, va_list args) { 347 | return NULL; 348 | } 349 | 350 | int GetStaticFieldID(void *env, void *clazz, const char *name, 351 | const char *sig) { 352 | return 0; 353 | } 354 | 355 | void *GetStaticObjectField(void *clazz, int fieldID) { return ""; } 356 | 357 | char *NewStringUTF(void *env, char *bytes) { return bytes; } 358 | 359 | char *GetStringUTFChars(void *env, char *string, int *isCopy) { return string; } 360 | 361 | int GetStringUTFLength(void *env, char *string) { return strlen(string); } 362 | 363 | void GetStringUTFRegion(void *env, char *str, int start, int len, char *buf) { 364 | return; 365 | } 366 | 367 | int GetFieldID(void *env, void *clazz, const char *name, const char *sig) { 368 | return 0; 369 | } 370 | 371 | int GetFloatField(void *env, void *obj, int fieldID) { return 0; } 372 | 373 | int GetArrayLength(void *env, jni_bytearray *obj) { return obj->size; } 374 | 375 | void *GetByteArrayElements(void *env, jni_bytearray *obj) { 376 | return obj->elements; 377 | } 378 | 379 | void *NewByteArray(void *env, size_t length) { 380 | jni_bytearray *result = malloc(sizeof(jni_bytearray)); 381 | result->elements = malloc(length); 382 | result->size = length; 383 | return result; 384 | } 385 | 386 | void *GetIntArrayElements(void *env, jni_intarray *obj) { 387 | return obj->elements; 388 | } 389 | 390 | int ReleaseByteArrayElements(void *env, jni_bytearray *obj) { 391 | free(obj->elements); 392 | free(obj); 393 | return 0; 394 | } 395 | 396 | int ReleaseIntArrayElements(void *env, jni_intarray *obj) { 397 | free(obj->elements); 398 | free(obj); 399 | return 0; 400 | } 401 | 402 | void SetByteArrayRegion(void *env, jni_bytearray *array, size_t start, 403 | size_t len, const unsigned char *buf) { 404 | memcpy(array->elements, &buf[start], len); 405 | } 406 | 407 | static char fake_env[0x1000]; 408 | 409 | void InitJNIEnv(void) { 410 | memset(fake_env, 'A', sizeof(fake_env)); 411 | *(uintptr_t *)(fake_env + 0x00) = 412 | (uintptr_t)fake_env; // just point to itself... 413 | *(uintptr_t *)(fake_env + 0x18) = (uintptr_t)FindClass; 414 | // *(uintptr_t *)(fake_env + 0x44) = (uintptr_t)ret0; // ExceptionClear 415 | // *(uintptr_t *)(fake_env + 0x54) = (uintptr_t)NewGlobalRef; 416 | *(uintptr_t *)(fake_env + 0x5C) = (uintptr_t)DeleteLocalRef; 417 | *(uintptr_t *)(fake_env + 0x74) = (uintptr_t)NewObjectV; 418 | *(uintptr_t *)(fake_env + 0x84) = (uintptr_t)GetMethodID; 419 | *(uintptr_t *)(fake_env + 0x8C) = (uintptr_t)CallObjectMethodV; 420 | *(uintptr_t *)(fake_env + 0x98) = (uintptr_t)CallBooleanMethodV; 421 | *(uintptr_t *)(fake_env + 0xC8) = (uintptr_t)CallIntMethodV; 422 | *(uintptr_t *)(fake_env + 0xE0) = (uintptr_t)CallFloatMethodV; 423 | *(uintptr_t *)(fake_env + 0xF8) = (uintptr_t)CallVoidMethodV; 424 | *(uintptr_t *)(fake_env + 0x178) = (uintptr_t)GetFieldID; 425 | *(uintptr_t *)(fake_env + 0x198) = (uintptr_t)GetFloatField; 426 | *(uintptr_t *)(fake_env + 0x1C4) = (uintptr_t)GetStaticMethodID; 427 | *(uintptr_t *)(fake_env + 0x1CC) = (uintptr_t)CallStaticObjectMethodV; 428 | *(uintptr_t *)(fake_env + 0x1D8) = (uintptr_t)CallStaticBooleanMethodV; 429 | *(uintptr_t *)(fake_env + 0x208) = (uintptr_t)CallStaticIntMethodV; 430 | *(uintptr_t *)(fake_env + 0x214) = (uintptr_t)CallStaticLongMethodV; 431 | *(uintptr_t *)(fake_env + 0x220) = (uintptr_t)CallStaticFloatMethodV; 432 | *(uintptr_t *)(fake_env + 0x238) = (uintptr_t)CallStaticVoidMethodV; 433 | *(uintptr_t *)(fake_env + 0x240) = (uintptr_t)GetStaticFieldID; 434 | *(uintptr_t *)(fake_env + 0x244) = (uintptr_t)GetStaticObjectField; 435 | *(uintptr_t *)(fake_env + 0x29C) = (uintptr_t)NewStringUTF; 436 | *(uintptr_t *)(fake_env + 0x2A0) = (uintptr_t)GetStringUTFLength; 437 | *(uintptr_t *)(fake_env + 0x2A4) = (uintptr_t)GetStringUTFChars; 438 | *(uintptr_t *)(fake_env + 0x2A8) = (uintptr_t)ret0; // ReleaseStringUTFChars 439 | *(uintptr_t *)(fake_env + 0x2AC) = (uintptr_t)GetArrayLength; 440 | *(uintptr_t *)(fake_env + 0x2E0) = (uintptr_t)GetByteArrayElements; 441 | *(uintptr_t *)(fake_env + 0x2C0) = (uintptr_t)NewByteArray; 442 | *(uintptr_t *)(fake_env + 0x2EC) = (uintptr_t)GetIntArrayElements; 443 | *(uintptr_t *)(fake_env + 0x300) = (uintptr_t)ReleaseByteArrayElements; 444 | *(uintptr_t *)(fake_env + 0x30C) = (uintptr_t)ReleaseIntArrayElements; 445 | *(uintptr_t *)(fake_env + 0x340) = (uintptr_t)SetByteArrayRegion; 446 | 447 | // *(uintptr_t *)(fake_env + 0x35C) = (uintptr_t)RegisterNatives; 448 | *(uintptr_t *)(fake_env + 0x374) = (uintptr_t)GetStringUTFRegion; 449 | } 450 | 451 | void *Android_JNI_GetEnv(void) { return fake_env; } 452 | 453 | int pthread_mutex_init_fake(pthread_mutex_t **uid, 454 | const pthread_mutexattr_t *mutexattr) { 455 | pthread_mutex_t *m = calloc(1, sizeof(pthread_mutex_t)); 456 | if (!m) 457 | return -1; 458 | 459 | const int recursive = (mutexattr && *(const int *)mutexattr == 1); 460 | *m = recursive ? PTHREAD_RECURSIVE_MUTEX_INITIALIZER 461 | : PTHREAD_MUTEX_INITIALIZER; 462 | 463 | int ret = pthread_mutex_init(m, mutexattr); 464 | if (ret < 0) { 465 | free(m); 466 | return -1; 467 | } 468 | 469 | *uid = m; 470 | 471 | return 0; 472 | } 473 | 474 | int pthread_mutex_destroy_fake(pthread_mutex_t **uid) { 475 | if (uid && *uid && (uintptr_t)*uid > 0x8000) { 476 | pthread_mutex_destroy(*uid); 477 | free(*uid); 478 | *uid = NULL; 479 | } 480 | return 0; 481 | } 482 | 483 | int pthread_mutex_lock_fake(pthread_mutex_t **uid) { 484 | int ret = 0; 485 | if (!*uid) { 486 | ret = pthread_mutex_init_fake(uid, NULL); 487 | } else if ((uintptr_t)*uid == 0x4000) { 488 | pthread_mutexattr_t attr; 489 | pthread_mutexattr_init(&attr); 490 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 491 | ret = pthread_mutex_init_fake(uid, &attr); 492 | pthread_mutexattr_destroy(&attr); 493 | } else if ((uintptr_t)*uid == 0x8000) { 494 | pthread_mutexattr_t attr; 495 | pthread_mutexattr_init(&attr); 496 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 497 | ret = pthread_mutex_init_fake(uid, &attr); 498 | pthread_mutexattr_destroy(&attr); 499 | } 500 | if (ret < 0) 501 | return ret; 502 | return pthread_mutex_lock(*uid); 503 | } 504 | 505 | int pthread_mutex_unlock_fake(pthread_mutex_t **uid) { 506 | int ret = 0; 507 | if (!*uid) { 508 | ret = pthread_mutex_init_fake(uid, NULL); 509 | } else if ((uintptr_t)*uid == 0x4000) { 510 | pthread_mutexattr_t attr; 511 | pthread_mutexattr_init(&attr); 512 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); 513 | ret = pthread_mutex_init_fake(uid, &attr); 514 | pthread_mutexattr_destroy(&attr); 515 | } else if ((uintptr_t)*uid == 0x8000) { 516 | pthread_mutexattr_t attr; 517 | pthread_mutexattr_init(&attr); 518 | pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); 519 | ret = pthread_mutex_init_fake(uid, &attr); 520 | pthread_mutexattr_destroy(&attr); 521 | } 522 | if (ret < 0) 523 | return ret; 524 | return pthread_mutex_unlock(*uid); 525 | } 526 | 527 | int pthread_cond_init_fake(pthread_cond_t **cnd, const int *condattr) { 528 | pthread_cond_t *c = calloc(1, sizeof(pthread_cond_t)); 529 | if (!c) 530 | return -1; 531 | 532 | *c = PTHREAD_COND_INITIALIZER; 533 | 534 | int ret = pthread_cond_init(c, NULL); 535 | if (ret < 0) { 536 | free(c); 537 | return -1; 538 | } 539 | 540 | *cnd = c; 541 | 542 | return 0; 543 | } 544 | 545 | int pthread_cond_broadcast_fake(pthread_cond_t **cnd) { 546 | if (!*cnd) { 547 | if (pthread_cond_init_fake(cnd, NULL) < 0) 548 | return -1; 549 | } 550 | return pthread_cond_broadcast(*cnd); 551 | } 552 | 553 | int pthread_cond_signal_fake(pthread_cond_t **cnd) { 554 | if (!*cnd) { 555 | if (pthread_cond_init_fake(cnd, NULL) < 0) 556 | return -1; 557 | } 558 | return pthread_cond_signal(*cnd); 559 | } 560 | 561 | int pthread_cond_destroy_fake(pthread_cond_t **cnd) { 562 | if (cnd && *cnd) { 563 | pthread_cond_destroy(*cnd); 564 | free(*cnd); 565 | *cnd = NULL; 566 | } 567 | return 0; 568 | } 569 | 570 | int pthread_cond_wait_fake(pthread_cond_t **cnd, pthread_mutex_t **mtx) { 571 | if (!*cnd) { 572 | if (pthread_cond_init_fake(cnd, NULL) < 0) 573 | return -1; 574 | } 575 | return pthread_cond_wait(*cnd, *mtx); 576 | } 577 | 578 | int pthread_cond_timedwait_fake(pthread_cond_t **cnd, pthread_mutex_t **mtx, 579 | const struct timespec *t) { 580 | if (!*cnd) { 581 | if (pthread_cond_init_fake(cnd, NULL) < 0) 582 | return -1; 583 | } 584 | return pthread_cond_timedwait(*cnd, *mtx, t); 585 | } 586 | 587 | int pthread_create_fake(pthread_t *thread, const void *unused, void *entry, 588 | void *arg) { 589 | return pthread_create(thread, NULL, entry, arg); 590 | } 591 | 592 | int pthread_once_fake(volatile int *once_control, void (*init_routine)(void)) { 593 | if (!once_control || !init_routine) 594 | return -1; 595 | if (__sync_lock_test_and_set(once_control, 1) == 0) 596 | (*init_routine)(); 597 | return 0; 598 | } 599 | 600 | int gettid(void) { return sceKernelGetThreadId(); } 601 | 602 | char *getcwd(char *buf, size_t size) { 603 | if (buf) { 604 | buf[0] = '\0'; 605 | return buf; 606 | } 607 | return NULL; 608 | } 609 | 610 | int this_width; 611 | int this_height; 612 | 613 | void setup_viewport(int width, int height) { 614 | int i3 = width * 3; 615 | if (i3 >= height * 4) { 616 | this_height = height; 617 | this_width = width; 618 | if (this_width > (this_height * 21) / 9) { 619 | this_width = (this_height * 21) / 9; 620 | } 621 | } else { 622 | this_width = width; 623 | this_height = i3 / 4; 624 | } 625 | int x = (width - this_width) / 2; 626 | int y = (height - this_height) / 2; 627 | glViewport(x, y, this_width, this_height); 628 | } 629 | 630 | GLuint frag, vert, fb, fb_tex, postfx_prog; 631 | uint16_t *postfx_indices; 632 | float *postfx_texcoords, *postfx_vertices; 633 | 634 | void loadShader(int is_vertex, char *file) { 635 | SceIoStat st; 636 | sceIoGetstat(file, &st); 637 | char *code = (char*)malloc(st.st_size); 638 | 639 | FILE *f = fopen(file, "rb"); 640 | fread(code, 1, st.st_size, f); 641 | fclose(f); 642 | 643 | GLint len = st.st_size - 1; 644 | GLuint shad = is_vertex ? vert : frag; 645 | glShaderSource(shad, 1, &code, &len); 646 | glCompileShader(shad); 647 | 648 | free(code); 649 | } 650 | 651 | extern int editText; 652 | int main_thread(SceSize args, void *argp) { 653 | 654 | int has_low_res; 655 | 656 | switch (options.msaa) { 657 | case 0: 658 | has_low_res = vglInitExtended(0, SCREEN_W, SCREEN_H, 659 | MEMORY_VITAGL_THRESHOLD_MB * 1024 * 1024, 660 | SCE_GXM_MULTISAMPLE_NONE); 661 | break; 662 | case 1: 663 | has_low_res = vglInitExtended(0, SCREEN_W, SCREEN_H, 664 | MEMORY_VITAGL_THRESHOLD_MB * 1024 * 1024, 665 | SCE_GXM_MULTISAMPLE_2X); 666 | break; 667 | default: 668 | has_low_res = vglInitExtended(0, SCREEN_W, SCREEN_H, 669 | MEMORY_VITAGL_THRESHOLD_MB * 1024 * 1024, 670 | SCE_GXM_MULTISAMPLE_4X); 671 | break; 672 | } 673 | if (has_low_res) { 674 | SCREEN_W = DEF_SCREEN_W; 675 | SCREEN_H = DEF_SCREEN_H; 676 | } 677 | 678 | if (options.postfx) { 679 | glGenTextures(1, &fb_tex); 680 | glBindTexture(GL_TEXTURE_2D, fb_tex); 681 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, SCREEN_W, SCREEN_H, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); 682 | glGenFramebuffers(1, &fb); 683 | glBindFramebuffer(GL_FRAMEBUFFER, fb); 684 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, fb_tex, 0); 685 | 686 | frag = glCreateShader(GL_FRAGMENT_SHADER); 687 | vert = glCreateShader(GL_VERTEX_SHADER); 688 | char path[512]; 689 | SceIoDirent d; 690 | SceUID fd = sceIoDopen("app0:shaders"); 691 | while (sceIoDread(fd, &d) > 0) { 692 | sprintf(path, "%d_", options.postfx); 693 | if (strstr(d.d_name, path)) { 694 | sprintf(path, "app0:shaders/%s", d.d_name); 695 | loadShader(strncmp(&d.d_name[strlen(d.d_name) - 5], "_f.cg", 5) == 0 ? 0 : 1, path); 696 | } 697 | } 698 | sceIoDclose(fd); 699 | 700 | postfx_prog = glCreateProgram(); 701 | glAttachShader(postfx_prog, frag); 702 | glAttachShader(postfx_prog, vert); 703 | glBindAttribLocation(postfx_prog, 0, "position"); 704 | glBindAttribLocation(postfx_prog, 1, "texcoord"); 705 | glLinkProgram(postfx_prog); 706 | } 707 | 708 | int (*ff3_render)(char *, int, int, int, int) = 709 | (void *)so_symbol(&ff3_mod, "render"); 710 | int (*ff3_touch)(int, int, int, int, float, float, float, float, 711 | unsigned int) = (void *)so_symbol(&ff3_mod, "touch"); 712 | 713 | readHeader(); 714 | setup_viewport(SCREEN_W, SCREEN_H); 715 | while (1) { 716 | 717 | SceTouchData touch; 718 | float coordinates[4] = {0.0f, 0.0f, 0.0f, 0.0f}; 719 | int n; 720 | 721 | sceTouchPeek(0, &touch, 1); 722 | 723 | int reportNum = touch.reportNum > 2 ? 2 : touch.reportNum; 724 | 725 | for (n = 0; n < reportNum; n++) { 726 | coordinates[n * 2] = (float)touch.report[n].x / 1920.0f; 727 | coordinates[n * 2 + 1] = (float)touch.report[n].y / 1088.0f; 728 | } 729 | 730 | SceCtrlData pad; 731 | sceCtrlPeekBufferPositiveExt2(0, &pad, 1); 732 | 733 | int mask = 0; 734 | 735 | if (pad.buttons & SCE_CTRL_TRIANGLE) 736 | mask |= 0x400; 737 | if (pad.buttons & SCE_CTRL_SQUARE) 738 | mask |= 0x800; 739 | if (pad.buttons & SCE_CTRL_L1) 740 | mask |= 0x200; 741 | if (pad.buttons & SCE_CTRL_R1) 742 | mask |= 0x100; 743 | if (pad.buttons & SCE_CTRL_CROSS) 744 | mask |= 0x1; 745 | if (pad.buttons & SCE_CTRL_CIRCLE) 746 | mask |= 0x2; 747 | if (pad.buttons & SCE_CTRL_START) 748 | mask |= 0x8; 749 | if (pad.buttons & SCE_CTRL_SELECT) 750 | mask |= 0x4; 751 | if (pad.buttons & SCE_CTRL_UP || pad.ly < 80) 752 | mask |= 0x40; 753 | if (pad.buttons & SCE_CTRL_DOWN || pad.ly > 170) 754 | mask |= 0x80; 755 | if (pad.buttons & SCE_CTRL_LEFT || pad.lx < 80) 756 | mask |= 0x20; 757 | if (pad.buttons & SCE_CTRL_RIGHT || pad.lx > 170) 758 | mask |= 0x10; 759 | 760 | ff3_touch(0, 0, reportNum, reportNum, coordinates[0], coordinates[1], 761 | coordinates[2], coordinates[3], mask); 762 | 763 | ff3_render(fake_env, 0, this_width, this_height, 0); 764 | if (options.postfx) { 765 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 766 | glBindTexture(GL_TEXTURE_2D, fb_tex); 767 | glUseProgram(postfx_prog); 768 | glEnableVertexAttribArray(0); 769 | glEnableVertexAttribArray(1); 770 | glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, &postfx_pos[0]); 771 | glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, &postfx_texcoord[0]); 772 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 773 | glUseProgram(0); 774 | glBindFramebuffer(GL_FRAMEBUFFER, fb); 775 | } 776 | vglSwapBuffers(editText == -1 ? GL_FALSE : GL_TRUE); 777 | } 778 | 779 | return 0; 780 | } 781 | 782 | void patch_game(void) { 783 | #ifdef DEBUG 784 | hook_thumb(ff3_mod.text_base + 0xe58b6, (uintptr_t)&printf); 785 | #endif 786 | } 787 | 788 | extern void *_ZdaPv; 789 | extern void *_ZdlPv; 790 | extern void *_Znaj; 791 | extern void *_Znwj; 792 | 793 | extern void *__aeabi_atexit; 794 | extern void *__aeabi_d2ulz; 795 | extern void *__aeabi_dcmpgt; 796 | extern void *__aeabi_dmul; 797 | extern void *__aeabi_f2d; 798 | extern void *__aeabi_fadd; 799 | extern void *__aeabi_fsub; 800 | extern void *__aeabi_idiv; 801 | extern void *__aeabi_idivmod; 802 | extern void *__aeabi_l2d; 803 | extern void *__aeabi_l2f; 804 | extern void *__aeabi_ldivmod; 805 | extern void *__aeabi_uidiv; 806 | extern void *__aeabi_uidivmod; 807 | extern void *__aeabi_uldivmod; 808 | extern void *__cxa_atexit; 809 | extern void *__cxa_finalize; 810 | extern void *__stack_chk_fail; 811 | 812 | static int __stack_chk_guard_fake = 0x42424242; 813 | static FILE __sF_fake[0x100][3]; 814 | 815 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, 816 | off_t offset) { 817 | return malloc(length); 818 | } 819 | 820 | int munmap(void *addr, size_t length) { 821 | free(addr); 822 | return 0; 823 | } 824 | 825 | void *AAssetManager_open(void *mgr, const char *filename, int mode) { 826 | printf("AAssetManager_open\n"); 827 | return NULL; 828 | } 829 | 830 | void *AAsset_close() { 831 | printf("AAsset_close\n"); 832 | return NULL; 833 | } 834 | 835 | void *AAssetManager_fromJava() { 836 | printf("AAssetManager_fromJava\n"); 837 | return NULL; 838 | } 839 | 840 | void *AAsset_read() { 841 | printf("AAsset_read\n"); 842 | return NULL; 843 | } 844 | 845 | void *AAsset_seek() { 846 | printf("AAsset_seek\n"); 847 | return NULL; 848 | } 849 | 850 | void *AAsset_getLength() { 851 | printf("AAsset_getLength\n"); 852 | return NULL; 853 | } 854 | 855 | void glTexParameteriHook(GLenum target, GLenum pname, GLint param) { 856 | if (options.bilinear) { 857 | if (pname == GL_TEXTURE_MIN_FILTER || pname == GL_TEXTURE_MAG_FILTER) { 858 | glTexParameteri(target, pname, GL_LINEAR); 859 | return; 860 | } 861 | } 862 | glTexParameteri(target, pname, param); 863 | } 864 | 865 | static so_default_dynlib default_dynlib[] = { 866 | {"AAssetManager_open", (uintptr_t)&AAssetManager_open}, 867 | {"AAsset_close", (uintptr_t)&AAsset_close}, 868 | {"AAssetManager_fromJava", (uintptr_t)&AAssetManager_fromJava}, 869 | {"AAsset_read", (uintptr_t)&AAsset_read}, 870 | {"AAsset_seek", (uintptr_t)&AAsset_seek}, 871 | {"AAsset_getLength", (uintptr_t)&AAsset_getLength}, 872 | {"SL_IID_ANDROIDSIMPLEBUFFERQUEUE", 873 | (uintptr_t)&SL_IID_ANDROIDSIMPLEBUFFERQUEUE}, 874 | {"SL_IID_AUDIOIODEVICECAPABILITIES", 875 | (uintptr_t)&SL_IID_AUDIOIODEVICECAPABILITIES}, 876 | {"SL_IID_BUFFERQUEUE", (uintptr_t)&SL_IID_BUFFERQUEUE}, 877 | {"SL_IID_DYNAMICSOURCE", (uintptr_t)&SL_IID_DYNAMICSOURCE}, 878 | {"SL_IID_ENGINE", (uintptr_t)&SL_IID_ENGINE}, 879 | {"SL_IID_LED", (uintptr_t)&SL_IID_LED}, 880 | {"SL_IID_NULL", (uintptr_t)&SL_IID_NULL}, 881 | {"SL_IID_METADATAEXTRACTION", (uintptr_t)&SL_IID_METADATAEXTRACTION}, 882 | {"SL_IID_METADATATRAVERSAL", (uintptr_t)&SL_IID_METADATATRAVERSAL}, 883 | {"SL_IID_OBJECT", (uintptr_t)&SL_IID_OBJECT}, 884 | {"SL_IID_OUTPUTMIX", (uintptr_t)&SL_IID_OUTPUTMIX}, 885 | {"SL_IID_PLAY", (uintptr_t)&SL_IID_PLAY}, 886 | {"SL_IID_VIBRA", (uintptr_t)&SL_IID_VIBRA}, 887 | {"SL_IID_VOLUME", (uintptr_t)&SL_IID_VOLUME}, 888 | {"SL_IID_PREFETCHSTATUS", (uintptr_t)&SL_IID_PREFETCHSTATUS}, 889 | {"SL_IID_PLAYBACKRATE", (uintptr_t)&SL_IID_PLAYBACKRATE}, 890 | {"SL_IID_SEEK", (uintptr_t)&SL_IID_SEEK}, 891 | {"SL_IID_RECORD", (uintptr_t)&SL_IID_RECORD}, 892 | {"SL_IID_EQUALIZER", (uintptr_t)&SL_IID_EQUALIZER}, 893 | {"SL_IID_DEVICEVOLUME", (uintptr_t)&SL_IID_DEVICEVOLUME}, 894 | {"SL_IID_PRESETREVERB", (uintptr_t)&SL_IID_PRESETREVERB}, 895 | {"SL_IID_ENVIRONMENTALREVERB", (uintptr_t)&SL_IID_ENVIRONMENTALREVERB}, 896 | {"SL_IID_EFFECTSEND", (uintptr_t)&SL_IID_EFFECTSEND}, 897 | {"SL_IID_3DGROUPING", (uintptr_t)&SL_IID_3DGROUPING}, 898 | {"SL_IID_3DCOMMIT", (uintptr_t)&SL_IID_3DCOMMIT}, 899 | {"SL_IID_3DLOCATION", (uintptr_t)&SL_IID_3DLOCATION}, 900 | {"SL_IID_3DDOPPLER", (uintptr_t)&SL_IID_3DDOPPLER}, 901 | {"SL_IID_3DSOURCE", (uintptr_t)&SL_IID_3DSOURCE}, 902 | {"SL_IID_3DMACROSCOPIC", (uintptr_t)&SL_IID_3DMACROSCOPIC}, 903 | {"SL_IID_MUTESOLO", (uintptr_t)&SL_IID_MUTESOLO}, 904 | {"SL_IID_DYNAMICINTERFACEMANAGEMENT", 905 | (uintptr_t)&SL_IID_DYNAMICINTERFACEMANAGEMENT}, 906 | {"SL_IID_MIDIMESSAGE", (uintptr_t)&SL_IID_MIDIMESSAGE}, 907 | {"SL_IID_MIDIMUTESOLO", (uintptr_t)&SL_IID_MIDIMUTESOLO}, 908 | {"SL_IID_MIDITEMPO", (uintptr_t)&SL_IID_MIDITEMPO}, 909 | {"SL_IID_MIDITIME", (uintptr_t)&SL_IID_MIDITIME}, 910 | {"SL_IID_AUDIODECODERCAPABILITIES", 911 | (uintptr_t)&SL_IID_AUDIODECODERCAPABILITIES}, 912 | {"SL_IID_AUDIOENCODERCAPABILITIES", 913 | (uintptr_t)&SL_IID_AUDIOENCODERCAPABILITIES}, 914 | {"SL_IID_AUDIOENCODER", (uintptr_t)&SL_IID_AUDIOENCODER}, 915 | {"SL_IID_BASSBOOST", (uintptr_t)&SL_IID_BASSBOOST}, 916 | {"SL_IID_PITCH", (uintptr_t)&SL_IID_PITCH}, 917 | {"SL_IID_RATEPITCH", (uintptr_t)&SL_IID_RATEPITCH}, 918 | {"SL_IID_VIRTUALIZER", (uintptr_t)&SL_IID_VIRTUALIZER}, 919 | {"SL_IID_VISUALIZATION", (uintptr_t)&SL_IID_VISUALIZATION}, 920 | {"SL_IID_ENGINECAPABILITIES", (uintptr_t)&SL_IID_ENGINECAPABILITIES}, 921 | {"SL_IID_THREADSYNC", (uintptr_t)&SL_IID_THREADSYNC}, 922 | {"SL_IID_ANDROIDEFFECT", (uintptr_t)&SL_IID_ANDROIDEFFECT}, 923 | {"SL_IID_ANDROIDEFFECTSEND", (uintptr_t)&SL_IID_ANDROIDEFFECTSEND}, 924 | {"SL_IID_ANDROIDEFFECTCAPABILITIES", 925 | (uintptr_t)&SL_IID_ANDROIDEFFECTCAPABILITIES}, 926 | {"SL_IID_ANDROIDCONFIGURATION", (uintptr_t)&SL_IID_ANDROIDCONFIGURATION}, 927 | {"_ZdaPv", (uintptr_t)&_ZdaPv}, 928 | {"_ZdlPv", (uintptr_t)&_ZdlPv}, 929 | {"_Znaj", (uintptr_t)&_Znaj}, 930 | {"_Znwj", (uintptr_t)&_Znwj}, 931 | {"_toupper_tab_", (uintptr_t)&_toupper_tab_}, 932 | {"__aeabi_atexit", (uintptr_t)&__aeabi_atexit}, 933 | {"__aeabi_d2ulz", (uintptr_t)&__aeabi_d2ulz}, 934 | {"__aeabi_dcmpgt", (uintptr_t)&__aeabi_dcmpgt}, 935 | {"__aeabi_dmul", (uintptr_t)&__aeabi_dmul}, 936 | {"__aeabi_f2d", (uintptr_t)&__aeabi_f2d}, 937 | {"__aeabi_fadd", (uintptr_t)&__aeabi_fadd}, 938 | {"__aeabi_fsub", (uintptr_t)&__aeabi_fsub}, 939 | {"__aeabi_idiv", (uintptr_t)&__aeabi_idiv}, 940 | {"__aeabi_idivmod", (uintptr_t)&__aeabi_idivmod}, 941 | {"__aeabi_l2d", (uintptr_t)&__aeabi_l2d}, 942 | {"__aeabi_l2f", (uintptr_t)&__aeabi_l2f}, 943 | {"__aeabi_ldivmod", (uintptr_t)&__aeabi_ldivmod}, 944 | {"__aeabi_uidiv", (uintptr_t)&__aeabi_uidiv}, 945 | {"__aeabi_uidivmod", (uintptr_t)&__aeabi_uidivmod}, 946 | {"__aeabi_uldivmod", (uintptr_t)&__aeabi_uldivmod}, 947 | {"__android_log_print", (uintptr_t)&__android_log_print}, 948 | {"__assert2", (uintptr_t)&__assert2}, 949 | {"__cxa_atexit", (uintptr_t)&__cxa_atexit}, 950 | {"__cxa_finalize", (uintptr_t)&__cxa_finalize}, 951 | {"__errno", (uintptr_t)&__errno}, 952 | {"__sF", (uintptr_t)&__sF_fake}, 953 | {"__stack_chk_fail", (uintptr_t)&__stack_chk_fail}, 954 | {"__stack_chk_guard", (uintptr_t)&__stack_chk_guard_fake}, 955 | {"abort", (uintptr_t)&abort}, 956 | {"atanf", (uintptr_t)&atan}, 957 | {"atan2f", (uintptr_t)&atan2f}, 958 | {"atoi", (uintptr_t)&atoi}, 959 | {"calloc", (uintptr_t)&calloc}, 960 | {"cos", (uintptr_t)&cos}, 961 | {"cosf", (uintptr_t)&cosf}, 962 | {"dlopen", (uintptr_t)&ret1}, 963 | {"dlsym", (uintptr_t)&ret1}, 964 | {"dlclose", (uintptr_t)&ret1}, 965 | {"fclose", (uintptr_t)&fclose}, 966 | {"floorf", (uintptr_t)&floorf}, 967 | {"fopen", (uintptr_t)&fopen}, 968 | {"fprintf", (uintptr_t)&fprintf}, 969 | {"fread", (uintptr_t)&fread}, 970 | {"free", (uintptr_t)&free}, 971 | {"fseek", (uintptr_t)&fseek}, 972 | {"ftell", (uintptr_t)&ftell}, 973 | {"fwrite", (uintptr_t)&fwrite}, 974 | {"gettimeofday", (uintptr_t)&gettimeofday}, 975 | {"glAlphaFunc", (uintptr_t)&glAlphaFunc}, 976 | {"glBindTexture", (uintptr_t)&glBindTexture}, 977 | {"glBlendFunc", (uintptr_t)&glBlendFunc}, 978 | {"glClear", (uintptr_t)&glClear}, 979 | {"glClearColor", (uintptr_t)&glClearColor}, 980 | {"glColor4ub", (uintptr_t)&glColor4ub}, 981 | {"glColorPointer", (uintptr_t)&glColorPointer}, 982 | {"glCullFace", (uintptr_t)&glCullFace}, 983 | {"glDeleteTextures", (uintptr_t)&glDeleteTextures}, 984 | {"glDepthFunc", (uintptr_t)&glDepthFunc}, 985 | {"glDepthMask", (uintptr_t)&glDepthMask}, 986 | {"glDisable", (uintptr_t)&glDisable}, 987 | {"glDisableClientState", (uintptr_t)&glDisableClientState}, 988 | {"glDrawArrays", (uintptr_t)&glDrawArrays}, 989 | {"glEnable", (uintptr_t)&glEnable}, 990 | {"glEnableClientState", (uintptr_t)&glEnableClientState}, 991 | {"glGenTextures", (uintptr_t)&glGenTextures}, 992 | {"glGetError", (uintptr_t)&glGetError}, 993 | {"glLightfv", (uintptr_t)&glLightfv}, 994 | {"glLoadIdentity", (uintptr_t)&glLoadIdentity}, 995 | {"glLoadMatrixf", (uintptr_t)&glLoadMatrixf}, 996 | {"glMatrixMode", (uintptr_t)&glMatrixMode}, 997 | {"glMultMatrixf", (uintptr_t)&glMultMatrixf}, 998 | {"glOrthof", (uintptr_t)&glOrthof}, 999 | {"glPopMatrix", (uintptr_t)&glPopMatrix}, 1000 | {"glPushMatrix", (uintptr_t)&glPushMatrix}, 1001 | {"glTranslatef", (uintptr_t)&glTranslatef}, 1002 | {"glTexCoordPointer", (uintptr_t)&glTexCoordPointer}, 1003 | {"glTexImage2D", (uintptr_t)&glTexImage2D}, 1004 | {"glTexParameteri", (uintptr_t)&glTexParameteriHook}, 1005 | {"glTexSubImage2D", (uintptr_t)&glTexSubImage2D}, 1006 | {"glVertexPointer", (uintptr_t)&glVertexPointer}, 1007 | {"localtime", (uintptr_t)&localtime}, 1008 | {"lrand48", (uintptr_t)&lrand48}, 1009 | {"malloc", (uintptr_t)&malloc}, 1010 | {"memchr", (uintptr_t)&memchr}, 1011 | {"memcmp", (uintptr_t)&memcmp}, 1012 | {"memcpy", (uintptr_t)&memcpy}, 1013 | {"memmove", (uintptr_t)&memmove}, 1014 | {"memset", (uintptr_t)&memset}, 1015 | {"mmap", (uintptr_t)&mmap}, 1016 | {"munmap", (uintptr_t)&munmap}, 1017 | {"pthread_cond_broadcast", (uintptr_t)&pthread_cond_broadcast_fake}, 1018 | {"pthread_cond_destroy", (uintptr_t)&pthread_cond_destroy_fake}, 1019 | {"pthread_cond_init", (uintptr_t)&pthread_cond_init_fake}, 1020 | {"pthread_cond_wait", (uintptr_t)&pthread_cond_wait_fake}, 1021 | {"pthread_create", (uintptr_t)&pthread_create_fake}, 1022 | {"pthread_join", (uintptr_t)&pthread_join}, 1023 | {"pthread_key_create", (uintptr_t)&pthread_key_create}, 1024 | {"pthread_key_delete", (uintptr_t)&pthread_key_delete}, 1025 | {"pthread_mutex_destroy", (uintptr_t)&pthread_mutex_destroy_fake}, 1026 | {"pthread_mutex_init", (uintptr_t)&pthread_mutex_init_fake}, 1027 | {"pthread_mutex_lock", (uintptr_t)&pthread_mutex_lock_fake}, 1028 | {"pthread_mutex_unlock", (uintptr_t)&pthread_mutex_unlock_fake}, 1029 | {"pthread_mutexattr_destroy", (uintptr_t)&pthread_mutexattr_destroy}, 1030 | {"pthread_mutexattr_init", (uintptr_t)&pthread_mutexattr_init}, 1031 | {"pthread_mutexattr_settype", (uintptr_t)&pthread_mutexattr_settype}, 1032 | {"pthread_setspecific", (uintptr_t)&pthread_setspecific}, 1033 | {"pthread_getspecific", (uintptr_t)&pthread_getspecific}, 1034 | {"qsort", (uintptr_t)&qsort}, 1035 | {"raise", (uintptr_t)&raise}, 1036 | {"realloc", (uintptr_t)&realloc}, 1037 | {"sin", (uintptr_t)&sin}, 1038 | {"sinf", (uintptr_t)&sinf}, 1039 | {"slCreateEngine", (uintptr_t)&slCreateEngine}, 1040 | {"snprintf", (uintptr_t)&snprintf}, 1041 | {"srand48", (uintptr_t)&srand48}, 1042 | {"sprintf", (uintptr_t)&sprintf}, 1043 | {"sqrt", (uintptr_t)&sqrt}, 1044 | {"sqrtf", (uintptr_t)&sqrtf}, 1045 | {"strcat", (uintptr_t)&strcat}, 1046 | {"strchr", (uintptr_t)&strchr}, 1047 | {"strcmp", (uintptr_t)&strcmp}, 1048 | {"strcpy", (uintptr_t)&strcpy}, 1049 | {"strlen", (uintptr_t)&strlen}, 1050 | {"strncasecmp", (uintptr_t)&strncasecmp}, 1051 | {"strncmp", (uintptr_t)&strncmp}, 1052 | {"strncpy", (uintptr_t)&strncpy}, 1053 | {"strrchr", (uintptr_t)&strrchr}, 1054 | {"strtok", (uintptr_t)&strtok}, 1055 | {"strtod", (uintptr_t)&strtod}, 1056 | {"strtol", (uintptr_t)&strtol}, 1057 | {"tanf", (uintptr_t)&tanf}, 1058 | {"time", (uintptr_t)&time}, 1059 | {"usleep", (uintptr_t)&usleep}, 1060 | {"vsnprintf", (uintptr_t)&vsnprintf}, 1061 | }; 1062 | 1063 | int check_kubridge(void) { 1064 | int search_unk[2]; 1065 | return _vshKernelSearchModuleByName("kubridge", search_unk); 1066 | } 1067 | 1068 | int file_exists(const char *path) { 1069 | SceIoStat stat; 1070 | return sceIoGetstat(path, &stat) >= 0; 1071 | } 1072 | 1073 | int main(int argc, char *argv[]) { 1074 | 1075 | // Check if we want to start the companion app 1076 | sceAppUtilInit(&(SceAppUtilInitParam){}, &(SceAppUtilBootParam){}); 1077 | SceAppUtilAppEventParam eventParam; 1078 | sceClibMemset(&eventParam, 0, sizeof(SceAppUtilAppEventParam)); 1079 | sceAppUtilReceiveAppEvent(&eventParam); 1080 | if (eventParam.type == 0x05) { 1081 | char buffer[2048]; 1082 | sceAppUtilAppEventParseLiveArea(&eventParam, buffer); 1083 | if (strstr(buffer, "-config")) 1084 | sceAppMgrLoadExec("app0:/companion.bin", NULL, NULL); 1085 | } 1086 | 1087 | loadOptions(); 1088 | 1089 | sceCtrlSetSamplingModeExt(SCE_CTRL_MODE_ANALOG_WIDE); 1090 | sceTouchSetSamplingState(SCE_TOUCH_PORT_FRONT, 1091 | SCE_TOUCH_SAMPLING_STATE_START); 1092 | 1093 | scePowerSetArmClockFrequency(444); 1094 | scePowerSetBusClockFrequency(222); 1095 | scePowerSetGpuClockFrequency(222); 1096 | scePowerSetGpuXbarClockFrequency(166); 1097 | 1098 | if (check_kubridge() < 0) 1099 | fatal_error("Error kubridge.skprx is not installed."); 1100 | 1101 | if (!file_exists("ur0:/data/libshacccg.suprx") && 1102 | !file_exists("ur0:/data/external/libshacccg.suprx")) 1103 | fatal_error("Error libshacccg.suprx is not installed."); 1104 | 1105 | InitJNIEnv(); 1106 | 1107 | if (so_load(&ff3_mod, SO_PATH, LOAD_ADDRESS) < 0) 1108 | fatal_error("Error could not load %s.", SO_PATH); 1109 | 1110 | so_relocate(&ff3_mod); 1111 | so_resolve(&ff3_mod, default_dynlib, sizeof(default_dynlib), 0); 1112 | patch_game(); 1113 | so_flush_caches(&ff3_mod); 1114 | 1115 | so_initialize(&ff3_mod); 1116 | 1117 | SceUID thid = 1118 | sceKernelCreateThread("main_thread", (SceKernelThreadEntry)main_thread, 1119 | 0x40, 1024 * 1024, 0, 0, NULL); 1120 | sceKernelStartThread(thid, 0, NULL); 1121 | return sceKernelExitDeleteThread(0); 1122 | } 1123 | -------------------------------------------------------------------------------- /loader/main.h: -------------------------------------------------------------------------------- 1 | #ifndef __MAIN_H__ 2 | #define __MAIN_H__ 3 | 4 | SceUID _vshKernelSearchModuleByName(const char *, int *); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /loader/shaders/movie_f.h: -------------------------------------------------------------------------------- 1 | #ifndef __movie_f__ 2 | #define __movie_f__ 3 | 4 | /* 5 | float4 main( 6 | float2 texcoord : TEXCOORD0, 7 | uniform sampler2D tex 8 | ) { 9 | return tex2D(tex, texcoord); 10 | } 11 | */ 12 | 13 | static unsigned int size_movie_f = 256; 14 | static unsigned char movie_f[] __attribute__((aligned(16))) = { 15 | 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x50, 0x03, 0x00, 0x01, 0x00, 0x00, 0x2f, 0x18, 0xe0, 0x2b, 16 | 0xc0, 0xa3, 0x06, 0x39, 0x01, 0x08, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 17 | 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0xa4, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 18 | 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 19 | 0x84, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 21 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0xc0, 0x3d, 0x03, 0x00, 22 | 0x00, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x48, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x38, 0x00, 0x00, 0x00, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x00, 0x01, 0x00, 0x04, 0x00, 0x00, 0x00, 26 | 0x00, 0xf9, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0xfa, 0x30, 0x00, 0x00, 0x00, 28 | 0x02, 0x04, 0x02, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x00, 0x00, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x65, 0x78, 0x00, 31 | }; 32 | 33 | #endif 34 | -------------------------------------------------------------------------------- /loader/shaders/movie_v.h: -------------------------------------------------------------------------------- 1 | #ifndef __movie_v__ 2 | #define __movie_v__ 3 | 4 | /* 5 | void main( 6 | float2 inPos, 7 | float2 inTex, 8 | float4 out pos : POSITION, 9 | float2 out texcoord : TEXCOORD0 10 | ) { 11 | texcoord = inTex; 12 | pos = float4(inPos, 1.f, 1.f); 13 | } 14 | */ 15 | 16 | static unsigned int size_movie_v = 276; 17 | static unsigned char movie_v[] __attribute__((aligned(16))) = { 18 | 0x47, 0x58, 0x50, 0x00, 0x01, 0x05, 0x50, 0x03, 0x14, 0x01, 0x00, 0x00, 0x0a, 0xf8, 0x9c, 0x1c, 19 | 0xb3, 0x51, 0x01, 0x8f, 0x04, 0x00, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 20 | 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0xc0, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 21 | 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x05, 0x00, 0x00, 0x00, 22 | 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x70, 0x00, 0x00, 0x00, 23 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 24 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0xc0, 0x3d, 0x03, 0x00, 25 | 0x00, 0x00, 0x00, 0x00, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6c, 0x00, 0x00, 0x00, 26 | 0x00, 0x00, 0x00, 0x00, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5c, 0x00, 0x00, 0x00, 27 | 0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 28 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x06, 29 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 30 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x44, 0xfa, 0x80, 0x00, 0x08, 0x83, 0x21, 0x05, 0x80, 0x38, 31 | 0x01, 0x00, 0x04, 0x90, 0x85, 0x11, 0xa5, 0x08, 0x01, 0x80, 0x56, 0x90, 0x81, 0x11, 0x83, 0x08, 32 | 0x00, 0x00, 0x20, 0xa0, 0x00, 0x50, 0x27, 0xfb, 0x20, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 33 | 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 34 | 0x01, 0x00, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x69, 0x6e, 0x50, 0x6f, 0x73, 0x00, 0x69, 0x6e, 35 | 0x54, 0x65, 0x78, 0x00, 36 | }; 37 | 38 | #endif 39 | -------------------------------------------------------------------------------- /loader/so_util.c: -------------------------------------------------------------------------------- 1 | /* so_util.c -- utils to load and hook .so modules 2 | * 3 | * Copyright (C) 2021 Andy Nguyen 4 | * 5 | * This software may be modified and distributed under the terms 6 | * of the MIT license. See the LICENSE file for details. 7 | */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | 18 | #include "main.h" 19 | #include "dialog.h" 20 | #include "so_util.h" 21 | 22 | static so_module *head = NULL, *tail = NULL; 23 | 24 | void hook_thumb(uintptr_t addr, uintptr_t dst) { 25 | if (addr == 0) 26 | return; 27 | addr &= ~1; 28 | if (addr & 2) { 29 | uint16_t nop = 0xbf00; 30 | kuKernelCpuUnrestrictedMemcpy((void *)addr, &nop, sizeof(nop)); 31 | addr += 2; 32 | } 33 | uint32_t hook[2]; 34 | hook[0] = 0xf000f8df; // LDR PC, [PC] 35 | hook[1] = dst; 36 | kuKernelCpuUnrestrictedMemcpy((void *)addr, hook, sizeof(hook)); 37 | } 38 | 39 | void hook_arm(uintptr_t addr, uintptr_t dst) { 40 | if (addr == 0) 41 | return; 42 | uint32_t hook[2]; 43 | hook[0] = 0xe51ff004; // LDR PC, [PC, #-0x4] 44 | hook[1] = dst; 45 | kuKernelCpuUnrestrictedMemcpy((void *)addr, hook, sizeof(hook)); 46 | } 47 | 48 | void hook_addr(uintptr_t addr, uintptr_t dst) { 49 | if (addr == 0) 50 | return; 51 | if (addr & 1) 52 | hook_thumb(addr, dst); 53 | else 54 | hook_arm(addr, dst); 55 | } 56 | 57 | void so_flush_caches(so_module *mod) { 58 | kuKernelFlushCaches((void *)mod->text_base, mod->text_size); 59 | } 60 | 61 | int so_load(so_module *mod, const char *filename, uintptr_t load_addr) { 62 | int res = 0; 63 | uintptr_t data_addr = 0; 64 | SceUID so_blockid; 65 | void *so_data; 66 | size_t so_size; 67 | 68 | memset(mod, 0, sizeof(so_module)); 69 | 70 | SceUID fd = sceIoOpen(filename, SCE_O_RDONLY, 0); 71 | if (fd < 0) 72 | return fd; 73 | 74 | so_size = sceIoLseek(fd, 0, SCE_SEEK_END); 75 | sceIoLseek(fd, 0, SCE_SEEK_SET); 76 | 77 | so_blockid = sceKernelAllocMemBlock("file", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, (so_size + 0xfff) & ~0xfff, NULL); 78 | if (so_blockid < 0) 79 | return so_blockid; 80 | 81 | sceKernelGetMemBlockBase(so_blockid, &so_data); 82 | 83 | sceIoRead(fd, so_data, so_size); 84 | sceIoClose(fd); 85 | 86 | if (memcmp(so_data, ELFMAG, SELFMAG) != 0) { 87 | res = -1; 88 | goto err_free_so; 89 | } 90 | 91 | mod->ehdr = (Elf32_Ehdr *)so_data; 92 | mod->phdr = (Elf32_Phdr *)((uintptr_t)so_data + mod->ehdr->e_phoff); 93 | mod->shdr = (Elf32_Shdr *)((uintptr_t)so_data + mod->ehdr->e_shoff); 94 | 95 | mod->shstr = (char *)((uintptr_t)so_data + mod->shdr[mod->ehdr->e_shstrndx].sh_offset); 96 | 97 | for (int i = 0; i < mod->ehdr->e_phnum; i++) { 98 | if (mod->phdr[i].p_type == PT_LOAD) { 99 | void *prog_data; 100 | size_t prog_size; 101 | 102 | if ((mod->phdr[i].p_flags & PF_X) == PF_X) { 103 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz, mod->phdr[i].p_align); 104 | 105 | SceKernelAllocMemBlockKernelOpt opt; 106 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 107 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 108 | opt.attr = 0x1; 109 | opt.field_C = (SceUInt32)load_addr; 110 | res = mod->text_blockid = kuKernelAllocMemBlock("rx_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RX, prog_size, &opt); 111 | if (res < 0) 112 | goto err_free_so; 113 | 114 | sceKernelGetMemBlockBase(mod->text_blockid, &prog_data); 115 | 116 | mod->phdr[i].p_vaddr += (Elf32_Addr)prog_data; 117 | 118 | mod->text_base = mod->phdr[i].p_vaddr; 119 | mod->text_size = mod->phdr[i].p_memsz; 120 | 121 | data_addr = (uintptr_t)prog_data + prog_size; 122 | } else { 123 | if (data_addr == 0) 124 | goto err_free_so; 125 | 126 | prog_size = ALIGN_MEM(mod->phdr[i].p_memsz + mod->phdr[i].p_vaddr - (data_addr - mod->text_base), mod->phdr[i].p_align); 127 | 128 | SceKernelAllocMemBlockKernelOpt opt; 129 | memset(&opt, 0, sizeof(SceKernelAllocMemBlockKernelOpt)); 130 | opt.size = sizeof(SceKernelAllocMemBlockKernelOpt); 131 | opt.attr = 0x1; 132 | opt.field_C = (SceUInt32)data_addr; 133 | res = mod->data_blockid = kuKernelAllocMemBlock("rw_block", SCE_KERNEL_MEMBLOCK_TYPE_USER_RW, prog_size, &opt); 134 | if (res < 0) 135 | goto err_free_text; 136 | 137 | sceKernelGetMemBlockBase(mod->data_blockid, &prog_data); 138 | 139 | mod->phdr[i].p_vaddr += (Elf32_Addr)mod->text_base; 140 | 141 | mod->data_base = mod->phdr[i].p_vaddr; 142 | mod->data_size = mod->phdr[i].p_memsz; 143 | } 144 | 145 | char *zero = malloc(prog_size); 146 | memset(zero, 0, prog_size); 147 | kuKernelCpuUnrestrictedMemcpy(prog_data, zero, prog_size); 148 | free(zero); 149 | 150 | kuKernelCpuUnrestrictedMemcpy((void *)mod->phdr[i].p_vaddr, (void *)((uintptr_t)so_data + mod->phdr[i].p_offset), mod->phdr[i].p_filesz); 151 | } 152 | } 153 | 154 | for (int i = 0; i < mod->ehdr->e_shnum; i++) { 155 | char *sh_name = mod->shstr + mod->shdr[i].sh_name; 156 | uintptr_t sh_addr = mod->text_base + mod->shdr[i].sh_addr; 157 | size_t sh_size = mod->shdr[i].sh_size; 158 | if (strcmp(sh_name, ".dynamic") == 0) { 159 | mod->dynamic = (Elf32_Dyn *)sh_addr; 160 | mod->num_dynamic = sh_size / sizeof(Elf32_Dyn); 161 | } else if (strcmp(sh_name, ".dynstr") == 0) { 162 | mod->dynstr = (char *)sh_addr; 163 | } else if (strcmp(sh_name, ".dynsym") == 0) { 164 | mod->dynsym = (Elf32_Sym *)sh_addr; 165 | mod->num_dynsym = sh_size / sizeof(Elf32_Sym); 166 | } else if (strcmp(sh_name, ".rel.dyn") == 0) { 167 | mod->reldyn = (Elf32_Rel *)sh_addr; 168 | mod->num_reldyn = sh_size / sizeof(Elf32_Rel); 169 | } else if (strcmp(sh_name, ".rel.plt") == 0) { 170 | mod->relplt = (Elf32_Rel *)sh_addr; 171 | mod->num_relplt = sh_size / sizeof(Elf32_Rel); 172 | } else if (strcmp(sh_name, ".init_array") == 0) { 173 | mod->init_array = (void *)sh_addr; 174 | mod->num_init_array = sh_size / sizeof(void *); 175 | } else if (strcmp(sh_name, ".hash") == 0) { 176 | mod->hash = (void *)sh_addr; 177 | } 178 | } 179 | 180 | if (mod->dynamic == NULL || 181 | mod->dynstr == NULL || 182 | mod->dynsym == NULL || 183 | mod->reldyn == NULL || 184 | mod->relplt == NULL) { 185 | res = -2; 186 | goto err_free_data; 187 | } 188 | 189 | for (int i = 0; i < mod->num_dynamic; i++) { 190 | switch (mod->dynamic[i].d_tag) { 191 | case DT_SONAME: 192 | mod->soname = mod->dynstr + mod->dynamic[i].d_un.d_ptr; 193 | break; 194 | default: 195 | break; 196 | } 197 | } 198 | 199 | sceKernelFreeMemBlock(so_blockid); 200 | 201 | if (!head && !tail) { 202 | head = mod; 203 | tail = mod; 204 | } else { 205 | tail->next = mod; 206 | tail = mod; 207 | } 208 | 209 | return 0; 210 | 211 | err_free_data: 212 | sceKernelFreeMemBlock(mod->data_blockid); 213 | err_free_text: 214 | sceKernelFreeMemBlock(mod->text_blockid); 215 | err_free_so: 216 | sceKernelFreeMemBlock(so_blockid); 217 | 218 | return res; 219 | } 220 | 221 | int so_relocate(so_module *mod) { 222 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 223 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 224 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 225 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 226 | 227 | int type = ELF32_R_TYPE(rel->r_info); 228 | switch (type) { 229 | case R_ARM_ABS32: 230 | if (sym->st_shndx != SHN_UNDEF) 231 | *ptr += mod->text_base + sym->st_value; 232 | else 233 | *ptr = mod->text_base + rel->r_offset; // make it crash for debugging 234 | break; 235 | 236 | case R_ARM_RELATIVE: 237 | *ptr += mod->text_base; 238 | break; 239 | 240 | case R_ARM_GLOB_DAT: 241 | case R_ARM_JUMP_SLOT: 242 | { 243 | if (sym->st_shndx != SHN_UNDEF) 244 | *ptr = mod->text_base + sym->st_value; 245 | else 246 | *ptr = mod->text_base + rel->r_offset; // make it crash for debugging 247 | break; 248 | } 249 | 250 | default: 251 | fatal_error("Error unknown relocation type %x\n", type); 252 | break; 253 | } 254 | } 255 | 256 | return 0; 257 | } 258 | 259 | uintptr_t so_resolve_link(so_module *mod, const char *symbol) { 260 | for (int i = 0; i < mod->num_dynamic; i++) { 261 | switch (mod->dynamic[i].d_tag) { 262 | case DT_NEEDED: 263 | { 264 | so_module *curr = head; 265 | while (curr) { 266 | if (curr != mod && strcmp(curr->soname, mod->dynstr + mod->dynamic[i].d_un.d_ptr) == 0) { 267 | uintptr_t link = so_symbol(curr, symbol); 268 | if (link) 269 | return link; 270 | } 271 | curr = curr->next; 272 | } 273 | 274 | break; 275 | } 276 | default: 277 | break; 278 | } 279 | } 280 | 281 | return 0; 282 | } 283 | 284 | int so_resolve(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only) { 285 | for (int i = 0; i < mod->num_reldyn + mod->num_relplt; i++) { 286 | Elf32_Rel *rel = i < mod->num_reldyn ? &mod->reldyn[i] : &mod->relplt[i - mod->num_reldyn]; 287 | Elf32_Sym *sym = &mod->dynsym[ELF32_R_SYM(rel->r_info)]; 288 | uintptr_t *ptr = (uintptr_t *)(mod->text_base + rel->r_offset); 289 | 290 | int type = ELF32_R_TYPE(rel->r_info); 291 | switch (type) { 292 | case R_ARM_ABS32: 293 | case R_ARM_GLOB_DAT: 294 | case R_ARM_JUMP_SLOT: 295 | { 296 | if (sym->st_shndx == SHN_UNDEF) { 297 | int resolved = 0; 298 | if (!default_dynlib_only) { 299 | uintptr_t link = so_resolve_link(mod, mod->dynstr + sym->st_name); 300 | if (link) { 301 | // debugPrintf("Resolved from dependencies: %s\n", mod->dynstr + sym->st_name); 302 | *ptr = link; 303 | resolved = 1; 304 | } 305 | } 306 | 307 | for (int j = 0; j < size_default_dynlib / sizeof(so_default_dynlib); j++) { 308 | if (strcmp(mod->dynstr + sym->st_name, default_dynlib[j].symbol) == 0) { 309 | if (resolved) { 310 | // debugPrintf("Overriden: %s\n", mod->dynstr + sym->st_name); 311 | } else { 312 | // debugPrintf("Resolved manually: %s\n", mod->dynstr + sym->st_name); 313 | } 314 | *ptr = default_dynlib[j].func; 315 | resolved = 1; 316 | break; 317 | } 318 | } 319 | 320 | if (!resolved) { 321 | // debugPrintf("Missing: %s\n", mod->dynstr + sym->st_name); 322 | } 323 | } 324 | 325 | break; 326 | } 327 | 328 | default: 329 | break; 330 | } 331 | } 332 | 333 | return 0; 334 | } 335 | 336 | void so_initialize(so_module *mod) { 337 | for (int i = 0; i < mod->num_init_array; i++) { 338 | if (mod->init_array[i]) 339 | mod->init_array[i](); 340 | } 341 | } 342 | 343 | uint32_t so_hash(const uint8_t *name) { 344 | uint64_t h = 0, g; 345 | while (*name) { 346 | h = (h << 4) + *name++; 347 | if ((g = (h & 0xf0000000)) != 0) 348 | h ^= g >> 24; 349 | h &= 0x0fffffff; 350 | } 351 | return h; 352 | } 353 | 354 | uintptr_t so_symbol(so_module *mod, const char *symbol) { 355 | if (mod->hash) { 356 | uint32_t hash = so_hash((const uint8_t *)symbol); 357 | uint32_t nbucket = mod->hash[0]; 358 | uint32_t *bucket = &mod->hash[2]; 359 | uint32_t *chain = &bucket[nbucket]; 360 | for (int i = bucket[hash % nbucket]; i; i = chain[i]) { 361 | if (mod->dynsym[i].st_shndx == SHN_UNDEF) 362 | continue; 363 | if (mod->dynsym[i].st_info != SHN_UNDEF && strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 364 | return mod->text_base + mod->dynsym[i].st_value; 365 | } 366 | } 367 | 368 | for (int i = 0; i < mod->num_dynsym; i++) { 369 | if (mod->dynsym[i].st_shndx == SHN_UNDEF) 370 | continue; 371 | if (mod->dynsym[i].st_info != SHN_UNDEF && strcmp(mod->dynstr + mod->dynsym[i].st_name, symbol) == 0) 372 | return mod->text_base + mod->dynsym[i].st_value; 373 | } 374 | 375 | return 0; 376 | } -------------------------------------------------------------------------------- /loader/so_util.h: -------------------------------------------------------------------------------- 1 | #ifndef __SO_UTIL_H__ 2 | #define __SO_UTIL_H__ 3 | 4 | #include "elf.h" 5 | 6 | #define ALIGN_MEM(x, align) (((x) + ((align) - 1)) & ~((align) - 1)) 7 | 8 | typedef struct so_module { 9 | struct so_module *next; 10 | 11 | SceUID text_blockid, data_blockid; 12 | uintptr_t text_base, data_base; 13 | size_t text_size, data_size; 14 | 15 | Elf32_Ehdr *ehdr; 16 | Elf32_Phdr *phdr; 17 | Elf32_Shdr *shdr; 18 | 19 | Elf32_Dyn *dynamic; 20 | Elf32_Sym *dynsym; 21 | Elf32_Rel *reldyn; 22 | Elf32_Rel *relplt; 23 | 24 | int (** init_array)(void); 25 | uint32_t *hash; 26 | 27 | int num_dynamic; 28 | int num_dynsym; 29 | int num_reldyn; 30 | int num_relplt; 31 | int num_init_array; 32 | 33 | char *soname; 34 | char *shstr; 35 | char *dynstr; 36 | } so_module; 37 | 38 | typedef struct { 39 | char *symbol; 40 | uintptr_t func; 41 | } so_default_dynlib; 42 | 43 | void hook_thumb(uintptr_t addr, uintptr_t dst); 44 | void hook_arm(uintptr_t addr, uintptr_t dst); 45 | void hook_addr(uintptr_t addr, uintptr_t dst); 46 | 47 | void so_flush_caches(so_module *mod); 48 | int so_load(so_module *mod, const char *filename, uintptr_t load_addr); 49 | int so_relocate(so_module *mod); 50 | int so_resolve(so_module *mod, so_default_dynlib *default_dynlib, int size_default_dynlib, int default_dynlib_only); 51 | void so_initialize(so_module *mod); 52 | uintptr_t so_symbol(so_module *mod, const char *symbol); 53 | 54 | #endif -------------------------------------------------------------------------------- /loader/stb_image.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "stb_image.h" -------------------------------------------------------------------------------- /loader/stb_truetype.c: -------------------------------------------------------------------------------- 1 | #define STB_TRUETYPE_IMPLEMENTATION 2 | #include "stb_truetype.h" -------------------------------------------------------------------------------- /sce_sys/icon0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/sce_sys/icon0.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/bg0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/sce_sys/livearea/contents/bg0.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/sce_sys/livearea/contents/config.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/startup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/sce_sys/livearea/contents/startup.png -------------------------------------------------------------------------------- /sce_sys/livearea/contents/template.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bg0.png 6 | 7 | 8 | 9 | startup.png 10 | 11 | 12 | 13 | 14 | psla:-config 15 | config.png 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /screenshots/game.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frangarcj/ff3_vita/0689c2afe21dcc70357bb0c98f8e66025f0a73fb/screenshots/game.png -------------------------------------------------------------------------------- /shaders/1_Negative_f.cg: -------------------------------------------------------------------------------- 1 | float4 main( 2 | float2 vTexcoord : TEXCOORD0, 3 | uniform sampler2D colorMap : TEXUNIT0) : COLOR 4 | { 5 | float4 sample = tex2D(colorMap, float2(vTexcoord.x, 1.0f - vTexcoord.y)); 6 | 7 | return float4(1.0 - sample.xyz, 1.0); 8 | } 9 | -------------------------------------------------------------------------------- /shaders/1_Negative_v.cg: -------------------------------------------------------------------------------- 1 | void main( 2 | float2 position, 3 | float2 texcoord, 4 | out float4 vPosition : POSITION, 5 | out float2 vTexcoord : TEXCOORD0) 6 | { 7 | vPosition = float4(position, 1.f, 1.f); 8 | vTexcoord = texcoord; 9 | } 10 | -------------------------------------------------------------------------------- /shaders/2_FXAA_f.cg: -------------------------------------------------------------------------------- 1 | /** 2 | Basic FXAA implementation based on the code on geeks3d.com with the 3 | modification that the texture2DLod stuff was removed since it's 4 | unsupported by WebGL. 5 | -- 6 | From: 7 | https://github.com/mitsuhiko/webgl-meincraft 8 | Copyright (c) 2011 by Armin Ronacher. 9 | Some rights reserved. 10 | Redistribution and use in source and binary forms, with or without 11 | modification, are permitted provided that the following conditions are 12 | met: 13 | * Redistributions of source code must retain the above copyright 14 | notice, this list of conditions and the following disclaimer. 15 | * Redistributions in binary form must reproduce the above 16 | copyright notice, this list of conditions and the following 17 | disclaimer in the documentation and/or other materials provided 18 | with the distribution. 19 | * The names of the contributors may not be used to endorse or 20 | promote products derived from this software without specific 21 | prior written permission. 22 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 23 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 24 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 25 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 26 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 27 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 28 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 29 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 30 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 31 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | */ 34 | 35 | #define FXAA_REDUCE_MIN (1.0/ 128.0) 36 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 37 | #define FXAA_SPAN_MAX 8.0 38 | 39 | float4 fxaa(sampler2D tex, float2 fragCoord, float2 resolution, 40 | float2 v_rgbNW, float2 v_rgbNE, 41 | float2 v_rgbSW, float2 v_rgbSE, 42 | float2 v_rgbM) { 43 | float2 inverseVP = float2(1.0 / resolution.x, 1.0 / resolution.y); 44 | float3 rgbNW = tex2D(tex, v_rgbNW).xyz; 45 | float3 rgbNE = tex2D(tex, v_rgbNE).xyz; 46 | float3 rgbSW = tex2D(tex, v_rgbSW).xyz; 47 | float3 rgbSE = tex2D(tex, v_rgbSE).xyz; 48 | float4 texColor = tex2D(tex, v_rgbM); 49 | float3 rgbM = texColor.xyz; 50 | float3 luma = float3(0.299, 0.587, 0.114); 51 | float lumaNW = dot(rgbNW, luma); 52 | float lumaNE = dot(rgbNE, luma); 53 | float lumaSW = dot(rgbSW, luma); 54 | float lumaSE = dot(rgbSE, luma); 55 | float lumaM = dot(rgbM, luma); 56 | float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); 57 | float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); 58 | 59 | float2 dir; 60 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 61 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 62 | 63 | float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 64 | (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); 65 | 66 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 67 | dir = min(float2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), 68 | max(float2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 69 | dir * rcpDirMin)) * inverseVP; 70 | 71 | float3 rgbA = 0.5 * ( 72 | tex2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + 73 | tex2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); 74 | float3 rgbB = rgbA * 0.5 + 0.25 * ( 75 | tex2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + 76 | tex2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); 77 | 78 | float lumaB = dot(rgbB, luma); 79 | if ((lumaB < lumaMin) || (lumaB > lumaMax)) 80 | return float4(rgbA, texColor.a); 81 | else 82 | return float4(rgbB, texColor.a); 83 | } 84 | 85 | static const float2 iResolution = float2(960.0f, 544.0f); 86 | 87 | float4 main( 88 | float2 vUv : TEXCOORD0, 89 | float2 v_rgbNW : TEXCOORD1, 90 | float2 v_rgbNE : TEXCOORD2, 91 | float2 v_rgbSW : TEXCOORD3, 92 | float2 v_rgbSE : TEXCOORD4, 93 | float2 v_rgbM : TEXCOORD5, 94 | uniform sampler2D iChannel0 : TEXUNIT0) : COLOR 95 | { 96 | float2 fragCoord = vUv * iResolution; 97 | 98 | return fxaa(iChannel0, fragCoord, iResolution, v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); 99 | } 100 | -------------------------------------------------------------------------------- /shaders/2_FXAA_v.cg: -------------------------------------------------------------------------------- 1 | static const float2 iResolution = float2(960.0f, 544.0f); 2 | 3 | void main( 4 | float2 position, 5 | float2 texcoord, 6 | out float4 vPosition : POSITION, 7 | out float2 vUv : TEXCOORD0, 8 | out float2 v_rgbNW : TEXCOORD1, 9 | out float2 v_rgbNE : TEXCOORD2, 10 | out float2 v_rgbSW : TEXCOORD3, 11 | out float2 v_rgbSE : TEXCOORD4, 12 | out float2 v_rgbM : TEXCOORD5) 13 | { 14 | vPosition = float4(position, 1.f, 1.f); 15 | 16 | vUv = (vPosition.xy + 1.0) * 0.5; 17 | vUv.y = vUv.y; 18 | float2 fragCoord = vUv * iResolution; 19 | 20 | float2 inverseVP = 1.0 / iResolution.xy; 21 | v_rgbNW = (fragCoord + float2(-1.0, -1.0)) * inverseVP; 22 | v_rgbNE = (fragCoord + float2(1.0, -1.0)) * inverseVP; 23 | v_rgbSW = (fragCoord + float2(-1.0, 1.0)) * inverseVP; 24 | v_rgbSE = (fragCoord + float2(1.0, 1.0)) * inverseVP; 25 | v_rgbM = float2(fragCoord * inverseVP); 26 | } 27 | -------------------------------------------------------------------------------- /shaders/3_Sepia_f.cg: -------------------------------------------------------------------------------- 1 | float4 main( 2 | float2 vTexcoord : TEXCOORD0, 3 | uniform sampler2D colorMap : TEXUNIT0) : COLOR 4 | { 5 | float4 sample = tex2D(colorMap, vTexcoord); 6 | 7 | float r = dot(sample.xyz, float3(0.393, 0.769, 0.189)); 8 | float g = dot(sample.xyz, float3(0.349, 0.686, 0.168)); 9 | float b = dot(sample.xyz, float3(0.272, 0.534, 0.131)); 10 | 11 | return float4(r, g, b, 1.0); 12 | } 13 | -------------------------------------------------------------------------------- /shaders/3_Sepia_v.cg: -------------------------------------------------------------------------------- 1 | void main( 2 | float2 position, 3 | float2 texcoord, 4 | uniform float4x4 wvp, 5 | out float4 vPosition : POSITION, 6 | out float2 vTexcoord : TEXCOORD0) 7 | { 8 | vPosition = float4(position, 1.f, 1.f); 9 | vTexcoord = float2(texcoord.x, 1 - texcoord.y); 10 | } 11 | -------------------------------------------------------------------------------- /shaders/4_Greyscale_f.cg: -------------------------------------------------------------------------------- 1 | float4 main( 2 | float2 vTexcoord : TEXCOORD0, 3 | uniform sampler2D colorMap : TEXUNIT0) : COLOR 4 | { 5 | float4 sample = tex2D(colorMap, vTexcoord); 6 | float grey = 0.21 * sample.x + 0.71 * sample.y + 0.07 * sample.z; 7 | 8 | return float4(grey, grey, grey, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /shaders/4_Greyscale_v.cg: -------------------------------------------------------------------------------- 1 | void main( 2 | float2 position, 3 | float2 texcoord, 4 | out float4 vPosition : POSITION, 5 | out float2 vTexcoord : TEXCOORD0) 6 | { 7 | vPosition = float4(position, 1.f, 1.f); 8 | vTexcoord = float2(texcoord.x, 1 - texcoord.y); 9 | } 10 | --------------------------------------------------------------------------------