├── .gitignore ├── .vscode ├── c_cpp_properties.json ├── settings.json └── tasks.json ├── Makefile ├── README.md ├── src ├── cshook.c ├── cshook.h ├── defines.h ├── hooks.c ├── main.c ├── offsets.h ├── printf.c ├── scrfct.c ├── scrfct.h ├── t5.c ├── t5.h ├── utils.c └── utils.h └── tmp └── T5GSCLoader ├── mp ├── welcome.mod └── welcome │ ├── main.gsc │ └── utils.gsc └── zm ├── welcome.mod └── welcome ├── main.gsc └── utils.gsc /.gitignore: -------------------------------------------------------------------------------- 1 | bin/* 2 | objs/* 3 | -------------------------------------------------------------------------------- /.vscode/c_cpp_properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "configurations": [ 3 | { 4 | "name": "PS3 SDK", 5 | "includePath": [ 6 | "${workspaceFolder}/src/", 7 | "${SCE_PS3_ROOT}/target/ppu/include", 8 | "${SCE_PS3_ROOT}/target/common/include", 9 | "${SCE_PS3_ROOT}/host-win32/ppu/lib/gcc/ppu-lv2/4.1.1/include" 10 | ], 11 | "compilerPath": "${SCE_PS3_ROOT}/host-win32/ppu/bin/ppu-lv2-gcc" 12 | } 13 | ], 14 | "version": 4 15 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "files.exclude": { 3 | "bin": true, 4 | "objs": true, 5 | }, 6 | "consoleIp": "192.168.1.100", 7 | "C_Cpp.errorSquiggles":"Disabled", 8 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "Build Debug", 8 | "type": "shell", 9 | "windows": { 10 | "command": "make clean && make -j" 11 | }, 12 | "options": {}, 13 | "problemMatcher": [], 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | }, 19 | { 20 | "label": "Deploy Debug", 21 | "type": "shell", 22 | "windows": { 23 | "command": "curl -T bin/debug/${workspaceFolderBasename}.sprx --ftp-create-dirs ftp://${config:consoleIp}/dev_hdd0/tmp/SprxLoader/BLES01031/${workspaceFolderBasename}.sprx" 24 | }, 25 | "options": {}, 26 | "problemMatcher": [], 27 | "group": { 28 | "kind": "build", 29 | "isDefault": true 30 | } 31 | }, 32 | { 33 | "label": "Build Release", 34 | "type": "shell", 35 | "windows": { 36 | "command": "make release -j" 37 | }, 38 | "options": {}, 39 | "problemMatcher": [], 40 | "group": { 41 | "kind": "build", 42 | "isDefault": true 43 | } 44 | }, 45 | { 46 | "label": "Deploy Release", 47 | "type": "shell", 48 | "windows": { 49 | "command": "curl -T bin/release/${workspaceFolderBasename}.sprx --ftp-create-dirs ftp://${config:consoleIp}/dev_hdd0/tmp/SprxLoader/BLES01031/${workspaceFolderBasename}.sprx" 50 | }, 51 | "options": {}, 52 | "problemMatcher": [], 53 | "group": { 54 | "kind": "build", 55 | "isDefault": true 56 | } 57 | }, 58 | { 59 | "label": "[GSC] Deploy scripts", 60 | "type": "shell", 61 | "windows": { 62 | // Be careful with ftp commands since the commands are with root privileges. 63 | // This command will only replace existing files on console. 64 | "command": "find tmp/T5GSCLoader -type f -exec curl --ftp-create-dirs -T '{}' ftp://${config:consoleIp}/dev_hdd0/'\\{}' ';'" 65 | }, 66 | "options": {}, 67 | "problemMatcher": [], 68 | "group": { 69 | "kind": "build", 70 | "isDefault": true 71 | } 72 | } 73 | ] 74 | } -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SOURCES_DIR := src 2 | OUTPUT_DIR := bin 3 | BUILD_TYPE ?= debug 4 | 5 | CELL_MK_DIR ?= $(SCE_PS3_ROOT)/samples/mk 6 | include $(CELL_MK_DIR)/sdk.makedef.mk 7 | 8 | PPU_SRCS := $(shell find $(SOURCES_DIR) -name "*.c") 9 | PPU_PRX_LDFLAGS += $(PRX_LDFLAGS_EXTRA) 10 | PPU_PRX_TARGET = $(OUTPUT_DIR)/$(BUILD_TYPE)/$(notdir $(CURDIR)).prx 11 | PPU_PRX_LDLIBS += -lc -lfs_stub 12 | 13 | ifeq ($(BUILD_TYPE),release) 14 | PPU_CFLAGS = -O2 15 | PRX_LDFLAGS_EXTRA = -s --stripe-unused-data 16 | else 17 | PPU_CFLAGS = -O0 18 | PRX_LDFLAGS_EXTRA = 19 | endif 20 | 21 | PPU_CFLAGS += -std=c99 -ffunction-sections -fdata-sections -fno-builtin-printf -nodefaultlibs -Wno-shadow -Wno-unused-parameter 22 | 23 | CLEANFILES = $(PRX_DIR)/$(PPU_SPRX_TARGET) 24 | 25 | include $(CELL_MK_DIR)/sdk.target.mk 26 | 27 | rebuild: 28 | $(MAKE) --no-print-directory clean 29 | $(MAKE) --no-print-directory all 30 | 31 | clean: 32 | @echo "# removing $(OUTPUT_DIR)/$(BUILD_TYPE) if it is empty."; 33 | @if [ -d $(OUTPUT_DIR)/$(BUILD_TYPE) ]; then \ 34 | rm -r $(OUTPUT_DIR)/$(BUILD_TYPE); \ 35 | fi 36 | 37 | @rmdir $(OUTPUT_DIR) ||: 38 | 39 | release: 40 | $(MAKE) rebuild BUILD_TYPE=release 41 | 42 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # T5GSCLoader 2 | 3 | T5GSCLoader is a PS3 module for Black Ops 1 that allows you to load custom game scripts, call C functions from them and make possible connect to others players by patching client/server checksum. 4 | 5 | ## Build 6 | * Clone the project and open a terminal/cmd on it. 7 | * Type `make release`. 8 | * The module will be created in `bin/release/`. 9 | 10 | ## Usage 11 | Create a folder in `/dev_hdd0/tmp/T5GSCLoader/mp` or `/dev_hdd0/tmp/T5GSCLoader/zm` and put your script in it. Beside your created folder create a .mod file that has the same name than your mod to enable it. You need to have a file in your mod folder named `main.gsc` with a `main()` function inside to get it load and started (see tmp directory for structure/examples). 12 | 13 | ## Development 14 | VS Code is used for this project, you can also use Visual Studio if you prefer but you'll need to created a new solution using the VSIX from the SDK and make sure to don't forget to include the required libraries `libc.a libfs_stub.a`. 15 | 16 | For a better experience with VS Code and use the tasks: 17 | 18 | * Install C/C++ Extension. 19 | * MSYS2 (unix tools). 20 | * Powershell 7.0 (support `&&` operator for tasks) or integrate msys2 shell in vscode. 21 | * Change the console IP in `.vscode/settings` by yours, and be sure you have a ftp server running on console (e.g webMAN). 22 | 23 | You can now open the folder in VS Code and press `Ctrl+Shift+B` to build or deploy the module to console, and deploy game scripts. 24 | 25 | Deployments tasks will just create or replace any existing file by FTP commands. 26 | 27 | The outputs made with a custom `printf` can be seen in Target Manager on "Console Output" menu. 28 | 29 | # Reversing 30 | Everything is identical on `XBOX` and `PC`, you can easily adapt for these platforms. 31 | 32 | ## Opcodes Checksum 33 | 34 | The game load and compile scripts each time that a game is started or joined (host or client). Each game scripts will be compiled and will become opcodes, Each opcodes will pass in order through few instructions that will compute a checksum: 35 | 36 | ``` 37 | 00596FC4 lwz r10, 8(r4) # Load the checksum from struct 38 | 00596FCC slwi r12, r10, 5 # Shift left by 5 bits (r10 << 5) 39 | 00596FD0 subf r6, r10, r12 # Substract checksum from the shifted checksum 40 | 00596FD4 add r5, r6, r21 # Add opcode value to checksum 41 | 00596FD8 stw r5, 8(r4) # Write checksum to struct 42 | ``` 43 | 44 | The lastest game script loaded is always the map one, right away this one loaded the checksum get copied from the function `Scr_GetChecksum`. On host side the checksum buffer is writed in the GameState that will be sent to clients. On client side, the checksum is read from GameState and compared with the client local value. This is the reason that if you change only a simple character in a GSC you'll not able to join/host with people. 45 | 46 | The function signature of `Scr_GetChecksum` on PC is `8B C1 69 C9 38 10 02 00`. 47 | 48 | **Note:** The checksum on client scripts (CSC) is computed but never checked on clients. 49 | 50 | ## Patching Checksum 51 | 52 | There is 3 values required for the client check: `checksum`, `programLen`, `substract`. 53 | 54 | A struct pointer is passed in `Scr_GetChecksum` and the three values will be copied into it. 55 | I thought about two ways to patch the checksum: 56 | 57 | * The first one is to dump the right checksum and just hook `Scr_GetCheckum` to pass the right one after you edited a script, simple and works well. But you'll need to dump each checksum for each map and gamemodes, since the checksum is based on opcodes, a gamemode or a map will also load his own script, so **you'll have to dump ~900 different value** to be able to play on each map/modes in multiplayer. 58 | 59 | * The second way is to load custom game scripts, **after the load of the main map script** which is the latest before the copy of the checksum, by this way we can **save the valid checksum** just-in time and replace it in `Scr_GetChecksum_Hook` when our custom game scripts have been loaded. 60 | 61 | I used the second one to don't need to worry about dumping checksums per map/gamemodes, **we can auto patch it** like this. Of course this way require additionnal hooks and code. 62 | 63 | ## Calling C functions 64 | 65 | I made a quick exemple to show how to call C functions from game script. 66 | You just have to make your function, and return his opd pointer from `Scr_GetFunction`, like i did for `setMemory` with hex strings. If you want to make your own function working with an entity like `self` ahead the function you need to hook `Scr_GetMethod` and use the second argument of this function (type) to return the according opd. 67 | 68 | ## Why I made this now? 69 | 70 | I made it for some people few months ago, to help and having some memories times by doing C and PPC. Then someone release his own with closed source when he heard that i made it. 71 | 72 | Recently, by cleaning my desktop i came across that folder, I said to myself it would be a cool to share this, maybe some people are curious about how game scripts check is made. 73 | 74 | Maybe this code will be useful to someone, who knows? 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/cshook.c: -------------------------------------------------------------------------------- 1 | #include "cshook.h" 2 | #include "utils.h" 3 | #include "inttypes.h" 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define CS_HOOK_CREATE_ERROR(num, err) (0xC5000000 | (num << 16) | (err & 0x0000FFFF)); 10 | 11 | uint32_t hooksInfoCount = 0; 12 | cs_hook_info hooksInfo[MAX_HOOKS_FUNCTIONS]; 13 | __attribute__((section(".text#"))) cs_hook_prehook prehooks[MAX_HOOKS_FUNCTIONS]; 14 | __attribute__((section(".text#"))) cs_hook_trampoline trampolines[MAX_HOOKS_FUNCTIONS]; 15 | 16 | int cs_hook_process_write(uint64_t address, const void *data, size_t size) 17 | { 18 | int res = sys_dbg_process_write(address, data, size); 19 | 20 | if (res == ENOSYS || res != CELL_OK) 21 | { 22 | /* 23 | // Put here a different methods to write to process in case of CEX console. 24 | // memcpy is not the best way since the function will crash if your firmware hasn't page restrictions patched (some cobra fw should support it). 25 | // I keep commented this since i have a dex console, free to you to make your own. 26 | 27 | memcpy((void*)(uintptr_t)address, data, size); 28 | */ 29 | return -1; 30 | } 31 | return CELL_OK; 32 | } 33 | 34 | int cs_hook_create_prehook(cs_hook_info *hookInfo) 35 | { 36 | if (!hookInfo) 37 | return -1; 38 | if (hookInfo->type != CS_HOOK_TYPE_CTR) 39 | return -2; 40 | 41 | int op[PREHOOK_INSTRUCTIONS_COUNT]; 42 | op[0] = 0x7C0802A6; 43 | op[1] = 0xF8010020; 44 | op[2] = 0xF8410028; 45 | op[3] = 0x3D800000 + (((int)hookInfo->detour >> 16) & 0xFFFF); 46 | op[4] = 0x618C0000 + ((int)hookInfo->detour & 0xFFFF); 47 | op[5] = 0x804C0004; 48 | op[6] = 0x818C0000; 49 | op[7] = 0x7D8903A6; 50 | op[8] = 0x4E800421; 51 | op[9] = 0xE8410028; 52 | op[10] = 0xE8010020; 53 | op[11] = 0x7C0803A6; 54 | op[12] = 0x4E800020; 55 | 56 | if (cs_hook_process_write((uintptr_t)&prehooks[hookInfo->index], &op, PREHOOK_INSTRUCTIONS_COUNT * 4) != CELL_OK) 57 | return -3; 58 | 59 | return 0; 60 | } 61 | 62 | int cs_hook_create_trampoline(cs_hook_info *hookInfo) 63 | { 64 | if (!hookInfo) 65 | return -1; 66 | if (hookInfo->type != CS_HOOK_TYPE_CTR) 67 | return -2; 68 | 69 | int op[TRAMPOLINE_INSTRUCTIONS_COUNT]; 70 | op[0] = 0x39800000; 71 | op[1] = 0x658C0000 + ((((int)hookInfo->source->function + 16) >> 16) & 0xFFFF); 72 | op[2] = 0x618C0000 + (((int)hookInfo->source->function + 16) & 0xFFFF); 73 | op[3] = 0x7D8903A6; 74 | op[4] = *(int*)(((int)hookInfo->source->function) + 0); 75 | op[5] = *(int*)(((int)hookInfo->source->function) + 4); 76 | op[6] = *(int*)(((int)hookInfo->source->function) + 8); 77 | op[7] = *(int*)(((int)hookInfo->source->function) + 12); 78 | op[8] = 0x4E800420; 79 | 80 | hookInfo->trampoline_opd.function = (void*)&trampolines[hookInfo->index]; 81 | hookInfo->trampoline_opd.toc = hookInfo->source->toc; 82 | *hookInfo->tramp = &hookInfo->trampoline_opd; 83 | 84 | if (cs_hook_process_write((uintptr_t)hookInfo->trampoline_opd.function, &op, TRAMPOLINE_INSTRUCTIONS_COUNT * 4) != CELL_OK) 85 | return -3; 86 | 87 | return 0; 88 | } 89 | 90 | int cs_hook_resolve_import_opd(cs_hook_info *hookInfo) 91 | { 92 | if (!hookInfo) 93 | return -1; 94 | if (hookInfo->type != CS_HOOK_TYPE_IMPORT) 95 | return -2; 96 | 97 | hookInfo->opd_import[0] = *(int*)(((int)hookInfo->source->function) + 4); 98 | hookInfo->opd_import[1] = *(int*)(((int)hookInfo->source->function) + 8); 99 | 100 | opd32 *import_opd = *(opd32**)(hookInfo->opd_import[0] << 16 | hookInfo->opd_import[1] & 0xFFFF); 101 | hookInfo->trampoline_opd.function = import_opd->function; 102 | hookInfo->trampoline_opd.toc = import_opd->toc; 103 | 104 | *hookInfo->tramp = &hookInfo->trampoline_opd; 105 | return 0; 106 | } 107 | 108 | cs_hook_info *cs_hook_get_info_from_source(popd32 source) 109 | { 110 | for (int i = 0; i < MAX_HOOKS_FUNCTIONS; i++) 111 | if (hooksInfo[i].source == source) 112 | return &hooksInfo[i]; 113 | 114 | return 0; 115 | } 116 | 117 | int cs_hook_enable(popd32 source) 118 | { 119 | cs_hook_info *hookInfo = cs_hook_get_info_from_source(source); 120 | if (hookInfo == 0) 121 | return -1; 122 | 123 | if (hookInfo->type == CS_HOOK_TYPE_CTR) 124 | { 125 | int op[4]; 126 | op[0] = 0x3D800000 + (((int)&prehooks[hookInfo->index] >> 16) & 0xFFFF); 127 | op[1] = 0x618C0000 + ((int)&prehooks[hookInfo->index] & 0xFFFF); 128 | op[2] = 0x7D8903A6; 129 | op[3] = 0x4E800420; 130 | if (cs_hook_process_write((uintptr_t)hookInfo->source->function, &op, 4 * 4) != CELL_OK) 131 | return -2; 132 | } 133 | else if (hookInfo->type == CS_HOOK_TYPE_IMPORT) 134 | { 135 | int op[2]; 136 | op[0] = 0x658C0000 + (((int)&hookInfo->detour >> 16) & 0xFFFF); 137 | op[1] = 0x818C0000 + ((int)&hookInfo->detour & 0xFFFF); 138 | if (cs_hook_process_write((uintptr_t)hookInfo->source->function + 4, &op, 4 * 2) != CELL_OK) 139 | return -2; 140 | } 141 | return 0; 142 | } 143 | 144 | int cs_hook_disable(popd32 source) 145 | { 146 | cs_hook_info *hookInfo = cs_hook_get_info_from_source(source); 147 | if (hookInfo == 0) 148 | return -1; 149 | 150 | if (hookInfo->type == CS_HOOK_TYPE_CTR) 151 | { 152 | int op[4]; 153 | op[0] = *(int*)(&trampolines[hookInfo->index] + 4 * 4); 154 | op[1] = *(int*)(&trampolines[hookInfo->index] + 5 * 4); 155 | op[2] = *(int*)(&trampolines[hookInfo->index] + 6 * 4); 156 | op[3] = *(int*)(&trampolines[hookInfo->index] + 7 * 4); 157 | if (cs_hook_process_write((uintptr_t)hookInfo->source->function, &op, 4 * 4) != CELL_OK) 158 | return -2; 159 | } 160 | else if (hookInfo->type == CS_HOOK_TYPE_IMPORT) 161 | { 162 | if (cs_hook_process_write((uintptr_t)hookInfo->source->function + 4, &hookInfo->opd_import, 4 * 2) != CELL_OK) 163 | return -2; 164 | } 165 | return 0; 166 | } 167 | 168 | int cs_hook_create(popd32 source, popd32 detour, popd32 *trampoline, bool enable, cs_hook_type type) 169 | { 170 | int err = 0; 171 | if (cs_hook_get_info_from_source(source) != 0) 172 | { 173 | return 1; 174 | } 175 | 176 | cs_hook_info *hookInfo = &hooksInfo[hooksInfoCount]; 177 | 178 | hookInfo->source = source; 179 | hookInfo->detour = detour; 180 | hookInfo->tramp = trampoline; 181 | hookInfo->index = hooksInfoCount; 182 | hookInfo->type = type; 183 | hooksInfoCount++; 184 | 185 | if (type == CS_HOOK_TYPE_CTR) 186 | { 187 | if ((err = cs_hook_create_prehook(hookInfo) < 0)) 188 | { 189 | return CS_HOOK_CREATE_ERROR(1, err); 190 | } 191 | if ((err = cs_hook_create_trampoline(hookInfo) < 0)) 192 | { 193 | return CS_HOOK_CREATE_ERROR(2, err); 194 | } 195 | } 196 | else if (type == CS_HOOK_TYPE_IMPORT) 197 | { 198 | if ((err = cs_hook_resolve_import_opd(hookInfo) < 0)) 199 | { 200 | return CS_HOOK_CREATE_ERROR(3, err); 201 | } 202 | } 203 | 204 | if (enable) 205 | { 206 | if (cs_hook_enable(hookInfo->source) < 0) 207 | { 208 | return CS_HOOK_CREATE_ERROR(4, err); 209 | } 210 | } 211 | return 0; 212 | } -------------------------------------------------------------------------------- /src/cshook.h: -------------------------------------------------------------------------------- 1 | #ifndef CSHOOK_H 2 | #define CSHOOK_H 3 | 4 | /* 5 | iMCSx's Simple Hook Library 6 | */ 7 | 8 | #include "defines.h" 9 | 10 | #define TRAMPOLINE_INSTRUCTIONS_COUNT 9 11 | #define PREHOOK_INSTRUCTIONS_COUNT 13 12 | #define MAX_HOOKS_FUNCTIONS 100 13 | 14 | #define cs_hook_install(name, type) cs_hook_create((popd32)name, (popd32)name##_Hook, (popd32*)&name##_Trampoline, true, type) 15 | 16 | typedef struct cs_hook_trampoline 17 | { 18 | char opcodes[TRAMPOLINE_INSTRUCTIONS_COUNT * 4]; 19 | } cs_hook_trampoline; 20 | 21 | typedef struct cs_hook_prehook 22 | { 23 | char opcodes[PREHOOK_INSTRUCTIONS_COUNT * 4]; 24 | } cs_hook_prehook; 25 | 26 | typedef enum cs_hook_type 27 | { 28 | CS_HOOK_TYPE_CTR, 29 | CS_HOOK_TYPE_IMPORT 30 | } cs_hook_type; 31 | 32 | typedef struct cs_hook_info 33 | { 34 | popd32 source; 35 | popd32 detour; 36 | popd32 *tramp; 37 | opd32 trampoline_opd; 38 | uint32_t index; 39 | cs_hook_type type; 40 | int opd_import[2]; 41 | } cs_hook_info; 42 | 43 | cs_hook_info *cs_hook_get_info_from_source(popd32 source); 44 | int cs_hook_create(popd32 source, popd32 detour, popd32 *trampoline, bool enable, cs_hook_type type); 45 | int cs_hook_enable(popd32 source); 46 | int cs_hook_disable(popd32 source); 47 | 48 | #endif -------------------------------------------------------------------------------- /src/defines.h: -------------------------------------------------------------------------------- 1 | #ifndef DEFINES_H 2 | #define DEFINES_H 3 | 4 | #include 5 | 6 | // Macros to automate naming/setting variables for natives/hooks 7 | #define t5nd(ret_type, name, args) \ 8 | typedef ret_type(*name##_t) args; \ 9 | uint64_t name##_opd; \ 10 | name##_t name; 11 | 12 | #define t5ni(name) \ 13 | name##_opd = isMultiplayer ? ((uint64_t)T5_##name << 32) | T5_TOC : ((uint64_t)T5_##name##_ZM << 32) | T5_TOC_ZM; \ 14 | name = (name##_t) & name##_opd; 15 | 16 | #define t5nhd(ret_type, name, args) \ 17 | typedef ret_type(*name##_t) args; \ 18 | uint64_t name##_opd; \ 19 | name##_t name; \ 20 | name##_t name##_Trampoline; \ 21 | ret_type name##_Hook args; 22 | 23 | #define t5nhi(name) \ 24 | name##_opd = isMultiplayer ? ((uint64_t)T5_##name << 32) | T5_TOC : ((uint64_t)T5_##name##_ZM << 32) | T5_TOC_ZM; \ 25 | name = (name##_t) & name##_opd; \ 26 | name##_Trampoline = 0; 27 | 28 | #define t5o(name) (isMultiplayer ? T5_##name : T5_##name##_ZM) 29 | 30 | #define MAX_GSC_COUNT 100 31 | 32 | #define T5INFO "[T5] Info: " 33 | #define T5WARNING "[T5] Warning: " 34 | #define T5ERROR "[T5] ERROR: " 35 | 36 | #define SCRIPTS_PATH "/dev_hdd0/tmp/T5GSCLoader" 37 | 38 | // Game structs 39 | typedef union DvarValue 40 | { 41 | bool enabled; 42 | int integer; 43 | uint32_t unsignedInt; 44 | int64_t integer64; 45 | uint64_t unsignedInt64; 46 | float value; 47 | float vector[4]; 48 | const char *string; 49 | char color[4]; 50 | } DvarValue; 51 | 52 | typedef enum scriptInstance_t 53 | { 54 | SCRIPTINSTANCE_SERVER = 0, 55 | SCRIPTINSTANCE_CLIENT = 1, 56 | SCRIPT_INSTANCE_MAX = 2 57 | } scriptInstance_t; 58 | 59 | typedef enum dvarType_t 60 | { 61 | DVAR_TYPE_BOOL = 0x0, 62 | DVAR_TYPE_FLOAT = 0x1, 63 | DVAR_TYPE_FLOAT_2 = 0x2, 64 | DVAR_TYPE_FLOAT_3 = 0x3, 65 | DVAR_TYPE_FLOAT_4 = 0x4, 66 | DVAR_TYPE_INT = 0x5, 67 | DVAR_TYPE_ENUM = 0x6, 68 | DVAR_TYPE_STRING = 0x7, 69 | DVAR_TYPE_COLOR = 0x8, 70 | DVAR_TYPE_INT64 = 0x9, 71 | DVAR_TYPE_LINEAR_COLOR_RGB = 0xA, 72 | DVAR_TYPE_COLOR_XYZ = 0xB, 73 | DVAR_TYPE_COUNT = 0xC 74 | } dvarType_t; 75 | 76 | typedef struct dvar_s 77 | { 78 | const char *name; 79 | const char *description; 80 | int hash; 81 | unsigned int flags; 82 | dvarType_t type; 83 | bool modified; 84 | bool loadedFromSaveGame; 85 | DvarValue current; 86 | DvarValue latched; 87 | DvarValue reset; 88 | DvarValue saved; 89 | char domain[10]; 90 | struct dvar_s *hashNext; 91 | } dvar_s; 92 | 93 | typedef struct scrVarPub_t 94 | { 95 | char _unsafe[0x38]; 96 | int checksum; 97 | int entId; 98 | int entFieldName; 99 | char *programHunkUser; 100 | char *programBuffer; 101 | char *endScriptBuffer; 102 | char _unsafe2[0x0C]; 103 | } scrVarPub_t; // 0x58 104 | 105 | typedef struct scrCompilePub_t 106 | { 107 | char _unsafe[0x20030]; 108 | int programLen; 109 | char _unsafe2[0x1004]; 110 | } scrCompilePub_t; // 0x21038 111 | 112 | typedef struct RawFile 113 | { 114 | char *name; 115 | int len; 116 | char *buffer; 117 | } RawFile; 118 | 119 | typedef enum XAssetType 120 | { 121 | ASSET_TYPE_RAWFILE = 0x26 122 | } XAssetType; 123 | 124 | typedef union XAssetHeader 125 | { 126 | struct RawFile *rawFile; 127 | void *data; 128 | } XAssetHeader; 129 | 130 | typedef struct XAsset 131 | { 132 | enum XAssetType type; 133 | union XAssetHeader header; 134 | } XAsset; 135 | 136 | typedef struct XAssetEntry 137 | { 138 | XAsset asset; 139 | char zoneIndex; 140 | bool inuse; 141 | uint16_t nextHash; 142 | uint16_t nextOverride; 143 | uint16_t usageFrame; 144 | char margin[0x10]; 145 | } XAssetEntry; 146 | 147 | typedef union XAssetEntryPoolEntry 148 | { 149 | struct XAssetEntry entry; 150 | union XAssetEntryPoolEntry *next; 151 | } XAssetEntryPoolEntry; 152 | 153 | // Customs 154 | typedef struct InflateData 155 | { 156 | char *deflatedBuffer; 157 | char *hunkMemoryBuffer; 158 | char _unsafe[0x18]; 159 | } InflateData; // 0x20? (unknown structure, ps3 only) 160 | 161 | typedef struct opd32 162 | { 163 | void *function; 164 | int toc; 165 | } opd32; 166 | typedef struct opd32 *popd32; 167 | 168 | typedef struct scrChecksum_t 169 | { 170 | int checksum; 171 | int programLen; 172 | int substract; 173 | } scrChecksum_t; // 0xC (unknown struct not in pdb) 174 | 175 | typedef struct RawFileData 176 | { 177 | char name[100]; 178 | int inflatedSize; 179 | int size; 180 | char buffer[0x20]; 181 | } RawFileData; 182 | 183 | typedef struct GSCLoaderRawfile 184 | { 185 | XAssetEntry entry; 186 | RawFile asset; 187 | RawFileData data; 188 | } GSCLoaderRawfile; 189 | 190 | typedef struct GSCLoader 191 | { 192 | char currentModName[256]; 193 | GSCLoaderRawfile rawFiles[MAX_GSC_COUNT]; 194 | } GSCLoader; 195 | 196 | #endif -------------------------------------------------------------------------------- /src/hooks.c: -------------------------------------------------------------------------------- 1 | #include "t5.h" 2 | 3 | #include "stdio.h" 4 | #include "string.h" 5 | #include "utils.h" 6 | #include "scrfct.h" 7 | 8 | #include 9 | #include 10 | 11 | int Scr_LoadScript_Hook(scriptInstance_t inst, const char *scriptName) 12 | { 13 | int res = Scr_LoadScript_Trampoline(inst, scriptName); 14 | 15 | char buffer[255]; 16 | dvar_s *mapname = Dvar_FindVar("mapname"); 17 | 18 | if (!mapname) 19 | { 20 | printf(T5ERROR "Dvar mapname not found."); 21 | return res; 22 | } 23 | 24 | // The game will load some script on boot (ZM), avoid it. 25 | if (strcmp(mapname->current.string, "frontend") == 0) 26 | { 27 | return res; 28 | } 29 | 30 | sprintf(buffer, isMultiplayer ? "maps/mp/%s" : "maps/%s", mapname->current.string); 31 | 32 | // Checking the current gsc loaded was the one for the current map. 33 | if (strcmp(scriptName, buffer) == 0) 34 | { 35 | 36 | // We can safely now save the checksum data, we will return this one later in a different hook once we loaded our scripts. 37 | checksums[inst].checksum = scrVarPub[inst].checksum; 38 | checksums[inst].programLen = scrCompilePub[inst].programLen; 39 | checksums[inst].substract = (int)scrVarPub[inst].endScriptBuffer - (int)scrVarPub[inst].programBuffer; 40 | 41 | // Clean all previous values. 42 | memset(loader.rawFiles, 0, sizeof(loader.rawFiles)); 43 | 44 | // Get active mod. 45 | char modPath[CELL_FS_MAX_FS_PATH_LENGTH]; 46 | get_or_create_mod_path(modPath); 47 | 48 | if (*modPath) 49 | { 50 | // Create asset entry for each file in our mod directory. 51 | if (create_assets_from_scripts(modPath)) 52 | { 53 | // Load the main file, any gsc used as include inside will be loaded too. 54 | char *mainMod = isMultiplayer ? "maps/mp/mod/main" : "maps/zm/mod/main"; 55 | Scr_LoadScript_Trampoline(inst, mainMod); 56 | 57 | // Get our main function handle to start it later in another hook. 58 | modHandle = Scr_GetFunctionHandle(0, mainMod, "main"); 59 | 60 | printf(T5INFO "Main function handle of '%s' is 0x%08X.", mainMod, modHandle); 61 | } 62 | else 63 | printf(T5ERROR "Couldn't load mod files from '%s'.", modPath); 64 | } 65 | else 66 | printf(T5WARNING "Nothing to load."); 67 | } 68 | 69 | return res; 70 | } 71 | 72 | int cellSpursLFQueuePushBody_Hook(CellSpursLFQueue *lfqueue, const void *buffer, unsigned int isBlocking) 73 | { 74 | // Hooked by replacing popd import because using CTR an instruction of the source function will overwrite toc in stack and cause crashs. 75 | 76 | InflateData *data = (InflateData*)(buffer); 77 | int ret = cellSpursLFQueuePushBody_Trampoline(lfqueue, buffer, isBlocking); 78 | GSCLoaderRawfile *lrf = get_loader_rawfile_from_deflated_buffer(data->deflatedBuffer); 79 | 80 | if (lrf) 81 | { 82 | char modPath[CELL_FS_MAX_FS_PATH_LENGTH]; 83 | get_or_create_mod_path(modPath); 84 | 85 | printf(T5INFO "Injecting '%s' data.", lrf->data.name); 86 | char *name = strrchr(lrf->data.name, '/'); 87 | 88 | if (*name++ && *modPath) 89 | { 90 | char filePath[CELL_FS_MAX_FS_PATH_LENGTH]; 91 | sprintf(filePath, "%s/%s", modPath, name); 92 | 93 | int fileSize = get_file_size(filePath); 94 | 95 | if (fileSize <= 0) 96 | { 97 | printf(T5ERROR "Cannot stat '%s' file", lrf->data.name); 98 | return ret; 99 | } 100 | 101 | int fd; 102 | CellFsErrno err = cellFsOpen(filePath, CELL_FS_O_RDONLY, &fd, NULL, 0); 103 | if (err == CELL_FS_SUCCEEDED) 104 | { 105 | uint64_t read; 106 | err = cellFsRead(fd, data->hunkMemoryBuffer, fileSize, &read); 107 | 108 | if (err == CELL_FS_SUCCEEDED && read == fileSize) 109 | data->hunkMemoryBuffer[fileSize] = 0; // GSC data should be null-terminated. 110 | else 111 | printf(T5ERROR "Failed to read '%s' file.", filePath); 112 | 113 | cellFsClose(fd); 114 | } 115 | else 116 | printf(T5ERROR "Cannot open '%s' file.", filePath); 117 | } 118 | else 119 | printf(T5ERROR "Cannot get the current mod path or asset name is wrong."); 120 | } 121 | 122 | return ret; 123 | } 124 | 125 | void Scr_GetChecksum_Hook(scrChecksum_t *checksum, scriptInstance_t inst) 126 | { 127 | Scr_GetChecksum_Trampoline(checksum, inst); 128 | 129 | if (checksums[inst].checksum != 0) 130 | { 131 | *checksum = checksums[inst]; 132 | printf(T5INFO "Checksum patched (0x%08X)", checksums[inst].checksum); 133 | memset(&checksums[inst], 0, sizeof(scrChecksum_t)); 134 | } 135 | } 136 | 137 | void Scr_LoadGameType_Hook(void) 138 | { 139 | // Start the gametype entry function by calling the default function. 140 | Scr_LoadGameType_Trampoline(); 141 | 142 | // Start our main function from our handle 143 | if (modHandle > 0) 144 | { 145 | unsigned short handle = Scr_ExecThread(0, modHandle, 0); 146 | Scr_FreeThread(handle, 0); 147 | printf(T5INFO "Mod '%s' started.", loader.currentModName); 148 | } 149 | } 150 | 151 | popd32 Scr_GetFunction_Hook(const char **pName, int *type) 152 | { 153 | popd32 opd = Scr_GetFunction_Trampoline(pName, type); 154 | if (opd == 0) 155 | { 156 | // Function name are always in lowercase, we should return an opd pointer. 157 | if (strcmp(*pName, "setmemory") == 0) 158 | { 159 | printf(T5INFO "Function 'setmemory' found."); 160 | return (popd32)&scrfct_setmemory; 161 | } 162 | return 0; 163 | } 164 | return opd; 165 | } -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "t5.h" 5 | #include "utils.h" 6 | #include 7 | #include 8 | 9 | SYS_MODULE_INFO(T5GSCLoader, 0, 1, 1); 10 | SYS_MODULE_START(start); 11 | 12 | void launcher() 13 | { 14 | sys_prx_get_module_list_t pInfo; 15 | pInfo.max = 25; 16 | sys_prx_id_t ids[pInfo.max]; 17 | pInfo.idlist = ids; 18 | pInfo.size = sizeof(pInfo); 19 | 20 | while (pInfo.count < 18) 21 | { 22 | sys_prx_get_module_list(0, &pInfo); 23 | sys_timer_sleep(1); 24 | } 25 | 26 | if (init_game() == 0) 27 | { 28 | printf(T5INFO "GSC Loader ready."); 29 | } 30 | 31 | sys_ppu_thread_exit(0); 32 | } 33 | 34 | int start(void) 35 | { 36 | printf("\n********************************************"); 37 | printf(" T5 GSC Loader by iMCSx "); 38 | printf("********************************************\n"); 39 | printf(T5INFO "Waiting modules..."); 40 | 41 | // Create a thread that wait eboot's modules are getting loaded to be sure that imports opd are resolved. 42 | sys_ppu_thread_t id; 43 | sys_ppu_thread_create(&id, launcher, 0, 1000, 0x8000, 0, "[GSC Loader] launcher"); 44 | return SYS_PRX_RESIDENT; 45 | } 46 | -------------------------------------------------------------------------------- /src/offsets.h: -------------------------------------------------------------------------------- 1 | #ifndef OFFSETS_H 2 | #define OFFSETS_H 3 | 4 | // Natives functions 5 | #define T5_cellSpursLFQueuePushBody 0x8C7080 6 | #define T5_cellSpursLFQueuePushBody_ZM 0x7DBFC0 7 | #define T5_DB_FindXAssetHeader 0x2601A0 8 | #define T5_DB_FindXAssetHeader_ZM 0x1B1E20 9 | #define T5_DB_LinkXAssetEntry 0x25EE38 10 | #define T5_DB_LinkXAssetEntry_ZM 0x1B0AC0 11 | #define T5_Dvar_FindVar 0x4C55A0 12 | #define T5_Dvar_FindVar_ZM 0x452AA0 13 | #define T5_Scr_ExecThread 0x5DD580 14 | #define T5_Scr_ExecThread_ZM 0x05C7670 15 | #define T5_Scr_FreeThread 0x5CB2C0 16 | #define T5_Scr_FreeThread_ZM 0x5B5138 17 | #define T5_Scr_GetChecksum 0x5BA818 18 | #define T5_Scr_GetChecksum_ZM 0x5A4248 19 | #define T5_Scr_GetFunction 0x310FD8 20 | #define T5_Scr_GetFunction_ZM 0x2BC3E0 21 | #define T5_Scr_GetMethod 0x315BA8 22 | #define T5_Scr_GetMethod_ZM 0x2B3F88 23 | #define T5_Scr_GetFunctionHandle 0x5A92A0 24 | #define T5_Scr_GetFunctionHandle_ZM 0x592E38 25 | #define T5_Scr_GetNumParam 0x5CB210 26 | #define T5_Scr_GetNumParam_ZM 0x5B5088 27 | #define T5_Scr_GetString 0x5D0D68 28 | #define T5_Scr_GetString_ZM 0x5BBCC0 29 | #define T5_Scr_LoadGameType 0x315728 30 | #define T5_Scr_LoadGameType_ZM 0x2FA0C0 31 | #define T5_Scr_LoadScript 0x5A9068 32 | #define T5_Scr_LoadScript_ZM 0x592C00 33 | #define T5_Scr_LoadScriptInternal 0x5A8D38 34 | #define T5_Scr_LoadScriptInternal_ZM 0x5928D0 35 | 36 | // Structs / Pointers 37 | #define T5_ScrCompilePub 0x1BE8870 38 | #define T5_ScrCompilePub_ZM 0x1846C88 39 | #define T5_ScrVarPub 0x1E8B200 40 | #define T5_ScrVarPub_ZM 0x1AE9600 41 | #define T5_TOC 0x00B576E8 42 | #define T5_TOC_ZM 0x00A544B0 43 | 44 | #endif -------------------------------------------------------------------------------- /src/printf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1995 Patrick Powell. 3 | * 4 | * This code is based on code written by Patrick Powell . 5 | * It may be used for any purpose as long as this notice remains intact on all 6 | * source code distributions. 7 | */ 8 | 9 | /* 10 | * Copyright (c) 2008 Holger Weiss. 11 | * 12 | * This version of the code is maintained by Holger Weiss . 13 | * My changes to the code may freely be used, modified and/or redistributed for 14 | * any purpose. It would be nice if additions and fixes to this file (including 15 | * trivial code cleanups) would be sent back in order to let me include them in 16 | * the version available at . 17 | * However, this is not a requirement for using or redistributing (possibly 18 | * modified) versions of this file, nor is leaving this notice intact mandatory. 19 | */ 20 | 21 | /* 22 | * History 23 | * 24 | * 2009-03-05 Hector Martin 25 | * 26 | * Hacked up and removed a lot of stuff including floating-point support, 27 | * a bunch of ifs and defines, locales, and tests 28 | * 29 | * 2008-01-20 Holger Weiss for C99-snprintf 1.1: 30 | * 31 | * Fixed the detection of infinite floating point values on IRIX (and 32 | * possibly other systems) and applied another few minor cleanups. 33 | * 34 | * 2008-01-06 Holger Weiss for C99-snprintf 1.0: 35 | * 36 | * Added a lot of new features, fixed many bugs, and incorporated various 37 | * improvements done by Andrew Tridgell , Russ Allbery 38 | * , Hrvoje Niksic , Damien Miller 39 | * , and others for the Samba, INN, Wget, and OpenSSH 40 | * projects. The additions include: support the "e", "E", "g", "G", and 41 | * "F" conversion specifiers (and use conversion style "f" or "F" for the 42 | * still unsupported "a" and "A" specifiers); support the "hh", "ll", "j", 43 | * "t", and "z" length modifiers; support the "#" flag and the (non-C99) 44 | * "'" flag; use localeconv(3) (if available) to get both the current 45 | * locale's decimal point character and the separator between groups of 46 | * digits; fix the handling of various corner cases of field width and 47 | * precision specifications; fix various floating point conversion bugs; 48 | * handle infinite and NaN floating point values; don't attempt to write to 49 | * the output buffer (which may be NULL) if a size of zero was specified; 50 | * check for integer overflow of the field width, precision, and return 51 | * values and during the floating point conversion; use the OUTCHAR() macro 52 | * instead of a function for better performance; provide asprintf(3) and 53 | * vasprintf(3) functions; add new test cases. The replacement functions 54 | * have been renamed to use an "rpl_" prefix, the function calls in the 55 | * main project (and in this file) must be redefined accordingly for each 56 | * replacement function which is needed (by using Autoconf or other means). 57 | * Various other minor improvements have been applied and the coding style 58 | * was cleaned up for consistency. 59 | * 60 | * 2007-07-23 Holger Weiss for Mutt 1.5.13: 61 | * 62 | * C99 compliant snprintf(3) and vsnprintf(3) functions return the number 63 | * of characters that would have been written to a sufficiently sized 64 | * buffer (excluding the '\0'). The original code simply returned the 65 | * length of the resulting output string, so that's been fixed. 66 | * 67 | * 1998-03-05 Michael Elkins for Mutt 0.90.8: 68 | * 69 | * The original code assumed that both snprintf(3) and vsnprintf(3) were 70 | * missing. Some systems only have snprintf(3) but not vsnprintf(3), so 71 | * the code is now broken down under HAVE_SNPRINTF and HAVE_VSNPRINTF. 72 | * 73 | * 1998-01-27 Thomas Roessler for Mutt 0.89i: 74 | * 75 | * The PGP code was using unsigned hexadecimal formats. Unfortunately, 76 | * unsigned formats simply didn't work. 77 | * 78 | * 1997-10-22 Brandon Long for Mutt 0.87.1: 79 | * 80 | * Ok, added some minimal floating point support, which means this probably 81 | * requires libm on most operating systems. Don't yet support the exponent 82 | * (e,E) and sigfig (g,G). Also, fmtint() was pretty badly broken, it just 83 | * wasn't being exercised in ways which showed it, so that's been fixed. 84 | * Also, formatted the code to Mutt conventions, and removed dead code left 85 | * over from the original. Also, there is now a builtin-test, run with: 86 | * gcc -DTEST_SNPRINTF -o snprintf snprintf.c -lm && ./snprintf 87 | * 88 | * 2996-09-15 Brandon Long for Mutt 0.43: 89 | * 90 | * This was ugly. It is still ugly. I opted out of floating point 91 | * numbers, but the formatter understands just about everything from the 92 | * normal C string format, at least as far as I can tell from the Solaris 93 | * 2.5 printf(3S) man page. 94 | */ 95 | 96 | #include 97 | 98 | #include 99 | #include 100 | #include 101 | #include 102 | 103 | #define MAX_PRINTF_BUFFER 256 104 | #define HAVE_UNSIGNED_LONG_LONG_INT 105 | #define HAVE_STDINT_H 106 | #define HAVE_INTTYPES_H 107 | #define HAVE_STDDEF_H 108 | #define HAVE_LONG_LONG_INT 109 | 110 | #define VA_START(ap, last) va_start(ap, last) 111 | #define VA_SHIFT(ap, value, type) /* No-op for ANSI C. */ 112 | 113 | #ifdef HAVE_INTTYPES_H 114 | #include /* For intmax_t (if not defined in ). */ 115 | #endif /* HAVE_INTTYPES_H */ 116 | 117 | #ifdef HAVE_STDDEF_H 118 | #include /* For ptrdiff_t. */ 119 | #endif /* HAVE_STDDEF_H */ 120 | 121 | #ifdef HAVE_STDINT_H 122 | #include /* For intmax_t. */ 123 | #endif /* HAVE_STDINT_H */ 124 | 125 | /* Support for unsigned long long int. We may also need ULLONG_MAX. */ 126 | #ifndef ULONG_MAX /* We may need ULONG_MAX as a fallback. */ 127 | #ifdef UINT_MAX 128 | #define ULONG_MAX UINT_MAX 129 | #else 130 | #define ULONG_MAX INT_MAX 131 | #endif /* defined(UINT_MAX) */ 132 | #endif /* !defined(ULONG_MAX) */ 133 | #ifdef ULLONG 134 | #undef ULLONG 135 | #endif /* defined(ULLONG) */ 136 | #ifdef HAVE_UNSIGNED_LONG_LONG_INT 137 | #define ULLONG unsigned long long int 138 | #ifndef ULLONG_MAX 139 | #define ULLONG_MAX ULONG_MAX 140 | #endif /* !defined(ULLONG_MAX) */ 141 | #else 142 | #define ULLONG unsigned long int 143 | #ifdef ULLONG_MAX 144 | #undef ULLONG_MAX 145 | #endif /* defined(ULLONG_MAX) */ 146 | #define ULLONG_MAX ULONG_MAX 147 | #endif /* HAVE_LONG_LONG_INT */ 148 | 149 | /* Support for uintmax_t. We also need UINTMAX_MAX. */ 150 | #ifdef UINTMAX_T 151 | #undef UINTMAX_T 152 | #endif /* defined(UINTMAX_T) */ 153 | #if HAVE_UINTMAX_T || defined(uintmax_t) 154 | #define UINTMAX_T uintmax_t 155 | #ifndef UINTMAX_MAX 156 | #define UINTMAX_MAX ULLONG_MAX 157 | #endif /* !defined(UINTMAX_MAX) */ 158 | #else 159 | #define UINTMAX_T ULLONG 160 | #ifdef UINTMAX_MAX 161 | #undef UINTMAX_MAX 162 | #endif /* defined(UINTMAX_MAX) */ 163 | #define UINTMAX_MAX ULLONG_MAX 164 | #endif /* HAVE_UINTMAX_T || defined(uintmax_t) */ 165 | 166 | /* Support for long long int. */ 167 | #ifndef LLONG 168 | #ifdef HAVE_LONG_LONG_INT 169 | #define LLONG long long int 170 | #else 171 | #define LLONG long int 172 | #endif /* HAVE_LONG_LONG_INT */ 173 | #endif /* !defined(LLONG) */ 174 | 175 | /* Support for intmax_t. */ 176 | #ifndef INTMAX_T 177 | #if HAVE_INTMAX_T || defined(intmax_t) 178 | #define INTMAX_T intmax_t 179 | #else 180 | #define INTMAX_T LLONG 181 | #endif /* HAVE_INTMAX_T || defined(intmax_t) */ 182 | #endif /* !defined(INTMAX_T) */ 183 | 184 | /* Support for uintptr_t. */ 185 | #ifndef UINTPTR_T 186 | #if HAVE_UINTPTR_T || defined(uintptr_t) 187 | #define UINTPTR_T uintptr_t 188 | #else 189 | #define UINTPTR_T unsigned long int 190 | #endif /* HAVE_UINTPTR_T || defined(uintptr_t) */ 191 | #endif /* !defined(UINTPTR_T) */ 192 | 193 | /* Support for ptrdiff_t. */ 194 | #ifndef PTRDIFF_T 195 | #if HAVE_PTRDIFF_T || defined(ptrdiff_t) 196 | #define PTRDIFF_T ptrdiff_t 197 | #else 198 | #define PTRDIFF_T long int 199 | #endif /* HAVE_PTRDIFF_T || defined(ptrdiff_t) */ 200 | #endif /* !defined(PTRDIFF_T) */ 201 | 202 | /* 203 | * We need an unsigned integer type corresponding to ptrdiff_t (cf. C99: 204 | * 7.19.6.1, 7). However, we'll simply use PTRDIFF_T and convert it to an 205 | * unsigned type if necessary. This should work just fine in practice. 206 | */ 207 | #ifndef UPTRDIFF_T 208 | #define UPTRDIFF_T PTRDIFF_T 209 | #endif /* !defined(UPTRDIFF_T) */ 210 | 211 | /* 212 | * We need a signed integer type corresponding to size_t (cf. C99: 7.19.6.1, 7). 213 | * However, we'll simply use size_t and convert it to a signed type if 214 | * necessary. This should work just fine in practice. 215 | */ 216 | #ifndef SSIZE_T 217 | #define SSIZE_T size_t 218 | #endif /* !defined(SSIZE_T) */ 219 | 220 | /* 221 | * Buffer size to hold the octal string representation of UINT128_MAX without 222 | * nul-termination ("3777777777777777777777777777777777777777777"). 223 | */ 224 | #ifdef MAX_CONVERT_LENGTH 225 | #undef MAX_CONVERT_LENGTH 226 | #endif /* defined(MAX_CONVERT_LENGTH) */ 227 | #define MAX_CONVERT_LENGTH 43 228 | 229 | /* Format read states. */ 230 | #define PRINT_S_DEFAULT 0 231 | #define PRINT_S_FLAGS 1 232 | #define PRINT_S_WIDTH 2 233 | #define PRINT_S_DOT 3 234 | #define PRINT_S_PRECISION 4 235 | #define PRINT_S_MOD 5 236 | #define PRINT_S_CONV 6 237 | 238 | /* Format flags. */ 239 | #define PRINT_F_MINUS (1 << 0) 240 | #define PRINT_F_PLUS (1 << 1) 241 | #define PRINT_F_SPACE (1 << 2) 242 | #define PRINT_F_NUM (1 << 3) 243 | #define PRINT_F_ZERO (1 << 4) 244 | #define PRINT_F_QUOTE (1 << 5) 245 | #define PRINT_F_UP (1 << 6) 246 | #define PRINT_F_UNSIGNED (1 << 7) 247 | #define PRINT_F_TYPE_G (1 << 8) 248 | #define PRINT_F_TYPE_E (1 << 9) 249 | 250 | /* Conversion flags. */ 251 | #define PRINT_C_CHAR 1 252 | #define PRINT_C_SHORT 2 253 | #define PRINT_C_LONG 3 254 | #define PRINT_C_LLONG 4 255 | //#define PRINT_C_LDOUBLE 5 256 | #define PRINT_C_SIZE 6 257 | #define PRINT_C_PTRDIFF 7 258 | #define PRINT_C_INTMAX 8 259 | 260 | #ifndef MAX 261 | #define MAX(x, y) ((x >= y) ? x : y) 262 | #endif /* !defined(MAX) */ 263 | #ifndef CHARTOINT 264 | #define CHARTOINT(ch) (ch - '0') 265 | #endif /* !defined(CHARTOINT) */ 266 | #ifndef ISDIGIT 267 | #define ISDIGIT(ch) ('0' <= (unsigned char)ch && (unsigned char)ch <= '9') 268 | #endif /* !defined(ISDIGIT) */ 269 | 270 | #define OUTCHAR(str, len, size, ch) \ 271 | do \ 272 | { \ 273 | if (len + 1 < size) \ 274 | str[len] = ch; \ 275 | (len)++; \ 276 | } while (/* CONSTCOND */ 0) 277 | 278 | static void fmtstr(char *, size_t *, size_t, const char *, int, int, int); 279 | static void fmtint(char *, size_t *, size_t, INTMAX_T, int, int, int, int); 280 | static void printsep(char *, size_t *, size_t); 281 | static int getnumsep(int); 282 | static int convert(UINTMAX_T, char *, size_t, int, int); 283 | 284 | int ttyWrite(int channel, const char *message, int length, int *written); 285 | 286 | int vsnprintf(char *str, size_t size, const char *format, va_list args) 287 | { 288 | if (!format) 289 | return 0; 290 | 291 | INTMAX_T value; 292 | unsigned char cvalue; 293 | const char *strvalue; 294 | INTMAX_T *intmaxptr; 295 | PTRDIFF_T *ptrdiffptr; 296 | SSIZE_T *sizeptr; 297 | LLONG *llongptr; 298 | long int *longptr; 299 | int *intptr; 300 | short int *shortptr; 301 | signed char *charptr; 302 | size_t len = 0; 303 | int overflow = 0; 304 | int base = 0; 305 | int cflags = 0; 306 | int flags = 0; 307 | int width = 0; 308 | int precision = -1; 309 | int state = PRINT_S_DEFAULT; 310 | char ch = *format++; 311 | 312 | /* 313 | * C99 says: "If `n' is zero, nothing is written, and `s' may be a null 314 | * pointer." (7.19.6.5, 2) We're forgiving and allow a NULL pointer 315 | * even if a size larger than zero was specified. At least NetBSD's 316 | * snprintf(3) does the same, as well as other versions of this file. 317 | * (Though some of these versions will write to a non-NULL buffer even 318 | * if a size of zero was specified, which violates the standard.) 319 | */ 320 | if (str == NULL && size != 0) 321 | size = 0; 322 | 323 | while (ch != '\0') 324 | switch (state) 325 | { 326 | case PRINT_S_DEFAULT: 327 | if (ch == '%') 328 | state = PRINT_S_FLAGS; 329 | else 330 | OUTCHAR(str, len, size, ch); 331 | ch = *format++; 332 | break; 333 | case PRINT_S_FLAGS: 334 | switch (ch) 335 | { 336 | case '-': 337 | flags |= PRINT_F_MINUS; 338 | ch = *format++; 339 | break; 340 | case '+': 341 | flags |= PRINT_F_PLUS; 342 | ch = *format++; 343 | break; 344 | case ' ': 345 | flags |= PRINT_F_SPACE; 346 | ch = *format++; 347 | break; 348 | case '#': 349 | flags |= PRINT_F_NUM; 350 | ch = *format++; 351 | break; 352 | case '0': 353 | flags |= PRINT_F_ZERO; 354 | ch = *format++; 355 | break; 356 | case '\'': /* SUSv2 flag (not in C99). */ 357 | flags |= PRINT_F_QUOTE; 358 | ch = *format++; 359 | break; 360 | default: 361 | state = PRINT_S_WIDTH; 362 | break; 363 | } 364 | break; 365 | case PRINT_S_WIDTH: 366 | if (ISDIGIT(ch)) 367 | { 368 | ch = CHARTOINT(ch); 369 | if (width > (INT_MAX - ch) / 10) 370 | { 371 | overflow = 1; 372 | goto out; 373 | } 374 | width = 10 * width + ch; 375 | ch = *format++; 376 | } 377 | else if (ch == '*') 378 | { 379 | /* 380 | * C99 says: "A negative field width argument is 381 | * taken as a `-' flag followed by a positive 382 | * field width." (7.19.6.1, 5) 383 | */ 384 | if ((width = va_arg(args, int)) < 0) 385 | { 386 | flags |= PRINT_F_MINUS; 387 | width = -width; 388 | } 389 | ch = *format++; 390 | state = PRINT_S_DOT; 391 | } 392 | else 393 | state = PRINT_S_DOT; 394 | break; 395 | case PRINT_S_DOT: 396 | if (ch == '.') 397 | { 398 | state = PRINT_S_PRECISION; 399 | ch = *format++; 400 | } 401 | else 402 | state = PRINT_S_MOD; 403 | break; 404 | case PRINT_S_PRECISION: 405 | if (precision == -1) 406 | precision = 0; 407 | if (ISDIGIT(ch)) 408 | { 409 | ch = CHARTOINT(ch); 410 | if (precision > (INT_MAX - ch) / 10) 411 | { 412 | overflow = 1; 413 | goto out; 414 | } 415 | precision = 10 * precision + ch; 416 | ch = *format++; 417 | } 418 | else if (ch == '*') 419 | { 420 | /* 421 | * C99 says: "A negative precision argument is 422 | * taken as if the precision were omitted." 423 | * (7.19.6.1, 5) 424 | */ 425 | if ((precision = va_arg(args, int)) < 0) 426 | precision = -1; 427 | ch = *format++; 428 | state = PRINT_S_MOD; 429 | } 430 | else 431 | state = PRINT_S_MOD; 432 | break; 433 | case PRINT_S_MOD: 434 | switch (ch) 435 | { 436 | case 'h': 437 | ch = *format++; 438 | if (ch == 'h') 439 | { /* It's a char. */ 440 | ch = *format++; 441 | cflags = PRINT_C_CHAR; 442 | } 443 | else 444 | cflags = PRINT_C_SHORT; 445 | break; 446 | case 'l': 447 | ch = *format++; 448 | if (ch == 'l') 449 | { /* It's a long long. */ 450 | ch = *format++; 451 | cflags = PRINT_C_LLONG; 452 | } 453 | else 454 | cflags = PRINT_C_LONG; 455 | break; 456 | case 'j': 457 | cflags = PRINT_C_INTMAX; 458 | ch = *format++; 459 | break; 460 | case 't': 461 | cflags = PRINT_C_PTRDIFF; 462 | ch = *format++; 463 | break; 464 | case 'z': 465 | cflags = PRINT_C_SIZE; 466 | ch = *format++; 467 | break; 468 | } 469 | state = PRINT_S_CONV; 470 | break; 471 | case PRINT_S_CONV: 472 | switch (ch) 473 | { 474 | case 'd': 475 | /* FALLTHROUGH */ 476 | case 'i': 477 | switch (cflags) 478 | { 479 | case PRINT_C_CHAR: 480 | value = (signed char)va_arg(args, int); 481 | break; 482 | case PRINT_C_SHORT: 483 | value = (short int)va_arg(args, int); 484 | break; 485 | case PRINT_C_LONG: 486 | value = va_arg(args, long int); 487 | break; 488 | case PRINT_C_LLONG: 489 | value = va_arg(args, LLONG); 490 | break; 491 | case PRINT_C_SIZE: 492 | value = va_arg(args, SSIZE_T); 493 | break; 494 | case PRINT_C_INTMAX: 495 | value = va_arg(args, INTMAX_T); 496 | break; 497 | case PRINT_C_PTRDIFF: 498 | value = va_arg(args, PTRDIFF_T); 499 | break; 500 | default: 501 | value = va_arg(args, int); 502 | break; 503 | } 504 | fmtint(str, &len, size, value, 10, width, 505 | precision, flags); 506 | break; 507 | case 'X': 508 | flags |= PRINT_F_UP; 509 | /* FALLTHROUGH */ 510 | case 'x': 511 | base = 16; 512 | /* FALLTHROUGH */ 513 | case 'o': 514 | if (base == 0) 515 | base = 8; 516 | /* FALLTHROUGH */ 517 | case 'u': 518 | if (base == 0) 519 | base = 10; 520 | flags |= PRINT_F_UNSIGNED; 521 | switch (cflags) 522 | { 523 | case PRINT_C_CHAR: 524 | value = (unsigned char)va_arg(args, 525 | unsigned int); 526 | break; 527 | case PRINT_C_SHORT: 528 | value = (unsigned short int)va_arg(args, 529 | unsigned int); 530 | break; 531 | case PRINT_C_LONG: 532 | value = va_arg(args, unsigned long int); 533 | break; 534 | case PRINT_C_LLONG: 535 | value = va_arg(args, ULLONG); 536 | break; 537 | case PRINT_C_SIZE: 538 | value = va_arg(args, size_t); 539 | break; 540 | case PRINT_C_INTMAX: 541 | value = va_arg(args, UINTMAX_T); 542 | break; 543 | case PRINT_C_PTRDIFF: 544 | value = va_arg(args, UPTRDIFF_T); 545 | break; 546 | default: 547 | value = va_arg(args, unsigned int); 548 | break; 549 | } 550 | fmtint(str, &len, size, value, base, width, 551 | precision, flags); 552 | break; 553 | case 'c': 554 | cvalue = va_arg(args, int); 555 | OUTCHAR(str, len, size, cvalue); 556 | break; 557 | case 's': 558 | strvalue = va_arg(args, char*); 559 | fmtstr(str, &len, size, strvalue, width, 560 | precision, flags); 561 | break; 562 | case 'p': 563 | /* 564 | * C99 says: "The value of the pointer is 565 | * converted to a sequence of printing 566 | * characters, in an implementation-defined 567 | * manner." (C99: 7.19.6.1, 8) 568 | */ 569 | if ((strvalue = (const char*)va_arg(args, void*)) == NULL) 570 | /* 571 | * We use the glibc format. BSD prints 572 | * "0x0", SysV "0". 573 | */ 574 | fmtstr(str, &len, size, "(nil)", width, 575 | -1, flags); 576 | else 577 | { 578 | /* 579 | * We use the BSD/glibc format. SysV 580 | * omits the "0x" prefix (which we emit 581 | * using the PRINT_F_NUM flag). 582 | */ 583 | flags |= PRINT_F_NUM; 584 | flags |= PRINT_F_UNSIGNED; 585 | fmtint(str, &len, size, 586 | (UINTPTR_T)strvalue, 16, width, 587 | precision, flags); 588 | } 589 | break; 590 | case 'n': 591 | switch (cflags) 592 | { 593 | case PRINT_C_CHAR: 594 | charptr = va_arg(args, signed char*); 595 | *charptr = len; 596 | break; 597 | case PRINT_C_SHORT: 598 | shortptr = va_arg(args, short int*); 599 | *shortptr = len; 600 | break; 601 | case PRINT_C_LONG: 602 | longptr = va_arg(args, long int*); 603 | *longptr = len; 604 | break; 605 | case PRINT_C_LLONG: 606 | llongptr = va_arg(args, LLONG*); 607 | *llongptr = len; 608 | break; 609 | case PRINT_C_SIZE: 610 | /* 611 | * C99 says that with the "z" length 612 | * modifier, "a following `n' conversion 613 | * specifier applies to a pointer to a 614 | * signed integer type corresponding to 615 | * size_t argument." (7.19.6.1, 7) 616 | */ 617 | sizeptr = va_arg(args, SSIZE_T*); 618 | *sizeptr = len; 619 | break; 620 | case PRINT_C_INTMAX: 621 | intmaxptr = va_arg(args, INTMAX_T*); 622 | *intmaxptr = len; 623 | break; 624 | case PRINT_C_PTRDIFF: 625 | ptrdiffptr = va_arg(args, PTRDIFF_T*); 626 | *ptrdiffptr = len; 627 | break; 628 | default: 629 | intptr = va_arg(args, int*); 630 | *intptr = len; 631 | break; 632 | } 633 | break; 634 | case '%': /* Print a "%" character verbatim. */ 635 | OUTCHAR(str, len, size, ch); 636 | break; 637 | default: /* Skip other characters. */ 638 | break; 639 | } 640 | ch = *format++; 641 | state = PRINT_S_DEFAULT; 642 | base = cflags = flags = width = 0; 643 | precision = -1; 644 | break; 645 | } 646 | out: 647 | if (len < size) 648 | str[len] = '\0'; 649 | else if (size > 0) 650 | str[size - 1] = '\0'; 651 | 652 | if (overflow || len >= INT_MAX) 653 | { 654 | return -1; 655 | } 656 | return (int)len; 657 | } 658 | 659 | static void 660 | fmtstr(char *str, size_t *len, size_t size, const char *value, int width, 661 | int precision, int flags) 662 | { 663 | int padlen, strln; /* Amount to pad. */ 664 | int noprecision = (precision == -1); 665 | 666 | if (value == NULL) /* We're forgiving. */ 667 | value = "(null)"; 668 | 669 | /* If a precision was specified, don't read the string past it. */ 670 | for (strln = 0; value[strln] != '\0' && 671 | (noprecision || strln < precision); 672 | strln++) 673 | continue; 674 | 675 | if ((padlen = width - strln) < 0) 676 | padlen = 0; 677 | if (flags & PRINT_F_MINUS) /* Left justify. */ 678 | padlen = -padlen; 679 | 680 | while (padlen > 0) 681 | { /* Leading spaces. */ 682 | OUTCHAR(str, *len, size, ' '); 683 | padlen--; 684 | } 685 | while (*value != '\0' && (noprecision || precision-- > 0)) 686 | { 687 | OUTCHAR(str, *len, size, *value); 688 | value++; 689 | } 690 | while (padlen < 0) 691 | { /* Trailing spaces. */ 692 | OUTCHAR(str, *len, size, ' '); 693 | padlen++; 694 | } 695 | } 696 | 697 | static void 698 | fmtint(char *str, size_t *len, size_t size, INTMAX_T value, int base, int width, 699 | int precision, int flags) 700 | { 701 | UINTMAX_T uvalue; 702 | char iconvert[MAX_CONVERT_LENGTH]; 703 | char sign = 0; 704 | char hexprefix = 0; 705 | int spadlen = 0; /* Amount to space pad. */ 706 | int zpadlen = 0; /* Amount to zero pad. */ 707 | int pos; 708 | int separators = (flags & PRINT_F_QUOTE); 709 | int noprecision = (precision == -1); 710 | 711 | if (flags & PRINT_F_UNSIGNED) 712 | uvalue = value; 713 | else 714 | { 715 | uvalue = (value >= 0) ? value : -value; 716 | if (value < 0) 717 | sign = '-'; 718 | else if (flags & PRINT_F_PLUS) /* Do a sign. */ 719 | sign = '+'; 720 | else if (flags & PRINT_F_SPACE) 721 | sign = ' '; 722 | } 723 | 724 | pos = convert(uvalue, iconvert, sizeof(iconvert), base, 725 | flags & PRINT_F_UP); 726 | 727 | if (flags & PRINT_F_NUM && uvalue != 0) 728 | { 729 | /* 730 | * C99 says: "The result is converted to an `alternative form'. 731 | * For `o' conversion, it increases the precision, if and only 732 | * if necessary, to force the first digit of the result to be a 733 | * zero (if the value and precision are both 0, a single 0 is 734 | * printed). For `x' (or `X') conversion, a nonzero result has 735 | * `0x' (or `0X') prefixed to it." (7.19.6.1, 6) 736 | */ 737 | switch (base) 738 | { 739 | case 8: 740 | if (precision <= pos) 741 | precision = pos + 1; 742 | break; 743 | case 16: 744 | hexprefix = (flags & PRINT_F_UP) ? 'X' : 'x'; 745 | break; 746 | } 747 | } 748 | 749 | if (separators) /* Get the number of group separators we'll print. */ 750 | separators = getnumsep(pos); 751 | 752 | zpadlen = precision - pos - separators; 753 | spadlen = width /* Minimum field width. */ 754 | - separators /* Number of separators. */ 755 | - MAX(precision, pos) /* Number of integer digits. */ 756 | - ((sign != 0) ? 1 : 0) /* Will we print a sign? */ 757 | - ((hexprefix != 0) ? 2 : 0); /* Will we print a prefix? */ 758 | 759 | if (zpadlen < 0) 760 | zpadlen = 0; 761 | if (spadlen < 0) 762 | spadlen = 0; 763 | 764 | /* 765 | * C99 says: "If the `0' and `-' flags both appear, the `0' flag is 766 | * ignored. For `d', `i', `o', `u', `x', and `X' conversions, if a 767 | * precision is specified, the `0' flag is ignored." (7.19.6.1, 6) 768 | */ 769 | if (flags & PRINT_F_MINUS) /* Left justify. */ 770 | spadlen = -spadlen; 771 | else if (flags & PRINT_F_ZERO && noprecision) 772 | { 773 | zpadlen += spadlen; 774 | spadlen = 0; 775 | } 776 | while (spadlen > 0) 777 | { /* Leading spaces. */ 778 | OUTCHAR(str, *len, size, ' '); 779 | spadlen--; 780 | } 781 | if (sign != 0) /* Sign. */ 782 | OUTCHAR(str, *len, size, sign); 783 | if (hexprefix != 0) 784 | { /* A "0x" or "0X" prefix. */ 785 | OUTCHAR(str, *len, size, '0'); 786 | OUTCHAR(str, *len, size, hexprefix); 787 | } 788 | while (zpadlen > 0) 789 | { /* Leading zeros. */ 790 | OUTCHAR(str, *len, size, '0'); 791 | zpadlen--; 792 | } 793 | while (pos > 0) 794 | { /* The actual digits. */ 795 | pos--; 796 | OUTCHAR(str, *len, size, iconvert[pos]); 797 | if (separators > 0 && pos > 0 && pos % 3 == 0) 798 | printsep(str, len, size); 799 | } 800 | while (spadlen < 0) 801 | { /* Trailing spaces. */ 802 | OUTCHAR(str, *len, size, ' '); 803 | spadlen++; 804 | } 805 | } 806 | 807 | static void 808 | printsep(char *str, size_t *len, size_t size) 809 | { 810 | OUTCHAR(str, *len, size, ','); 811 | } 812 | 813 | static int 814 | getnumsep(int digits) 815 | { 816 | int separators = (digits - ((digits % 3 == 0) ? 1 : 0)) / 3; 817 | return separators; 818 | } 819 | 820 | static int 821 | convert(UINTMAX_T value, char *buf, size_t size, int base, int caps) 822 | { 823 | const char *digits = caps ? "0123456789ABCDEF" : "0123456789abcdef"; 824 | size_t pos = 0; 825 | 826 | /* We return an unterminated buffer with the digits in reverse order. */ 827 | do 828 | { 829 | buf[pos++] = digits[value % base]; 830 | value /= base; 831 | } while (value != 0 && pos < size); 832 | 833 | return (int)pos; 834 | } 835 | 836 | int vsprintf(char *buf, const char *fmt, va_list args) 837 | { 838 | return vsnprintf(buf, INT_MAX, fmt, args); 839 | } 840 | 841 | int sprintf(char *buffer, const char *fmt, ...) 842 | { 843 | va_list args; 844 | int i; 845 | 846 | va_start(args, fmt); 847 | i = vsprintf(buffer, fmt, args); 848 | va_end(args); 849 | return i; 850 | } 851 | 852 | int snprintf(char *buffer, size_t len, const char *fmt, ...) 853 | { 854 | va_list args; 855 | int i; 856 | 857 | va_start(args, fmt); 858 | i = vsnprintf(buffer, len, fmt, args); 859 | va_end(args); 860 | return i; 861 | } 862 | 863 | // Customize printf to see outputs 864 | int printf(const char *fmt, ...) 865 | { 866 | char buffer[MAX_PRINTF_BUFFER]; 867 | va_list args; 868 | int written = 0; 869 | 870 | va_start(args, fmt); 871 | vsnprintf(buffer, MAX_PRINTF_BUFFER, fmt, args); 872 | va_end(args); 873 | strcat(buffer, "\n"); 874 | sys_tty_write(1, buffer, strlen(buffer), &written); 875 | return written; 876 | } 877 | -------------------------------------------------------------------------------- /src/scrfct.c: -------------------------------------------------------------------------------- 1 | #include "scrfct.h" 2 | #include 3 | #include 4 | #include 5 | #include "utils.h" 6 | #include "t5.h" 7 | 8 | void scrfct_setmemory() 9 | { 10 | // Verify that we have 2 parameters for this function. 11 | if (Scr_GetNumParam(0) == 2) 12 | { 13 | // Getting parameters, we don't check the type using a function again here but we could have did it. 14 | char *hexAddress = Scr_GetString(0, 0); 15 | char *hexData = Scr_GetString(1, 0); 16 | 17 | if (hexAddress && hexData) 18 | { 19 | // Allocate input size +2 in case to handle zero padding and null terminated char. 20 | char hexAddressFixed[strlen(hexAddress) + 2]; 21 | char hexDataFixed[strlen(hexData) + 2]; 22 | 23 | hex_str_to_padded_hex_str(hexAddressFixed, hexAddress); 24 | hex_str_to_padded_hex_str(hexDataFixed, hexData); 25 | 26 | size_t addressSize = strlen(hexAddressFixed)/2; 27 | size_t dataSize = strlen(hexDataFixed)/2; 28 | 29 | int offset = hex_str_to_int32(hexAddressFixed, addressSize); 30 | 31 | char buffer[dataSize]; 32 | memset(buffer, 0, dataSize); 33 | hex_str_to_buffer(buffer, hexDataFixed, dataSize); 34 | 35 | sys_dbg_process_write(offset, buffer, dataSize); 36 | 37 | printf(T5INFO "Function 'setMemory' called from gsc with the following parameters: %s, %s.", hexAddress, hexData); 38 | } 39 | else 40 | printf(T5ERROR "Cannot resolve setmemory parameters call from gsc."); 41 | } 42 | } -------------------------------------------------------------------------------- /src/scrfct.h: -------------------------------------------------------------------------------- 1 | #ifndef SCRFCT_H 2 | #define SCRFCT_H 3 | 4 | void scrfct_setmemory(void); 5 | 6 | #endif /* SCRFCT_H */ -------------------------------------------------------------------------------- /src/t5.c: -------------------------------------------------------------------------------- 1 | #include "t5.h" 2 | 3 | #include "offsets.h" 4 | #include "cshook.h" 5 | #include "utils.h" 6 | 7 | #include "string.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | void init_offsets() 15 | { 16 | // Set native offsets 17 | t5ni(DB_FindXAssetHeader); 18 | t5ni(DB_LinkXAssetEntry); 19 | t5ni(Dvar_FindVar); 20 | t5ni(Scr_GetFunctionHandle); 21 | t5ni(Scr_ExecThread); 22 | t5ni(Scr_FreeThread); 23 | t5ni(Scr_GetNumParam); 24 | t5ni(Scr_GetString); 25 | 26 | // Set struct offsets 27 | scrVarPub = (scrVarPub_t*)(t5o(ScrVarPub)); 28 | scrCompilePub = (scrCompilePub_t*)(t5o(ScrCompilePub)); 29 | } 30 | 31 | int init_hooks() 32 | { 33 | // Set hooks offsets 34 | t5nhi(cellSpursLFQueuePushBody); 35 | t5nhi(Scr_GetChecksum); 36 | t5nhi(Scr_LoadGameType); 37 | t5nhi(Scr_LoadScript); 38 | t5nhi(Scr_GetFunction); 39 | 40 | // Create and enable hooks 41 | int res; 42 | if ((res = cs_hook_install(cellSpursLFQueuePushBody, CS_HOOK_TYPE_IMPORT)) < 0) 43 | return res; 44 | if ((res = cs_hook_install(Scr_GetChecksum, CS_HOOK_TYPE_CTR)) < 0) 45 | return res; 46 | if ((res = cs_hook_install(Scr_LoadGameType, CS_HOOK_TYPE_CTR)) < 0) 47 | return res; 48 | if ((res = cs_hook_install(Scr_LoadScript, CS_HOOK_TYPE_CTR)) < 0) 49 | return res; 50 | if ((res = cs_hook_install(Scr_GetFunction, CS_HOOK_TYPE_CTR)) < 0) 51 | return res; 52 | 53 | return 0; 54 | } 55 | 56 | GSCLoaderRawfile *get_loader_rawfile_from_deflated_buffer(char *deflatedBuffer) 57 | { 58 | for (int i = 0; i < MAX_GSC_COUNT; i++) 59 | { 60 | if ((uintptr_t)deflatedBuffer == (uintptr_t)(loader.rawFiles[i].data.buffer + 2)) 61 | return &loader.rawFiles[i]; 62 | } 63 | return NULL; 64 | } 65 | 66 | void get_or_create_mod_path(char *path) 67 | { 68 | int fd; 69 | CellFsErrno err; 70 | char pathMods[CELL_FS_MAX_FS_PATH_LENGTH]; 71 | sprintf(pathMods, SCRIPTS_PATH "/%s", isMultiplayer ? "mp" : "zm"); 72 | 73 | // Check scripts folder existing and create it if necessary 74 | err = cellFsMkdir(SCRIPTS_PATH, CELL_FS_DEFAULT_CREATE_MODE_1); 75 | if (err != CELL_FS_SUCCEEDED && err != CELL_FS_EEXIST) 76 | { 77 | printf(T5ERROR "Cannot create the path '%s' (0x%08X).", SCRIPTS_PATH, err); 78 | return; 79 | } 80 | 81 | err = cellFsOpendir(pathMods, &fd); 82 | if (err != CELL_FS_SUCCEEDED) 83 | { 84 | cellFsClosedir(fd); 85 | printf(T5ERROR "Cannot open 'scripts' directory (0x%08X).", err); 86 | return; 87 | } 88 | 89 | uint64_t read; 90 | CellFsDirent ent; 91 | read = sizeof(CellFsDirent); 92 | while (!cellFsReaddir(fd, &ent, &read)) 93 | { 94 | if (!read) 95 | break; 96 | 97 | if (strstr(ent.d_name, ".mod")) 98 | { 99 | strcpy(loader.currentModName, ent.d_name); 100 | sprintf(path, SCRIPTS_PATH "/%s/%s", isMultiplayer ? "mp" : "zm", ent.d_name); 101 | path[strlen(path) - 4] = 0; 102 | } 103 | } 104 | 105 | cellFsClosedir(fd); 106 | } 107 | 108 | bool create_assets_from_scripts(char *path) 109 | { 110 | int fd; 111 | CellFsErrno err = cellFsOpendir(path, &fd); 112 | if (err != CELL_FS_SUCCEEDED) 113 | { 114 | cellFsClosedir(fd); 115 | printf(T5ERROR "Cannot open '%s' directory (0x%08X).", path, err); 116 | } 117 | 118 | uint64_t read; 119 | CellFsDirent ent; 120 | read = sizeof(CellFsDirent); 121 | int assetIndex = 0; 122 | bool mainLinked = false; 123 | 124 | while (!cellFsReaddir(fd, &ent, &read) && assetIndex < MAX_GSC_COUNT) 125 | { 126 | if (!read) 127 | break; 128 | 129 | if (strstr(ent.d_name, ".gsc") != NULL) 130 | { 131 | printf(T5INFO "Creating a new asset entry for '%s'.", ent.d_name); 132 | 133 | loader.rawFiles[assetIndex].asset.name = (char*)&loader.rawFiles[assetIndex].data.name; 134 | loader.rawFiles[assetIndex].asset.buffer = (char*)&loader.rawFiles[assetIndex].data.inflatedSize; 135 | loader.rawFiles[assetIndex].asset.len = 0xDEAD; 136 | 137 | set_empty_deflated_data(loader.rawFiles[assetIndex].data.buffer); 138 | 139 | sprintf(loader.rawFiles[assetIndex].data.name, "maps/%s/mod/%s", isMultiplayer ? "mp" : "zm", ent.d_name); 140 | 141 | char filePath[CELL_FS_MAX_FS_PATH_LENGTH]; 142 | sprintf(filePath, "%s/%s", path, ent.d_name); 143 | int fileSize = get_file_size(filePath); 144 | 145 | loader.rawFiles[assetIndex].data.inflatedSize = fileSize + 1; 146 | loader.rawFiles[assetIndex].data.size = 0x1B; 147 | loader.rawFiles[assetIndex].entry.asset.type = ASSET_TYPE_RAWFILE; 148 | loader.rawFiles[assetIndex].entry.asset.header.rawFile = &loader.rawFiles[assetIndex].asset; 149 | 150 | XAssetEntryPoolEntry *entry = 0; 151 | XAssetHeader header; 152 | 153 | DB_FindXAssetHeader(&header, ASSET_TYPE_RAWFILE, loader.rawFiles[assetIndex].asset.name, true, -1); 154 | 155 | if (header.rawFile != &loader.rawFiles[assetIndex].asset) 156 | { 157 | entry = DB_LinkXAssetEntry(&loader.rawFiles[assetIndex].entry, 0); 158 | if (!entry) 159 | { 160 | printf(T5ERROR "Linking asset '%s' failed.", loader.rawFiles[assetIndex].asset.name); 161 | continue; 162 | } 163 | if (strcmp(ent.d_name, "main.gsc") == 0) 164 | mainLinked = true; 165 | 166 | assetIndex++; 167 | } 168 | } 169 | } 170 | cellFsClosedir(fd); 171 | return mainLinked && (assetIndex > 0); 172 | } 173 | 174 | int init_game() 175 | { 176 | // Current process is Black Ops MP 1.13 MP or ZM? 177 | if (*(int*)(0x1002C) == 0xB5A4A0) 178 | isMultiplayer = true; 179 | else if (*(int*)(0x1002C) == 0xA56728) 180 | isMultiplayer = false; 181 | else 182 | { 183 | printf(T5ERROR "The process is not Black Ops 1.13 MP/ZM."); 184 | return -1; 185 | } 186 | 187 | // Init offsets / hooks according MP/ZM 188 | int err; 189 | init_offsets(); 190 | if ((err = init_hooks()) < 0) 191 | { 192 | printf(T5ERROR "Hooks install failed (0x%08X).", err); 193 | return -2; 194 | } 195 | 196 | // Show a warning on boot in case no mod is set 197 | char modPath[CELL_FS_MAX_FS_PATH_LENGTH]; 198 | get_or_create_mod_path(modPath); 199 | if (!*modPath) 200 | { 201 | printf(T5WARNING "Mod file not found, create a .mod file in '%s/%s' with a name that is equal to a mod folder to load it (no game restart required using ftp).", SCRIPTS_PATH, isMultiplayer ? "mp" : "zm"); 202 | } 203 | return 0; 204 | } -------------------------------------------------------------------------------- /src/t5.h: -------------------------------------------------------------------------------- 1 | #ifndef T5_H 2 | #define T5_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include "defines.h" 11 | 12 | bool isMultiplayer; 13 | char scriptPath[255]; 14 | int modHandle; 15 | 16 | GSCLoader loader; 17 | scrChecksum_t checksums[3]; 18 | scrVarPub_t *scrVarPub; 19 | scrCompilePub_t *scrCompilePub; 20 | 21 | t5nd(XAssetHeader *, DB_FindXAssetHeader, (XAssetHeader *header, XAssetType type, const char *name, bool errorIfMissing, int waitTime)); 22 | t5nd(XAssetEntryPoolEntry*, DB_LinkXAssetEntry, (XAssetEntry *newEntry, int allowOverride)); 23 | t5nd(dvar_s*, Dvar_FindVar, (const char *name)); 24 | t5nd(int, Scr_GetFunctionHandle, (scriptInstance_t inst, const char *scriptName, const char *functionName)); 25 | t5nd(unsigned short, Scr_ExecThread, (scriptInstance_t inst, int handle, int paramcount)); 26 | t5nd(void, Scr_FreeThread, (unsigned short handle, scriptInstance_t inst)); 27 | t5nd(int, Scr_GetNumParam, (int scriptInstance)); 28 | t5nd(char*, Scr_GetString, (unsigned int index, scriptInstance_t scriptInstance)); 29 | 30 | t5nhd(int, cellSpursLFQueuePushBody, (CellSpursLFQueue *lfqueue, const void *buffer, unsigned int isBlocking)); 31 | t5nhd(int, Scr_LoadScript, (scriptInstance_t inst, const char *scriptName)); 32 | t5nhd(void, Scr_GetChecksum, (scrChecksum_t *vmChecksum, scriptInstance_t inst)); 33 | t5nhd(void, Scr_LoadGameType, (void)); 34 | t5nhd(popd32, Scr_GetFunction, (const char **pName, int *type)); 35 | 36 | GSCLoaderRawfile *get_loader_rawfile_from_deflated_buffer(char *inflatedBuffer); 37 | void get_or_create_mod_path(char *path); 38 | bool create_assets_from_scripts(char *path); 39 | int init_game(); 40 | 41 | #endif -------------------------------------------------------------------------------- /src/utils.c: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | int sys_dbg_process_write(uint64_t address, const void *data, size_t size) 9 | { 10 | system_call_4(905, sys_process_getpid(), address, size, (uintptr_t)data); 11 | return_to_user_prog(int); 12 | } 13 | 14 | size_t get_file_size(char *filePath) 15 | { 16 | int size = 0; 17 | CellFsStat fstat; 18 | CellFsErrno err = cellFsStat(filePath, &fstat); 19 | if (err != CELL_FS_SUCCEEDED) 20 | { 21 | return err; 22 | } 23 | return fstat.st_size; 24 | } 25 | 26 | void set_empty_deflated_data(char *buffer) 27 | { 28 | int op[7]; 29 | op[0] = 0x789C05B0; 30 | op[1] = 0xB1110000; 31 | op[2] = 0x04C48CC3; 32 | op[3] = 0x6869543A; 33 | op[4] = 0xF7BBE758; 34 | op[5] = 0xAEE75302; 35 | op[6] = 0x109802FE; 36 | sys_dbg_process_write((uintptr_t)buffer, &op, 7 * 4); 37 | } 38 | 39 | void hex_str_to_padded_hex_str(char *out, char *hexStr) 40 | { 41 | char *outPtr; 42 | char *tmp = hexStr; 43 | 44 | if (!tmp || !out) 45 | return; 46 | 47 | if (tmp[1] == 'x') 48 | tmp += 2; 49 | 50 | int hexLen = strlen(tmp); 51 | 52 | if ((hexLen % 2) != 0) // must be even 53 | { 54 | hexLen++; 55 | out[0] = '0'; 56 | outPtr = &out[1]; 57 | } 58 | else 59 | outPtr = out; 60 | 61 | strcpy(outPtr, tmp); 62 | } 63 | 64 | void hex_str_to_buffer(char *out, char *hexStr, size_t hexLen) 65 | { 66 | if (!out || !hexStr || hexLen < 1) 67 | return; 68 | 69 | size_t index = 0; 70 | while (index < (hexLen * 2)) 71 | { 72 | char c = hexStr[index]; 73 | int value = 0; 74 | if (c >= '0' && c <= '9') 75 | value = (c - '0'); 76 | else if (c >= 'A' && c <= 'F') 77 | value = (10 + (c - 'A')); 78 | else if (c >= 'a' && c <= 'f') 79 | value = (10 + (c - 'a')); 80 | else 81 | return; 82 | 83 | out[(index / 2)] += (value << (((index + 1) % 2) * 4)); 84 | index++; 85 | } 86 | } 87 | 88 | int hex_str_to_int32(char *hexStr, size_t hexLen) 89 | { 90 | char out[4]; 91 | memset(out, 0, 4); 92 | int start = (4 - hexLen); 93 | if (start < 0) 94 | start = 0; 95 | 96 | hex_str_to_buffer((char*)(out + start), hexStr, hexLen); 97 | 98 | return *(int*)(&out); 99 | } 100 | -------------------------------------------------------------------------------- /src/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef UTILS_H 2 | #define UTILS_H 3 | 4 | #include 5 | #include 6 | 7 | // Fixing the define only to remove the annoying intellisense issue.. 8 | #ifdef SYS_MODULE_INFO_SECTION 9 | #undef SYS_MODULE_INFO_SECTION 10 | #define SYS_MODULE_INFO_SECTION __attribute__((section(SYS_MODINFO_SECTION_NAME ", \"a\",@progbits#"))) 11 | #endif 12 | 13 | int sys_dbg_process_write(uint64_t address, const void *data, size_t size); 14 | size_t get_file_size(char *filePath); 15 | void set_empty_deflated_data(char *buffer); 16 | void hex_str_to_padded_hex_str(char *out, char *hexStr); 17 | void hex_str_to_buffer(char *out, char *hexStr, size_t hexLen); 18 | int hex_str_to_int32(char *hexStr, size_t hexLen); 19 | 20 | #endif /* UTILS_H */ -------------------------------------------------------------------------------- /tmp/T5GSCLoader/mp/welcome.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iMCSx/T5GSCLoader/750ecd0a6b9ca1ad58902ba0404cb83443c6d7a2/tmp/T5GSCLoader/mp/welcome.mod -------------------------------------------------------------------------------- /tmp/T5GSCLoader/mp/welcome/main.gsc: -------------------------------------------------------------------------------- 1 | #include maps\mp\_utility; 2 | #include common_scripts\utility; 3 | #include maps\mp\gametypes\_hud_util; 4 | 5 | #include maps\mp\mod\utils; 6 | 7 | main() 8 | { 9 | applyPatches(); 10 | level thread onPlayerConnect(); 11 | } 12 | 13 | onPlayerConnect() 14 | { 15 | for(;;) 16 | { 17 | level waittill("connected", player); 18 | player thread onPlayerSpawned(); 19 | } 20 | } 21 | 22 | onPlayerSpawned() 23 | { 24 | self endon("disconnect"); 25 | for(;;) 26 | { 27 | self waittill("spawned_player"); 28 | self iprintln("Hello from ^1injected ^7GSC"); 29 | } 30 | } -------------------------------------------------------------------------------- /tmp/T5GSCLoader/mp/welcome/utils.gsc: -------------------------------------------------------------------------------- 1 | #include maps\mp\_utility; 2 | #include common_scripts\utility; 3 | #include maps\mp\gametypes\_hud_util; 4 | 5 | applyPatches() 6 | { 7 | setMemory("0x10042000", "12345678"); 8 | setMemory("0x10042010", "4578656D706C65206F6620737472696E67"); 9 | } -------------------------------------------------------------------------------- /tmp/T5GSCLoader/zm/welcome.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iMCSx/T5GSCLoader/750ecd0a6b9ca1ad58902ba0404cb83443c6d7a2/tmp/T5GSCLoader/zm/welcome.mod -------------------------------------------------------------------------------- /tmp/T5GSCLoader/zm/welcome/main.gsc: -------------------------------------------------------------------------------- 1 | #include maps\_utility; 2 | #include common_scripts\utility; 3 | #include maps\_hud_util; 4 | 5 | #include maps\zm\mod\utils; 6 | 7 | main() 8 | { 9 | applyPatches(); 10 | level thread onPlayerConnect(); 11 | } 12 | 13 | onPlayerConnect() 14 | { 15 | for(;;) 16 | { 17 | level waittill("connected", player); 18 | player thread onPlayerSpawned(); 19 | } 20 | } 21 | 22 | onPlayerSpawned() 23 | { 24 | self endon("disconnect"); 25 | for(;;) 26 | { 27 | self waittill("spawned_player"); 28 | self iprintln("Hello from ^1injected ^7GSC"); 29 | } 30 | } -------------------------------------------------------------------------------- /tmp/T5GSCLoader/zm/welcome/utils.gsc: -------------------------------------------------------------------------------- 1 | #include maps\_utility; 2 | #include common_scripts\utility; 3 | #include maps\_hud_util; 4 | 5 | applyPatches() 6 | { 7 | setMemory("0x10042000", "12345678"); 8 | setMemory("0x10042010", "4578656D706C65206F6620737472696E67"); 9 | } --------------------------------------------------------------------------------