├── .gitignore ├── .vscode ├── settings.json └── c_cpp_properties.json ├── src ├── common.h ├── debug.c └── bluetooth.c ├── X1Vita.yml ├── CMakeLists.txt └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | build/ -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.associations": { 3 | "typeinfo": "c", 4 | "common.h": "c" 5 | } 6 | } -------------------------------------------------------------------------------- /src/common.h: -------------------------------------------------------------------------------- 1 | #define ConfigPath "ux0:data/X1Vita/swapltrt" 2 | int SetSwapStatus(int *status); 3 | int GetSwapStatus(); 4 | int GetPidVid(int *vid, int *pid); 5 | int GetBuff(int port, const char* buff); 6 | int GetPortBuff(const char *buff); 7 | #define CONTROLLER_COUNT 5 -------------------------------------------------------------------------------- /X1Vita.yml: -------------------------------------------------------------------------------- 1 | X1Vita: 2 | attributes: 0 3 | version: 4 | major: 1 5 | minor: 1 6 | main: 7 | start: module_start 8 | stop: module_stop 9 | modules: 10 | X1VitaDebug: 11 | syscall: true 12 | functions: 13 | - GetPidVid 14 | - GetBuff 15 | - SetSwapStatus 16 | - GetSwapStatus 17 | - GetPortBuff -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "Linux", 5 | "includePath": [ 6 | "${workspaceFolder}/**", 7 | "${VITASDKINC}/**" 8 | ], 9 | "defines": [], 10 | "compilerPath": "/usr/bin/gcc", 11 | "cStandard": "gnu11", 12 | "cppStandard": "gnu++14", 13 | "intelliSenseMode": "linux-gcc-x64" 14 | } 15 | ], 16 | "version": 4 17 | } -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2.0) 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 | include("$ENV{VITASDK}/share/vita.cmake" REQUIRED) 12 | 13 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wl,-q -Wall -Wno-unknown-pragmas -O3 -Wno-unused-function") 14 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-exceptions") 15 | 16 | project(X1Vita) 17 | 18 | add_executable(${PROJECT_NAME} 19 | src/bluetooth.c 20 | ) 21 | 22 | 23 | set_target_properties(${PROJECT_NAME} 24 | PROPERTIES LINK_FLAGS "-nostdlib" 25 | COMPILE_FLAGS "-D__VITA_KERNEL__" 26 | ) 27 | 28 | target_link_libraries(${PROJECT_NAME} 29 | gcc 30 | taihenForKernel_stub 31 | SceSysclibForDriver_stub 32 | SceSysmemForDriver_stub 33 | SceSysmemForKernel_stub 34 | SceThreadmgrForDriver_stub 35 | SceIofilemgrForDriver_stub 36 | taihenModuleUtils_stub 37 | SceBtForDriver_stub 38 | SceDebugForDriver_stub 39 | SceKernelSuspendForDriver_stub 40 | SceModulemgrForKernel_stub 41 | SceCtrlForDriver_stub 42 | SceSblAIMgrForDriver_stub 43 | SceSysrootForDriver_stub 44 | SceProcessmgrForDriver_stub 45 | ) 46 | 47 | vita_create_self(X1VitaCompanion.self X1VitaCompanion UNSAFE) 48 | vita_create_self(${PROJECT_NAME}.skprx ${PROJECT_NAME} CONFIG ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.yml UNSAFE) 49 | vita_create_stubs(stubs ${PROJECT_NAME} ${CMAKE_SOURCE_DIR}/${PROJECT_NAME}.yml KERNEL) 50 | 51 | 52 | add_executable(X1VitaCompanion 53 | src/debug.c 54 | src/common.h 55 | ) 56 | 57 | target_link_libraries(X1VitaCompanion 58 | SceLibKernel_stub 59 | taihen_stub 60 | SceVshBridge_stub 61 | ${CMAKE_SOURCE_DIR}/build/Debug/stubs/libX1Vita_stub_weak.a 62 | SceThreadmgrForDriver_stub 63 | SceShellSvc_stub 64 | vita2d 65 | SceDisplay_stub 66 | SceGxm_stub 67 | SceSysmodule_stub 68 | SceCtrl_stub 69 | ScePgf_stub 70 | ScePvf_stub 71 | SceCommonDialog_stub 72 | freetype 73 | png 74 | jpeg 75 | z 76 | m 77 | c 78 | SceAppMgr_stub 79 | ) 80 | 81 | vita_create_vpk("X1VitaCompanion.vpk" "DEBG00001" "X1VitaCompanion.self" 82 | VERSION "01.00" 83 | NAME "X1Vita Companion" 84 | ) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # X1Vita 2 | 3 | **Download**: https://github.com/Ibrahim778/X1Vita/releases 4 | 5 | **Enable the plugin:** 6 | 7 | 1. Add X1Vita.skprx to taiHEN's config (ux0:/tai/config.txt) or (ur0:/tai/config.txt): 8 | ``` 9 | *KERNEL 10 | ux0:tai/X1Vita.skprx 11 | ``` 12 | 2. You need to refresh the config.txt by rebooting or through VitaShell. 13 | 14 | **Using it for the first time (pairing the controller):** 15 | 16 | 1. Go to Settings -> Devices -> Bluetooth Devices 17 | 2. Press the pair on the controller for about 3-4 seconds, until the logo blinks very quickly 18 | 3. The controller will then connect and be paired (You may get a message saying "Do you want to connect to (blank)" just press ok and ignore the error) 19 | 20 | **Using it once paired (see above):** 21 | 1. Just press the xbox button and it will connect to the Vita 22 | 23 | **Note**: If you are using multiple controllers the controller ports will be set in the order you connect (if available), so first connection -> port 1 second connection -> port 2 etc, simply powering off the controller (ie removing batteries) will not trigger a disconnect on vita. You need to hold the Xbox Button till it turns off. 24 | **Note**: If you use Mai, don't put the plugin inside ux0:/plugins because Mai will load all stuff you put in there... 25 | 26 | Made for the kyuhen homebrew contest. 27 | Based on ds4vita 28 | 29 | # FAQ 30 | **1. My controller connects but doesn't do anything!** 31 | There can several reasons but here are the most common: 32 | 1. Your using a 3rd party controller. (see note below) 33 | 2. Your using an official controller other than the Xbox One (For e.g. Xbox One Eliete, Eliete 2) (see note below) 34 | 3. Your using the new Xbox one controllers, ie the ones with the screenshot button (including the series controllers) are **not** suppourted, this is simply because the vita's bluetooth version does not suppourt them so they simply cannot connect no matter what. 35 | 4. You installed the plugin incorrectly (install and open the companion to see if X1Vita is detected) 36 | 37 | **2. My controller doesn't show up in bluetooth devices!** 38 | Microsoft updated the controller firmware that for some reason made it invisible to the vita, to fix this, downgrade your controllers firmware with [this guide](https://support.xbox.com/en-GB/help/hardware-network/accessories/controller-firmware-reversion). 39 | Note: This fix will *only* work on xbox one controllers. Xbox series controllers use an unsupported bluetooth version and as such will never be able to be used. 40 | 41 | ## Note about controllers not working 42 | For now only official Xbox One controllers are suppourted all others will be ignored. I am no longer adding 3rd party suppourt as they use different payloads (sometimes). 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "common.h" 7 | 8 | #define StartY 20 9 | #define TextColour RGBA8(0,255,0,255) 10 | #define SelectColour RGBA8(0,247,255,255) 11 | #define MaxSelections 2 12 | 13 | void DebugScreen(); 14 | void TriggerSwap(); 15 | void WarningSreen(); 16 | void SelectScreen(); 17 | extern SceUID _vshKernelSearchModuleByName(const char *name, SceUInt64 *unk); 18 | 19 | vita2d_pgf *pgf; 20 | 21 | int moduleLoaded = 0; 22 | 23 | char Selections[MaxSelections][400] = { "Debug Bluetooth", "Swap triggers and bumpers" }; 24 | void (*functions[MaxSelections])(void) = { &DebugScreen, &TriggerSwap }; 25 | 26 | void drawBuff() 27 | { 28 | int currentY = 20; 29 | if(!moduleLoaded) 30 | { 31 | vita2d_pgf_draw_text(pgf, (960/2) - 100, (544/2)-10, RGBA8(0,255,0,255), 1.0f, "Error X1Vita not found!"); 32 | return; 33 | } 34 | else 35 | { 36 | vita2d_pgf_draw_text(pgf, 960 - 150, currentY, RGBA8(0,255,0,255), 1.0f, "X1Vita Found!"); 37 | } 38 | int swapStatus = GetSwapStatus(); 39 | char buff[0x12]; 40 | GetBuff(0, buff); 41 | 42 | char outBuff[0x400]; 43 | memset(outBuff, 0, 0x400); 44 | 45 | int currentX = 0; 46 | for (int i = 0; i < 0x12; i++) 47 | { 48 | sprintf(outBuff, "%02X", buff[i]); 49 | vita2d_pgf_draw_text(pgf, currentX, currentY, RGBA8(0,255,0,255), 1.0f, outBuff); 50 | currentX += 40; 51 | } 52 | vita2d_pgf_draw_text(pgf, currentX, currentY, RGBA8(0,255,0,255), 1.0f, outBuff); 53 | 54 | currentY += 25; 55 | currentX = 0; 56 | 57 | GetPortBuff(buff); 58 | for (int i = 0; i < CONTROLLER_COUNT; i++) 59 | { 60 | sprintf(outBuff, "%02X", buff[i]); 61 | vita2d_pgf_draw_text(pgf, currentX, currentY, RGBA8(0,255,0,255), 1.0f, outBuff); 62 | currentX += 40; 63 | } 64 | 65 | currentY += 25; 66 | int pid; 67 | int vid; 68 | GetPidVid(&pid, &vid); 69 | 70 | sprintf(outBuff, "Last connection attempt had PID: 0x%X VID: 0x%X\n", pid, vid); 71 | vita2d_pgf_draw_text(pgf, 0, currentY, SelectColour, 1.0f,outBuff); 72 | currentY += 25; 73 | if(swapStatus) vita2d_pgf_draw_text(pgf, 0, currentY, SelectColour, 1.0f, "Triggers and bumpers are swapped"); 74 | else vita2d_pgf_draw_text(pgf, 0, currentY, SelectColour, 1.0f, "Triggers and bumpers are not swapped"); 75 | currentY += 25; 76 | vita2d_pgf_draw_text(pgf, 0, currentY, TextColour, 1.0f, "Hold Select + Start + L + R to exit"); 77 | } 78 | 79 | void TriggerSwap() 80 | { 81 | vita2d_start_drawing(); 82 | vita2d_clear_screen(); 83 | 84 | if(GetSwapStatus()) 85 | { 86 | vita2d_pgf_draw_text(pgf, 0, StartY, TextColour, 1.0f, "Triggers are no longer swapped"); 87 | int swap = 0; 88 | SetSwapStatus(&swap); 89 | } 90 | else 91 | { 92 | vita2d_pgf_draw_text(pgf, 0, StartY, TextColour, 1.0f, "Triggers are now swapped"); 93 | int swap = 1; 94 | SetSwapStatus(&swap); 95 | } 96 | 97 | vita2d_end_drawing(); 98 | vita2d_swap_buffers(); 99 | sceShellUtilUnlock(SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2); 100 | sceKernelDelayThread(3 * 1000000); 101 | sceShellUtilLock(SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2); 102 | } 103 | 104 | void DebugScreen() 105 | { 106 | SceCtrlData buttons; 107 | do 108 | { 109 | sceCtrlPeekBufferPositive(0, &buttons, 1); 110 | vita2d_start_drawing(); 111 | vita2d_clear_screen(); 112 | drawBuff(); 113 | vita2d_end_drawing(); 114 | vita2d_swap_buffers(); 115 | } while (((buttons.buttons & SCE_CTRL_START) && (buttons.buttons & SCE_CTRL_SELECT) && (buttons.buttons & SCE_CTRL_LTRIGGER) && (buttons.buttons & SCE_CTRL_RTRIGGER)) == 0); 116 | SelectScreen(); 117 | sceKernelDelayThread(20000); 118 | } 119 | 120 | void PrintSelection(int selection) 121 | { 122 | int printY = StartY; 123 | vita2d_pgf_draw_text(pgf, 0, printY, TextColour, 1.0f, "Thank you for using X1Vita! Press Start to exit"); 124 | printY += StartY + 10; 125 | for (int i = 0; i < MaxSelections; i++) 126 | { 127 | if(selection == i) vita2d_pgf_draw_text(pgf, 0, printY, SelectColour, 1.0f, Selections[i]); 128 | else vita2d_pgf_draw_text(pgf, 0, printY, TextColour, 1.0f, Selections[i]); 129 | printY += StartY; 130 | } 131 | printY += StartY + 10; 132 | vita2d_pgf_draw_text(pgf, 0, printY, TextColour, 1.0f, "If you have any issues you can contact me via discord: M Ibrahim#0197"); 133 | } 134 | 135 | void WarningScreen() 136 | { 137 | sceShellUtilUnlock(SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2); 138 | while (1) 139 | { 140 | vita2d_start_drawing(); 141 | vita2d_clear_screen(); 142 | vita2d_pgf_draw_text(pgf, (960/2) - 100, (544/2)-10, RGBA8(0,255,0,255), 1.0f, "Error X1Vita not found!"); 143 | vita2d_end_drawing(); 144 | vita2d_swap_buffers(); 145 | } 146 | } 147 | 148 | void SelectScreen() 149 | { 150 | SceCtrlData pad; 151 | int CurrentSelection = 0; 152 | do 153 | { 154 | sceCtrlPeekBufferPositive(0, &pad, 1); 155 | vita2d_start_drawing(); 156 | vita2d_clear_screen(); 157 | PrintSelection(CurrentSelection); 158 | vita2d_end_drawing(); 159 | vita2d_swap_buffers(); 160 | 161 | if(pad.buttons & SCE_CTRL_UP) 162 | { 163 | if(CurrentSelection > 0) 164 | { 165 | CurrentSelection--; 166 | sceKernelDelayThread(200000); 167 | } 168 | } 169 | if(pad.buttons & SCE_CTRL_DOWN) 170 | { 171 | if(CurrentSelection < MaxSelections - 1) 172 | { 173 | CurrentSelection++; 174 | sceKernelDelayThread(200000); 175 | } 176 | } 177 | if(pad.buttons & SCE_CTRL_CROSS) 178 | { 179 | functions[CurrentSelection](); 180 | sceKernelDelayThread(200000); 181 | } 182 | } while (!(pad.buttons & SCE_CTRL_START)); 183 | 184 | } 185 | 186 | int main() 187 | { 188 | sceShellUtilInitEvents(0); 189 | sceShellUtilLock(SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2); 190 | 191 | SceUInt64 searchBuff = 0; 192 | moduleLoaded = (_vshKernelSearchModuleByName("X1Vita", &searchBuff) >= 0); 193 | 194 | vita2d_init(); 195 | vita2d_set_clear_color(RGBA8(0x40, 0x40, 0x40, 0xFF)); 196 | 197 | pgf = vita2d_load_default_pgf(); 198 | 199 | if(!moduleLoaded) WarningScreen(); 200 | else SelectScreen(); 201 | 202 | vita2d_fini(); 203 | vita2d_free_pgf(pgf); 204 | sceShellUtilUnlock(SCE_SHELL_UTIL_LOCK_TYPE_PS_BTN_2); 205 | return sceKernelExitProcess(0); 206 | } 207 | -------------------------------------------------------------------------------- /src/bluetooth.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include "common.h" 14 | 15 | typedef struct 16 | { 17 | unsigned int mac0; 18 | unsigned int mac1; 19 | int connected; 20 | int pid; 21 | int vid; 22 | } ControllerInfo; 23 | 24 | #define MICROSOFT_VID 0x45E 25 | #define XBOX_CONTROLLER_PID 0x2FD 26 | #define CONTROLLER_ANALOG_THRESHOLD 4 27 | #define DEADZONE_ANALOG(a) ((a) > 127 - CONTROLLER_ANALOG_THRESHOLD && (a) < 127 + CONTROLLER_ANALOG_THRESHOLD ? 127 : (a)) 28 | #define abs(x) (((x) < 0) ? -(x) : (x)) 29 | 30 | static SceUID bt_mempool_uid = -1; 31 | static SceUID bt_thread_uid = -1; 32 | static SceUID bt_cb_uid = -1; 33 | static int bt_thread_run = 1; 34 | 35 | static int lt_rt_swap = 0; 36 | static int ignoreHook = 0; 37 | 38 | static int lastPID = -1, lastVID = -1; 39 | 40 | static ControllerInfo controllers[CONTROLLER_COUNT]; 41 | static char current_recieved_input[CONTROLLER_COUNT][0x12]; 42 | static char battery_info[CONTROLLER_COUNT][0x12]; 43 | 44 | static char original_input[CONTROLLER_COUNT]; 45 | 46 | int module_get_export_func(SceUID pid, const char *modname, uint32_t libnid, uint32_t funcnid, uintptr_t *func); 47 | static int (* ksceKernelSysrootCheckModelCapability)(int capability) = NULL; 48 | 49 | 50 | #pragma region Definitions 51 | 52 | #define UNBIND_FUNC_HOOK(name) \ 53 | do { \ 54 | if (name##_hook_uid > 0) { \ 55 | taiHookReleaseForKernel(name##_hook_uid, name##_ref); \ 56 | } \ 57 | } while(0) 58 | 59 | #define DECL_FUNC_HOOK(name, ...) \ 60 | static tai_hook_ref_t name##_ref; \ 61 | static SceUID name##_hook_uid = -1; \ 62 | static int name##_hook_func(__VA_ARGS__) 63 | 64 | #define DECL_FUNC_HOOK_PATCH_CTRL(type, name, triggers, logic) \ 65 | DECL_FUNC_HOOK(SceCtrl_##name, int port, SceCtrlData *pad_data, int count) \ 66 | { \ 67 | int ret = TAI_CONTINUE(int, SceCtrl_ ##name##_ref, port, pad_data, count, triggers); \ 68 | if (ret >= 0 && controllers[port].connected) \ 69 | patch_ctrl_data_all_##type(port, pad_data, count, triggers, logic); \ 70 | return ret; \ 71 | } 72 | 73 | #define BIND_FUNC_OFFSET_HOOK(name, pid, modid, segidx, offset, thumb) \ 74 | name##_hook_uid = taiHookFunctionOffsetForKernel((pid), \ 75 | &name##_ref, (modid), (segidx), (offset), thumb, name##_hook_func) 76 | 77 | #define BIND_FUNC_EXPORT_HOOK(name, pid, module, lib_nid, func_nid) \ 78 | name##_hook_uid = taiHookFunctionExportForKernel((pid), \ 79 | &name##_ref, (module), (lib_nid), (func_nid), name##_hook_func) 80 | #pragma endregion Definitions 81 | 82 | #pragma region Generics 83 | int findPort(unsigned int mac0, unsigned int mac1) 84 | { 85 | for (int i = 0; i < CONTROLLER_COUNT; i++) 86 | { 87 | if(controllers[i].mac0 == mac0 && controllers[i].mac1 == mac1) 88 | { 89 | return i; 90 | } 91 | } 92 | return -1; 93 | } 94 | 95 | int findFreePort() 96 | { 97 | SceCtrlPortInfo info; 98 | ksceCtrlGetControllerPortInfo(&info); 99 | if(ksceKernelSysrootCheckModelCapability(1)) 100 | { 101 | for (int i = 1; i < CONTROLLER_COUNT; i++) 102 | { 103 | if(info.port[i] != SCE_CTRL_TYPE_DS4 && info.port[i] != SCE_CTRL_TYPE_DS3) 104 | { 105 | return i; 106 | } 107 | } 108 | } 109 | else 110 | { 111 | if(info.port[1]) return -1; 112 | else return 1; 113 | } 114 | return -1; 115 | } 116 | 117 | int checkFileExist(const char *file) 118 | { 119 | int fd = ksceIoOpen(file, SCE_O_RDONLY, 0); 120 | if(fd < 0) return 0; 121 | ksceIoClose(fd); 122 | return 1; 123 | } 124 | 125 | int checkDirExist(const char *file) 126 | { 127 | int fd = ksceIoDopen(file); 128 | if(fd < 0) return 0; 129 | ksceIoDclose(fd); 130 | return 1; 131 | } 132 | 133 | void createFile(const char *file) 134 | { 135 | int fd = ksceIoOpen(file, SCE_O_WRONLY | SCE_O_CREAT, 0777); 136 | ksceIoClose(fd); 137 | } 138 | 139 | static inline void controller_input_reset(void) 140 | { 141 | for (int i = 0; i < CONTROLLER_COUNT; i++) 142 | { 143 | memset(current_recieved_input[i], 0, sizeof current_recieved_input[i]); 144 | } 145 | } 146 | 147 | //Returns 1 if any controller is connected else 0 148 | int getConnectionStatus() 149 | { 150 | int found = 0; 151 | for (int i = 1; i < CONTROLLER_COUNT; i++) 152 | { 153 | if(controllers[i].connected) 154 | { 155 | found = 1; 156 | break; 157 | } 158 | } 159 | return found; 160 | } 161 | 162 | static int is_controller(const unsigned short vid_pid[2]) 163 | { 164 | lastVID = vid_pid[0]; 165 | lastPID = vid_pid[1]; 166 | return (vid_pid[0] == MICROSOFT_VID) && 167 | (vid_pid[1] == XBOX_CONTROLLER_PID); 168 | } 169 | 170 | static inline void *mempool_alloc(unsigned int size) 171 | { 172 | return ksceKernelAllocHeapMemory(bt_mempool_uid, size); 173 | } 174 | 175 | static inline void mempool_free(void *ptr) 176 | { 177 | ksceKernelFreeHeapMemory(bt_mempool_uid, ptr); 178 | } 179 | #pragma endregion Generics 180 | #pragma region Exports 181 | //Exports 182 | 183 | //Get weather the triggers or bumpers are swaped (bool) 184 | int GetSwapStatus() 185 | { 186 | return lt_rt_swap; 187 | } 188 | 189 | //Set weather the triggers or bumpers are swaped (bool). Returns 0 if success < 0 on error. 190 | int SetSwapStatus(int *status) 191 | { 192 | int setRes = ksceKernelMemcpyUserToKernel(<_rt_swap, (uintptr_t)status, sizeof(int)); 193 | if(setRes < 0) return setRes; 194 | if(!lt_rt_swap) 195 | { 196 | if(checkFileExist(ConfigPath)) 197 | { 198 | ksceIoRemove(ConfigPath); 199 | return 0; 200 | } 201 | } 202 | if(lt_rt_swap) 203 | { 204 | if(!checkFileExist(ConfigPath)) 205 | { 206 | if(!checkDirExist("ux0:data/X1Vita")) 207 | ksceIoMkdir("ux0:data/X1Vita", SCE_S_IWUSR | SCE_S_IRUSR); 208 | createFile(ConfigPath); 209 | return 0; 210 | } 211 | } 212 | return 0; 213 | } 214 | 215 | //Get PID and VID of last request 216 | int GetPidVid(int *vid, int *pid) 217 | { 218 | int ret; 219 | ret = ksceKernelMemcpyKernelToUser((uintptr_t)vid, &lastVID, sizeof(lastVID)); 220 | if (ret < 0) return ret; 221 | return ksceKernelMemcpyKernelToUser((uintptr_t)pid, &lastPID, sizeof(lastPID)); 222 | } 223 | 224 | //Get Port Info 225 | int GetPortBuff(const char* buff) 226 | { 227 | return ksceKernelMemcpyKernelToUser((uintptr_t)buff, original_input, 5); 228 | } 229 | 230 | //Get current data recieved from the connected bluetooth device 231 | int GetBuff(int port, const char* buff) 232 | { 233 | //We use memcpy instead of strncpy so it copies the whole thing if not values will be cut off. 234 | return ksceKernelMemcpyKernelToUser((uintptr_t)buff, ¤t_recieved_input[port], 0x12); 235 | } 236 | #pragma endregion Exports 237 | #pragma region Hooks 238 | DECL_FUNC_HOOK(SceBt_sub_22999C8, void *dev_base_ptr, int r1) 239 | { 240 | unsigned int flags = *(unsigned int *)(r1 + 4); 241 | 242 | if (dev_base_ptr && !(flags & 2)) { 243 | const void *dev_info = *(const void **)(dev_base_ptr + 0x14A4); 244 | const unsigned short *vid_pid = (const unsigned short *)(dev_info + 0x28); 245 | 246 | if (is_controller(vid_pid)) { 247 | unsigned int *v8_ptr = (unsigned int *)(*(unsigned int *)dev_base_ptr + 8); 248 | 249 | /* 250 | * We need to enable the following bits in order to make the Vita 251 | * accept the new connection, otherwise it will refuse it. 252 | */ 253 | *v8_ptr |= 0x11000; 254 | } 255 | } 256 | 257 | return TAI_CONTINUE(int, SceBt_sub_22999C8_ref, dev_base_ptr, r1); 258 | } 259 | DECL_FUNC_HOOK(SceCtrl_ksceCtrlGetControllerPortInfo, SceCtrlPortInfo *info) 260 | { 261 | int ret = TAI_CONTINUE(int, SceCtrl_ksceCtrlGetControllerPortInfo_ref, info); 262 | 263 | if(!ignoreHook) 264 | { 265 | if(getConnectionStatus() && ksceKernelSysrootCheckModelCapability(1)) 266 | info->port[0] = SCE_CTRL_TYPE_VIRT; 267 | 268 | original_input[0] = info->port[0]; 269 | 270 | for (int i = 1; i < CONTROLLER_COUNT; i++) 271 | { 272 | if(controllers[i].connected) info->port[i] = SCE_CTRL_TYPE_DS4; 273 | original_input[i] = info->port[i]; 274 | } 275 | } 276 | return ret; 277 | } 278 | #pragma endregion Hooks 279 | 280 | #pragma region Functions 281 | static int controller_send_report(unsigned int mac0, unsigned int mac1, uint8_t flags, uint8_t report, size_t len, const void *data) 282 | { 283 | SceBtHidRequest *req; 284 | unsigned char *buf; 285 | 286 | req = mempool_alloc(sizeof(*req)); 287 | if (!req) { 288 | return -1; 289 | } 290 | 291 | if ((buf = mempool_alloc((len + 1) * sizeof(*buf))) == NULL) { 292 | return -1; 293 | } 294 | 295 | buf[0] = report; 296 | memcpy(buf + 1, data, len); 297 | 298 | memset(req, 0, sizeof(*req)); 299 | req->type = 1; // 0xA2 -> type = 1 300 | req->buffer = buf; 301 | req->length = len + 1; 302 | req->next = req; 303 | 304 | int res = ksceBtHidTransfer(mac0, mac1, req); 305 | if(res < 0) return -1; 306 | mempool_free(buf); 307 | mempool_free(req); 308 | 309 | return 0; 310 | } 311 | 312 | static int controller_send_0x11_report(unsigned int mac0, unsigned int mac1) 313 | { 314 | unsigned char data[] = { 315 | 0x80, 316 | 0x0F, 317 | 0x00, 318 | 0x00, 319 | 0x00, 320 | 0x00, 321 | 0x00, 322 | 0x00, 323 | 0x00, 324 | 0x00, 325 | 0x00, 326 | 0x00, 327 | }; 328 | 329 | if (controller_send_report(mac0, mac1, 0, 0x11, sizeof(data), data) < 0) { 330 | return -1; 331 | } 332 | 333 | return 0; 334 | } 335 | static void enqueue_read_request(unsigned int mac0, unsigned int mac1, SceBtHidRequest *request, unsigned char *buffer, unsigned int length) 336 | { 337 | memset(request, 0, sizeof(*request)); 338 | memset(buffer, 0, length); 339 | 340 | request->type = 0; 341 | request->buffer = buffer; 342 | request->length = length; 343 | request->next = request; 344 | 345 | ksceBtHidTransfer(mac0, mac1, request); 346 | } 347 | static int bt_cb_func(int notifyId, int notifyCount, int notifyArg, void *common) 348 | { 349 | 350 | static SceBtHidRequest hid_request; 351 | static unsigned char recv_buff[0x100]; 352 | while (1) { 353 | int ret; 354 | SceBtEvent hid_event; 355 | 356 | memset(&hid_event, 0, sizeof(hid_event)); 357 | 358 | do { 359 | ret = ksceBtReadEvent(&hid_event, 1); 360 | } while (ret == SCE_BT_ERROR_CB_OVERFLOW); 361 | 362 | if (ret <= 0) { 363 | break; 364 | } 365 | 366 | unsigned short vid_pid[2]; 367 | ksceBtGetVidPid(hid_event.mac0, hid_event.mac1, vid_pid); 368 | if(!is_controller(vid_pid)) 369 | continue; 370 | 371 | switch (hid_event.id) 372 | { 373 | case 0x05: 374 | { /* Connection accepted event */ 375 | if (is_controller(vid_pid)) 376 | { 377 | int port = findFreePort(); 378 | if(port < 0) 379 | { 380 | ksceBtStartDisconnect(hid_event.mac0, hid_event.mac1); 381 | break; 382 | } 383 | controllers[port].connected = 1; 384 | if(port == 1) controllers[0].connected = 1; 385 | controllers[port].mac0 = hid_event.mac0; 386 | controllers[port].mac1 = hid_event.mac1; 387 | controllers[port].pid = vid_pid[1]; 388 | controllers[port].vid = vid_pid[0]; 389 | controller_send_0x11_report(hid_event.mac0, hid_event.mac1); 390 | } 391 | break; 392 | } 393 | 394 | case 0x06: 395 | { 396 | int port = findPort(hid_event.mac0, hid_event.mac1); 397 | controllers[port].connected = 0; 398 | if(port == 1) controllers[0].connected = 0; 399 | break; 400 | } 401 | 402 | case 0x0A: 403 | { 404 | int port = findPort(hid_event.mac0, hid_event.mac1); 405 | if(recv_buff[0] != 0x4) 406 | { 407 | memcpy(current_recieved_input[port], recv_buff, 0x11); 408 | if(port == 1) 409 | { 410 | memcpy(current_recieved_input[0], recv_buff, 0x11); 411 | } 412 | } 413 | else memcpy(battery_info[port], recv_buff, 0x11); 414 | enqueue_read_request(hid_event.mac0, hid_event.mac1, &hid_request, recv_buff, sizeof(recv_buff)); 415 | break; 416 | } 417 | 418 | case 0x0B: 419 | enqueue_read_request(hid_event.mac0, hid_event.mac1, &hid_request, recv_buff, sizeof(recv_buff)); 420 | break; 421 | } 422 | } 423 | 424 | return 0; 425 | } 426 | static int controllervita_bt_thread(SceSize args, void *argp) 427 | { 428 | bt_cb_uid = ksceKernelCreateCallback("controllervita_bt_callback", 0, bt_cb_func, NULL); 429 | 430 | ksceBtRegisterCallback(bt_cb_uid, 0, 0xFFFFFFFF, 0xFFFFFFFF); 431 | 432 | 433 | while (bt_thread_run) 434 | { 435 | ksceKernelDelayThreadCB(200 * 1000); 436 | } 437 | 438 | for (int i = 0; i < CONTROLLER_COUNT; i++) 439 | { 440 | if(controllers[i].connected) ksceBtStartDisconnect(controllers[i].mac0, controllers[i].mac1); 441 | } 442 | 443 | ksceBtUnregisterCallback(bt_cb_uid); 444 | 445 | ksceKernelDeleteCallback(bt_cb_uid); 446 | 447 | return 0; 448 | } 449 | static void patch_ctrl_data(SceCtrlData *pad_data, int triggers, int port, int isPositive) 450 | { 451 | if(!(controllers[port].vid == MICROSOFT_VID && controllers[port].pid == XBOX_CONTROLLER_PID)) return; 452 | int leftX = 0x80; 453 | int leftY = 0x80; 454 | int rightX = 0x80; 455 | int rightY = 0x80; 456 | int joyStickMoved = 0; 457 | unsigned int buttons = 0; 458 | 459 | int lt = ((current_recieved_input[port][9] + (255 * current_recieved_input[port][10])) / 4); 460 | int rt = ((current_recieved_input[port][11] + (255 * current_recieved_input[port][12])) / 4); 461 | 462 | //Xbox button 463 | if(current_recieved_input[port][0] & 0x02) 464 | { 465 | if(current_recieved_input[port][1] & 0x01) 466 | { 467 | buttons |= SCE_CTRL_PSBUTTON; 468 | ksceCtrlSetButtonEmulation(0, 0, 0, SCE_CTRL_PSBUTTON, 8); 469 | } 470 | } //Any other button call 471 | else if(current_recieved_input[port][0] & 0x1) 472 | { 473 | 474 | //DPad 475 | switch (current_recieved_input[port][13]) 476 | { 477 | case 0x1: 478 | buttons |= SCE_CTRL_UP; 479 | break; 480 | case 0x2: 481 | buttons |= SCE_CTRL_UP; 482 | buttons |= SCE_CTRL_RIGHT; 483 | break; 484 | case 0x3: 485 | buttons |= SCE_CTRL_RIGHT; 486 | break; 487 | case 0x4: 488 | buttons |= SCE_CTRL_RIGHT; 489 | buttons |= SCE_CTRL_DOWN; 490 | break; 491 | case 0x5: 492 | buttons |= SCE_CTRL_DOWN; 493 | break; 494 | case 0x6: 495 | buttons |= SCE_CTRL_DOWN; 496 | buttons |= SCE_CTRL_LEFT; 497 | break; 498 | case 0x7: 499 | buttons |= SCE_CTRL_LEFT; 500 | break; 501 | case 0x8: 502 | buttons |= SCE_CTRL_LEFT; 503 | buttons |= SCE_CTRL_UP; 504 | break; 505 | } 506 | 507 | //RB LB and ABXY. For some reason the buttons aren't or'ed together when pushed separetly which is why we need to do it like this. 508 | switch (current_recieved_input[port][14]) 509 | { 510 | case 0x1: 511 | buttons |= SCE_CTRL_CROSS; 512 | break; 513 | case 0x2: 514 | buttons |= SCE_CTRL_CIRCLE; 515 | break; 516 | case 0x8: 517 | buttons |= SCE_CTRL_SQUARE; 518 | break; 519 | case 0x10: 520 | buttons |= SCE_CTRL_TRIANGLE; 521 | break; 522 | case 0x80: 523 | //RB 524 | if(!lt_rt_swap) 525 | { 526 | if(triggers) buttons |= SCE_CTRL_R1; 527 | else buttons |= SCE_CTRL_RTRIGGER; 528 | } 529 | else 530 | { 531 | pad_data->rt = 255; 532 | } 533 | break; 534 | case 0x40: 535 | //LB 536 | if(!lt_rt_swap) 537 | { 538 | if(triggers) buttons |= SCE_CTRL_L1; 539 | else buttons |= SCE_CTRL_LTRIGGER; 540 | } 541 | else 542 | { 543 | pad_data->lt = 255; 544 | } 545 | break; 546 | default: 547 | { 548 | if(current_recieved_input[port][14] & 0x1) buttons |= SCE_CTRL_CROSS; 549 | if(current_recieved_input[port][14] & 0x2) buttons |= SCE_CTRL_CIRCLE; 550 | if(current_recieved_input[port][14] & 0x8) buttons |= SCE_CTRL_SQUARE; 551 | if(current_recieved_input[port][14] & 0x10) buttons |= SCE_CTRL_TRIANGLE; 552 | if(current_recieved_input[port][14] & 0x80) 553 | { 554 | if(!lt_rt_swap) 555 | { 556 | if(triggers) buttons |= SCE_CTRL_R1; 557 | else buttons |= SCE_CTRL_RTRIGGER; 558 | } 559 | else 560 | { 561 | pad_data->rt = 255; 562 | } 563 | } 564 | if(current_recieved_input[port][14] & 0x40) 565 | { 566 | if(!lt_rt_swap) 567 | { 568 | if(triggers) buttons |= SCE_CTRL_L1; 569 | else buttons |= SCE_CTRL_LTRIGGER; 570 | } 571 | else 572 | { 573 | pad_data->lt = 255; 574 | } 575 | } 576 | break; 577 | } 578 | } 579 | 580 | //Start 581 | //if(current_recieved_input[port][15] == 0x8) buttons |= SCE_CTRL_START; 582 | //Select 583 | if(current_recieved_input[port][16] == 0x1) buttons |= SCE_CTRL_SELECT; 584 | 585 | //R3 L3 Start and Xbox button on newer firmwares controllers 586 | switch (current_recieved_input[port][15]) 587 | { 588 | case 0x10: 589 | { 590 | buttons |= SCE_CTRL_PSBUTTON; 591 | ksceCtrlSetButtonEmulation(0, 0, 0, SCE_CTRL_PSBUTTON, 8); 592 | break; 593 | } 594 | case 0x8: 595 | buttons |= SCE_CTRL_START; 596 | break; 597 | case 0x40: 598 | buttons |= SCE_CTRL_R3; 599 | break; 600 | case 0x20: 601 | buttons |= SCE_CTRL_L3; 602 | 603 | default: 604 | if(current_recieved_input[port][15] & 0x8) 605 | { 606 | buttons |= SCE_CTRL_START; 607 | } 608 | if(current_recieved_input[port][15] & 0x40) 609 | { 610 | buttons |= SCE_CTRL_R3; 611 | } 612 | if(current_recieved_input[port][15] & 0x20) 613 | { 614 | buttons |= SCE_CTRL_L3; 615 | } 616 | if(current_recieved_input[port][15] & 0x10) 617 | { 618 | buttons |= SCE_CTRL_PSBUTTON; 619 | ksceCtrlSetButtonEmulation(0, 0, 0, SCE_CTRL_PSBUTTON, 8); 620 | } 621 | break; 622 | } 623 | 624 | //Joysticks 625 | //Left Joystick X 626 | leftX = DEADZONE_ANALOG((current_recieved_input[port][2])); 627 | //Left Joystick Y 628 | leftY = DEADZONE_ANALOG((current_recieved_input[port][4])); 629 | 630 | //Right Joystick X 631 | rightX = DEADZONE_ANALOG((current_recieved_input[port][6])); 632 | //Right Joystick Y 633 | rightY = DEADZONE_ANALOG((current_recieved_input[port][8])); 634 | 635 | if(leftX != 128 && leftY != 128) 636 | joyStickMoved = 1; 637 | 638 | } 639 | 640 | //LT RT 641 | if(lt > 10) 642 | { 643 | if(!lt_rt_swap) 644 | { 645 | // if triggers = true, ltrigger = L2 & L1 = L1. Else Ltigger = L1 and LTRIGGER = L2 646 | if(triggers) buttons |= SCE_CTRL_LTRIGGER; 647 | else buttons |= SCE_CTRL_L1; 648 | } 649 | else 650 | { 651 | if(triggers) buttons |= SCE_CTRL_L1; 652 | else buttons |= SCE_CTRL_LTRIGGER; 653 | } 654 | } 655 | if(rt > 10) 656 | { 657 | if(!lt_rt_swap) 658 | { 659 | if(triggers) buttons |= SCE_CTRL_RTRIGGER; 660 | else buttons |= SCE_CTRL_R1; 661 | } 662 | else 663 | { 664 | if(triggers) buttons |= SCE_CTRL_R1; 665 | else buttons |= SCE_CTRL_RTRIGGER; 666 | } 667 | } 668 | 669 | if(!lt_rt_swap) 670 | { 671 | pad_data->lt = lt; 672 | pad_data->rt = rt; 673 | } 674 | //Joysticks 675 | if(joyStickMoved) 676 | { 677 | pad_data->ry = rightY; 678 | pad_data->rx = rightX; 679 | pad_data->lx = leftX; 680 | pad_data->ly = leftY; 681 | } 682 | 683 | pad_data->buttons |= buttons; 684 | if(!isPositive) pad_data->buttons = 0xFFFFFFFF - pad_data->buttons; 685 | if(buttons != 0 || joyStickMoved) ksceKernelPowerTick(0); 686 | } 687 | static void patch_ctrl_data_all_kernel(int port, SceCtrlData *pad_data, int count, int triggers, int isPositive) 688 | { 689 | unsigned int i; 690 | 691 | for (i = 0; i < count; i++, pad_data++) 692 | patch_ctrl_data(pad_data, triggers, port, isPositive); 693 | } 694 | #pragma endregion Functions 695 | 696 | #pragma region CtrlHooks 697 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferPositive, 0, 1) 698 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferPositive, 0, 1) 699 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferNegative, 0, 0) 700 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferNegative, 0, 0) 701 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferPositiveExt, 0, 1) 702 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferPositiveExt, 0, 1) 703 | 704 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferPositive2, 1, 1) 705 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferPositive2, 1, 1) 706 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferNegative2, 1, 1) 707 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferNegative2, 1, 1) 708 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlPeekBufferPositiveExt2, 1, 1) 709 | DECL_FUNC_HOOK_PATCH_CTRL(kernel, ksceCtrlReadBufferPositiveExt2, 1, 1) 710 | //0xFFFFFFFF 711 | #pragma endregion CtrlHooks 712 | 713 | void _start() __attribute__ ((weak, alias ("module_start"))); 714 | 715 | int module_start(SceSize argc, const void *args) 716 | { 717 | module_get_export_func(KERNEL_PID, "SceSysmem", TAI_ANY_LIBRARY, 0x8AA268D6, (uintptr_t *)&ksceKernelSysrootCheckModelCapability); 718 | memset(controllers, 0, sizeof(controllers)); 719 | int ret; 720 | tai_module_info_t SceBt_modinfo; 721 | 722 | SceBt_modinfo.size = sizeof(SceBt_modinfo); 723 | ret = taiGetModuleInfoForKernel(KERNEL_PID, "SceBt", &SceBt_modinfo); 724 | if (ret < 0) { 725 | goto error_find_scebt; 726 | } 727 | 728 | tai_module_info_t SceCtrl_modinfo; 729 | SceCtrl_modinfo.size = sizeof(SceCtrl_modinfo); 730 | 731 | if (taiGetModuleInfoForKernel(KERNEL_PID, "SceCtrl", &SceCtrl_modinfo) < 0) { 732 | ksceDebugPrintf("Error finding SceBt module\n"); 733 | return SCE_KERNEL_START_FAILED; 734 | } 735 | 736 | #pragma region BindHooks 737 | BIND_FUNC_EXPORT_HOOK(SceCtrl_ksceCtrlPeekBufferPositive, KERNEL_PID, "SceCtrl", TAI_ANY_LIBRARY, 0xEA1D3A34); 738 | BIND_FUNC_EXPORT_HOOK(SceCtrl_ksceCtrlReadBufferPositive, KERNEL_PID, "SceCtrl", TAI_ANY_LIBRARY, 0x9B96A1AA); 739 | BIND_FUNC_EXPORT_HOOK(SceCtrl_ksceCtrlPeekBufferNegative, KERNEL_PID, "SceCtrl", TAI_ANY_LIBRARY, 0x19895843); 740 | BIND_FUNC_EXPORT_HOOK(SceCtrl_ksceCtrlReadBufferNegative, KERNEL_PID, "SceCtrl", TAI_ANY_LIBRARY, 0x8D4E0DD1); 741 | BIND_FUNC_EXPORT_HOOK(SceCtrl_ksceCtrlGetControllerPortInfo, KERNEL_PID, "SceCtrl", TAI_ANY_LIBRARY, 0xF11D0D30); 742 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlPeekBufferPositiveExt, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x3928, 1); 743 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlReadBufferPositiveExt, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x3BCC, 1); 744 | 745 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlPeekBufferPositive2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x3EF8, 1); 746 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlReadBufferPositive2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x449C, 1); 747 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlPeekBufferNegative2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x41C8, 1); 748 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlReadBufferNegative2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x47F0, 1); 749 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlPeekBufferPositiveExt2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x4B48, 1); 750 | BIND_FUNC_OFFSET_HOOK(SceCtrl_ksceCtrlReadBufferPositiveExt2, KERNEL_PID, SceCtrl_modinfo.modid, 0, 0x4E14, 1); 751 | BIND_FUNC_OFFSET_HOOK(SceBt_sub_22999C8, KERNEL_PID, SceBt_modinfo.modid, 0, 0x22999C8 - 0x2280000, 1); 752 | #pragma endregion BindHooks 753 | 754 | SceKernelHeapCreateOpt opt; 755 | opt.size = 0x1C; 756 | opt.uselock = 0x100; 757 | opt.field_8 = 0x10000; 758 | opt.field_C = 0; 759 | opt.field_14 = 0; 760 | opt.field_18 = 0; 761 | 762 | bt_mempool_uid = ksceKernelCreateHeap("controllervita_mempool", 0x100, &opt); 763 | 764 | bt_thread_uid = ksceKernelCreateThread("controllervita_bt_thread", controllervita_bt_thread, 765 | 0x3C, 0x1000, 0, 0x10000, 0); 766 | ksceKernelStartThread(bt_thread_uid, 0, NULL); 767 | 768 | lt_rt_swap = checkFileExist(ConfigPath); 769 | 770 | return SCE_KERNEL_START_SUCCESS; 771 | 772 | error_find_scebt: 773 | return SCE_KERNEL_START_FAILED; 774 | } 775 | int module_stop(SceSize argc, const void *args) 776 | { 777 | SceUInt timeout = 0xFFFFFFFF; 778 | 779 | if (bt_thread_uid > 0) { 780 | bt_thread_run = 0; 781 | ksceKernelWaitThreadEnd(bt_thread_uid, NULL, &timeout); 782 | ksceKernelDeleteThread(bt_thread_uid); 783 | } 784 | 785 | if (bt_mempool_uid > 0) { 786 | ksceKernelDeleteHeap(bt_mempool_uid); 787 | } 788 | #pragma region UnbindHooks 789 | UNBIND_FUNC_HOOK(SceBt_sub_22999C8); 790 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferPositive); 791 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferPositive); 792 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferNegative); 793 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferNegative); 794 | 795 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferPositiveExt); 796 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferPositiveExt); 797 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlGetControllerPortInfo); 798 | 799 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferPositive2); 800 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferPositive2); 801 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferNegative2); 802 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferNegative2); 803 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlPeekBufferPositiveExt2); 804 | UNBIND_FUNC_HOOK(SceCtrl_ksceCtrlReadBufferPositiveExt2); 805 | #pragma endregion UnbindHooks 806 | return SCE_KERNEL_STOP_SUCCESS; 807 | } 808 | --------------------------------------------------------------------------------