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