├── Client.h ├── DetourHook.cpp ├── DetourHook.hpp ├── PluginCallbacks.h ├── README.md ├── Typedefs.h └── prx.cpp /Client.h: -------------------------------------------------------------------------------- 1 | #include "Typedefs.h" 2 | #include "Platform.h" 3 | 4 | class IVEngineClient 5 | { 6 | public: 7 | virtual int GetIntersectingSurfaces( 8 | const void* *model, 9 | const Vector &vCenter, 10 | const float radius, 11 | const bool bOnlyVisibleSurfaces, 12 | void* *pInfos, 13 | const int nMaxInfos) = 0; 14 | 15 | virtual Vector GetLightForPoint(const Vector &pos, bool bClamp) = 0; 16 | 17 | virtual void* *TraceLineMaterialAndLighting(const Vector &start, const Vector &end, 18 | Vector &diffuseLightColor, Vector& baseColor) = 0; 19 | 20 | virtual const char *ParseFile(const char *data, char *token, int maxlen) = 0; 21 | virtual bool CopyFile(const char *source, const char *destination) = 0; 22 | 23 | virtual void GetScreenSize(int& width, int& height) = 0; 24 | 25 | virtual void ServerCmd(const char *szCmdString, bool bReliable = true) = 0; 26 | 27 | virtual void ClientCmd(const char *szCmdString) = 0; 28 | } -------------------------------------------------------------------------------- /DetourHook.cpp: -------------------------------------------------------------------------------- 1 | #include "DetourHook.hpp" 2 | 3 | #define POWERPC_REGISTERINDEX_R0 0 4 | #define POWERPC_REGISTERINDEX_R1 1 5 | #define POWERPC_REGISTERINDEX_R2 2 6 | #define POWERPC_REGISTERINDEX_R3 3 7 | #define POWERPC_REGISTERINDEX_R4 4 8 | #define POWERPC_REGISTERINDEX_R5 5 9 | #define POWERPC_REGISTERINDEX_R6 6 10 | #define POWERPC_REGISTERINDEX_R7 7 11 | #define POWERPC_REGISTERINDEX_R8 8 12 | #define POWERPC_REGISTERINDEX_R9 9 13 | #define POWERPC_REGISTERINDEX_R10 10 14 | #define POWERPC_REGISTERINDEX_R11 11 15 | #define POWERPC_REGISTERINDEX_R12 12 16 | #define POWERPC_REGISTERINDEX_R13 13 17 | #define POWERPC_REGISTERINDEX_R14 14 18 | #define POWERPC_REGISTERINDEX_R15 15 19 | #define POWERPC_REGISTERINDEX_R16 16 20 | #define POWERPC_REGISTERINDEX_R17 17 21 | #define POWERPC_REGISTERINDEX_R18 18 22 | #define POWERPC_REGISTERINDEX_R19 19 23 | #define POWERPC_REGISTERINDEX_R20 20 24 | #define POWERPC_REGISTERINDEX_R21 21 25 | #define POWERPC_REGISTERINDEX_R22 22 26 | #define POWERPC_REGISTERINDEX_R23 23 27 | #define POWERPC_REGISTERINDEX_R24 24 28 | #define POWERPC_REGISTERINDEX_R25 25 29 | #define POWERPC_REGISTERINDEX_R26 26 30 | #define POWERPC_REGISTERINDEX_R27 27 31 | #define POWERPC_REGISTERINDEX_R28 28 32 | #define POWERPC_REGISTERINDEX_R29 29 33 | #define POWERPC_REGISTERINDEX_R30 30 34 | #define POWERPC_REGISTERINDEX_R31 31 35 | #define POWERPC_REGISTERINDEX_SP 1 36 | #define POWERPC_REGISTERINDEX_RTOC 2 37 | 38 | #define MASK_N_BITS(N) ( ( 1 << ( N ) ) - 1 ) 39 | 40 | #define POWERPC_HI(X) ( ( X >> 16 ) & 0xFFFF ) 41 | #define POWERPC_LO(X) ( X & 0xFFFF ) 42 | 43 | // PowerPC most significant bit is addressed as bit 0 in documentation. 44 | #define POWERPC_BIT32(N) ( 31 - N ) 45 | 46 | // Opcode is bits 0-5. 47 | // Allowing for op codes ranging from 0-63. 48 | #define POWERPC_OPCODE(OP) (uint32_t)( OP << 26 ) 49 | #define POWERPC_OPCODE_ADDI POWERPC_OPCODE( 14 ) 50 | #define POWERPC_OPCODE_ADDIS POWERPC_OPCODE( 15 ) 51 | #define POWERPC_OPCODE_BC POWERPC_OPCODE( 16 ) 52 | #define POWERPC_OPCODE_B POWERPC_OPCODE( 18 ) 53 | #define POWERPC_OPCODE_BCCTR POWERPC_OPCODE( 19 ) 54 | #define POWERPC_OPCODE_ORI POWERPC_OPCODE( 24 ) 55 | #define POWERPC_OPCODE_EXTENDED POWERPC_OPCODE( 31 ) // Use extended opcodes. 56 | #define POWERPC_OPCODE_STW POWERPC_OPCODE( 36 ) 57 | #define POWERPC_OPCODE_LWZ POWERPC_OPCODE( 32 ) 58 | #define POWERPC_OPCODE_LD POWERPC_OPCODE( 58 ) 59 | #define POWERPC_OPCODE_STD POWERPC_OPCODE( 62 ) 60 | #define POWERPC_OPCODE_MASK POWERPC_OPCODE( 63 ) 61 | 62 | #define POWERPC_EXOPCODE(OP) ( OP << 1 ) 63 | #define POWERPC_EXOPCODE_BCCTR POWERPC_EXOPCODE( 528 ) 64 | #define POWERPC_EXOPCODE_MTSPR POWERPC_EXOPCODE( 467 ) 65 | 66 | // SPR field is encoded as two 5 bit bitfields. 67 | #define POWERPC_SPR(SPR) (uint32_t)( ( ( SPR & 0x1F ) << 5 ) | ( ( SPR >> 5 ) & 0x1F ) ) 68 | 69 | // Instruction helpers. 70 | // rD - Destination register. 71 | // rS - Source register. 72 | // rA/rB - Register inputs. 73 | // SPR - Special purpose register. 74 | // UIMM/SIMM - Unsigned/signed immediate. 75 | #define POWERPC_ADDI(rD, rA, SIMM) (uint32_t)( POWERPC_OPCODE_ADDI | ( rD << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | SIMM ) 76 | #define POWERPC_ADDIS(rD, rA, SIMM) (uint32_t)( POWERPC_OPCODE_ADDIS | ( rD << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | SIMM ) 77 | #define POWERPC_LIS(rD, SIMM) POWERPC_ADDIS( rD, 0, SIMM ) // Mnemonic for addis %rD, 0, SIMM 78 | #define POWERPC_LI(rD, SIMM) POWERPC_ADDI( rD, 0, SIMM ) // Mnemonic for addi %rD, 0, SIMM 79 | #define POWERPC_MTSPR(SPR, rS) (uint32_t)( POWERPC_OPCODE_EXTENDED | ( rS << POWERPC_BIT32( 10 ) ) | ( POWERPC_SPR( SPR ) << POWERPC_BIT32( 20 ) ) | POWERPC_EXOPCODE_MTSPR ) 80 | #define POWERPC_MTCTR(rS) POWERPC_MTSPR( 9, rS ) // Mnemonic for mtspr 9, rS 81 | #define POWERPC_ORI(rS, rA, UIMM) (uint32_t)( POWERPC_OPCODE_ORI | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | UIMM ) 82 | #define POWERPC_BCCTR(BO, BI, LK) (uint32_t)( POWERPC_OPCODE_BCCTR | ( BO << POWERPC_BIT32( 10 ) ) | ( BI << POWERPC_BIT32( 15 ) ) | ( LK & 1 ) | POWERPC_EXOPCODE_BCCTR ) 83 | #define POWERPC_STD(rS, DS, rA) (uint32_t)( POWERPC_OPCODE_STD | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | ( (int16_t)DS & 0xFFFF ) ) 84 | #define POWERPC_LD(rS, DS, rA) (uint32_t)( POWERPC_OPCODE_LD | ( rS << POWERPC_BIT32( 10 ) ) | ( rA << POWERPC_BIT32( 15 ) ) | ( (int16_t)DS & 0xFFFF ) ) 85 | 86 | // Branch related fields. 87 | #define POWERPC_BRANCH_LINKED 1 88 | #define POWERPC_BRANCH_ABSOLUTE 2 89 | #define POWERPC_BRANCH_TYPE_MASK ( POWERPC_BRANCH_LINKED | POWERPC_BRANCH_ABSOLUTE ) 90 | 91 | #define POWERPC_BRANCH_OPTIONS_ALWAYS ( 20 ) 92 | 93 | uint8_t DetourHook::s_TrampolineBuffer[]{}; 94 | size_t DetourHook::s_TrampolineSize = 0; 95 | 96 | DetourHook::DetourHook() 97 | : m_HookTarget(nullptr), m_HookAddress(nullptr), m_TrampolineAddress(nullptr), m_OriginalLength(0) 98 | { 99 | memset(m_TrampolineOpd, 0, sizeof(m_TrampolineOpd)); 100 | memset(m_OriginalInstructions, 0, sizeof(m_OriginalInstructions)); 101 | } 102 | 103 | DetourHook::DetourHook(uint32_t fnAddress, uintptr_t fnCallback) 104 | : m_HookTarget(nullptr), m_HookAddress(nullptr), m_TrampolineAddress(nullptr), m_OriginalLength(0) 105 | { 106 | memset(m_TrampolineOpd, 0, sizeof(m_TrampolineOpd)); 107 | memset(m_OriginalInstructions, 0, sizeof(m_OriginalInstructions)); 108 | 109 | Hook(fnAddress, fnCallback); 110 | } 111 | 112 | DetourHook::~DetourHook() 113 | { 114 | UnHook(); 115 | } 116 | 117 | size_t DetourHook::GetHookSize(const void* branchTarget, bool linked, bool preserveRegister) 118 | { 119 | return JumpWithOptions(nullptr, branchTarget, linked, preserveRegister, POWERPC_BRANCH_OPTIONS_ALWAYS, 0, POWERPC_REGISTERINDEX_R0); 120 | } 121 | 122 | size_t DetourHook::Jump(void* destination, const void* branchTarget, bool linked, bool preserveRegister) 123 | { 124 | return JumpWithOptions(destination, branchTarget, linked, preserveRegister, POWERPC_BRANCH_OPTIONS_ALWAYS, 0, POWERPC_REGISTERINDEX_R0); 125 | } 126 | 127 | size_t DetourHook::JumpWithOptions(void* destination, const void* branchTarget, bool linked, bool preserveRegister, uint32_t branchOptions, uint8_t conditionRegisterBit, uint8_t registerIndex) 128 | { 129 | uint32_t BranchFarAsm[] = { 130 | POWERPC_LIS(registerIndex, POWERPC_HI((uint32_t)branchTarget)), // lis %rX, branchTarget@hi 131 | POWERPC_ORI(registerIndex, registerIndex, POWERPC_LO((uint32_t)branchTarget)), // ori %rX, %rX, branchTarget@lo 132 | //POWERPC_ADDI(registerIndex, registerIndex, POWERPC_LO((uint32_t)branchTarget)), 133 | 0x7c0903a6, 134 | //POWERPC_MTCTR(registerIndex), // mtctr %rX 135 | POWERPC_BCCTR(branchOptions, conditionRegisterBit, linked) // bcctr (bcctr 20, 0 == bctr) 136 | }; 137 | 138 | uint32_t BranchFarAsmPreserve[] = { 139 | POWERPC_STD(registerIndex, -0x30, POWERPC_REGISTERINDEX_R1), // std %rX, -0x30(%r1) 140 | POWERPC_LIS(registerIndex, POWERPC_HI((uint32_t)branchTarget)), // lis %rX, branchTarget@hi 141 | POWERPC_ORI(registerIndex, registerIndex, POWERPC_LO((uint32_t)branchTarget)), // ori %rX, %rX, branchTarget@lo 142 | POWERPC_MTCTR(registerIndex), // mtctr %rX 143 | POWERPC_LD(registerIndex, -0x30, POWERPC_REGISTERINDEX_R1), // ld %rX, -0x30(%r1) 144 | POWERPC_BCCTR(branchOptions, conditionRegisterBit, linked) // bcctr (bcctr 20, 0 == bctr) 145 | }; 146 | 147 | uint32_t* BranchAsm = preserveRegister ? BranchFarAsmPreserve : BranchFarAsm; 148 | size_t BranchAsmSize = preserveRegister ? sizeof(BranchFarAsmPreserve) : sizeof(BranchFarAsm); 149 | 150 | if (destination) 151 | WriteProcessMemory(sys_process_getpid(), destination, BranchAsm, BranchAsmSize); 152 | 153 | return BranchAsmSize; 154 | } 155 | 156 | size_t DetourHook::RelocateBranch(uint32_t* destination, uint32_t* source) 157 | { 158 | uint32_t Instruction = *source; 159 | uint32_t InstructionAddress = (uint32_t)source; 160 | 161 | // Absolute branches dont need to be handled. 162 | if (Instruction & POWERPC_BRANCH_ABSOLUTE) 163 | { 164 | WriteProcessMemory(sys_process_getpid(), destination, &Instruction, sizeof(Instruction)); 165 | return sizeof(Instruction); 166 | } 167 | 168 | int32_t BranchOffsetBitSize = 0; 169 | int32_t BranchOffsetBitBase = 0; 170 | uint32_t BranchOptions = 0; 171 | uint8_t ConditionRegisterBit = 0; 172 | 173 | switch (Instruction & POWERPC_OPCODE_MASK) 174 | { 175 | // B - Branch 176 | // [Opcode] [Address] [Absolute] [Linked] 177 | // 0-5 6-29 30 31 178 | // 179 | // Example 180 | // 010010 0000 0000 0000 0000 0000 0001 0 0 181 | case POWERPC_OPCODE_B: 182 | BranchOffsetBitSize = 24; 183 | BranchOffsetBitBase = 2; 184 | BranchOptions = POWERPC_BRANCH_OPTIONS_ALWAYS; 185 | ConditionRegisterBit = 0; 186 | break; 187 | 188 | // BC - Branch Conditional 189 | // [Opcode] [Branch Options] [Condition Register] [Address] [Absolute] [Linked] 190 | // 0-5 6-10 11-15 16-29 30 31 191 | // 192 | // Example 193 | // 010000 00100 00001 00 0000 0000 0001 0 0 194 | case POWERPC_OPCODE_BC: 195 | BranchOffsetBitSize = 14; 196 | BranchOffsetBitBase = 2; 197 | BranchOptions = (Instruction >> POWERPC_BIT32(10)) & MASK_N_BITS(5); 198 | ConditionRegisterBit = (Instruction >> POWERPC_BIT32(15)) & MASK_N_BITS(5); 199 | break; 200 | } 201 | 202 | // Even though the address part of the instruction begins from bit 29 in the case of bc and b. 203 | // The value of the first bit is 4 as all addresses are aligned to for 4 for code therefore, 204 | // the branch offset can be caluclated by anding in place and removing any suffix bits such as the 205 | // link register or absolute flags. 206 | int32_t BranchOffset = Instruction & (MASK_N_BITS(BranchOffsetBitSize) << BranchOffsetBitBase); 207 | 208 | // Check if the MSB of the offset is set. 209 | if (BranchOffset >> ((BranchOffsetBitSize + BranchOffsetBitBase) - 1)) 210 | { 211 | // Add the nessasary bits to our integer to make it negative. 212 | BranchOffset |= ~MASK_N_BITS(BranchOffsetBitSize + BranchOffsetBitBase); 213 | } 214 | 215 | void* BranchAddress = reinterpret_cast(InstructionAddress + BranchOffset); 216 | 217 | return JumpWithOptions(destination, BranchAddress, Instruction & POWERPC_BRANCH_LINKED, true, BranchOptions, ConditionRegisterBit, POWERPC_REGISTERINDEX_R0); 218 | } 219 | 220 | size_t DetourHook::RelocateCode(uint32_t* destination, uint32_t* source) 221 | { 222 | uint32_t Instruction = *source; 223 | 224 | switch (Instruction & POWERPC_OPCODE_MASK) 225 | { 226 | case POWERPC_OPCODE_B: // B BL BA BLA 227 | case POWERPC_OPCODE_BC: // BEQ BNE BLT BGE 228 | return RelocateBranch(destination, source); 229 | default: 230 | WriteProcessMemory(sys_process_getpid(), destination, &Instruction, sizeof(Instruction)); 231 | return sizeof(Instruction); 232 | } 233 | } 234 | 235 | void DetourHook::Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride) 236 | { 237 | m_HookAddress = reinterpret_cast(fnAddress); 238 | m_HookTarget = reinterpret_cast(*reinterpret_cast(fnCallback)); 239 | 240 | // Get the size of the hook but don't hook anything yet 241 | size_t HookSize = GetHookSize(m_HookTarget, false, false); 242 | 243 | // Save the original instructions for unhooking later on. 244 | WriteProcessMemory(sys_process_getpid(), m_OriginalInstructions, m_HookAddress, HookSize); 245 | 246 | m_OriginalLength = HookSize; 247 | 248 | // Create trampoline and copy and fix instructions to the trampoline. 249 | m_TrampolineAddress = &s_TrampolineBuffer[s_TrampolineSize]; 250 | 251 | for (size_t i = 0; i < (HookSize / sizeof(uint32_t)); i++) 252 | { 253 | uint32_t* InstructionAddress = reinterpret_cast((uint32_t)m_HookAddress + (i * sizeof(uint32_t))); 254 | 255 | s_TrampolineSize += RelocateCode((uint32_t*)&s_TrampolineBuffer[s_TrampolineSize], InstructionAddress); 256 | } 257 | 258 | // Trampoline branches back to the original function after the branch we used to hook. 259 | void* AfterBranchAddress = reinterpret_cast((uint32_t)m_HookAddress + HookSize); 260 | 261 | s_TrampolineSize += Jump(&s_TrampolineBuffer[s_TrampolineSize], AfterBranchAddress, false, true); 262 | 263 | // Finally write the branch to the function that we are hooking. 264 | Jump(m_HookAddress, m_HookTarget, false, false); 265 | 266 | m_TrampolineOpd[0] = reinterpret_cast(m_TrampolineAddress); 267 | m_TrampolineOpd[1] = tocOverride != 0 ? tocOverride : GetCurrentToc(); 268 | } 269 | 270 | bool DetourHook::UnHook() 271 | { 272 | if (m_HookAddress && m_OriginalLength) 273 | { 274 | WriteProcessMemory(sys_process_getpid(), m_HookAddress, m_OriginalInstructions, m_OriginalLength); 275 | 276 | m_OriginalLength = 0; 277 | m_HookAddress = nullptr; 278 | 279 | return true; 280 | } 281 | 282 | return false; 283 | } 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | ImportExportHook::ImportExportHook(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback) 292 | : DetourHook(), m_LibaryName(libaryName), m_Fnid(fnid) 293 | { 294 | HookByFnid(type, libaryName, fnid, fnCallback); 295 | } 296 | 297 | ImportExportHook::~ImportExportHook() 298 | { 299 | UnHook(); 300 | } 301 | 302 | void ImportExportHook::Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride) 303 | { 304 | // not implemented 305 | } 306 | 307 | bool ImportExportHook::UnHook() 308 | { 309 | // not implemented 310 | return false; 311 | } 312 | 313 | void ImportExportHook::HookByFnid(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback) 314 | { 315 | opd_s* fnOpd = nullptr; 316 | 317 | switch (type) 318 | { 319 | case HookType::Import: 320 | { 321 | fnOpd = FindImportByName(libaryName.c_str(), fnid); 322 | break; 323 | } 324 | case HookType::Export: 325 | { 326 | fnOpd = FindExportByName(libaryName.c_str(), fnid); 327 | break; 328 | } 329 | } 330 | 331 | if (fnOpd == nullptr) 332 | return; 333 | 334 | DetourHook::Hook(fnOpd->sub, fnCallback, fnOpd->toc); 335 | } 336 | 337 | opd_s* ImportExportHook::FindExportByName(const char* module, uint32_t fnid) 338 | { 339 | #ifdef VSH_PROC 340 | uint32_t* segment15 = *reinterpret_cast(0x1008C); // 0x1008C or 0x10094 341 | uint32_t exportAdressTable = segment15[0x984 / sizeof(uint32_t)]; 342 | exportStub_s* exportStub = reinterpret_cast(exportAdressTable); 343 | #else 344 | uint32_t* sysProcessPrxInfo = *reinterpret_cast(0x101DC); // 0x101DC or 0x101E4 345 | uint32_t exportAdressTable = sysProcessPrxInfo[4]; 346 | exportStub_s* exportStub = reinterpret_cast(exportAdressTable); 347 | #endif 348 | 349 | while (exportStub->ssize == 0x1C00) 350 | { 351 | if (!strcmp(module, exportStub->name)) 352 | { 353 | for (int16_t i = 0; i < exportStub->exports; i++) 354 | { 355 | if (exportStub->fnid[i] == fnid) 356 | { 357 | return exportStub->stub[i]; 358 | } 359 | } 360 | } 361 | exportStub++; 362 | } 363 | 364 | return nullptr; 365 | } 366 | 367 | opd_s* ImportExportHook::FindImportByName(const char* module, uint32_t fnid) 368 | { 369 | #ifdef VSH_PROC 370 | uint32_t* segment15 = *reinterpret_cast(0x1008C); // 0x1008C or 0x10094 371 | uint32_t exportAdressTable = segment15[0x984 / sizeof(uint32_t)]; 372 | importStub_s* importStub = reinterpret_cast(exportAdressTable); 373 | #else 374 | uint32_t* sysProcessPrxInfo = *reinterpret_cast(0x101DC); // 0x101DC or 0x101E4 375 | uint32_t importAdressTable = sysProcessPrxInfo[6]; 376 | importStub_s* importStub = reinterpret_cast(importAdressTable); 377 | #endif 378 | 379 | while (importStub->ssize == 0x2C00) 380 | { 381 | if (!strcmp(module, importStub->name)) 382 | { 383 | for (int16_t i = 0; i < importStub->imports; i++) 384 | { 385 | if (importStub->fnid[i] == fnid) 386 | { 387 | return importStub->stub[i]; 388 | } 389 | } 390 | } 391 | importStub++; 392 | } 393 | 394 | return nullptr; 395 | } -------------------------------------------------------------------------------- /DetourHook.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | reference https://gist.github.com/iMoD1998/4aa48d5c990535767a3fc3251efc0348 (10x cleaner version than legacy detour class) 3 | reference https://github.com/skiff/libpsutil/blob/master/libpsutil/system/memory.cpp (tocOverride idea) 4 | reference https://pastebin.com/yezsesij (legacy detour class) 5 | Check https://pastebin.com/VCfMb46E (for GetCurrentToc(), WriteProcessMemory, MARK_AS_EXECUTABLE) 6 | 7 | TODO: 8 | - make derive class for imports and exports 9 | - make derive class for class symbol hooking. e.g: "17CNetworkObjectMgr" on gtav 10 | - make derive class for hooking a single instruction b or bl 11 | - make derive class for hooking a vtable functions or integrate with "class symbol hooking" 12 | - ???? HookByFnid @ImportExportHook "not implemented" seriously whats the point of the derived class. <- that needs to be changed 13 | 14 | */ 15 | #ifndef BITCH 16 | #define BITCH 1 17 | 18 | #include // __ALWAYS_INLINE 19 | #include 20 | #include // sys_process_getpid 21 | 22 | 23 | #define MARK_AS_EXECUTABLE __attribute__((section(".text"))) 24 | #define GetRawDestination(type, destination) (type)*(type*)(destination) 25 | #define PROC_TOC 0x01C85330 26 | 27 | struct opd_s 28 | { 29 | uint32_t sub; 30 | uint32_t toc; 31 | }; 32 | 33 | static bool use_hen_syscalls = false; 34 | 35 | static uint32_t sys_dbg_write_process_memory(uint32_t pid, void* address, const void* data, size_t size) 36 | { 37 | system_call_4(905, (uint64_t)pid, (uint64_t)address, (uint64_t)size, (uint64_t)data); 38 | return_to_user_prog(uint32_t); 39 | } 40 | 41 | static uint32_t sys_hen_write_process_memory(uint32_t pid, void* address, const void* data, size_t size) 42 | { 43 | system_call_6(8, 0x7777, 0x32, (uint64_t)pid, (uint64_t)address, (uint64_t)data, (uint64_t)size); 44 | return_to_user_prog(uint32_t); 45 | } 46 | 47 | static uint32_t WriteProcessMemory(uint32_t pid, void* address, const void* data, size_t size) 48 | { 49 | if (!use_hen_syscalls) 50 | { 51 | uint32_t write = sys_dbg_write_process_memory(pid, address, data, size); 52 | if (write == SUCCEEDED) 53 | { 54 | return write; 55 | } 56 | } 57 | 58 | use_hen_syscalls = true; 59 | return sys_hen_write_process_memory(pid, address, data, size); 60 | } 61 | 62 | static uint32_t GetCurrentToc() 63 | { 64 | uint32_t* entry_point = *reinterpret_cast(0x1001C); // ElfHeader->e_entry 65 | return entry_point[1]; 66 | } 67 | 68 | template 69 | R(*GameCall(std::uint32_t addr, std::uint32_t toc = PROC_TOC))(...) 70 | { 71 | volatile opd_s opd = { addr, toc }; 72 | R(*func)(...) = (R(*)(...))&opd; 73 | return func; 74 | } 75 | 76 | // when calling some game functions with GameCallV2 the game crashes in Minecraft PS3 Edition so use GameCall instead 77 | template 78 | R GameCallV2(std::uint32_t addr, TArgs... args) 79 | { 80 | volatile opd_s opd = { addr, PROC_TOC }; 81 | R(*func)(TArgs...) = (R(*)(TArgs...))&opd; 82 | return func(args...); 83 | } 84 | 85 | template 86 | R(*VmtCall(std::uint32_t vmt, std::uint32_t offset))(...) 87 | { 88 | std::uint32_t* funcAdr = (std::uint32_t*)(*(volatile std::uint32_t*)(vmt + offset)); 89 | volatile opd_s opd = { funcAdr[0], funcAdr[1] }; 90 | R(*func)(...) = (R(*)(...))&opd; 91 | return func; 92 | } 93 | 94 | template 95 | bool write_mem(uint32_t address, var value) 96 | { 97 | return (WriteProcessMemory(sys_process_getpid(), (void*)address, &value, sizeof(var)) == SUCCEEDED); 98 | } 99 | 100 | 101 | static __attribute__((naked)) void _savegpr0_N() 102 | { 103 | // https://pastebin.com/a7Ci8aAi 104 | __asm 105 | ( 106 | "std %r14, -144(%r1);" // _savegpr0_14 107 | "std %r15, -136(%r1);" // _savegpr0_15 108 | "std %r16, -128(%r1);" // _savegpr0_16 109 | "std %r17, -120(%r1);" // _savegpr0_17 110 | "std %r18, -112(%r1);" // _savegpr0_18 111 | "std %r19, -104(%r1);" // _savegpr0_19 112 | "std %r20, -96(%r1);" // _savegpr0_20 113 | "std %r21, -88(%r1);" // _savegpr0_21 114 | "std %r22, -80(%r1);" // _savegpr0_22 115 | "std %r23, -72(%r1);" // _savegpr0_23 116 | "std %r24, -64(%r1);" // _savegpr0_24 117 | "std %r25, -56(%r1);" // _savegpr0_25 118 | "std %r26, -48(%r1);" // _savegpr0_26 119 | "std %r27, -40(%r1);" // _savegpr0_27 120 | "std %r28, -32(%r1);" // _savegpr0_28 121 | "std %r29, -24(%r1);" // _savegpr0_29 122 | "std %r30, -16(%r1);" // _savegpr0_30 123 | "std %r31, -8(%r1);" // _savegpr0_31 124 | "std %r0, 16(%r1);" 125 | "blr;" 126 | ); 127 | } 128 | 129 | static uint32_t RelinkGPLR(uint32_t SFSOffset, uint32_t* SaveStubAddress, uint32_t* OriginalAddress) 130 | { 131 | uint32_t Instruction = 0, Replacing; 132 | uint32_t* Saver = *(uint32_t**)_savegpr0_N; 133 | 134 | if (SFSOffset & 0x2000000) 135 | SFSOffset |= 0xFC000000; 136 | 137 | Replacing = OriginalAddress[SFSOffset / 4]; 138 | 139 | for (int i = 0; i < 20; i++) 140 | { 141 | if (Replacing == Saver[i]) 142 | { 143 | int NewOffset = (int)&Saver[i] - (int)SaveStubAddress; 144 | Instruction = 0x48000001 | (NewOffset & 0x3FFFFFC); 145 | } 146 | } 147 | return Instruction; 148 | } 149 | 150 | static void PatchInJump(uint32_t* address, uint32_t destination, bool linked) 151 | { 152 | uint32_t inst_lis = 0x3D600000 + ((destination >> 16) & 0xFFFF); // lis %r11, dest>>16 153 | write_mem((uint32_t)&address[0], inst_lis); 154 | if (destination & 0x8000) // if bit 16 is 1 155 | write_mem((uint32_t)&address[0], inst_lis += 1); 156 | 157 | write_mem((uint32_t)&address[1], 0x396B0000 + (destination & 0xFFFF)); // addi %r11, %r11, dest&OxFFFF 158 | write_mem((uint32_t)&address[2], 0x7D6903A6); // mtctr %r11 159 | write_mem((uint32_t)&address[3], 0x4E800420); // bctr 160 | if (linked) 161 | write_mem((uint32_t)&address[3], 0x4E800421); // bctrl 162 | 163 | __dcbst(address); 164 | __sync(); 165 | __isync(); 166 | } 167 | 168 | static void HookFunctionStart(uint32_t* address, uint32_t destination, uint32_t* original) 169 | { 170 | uint32_t addressRelocation = (uint32_t)(&address[4]); // Replacing 4 instructions with a jump, this is the stub return address 171 | 172 | // build the stub 173 | // make a jump to go to the original function start+4 instructions 174 | uint32_t inst_lis = 0x3D600000 + ((addressRelocation >> 16) & 0xFFFF); // lis r11, 0 | Load Immediate Shifted 175 | write_mem((uint32_t)&original[0], inst_lis); 176 | 177 | if (addressRelocation & 0x8000) // If bit 16 is 1 178 | write_mem((uint32_t)&original[0], inst_lis += 1); // lis r11, 0 | Load Immediate Shifted 179 | 180 | write_mem((uint32_t)&original[1], 0x396B0000 + (addressRelocation & 0xFFFF)); // addi r11, r11, (value of AddressRelocation & 0xFFFF) | Add Immediate 181 | write_mem((uint32_t)&original[2], 0x7D6903A6); // mtspr CTR, r11 | Move to Special-Purpose Register CTR 182 | 183 | // Instructions [3] through [6] are replaced with the original instructions from the function hook 184 | // Copy original instructions over, relink stack frame saves to local ones 185 | for (int i = 0; i < 4; i++) 186 | { 187 | if ((address[i] & 0xF8000000) == 0x48000000) // branch with link 188 | { 189 | write_mem((uint32_t)&original[i + 3], RelinkGPLR((address[i] & ~0x48000003), &original[i + 3], &address[i])); 190 | } 191 | else 192 | { 193 | write_mem((uint32_t)&original[i + 3], address[i]); 194 | } 195 | } 196 | write_mem((uint32_t)&original[7], 0x4E800420); // bctr | Branch unconditionally 197 | 198 | __dcbst(original); // Data Cache Block Store | Allows a program to copy the contents of a modified block to main memory. 199 | __sync(); // Synchronize | Ensure the dcbst instruction has completed. 200 | __isync(); // Instruction Synchronize | Refetches any instructions that might have been fetched prior to this instruction. 201 | 202 | PatchInJump(address, *(uint32_t*)destination, false); // Redirect Function to ours 203 | 204 | /* 205 | * So in the end, this will produce: 206 | * 207 | * lis r11, ((AddressRelocation >> 16) & 0xFFFF [+ 1]) 208 | * addi r11, r11, (AddressRelocation & 0xFFFF) 209 | * mtspr CTR, r11 210 | * branch (GPR) 211 | * dcbst (SaveStub) 212 | * sync 213 | */ 214 | } 215 | 216 | #define MAX_HOOKFUNCTIONS_ASM_SIZE 300 217 | static MARK_AS_EXECUTABLE uint8_t g_hookFunctionsAsm[300]; 218 | static int g_hookFunctionsAsmCount = 0; 219 | 220 | static bool HookFunctionStartAuto(uint32_t* address, uint32_t destination, uint32_t** original) 221 | { 222 | // check for buffer overflow 223 | if (g_hookFunctionsAsmCount > (MAX_HOOKFUNCTIONS_ASM_SIZE - 2)) 224 | return false; 225 | 226 | uint32_t* stubAddr = (uint32_t*)&g_hookFunctionsAsm[g_hookFunctionsAsmCount]; 227 | 228 | HookFunctionStart(address, destination, stubAddr); 229 | 230 | // write an opd into memory 231 | write_mem((uint32_t)&stubAddr[8], ((uint64_t)stubAddr << 32) | GetCurrentToc()); // (uint32_t)__builtin_get_toc() 232 | *original = &stubAddr[8]; 233 | 234 | // Increment asm byte count 235 | // 0x20 bytes from HookFunctionStart 236 | // 0x8 bytes for opd 237 | g_hookFunctionsAsmCount += 0x28; 238 | 239 | return true; 240 | } 241 | 242 | static void PatchInBranch(uint32_t* address, uint32_t destination, bool linked) 243 | { 244 | destination = GetRawDestination(uint32_t, destination); 245 | uint32_t inst_branch = 0x48000000 + ((destination - (uint32_t)address) & 0x3FFFFFF); // b Destination 246 | write_mem((uint32_t)&address[0], inst_branch); 247 | 248 | if (linked) 249 | write_mem((uint32_t)&address[0], inst_branch += 1); // bl Destination 250 | } 251 | 252 | static void PatchInVmt(uint32_t* address, uint32_t destination) 253 | { 254 | write_mem((uint32_t)&address[0], GetRawDestination(uint64_t, destination)); // uint64_t so it includes the toc 255 | } 256 | 257 | 258 | 259 | #include 260 | 261 | //struct opd_s 262 | //{ 263 | // uint32_t sub; 264 | // uint32_t toc; 265 | //}; 266 | 267 | struct importStub_s 268 | { 269 | int16_t ssize; 270 | int16_t header1; 271 | int16_t header2; 272 | int16_t imports; 273 | int32_t zero1; 274 | int32_t zero2; 275 | const char* name; 276 | uint32_t* fnid; 277 | opd_s** stub; 278 | int32_t zero3; 279 | int32_t zero4; 280 | int32_t zero5; 281 | int32_t zero6; 282 | }; 283 | 284 | struct exportStub_s 285 | { 286 | int16_t ssize; 287 | int16_t header1; 288 | int16_t header2; 289 | int16_t exports; // number of exports 290 | int32_t zero1; 291 | int32_t zero2; 292 | const char* name; 293 | uint32_t* fnid; 294 | opd_s** stub; 295 | }; 296 | 297 | #define MARK_AS_EXECUTABLE __attribute__((section(".text"))) 298 | 299 | class DetourHook 300 | { 301 | public: 302 | DetourHook(); 303 | DetourHook(uintptr_t fnAddress, uintptr_t fnCallback); 304 | DetourHook(DetourHook const&) = delete; 305 | DetourHook(DetourHook&&) = delete; 306 | DetourHook& operator=(DetourHook const&) = delete; 307 | DetourHook& operator=(DetourHook&&) = delete; 308 | virtual ~DetourHook(); 309 | 310 | virtual void Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride = 0); 311 | virtual bool UnHook(); 312 | 313 | // also works 314 | /*template 315 | T GetOriginal() const 316 | { 317 | return T(m_TrampolineOpd); 318 | }*/ 319 | 320 | template 321 | R GetOriginal(TArgs... args) 322 | { 323 | R(*original)(TArgs...) = (R(*)(TArgs...))m_TrampolineOpd; 324 | return original(args...); 325 | } 326 | 327 | 328 | private: 329 | /*** 330 | * Writes an unconditional branch to the destination address that will branch to the target address. 331 | * @param destination Where the branch will be written to. 332 | * @param branchTarget The address the branch will jump to. 333 | * @param linked Branch is a call or a jump? aka bl or b 334 | * @param preserveRegister Preserve the register clobbered after loading the branch address. 335 | * @returns size of relocating the instruction in bytes 336 | */ 337 | size_t Jump(void* destination, const void* branchTarget, bool linked, bool preserveRegister); 338 | 339 | /*** 340 | * Writes both conditional and unconditional branches using the count register to the destination address that will branch to the target address. 341 | * @param destination Where the branch will be written to. 342 | * @param branchTarget The address the branch will jump to. 343 | * @param linked Branch is a call or a jump? aka bl or b 344 | * @param preserveRegister Preserve the register clobbered after loading the branch address. 345 | * @param branchOptions Options for determining when a branch to be followed. 346 | * @param conditionRegisterBit The bit of the condition register to compare. 347 | * @param registerIndex Register to use when loading the destination address into the count register. 348 | * @returns size of relocating the instruction in bytes 349 | */ 350 | size_t JumpWithOptions(void* destination, const void* branchTarget, bool linked, bool preserveRegister, 351 | uint32_t branchOptions, uint8_t conditionRegisterBit, uint8_t registerIndex); 352 | 353 | /*** 354 | * Copies and fixes relative branch instructions to a new location. 355 | * @param destination Where to write the new branch. 356 | * @param source Address to the instruction that is being relocated. 357 | * @returns size of relocating the instruction in bytes 358 | */ 359 | size_t RelocateBranch(uint32_t* destination, uint32_t* source); 360 | 361 | /*** 362 | * Copies an instruction enusuring things such as PC relative offsets are fixed. 363 | * @param destination Where to write the new instruction(s). 364 | * @param source Address to the instruction that is being copied. 365 | * @returns size of relocating the instruction in bytes 366 | */ 367 | size_t RelocateCode(uint32_t* destination, uint32_t* source); 368 | 369 | /*** 370 | * Get's size of method hook in bytes 371 | * @param branchTarget The address the branch will jump to. 372 | * @param linked Branch is a call or a jump? aka bl or b 373 | * @param preserveRegister Preserve the register clobbered after loading the branch address. 374 | * @returns size of hook in bytes 375 | */ 376 | size_t GetHookSize(const void* branchTarget, bool linked, bool preserveRegister); 377 | 378 | protected: 379 | const void* m_HookTarget; // The funtion we are pointing the hook to. 380 | void* m_HookAddress; // The function we are hooking. 381 | uint8_t* m_TrampolineAddress; // Pointer to the trampoline for this detour. 382 | uint32_t m_TrampolineOpd[2]; // opd_s of the trampoline for this detour. 383 | uint8_t m_OriginalInstructions[30]; // Any bytes overwritten by the hook. 384 | size_t m_OriginalLength; // The amount of bytes overwritten by the hook. 385 | 386 | // Shared 387 | MARK_AS_EXECUTABLE static uint8_t s_TrampolineBuffer[1024]; 388 | static size_t s_TrampolineSize; 389 | }; 390 | 391 | // list of fnids https://github.com/aerosoul94/ida_gel/blob/master/src/ps3/ps3.xml 392 | class ImportExportHook : public DetourHook 393 | { 394 | public: 395 | enum HookType { Import = 0, Export = 1 }; 396 | public: 397 | ImportExportHook(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback); 398 | virtual ~ImportExportHook(); 399 | 400 | virtual void Hook(uintptr_t fnAddress, uintptr_t fnCallback, uintptr_t tocOverride = 0) override; 401 | virtual bool UnHook() override; 402 | 403 | static opd_s* FindExportByName(const char* module, uint32_t fnid); 404 | static opd_s* FindImportByName(const char* module, uint32_t fnid); 405 | 406 | private: 407 | void HookByFnid(HookType type, const std::string& libaryName, uint32_t fnid, uintptr_t fnCallback); 408 | 409 | private: 410 | std::string m_LibaryName; 411 | uint32_t m_Fnid; 412 | }; 413 | #endif -------------------------------------------------------------------------------- /PluginCallbacks.h: -------------------------------------------------------------------------------- 1 | #ifndef PluginCallbacks 2 | #define PluginCallbacks 3 | 4 | #include "Typedefs.h" 5 | 6 | ; class InterfaceReg 7 | { 8 | public: 9 | InterfaceReg(InstantiateInterfaceFn fn, const char *pName); 10 | 11 | public: 12 | InstantiateInterfaceFn m_CreateFn; 13 | const char *m_pName; 14 | 15 | InterfaceReg *m_pNext; // For the global list. 16 | static InterfaceReg *s_pInterfaceRegs; 17 | }; 18 | 19 | InterfaceReg *InterfaceReg::s_pInterfaceRegs = 0; 20 | 21 | InterfaceReg::InterfaceReg(InstantiateInterfaceFn fn, const char *pName) : 22 | m_pName(pName) 23 | { 24 | m_CreateFn = fn; 25 | m_pNext = s_pInterfaceRegs; 26 | s_pInterfaceRegs = this; 27 | } 28 | 29 | class IGameEventListener 30 | { 31 | public: 32 | virtual ~IGameEventListener(void) {}; 33 | 34 | virtual void FireGameEvent(KeyValues * event) = 0; 35 | }; 36 | 37 | typedef enum 38 | { 39 | PLUGIN_CONTINUE = 0, // keep going 40 | PLUGIN_OVERRIDE, // run the game dll function but use our return value instead 41 | PLUGIN_STOP, // don't run the game dll function at all 42 | } PLUGIN_RESULT; 43 | 44 | class IServerPluginCallbacks 45 | { 46 | public: 47 | virtual bool Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory) = 0; 48 | 49 | virtual void Unload(void) = 0; 50 | 51 | virtual void Pause(void) = 0; 52 | 53 | virtual void UnPause(void) = 0; 54 | 55 | virtual const char *GetPluginDescription(void) = 0; 56 | 57 | virtual void LevelInit(char const *pMapName) = 0; 58 | 59 | virtual void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) = 0; 60 | 61 | virtual void GameFrame(bool simulating) = 0; 62 | 63 | virtual void LevelShutdown(void) = 0; 64 | 65 | virtual void ClientActive(edict_t *pEntity) = 0; 66 | 67 | virtual void ClientDisconnect(edict_t *pEntity) = 0; 68 | 69 | virtual void ClientPutInServer(edict_t *pEntity, char const *playername) = 0; 70 | 71 | virtual void SetCommandClient(int index) = 0; 72 | 73 | virtual void ClientSettingsChanged(edict_t *pEdict) = 0; 74 | 75 | virtual PLUGIN_RESULT ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) = 0; 76 | 77 | virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity, const void* &args) = 0; 78 | 79 | virtual PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID) = 0; 80 | 81 | virtual void OnQueryCvarValueFinished(void* iCookie, edict_t *pPlayerEntity, void* eStatus, const char *pCvarName, const char *pCvarValue) = 0; 82 | 83 | virtual void OnEdictAllocated(edict_t *edict) = 0; 84 | virtual void OnEdictFreed(const edict_t *edict) = 0; 85 | }; 86 | 87 | 88 | typedef void* KeyValues; 89 | 90 | 91 | 92 | class CEmptyServerPlugin : public IServerPluginCallbacks, public IGameEventListener 93 | { 94 | public: 95 | CEmptyServerPlugin(); 96 | ~CEmptyServerPlugin(); 97 | 98 | virtual bool Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory); 99 | virtual void Unload(void); 100 | virtual void Pause(void); 101 | virtual void UnPause(void); 102 | virtual const char *GetPluginDescription(void); 103 | virtual void LevelInit(char const *pMapName); 104 | virtual void ServerActivate(edict_t *pEdictList, int edictCount, int clientMax); 105 | virtual void GameFrame(bool simulating); 106 | virtual void LevelShutdown(void); 107 | virtual void ClientActive(edict_t *pEntity); 108 | virtual void ClientDisconnect(edict_t *pEntity); 109 | virtual void ClientPutInServer(edict_t *pEntity, char const *playername); 110 | virtual void SetCommandClient(int index); 111 | virtual void ClientSettingsChanged(edict_t *pEdict); 112 | virtual PLUGIN_RESULT ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen); 113 | virtual PLUGIN_RESULT ClientCommand(edict_t *pEntity, const void* &args); 114 | virtual PLUGIN_RESULT NetworkIDValidated(const char *pszUserName, const char *pszNetworkID); 115 | virtual void OnQueryCvarValueFinished(void* iCookie, edict_t *pPlayerEntity, void* eStatus, const char *pCvarName, const char *pCvarValue); 116 | virtual void OnEdictAllocated(edict_t *edict); 117 | virtual void OnEdictFreed(const edict_t *edict); 118 | 119 | virtual void FireGameEvent(KeyValues * event); 120 | }; 121 | 122 | 123 | #define EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(className, interfaceNamespace, interfaceName, versionName, globalVarName) \ 124 | static void* __Create##className##interfaceName##_interface() { return static_cast(&globalVarName); } \ 125 | static InterfaceReg __g_Create##className##interfaceName##_reg(__Create##className##interfaceName##_interface, versionName); 126 | 127 | #define EXPOSE_SINGLE_INTERFACE_GLOBALVAR(className, interfaceName, versionName, globalVarName) \ 128 | EXPOSE_SINGLE_INTERFACE_GLOBALVAR_WITH_NAMESPACE(className, , interfaceName, versionName, globalVarName) 129 | 130 | #define INTERFACEVERSION_ISERVERPLUGINCALLBACKS "ISERVERPLUGINCALLBACKS003" 131 | 132 | #define PS3CLVERMAJOR 0 133 | #define PS3CLVERMINOR 1 134 | 135 | #ifndef PS3CLVERMAJOR 136 | #define PS3CLVERMAJOR ((APPCHANGELISTVERSION / 256) % 256) 137 | #define PS3CLVERMINOR (APPCHANGELISTVERSION % 256) 138 | #endif 139 | 140 | #define PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER(ps3modulename) SYS_MODULE_INFO( ps3modulename##_rel, 0, PS3CLVERMAJOR, PS3CLVERMINOR) 141 | 142 | #define PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER(ps3modulename) _##ps3modulename##_ps3_prx_entry 143 | 144 | #define PS3_PRX_APPSYSTEM_MODULE( ps3modulename ) \ 145 | \ 146 | PS3_PRX_SYS_MODULE_INFO_FULLMACROREPLACEMENTHELPER(ps3modulename); \ 147 | SYS_MODULE_START(PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER(ps3modulename)); \ 148 | \ 149 | extern "C" int PS3_PRX_SYS_MODULE_START_NAME_FULLMACROREPLACEMENTHELPER(ps3modulename)(unsigned int args, void *pArg) \ 150 | { \ 151 | PS3_LoadAppSystemInterface_Parameters_t *pParams = reinterpret_cast< PS3_LoadAppSystemInterface_Parameters_t * >(pArg); \ 152 | pParams->pfnCreateInterface = CreateInterface; \ 153 | return SYS_PRX_RESIDENT; \ 154 | } \ 155 | 156 | #endif -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PS3Plugins 2 | Compile with 3 | * General->Platform Toolset: `PPU SNC` 4 | * Linker->General->Additional Dependencies: `libsn.a;libm.a;libio_stub.a;libfs_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libdbg_libio_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libc_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libm.a;$(SCE_PS3_ROOT)\target\ppu\lib\libsysutil_np_trophy_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libio_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libsysutil_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libsysmodule_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libsyscall.a;$(SCE_PS3_ROOT)\target\ppu\lib\libgcm_sys_stub.a;$(SCE_PS3_ROOT)\host-win32\spu\lib\gcc\spu-lv2\4.1.1\libgcc.a;$(SCE_PS3_ROOT)\target\ppu\lib\libpadfilter.a;$(SCE_PS3_ROOT)\target\ppu\lib\libstdc++.a;C:\cell\host-win32\ppu\lib\gcc\ppu-lv2\4.1.1\libsupc++.a;$(SCE_PS3_ROOT)\target\ppu\lib\libsysutil_oskdialog_ext_stub.a;$(SCE_PS3_ROOT)\target\ppu\lib\libnet_stub.a` 5 | * C/C++->Language->C++ Language Standard: `Use C++11 (-Xstd=cpp11)` 6 | -------------------------------------------------------------------------------- /Typedefs.h: -------------------------------------------------------------------------------- 1 | #ifndef Typedefs 2 | #define Typedefs 3 | 4 | typedef void* (*CreateInterfaceFn)(const char *pName, int *pReturnCode); 5 | typedef void* (*InstantiateInterfaceFn)(); 6 | typedef void* edict_t; 7 | typedef void* KeyValues; 8 | typedef unsigned char byte; 9 | struct Vector 10 | { 11 | float x, y, z; 12 | }; 13 | #endif -------------------------------------------------------------------------------- /prx.cpp: -------------------------------------------------------------------------------- 1 | #include "Client.h" 2 | #include "PluginCallbacks.h" 3 | #include "Typedefs.h" 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "DetourHook.hpp" 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | IVEngineClient* client; 26 | uint64_t enginebase; 27 | 28 | #define ioctl( s, cmd, pVal ) setsockopt( s, SOL_SOCKET, cmd, pVal, sizeof( *( pVal ) ) ) 29 | 30 | SYS_LIB_DECLARE_WITH_STUB( LIBNAME, SYS_LIB_AUTO_EXPORT, STUBNAME ); 31 | SYS_LIB_EXPORT( _PortalPlugin_export_function, LIBNAME ); 32 | 33 | extern "C" int _PortalPlugin_export_function(void) 34 | { 35 | return CELL_OK; 36 | } 37 | 38 | struct PS3_PrxLoadParametersBase_t 39 | { 40 | int32_t cbSize; 41 | sys_prx_id_t sysPrxId; 42 | uint64_t uiFlags; 43 | uint64_t reserved[7]; 44 | }; 45 | 46 | struct PS3_LoadAppSystemInterface_Parameters_t : public PS3_PrxLoadParametersBase_t 47 | { 48 | typedef void* (*PFNCREATEINTERFACE)(const char *pName, int *pReturnCode); 49 | 50 | PFNCREATEINTERFACE pfnCreateInterface; // [OUT] [ app system module create interface entry point ] 51 | uint64_t reserved[8]; 52 | }; 53 | 54 | CEmptyServerPlugin* g_EmptyServerPlugin; 55 | 56 | void* CreateInterface(const char *pName, int *pReturnCode) 57 | { 58 | g_EmptyServerPlugin = new CEmptyServerPlugin(); 59 | return g_EmptyServerPlugin; 60 | } 61 | 62 | EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CEmptyServerPlugin, IServerPluginCallbacks, INTERFACEVERSION_ISERVERPLUGINCALLBACKS, (*g_EmptyServerPlugin)); 63 | 64 | PS3_PRX_APPSYSTEM_MODULE(PortalPlugin); 65 | 66 | CEmptyServerPlugin::CEmptyServerPlugin() 67 | { 68 | 69 | } 70 | 71 | CEmptyServerPlugin::~CEmptyServerPlugin() 72 | { 73 | } 74 | 75 | unsigned short NET_HostToNetShort(unsigned short us_in) 76 | { 77 | return htons(us_in); 78 | } 79 | 80 | 81 | bool NET_StringToSockaddr(const char *s, struct sockaddr *sadr) 82 | { 83 | char *colon; 84 | char copy[128]; 85 | memset(sadr, 0, sizeof(*sadr)); 86 | ((struct sockaddr_in *)sadr)->sin_family = AF_INET; 87 | ((struct sockaddr_in *)sadr)->sin_port = 0; 88 | 89 | strncpy(copy, s, sizeof(copy)); 90 | // strip off a trailing :port if present 91 | for (colon = copy; *colon; colon++) 92 | { 93 | if (*colon == ':') 94 | { 95 | *colon = 0; 96 | ((struct sockaddr_in *)sadr)->sin_port = NET_HostToNetShort((short)atoi(colon + 1)); 97 | } 98 | } 99 | 100 | if (copy[0] >= '0' && copy[0] <= '9' && strstr(copy, ".")) 101 | { 102 | *(int *)&((struct sockaddr_in *)sadr)->sin_addr = inet_addr(copy); 103 | } 104 | else 105 | { 106 | 107 | struct hostent *h; 108 | if ((h = gethostbyname(copy)) == NULL) 109 | return false; 110 | *(int *)&((struct sockaddr_in *)sadr)->sin_addr = *(int *)h->h_addr_list[0]; 111 | } 112 | 113 | return true; 114 | } 115 | 116 | int NET_OpenSocket(const char *net_interface, int& port, int protocol) 117 | { 118 | int newsocket = -1; 119 | struct sockaddr_in address; 120 | if (protocol == IPPROTO_TCP) 121 | { 122 | newsocket = socket(AF_INET, SOCK_STREAM, 6); 123 | } 124 | else 125 | { 126 | newsocket = socket(AF_INET, SOCK_DGRAM, protocol); 127 | } 128 | unsigned int opt = 1; 129 | int ret = ioctl(newsocket, SO_NBIO, (unsigned long*)&opt); 130 | if (protocol == IPPROTO_TCP) 131 | { 132 | return newsocket; 133 | } 134 | 135 | ret = setsockopt(newsocket, SOL_SOCKET, SO_BROADCAST, (char*)&opt, sizeof(opt)); 136 | if (!net_interface || !net_interface[0] || !strcmp(net_interface, "localhost")) 137 | { 138 | address.sin_addr.s_addr = INADDR_ANY; 139 | } 140 | else 141 | { 142 | NET_StringToSockaddr(net_interface, (struct sockaddr *)&address); 143 | } 144 | 145 | address.sin_port = NET_HostToNetShort((short)(port)); 146 | address.sin_family = AF_INET; 147 | 148 | char dof[128]; 149 | unsigned int writelen; 150 | //sys_tty_write(0, dof, snprintf(dof, 128, "NET_OpenSocket %x %i\n", address.sin_addr.s_addr, address.sin_port), &writelen); 151 | ret = bind(newsocket, (struct sockaddr *)&address, sizeof(address)); 152 | 153 | return newsocket; 154 | } 155 | 156 | typedef enum 157 | { 158 | NA_NULL = 0, 159 | NA_LOOPBACK, 160 | NA_IP, 161 | NA_BROADCAST, 162 | } netadrtype_t; 163 | 164 | typedef struct netadr_s{ 165 | public: 166 | bool SetFromSockadr(const struct sockaddr *s); 167 | unsigned short type; 168 | unsigned short port; 169 | unsigned char ip[4]; 170 | } netadr_t; 171 | 172 | 173 | bool netadr_t::SetFromSockadr(const struct sockaddr * s) 174 | { 175 | type = NA_IP; 176 | *(int *)&ip = ((struct sockaddr_in *)s)->sin_addr.s_addr; 177 | port = ((struct sockaddr_in *)s)->sin_port; 178 | return true; 179 | } 180 | 181 | struct ns_address 182 | { 183 | netadr_s m_adr; 184 | uint64_t m_steamID; 185 | uint32_t m_AddrType; 186 | 187 | bool SetFromSockadr(const struct sockaddr * s) 188 | { 189 | m_AddrType = 0; 190 | m_steamID = 0; 191 | return m_adr.SetFromSockadr(s); 192 | } 193 | }; 194 | 195 | DetourHook* OpenSocketInternalHk; 196 | int funnysocket[4] = { -1, -1, -1, -1 }; 197 | 198 | typedef void(*LOL)(unsigned long long thisptr, int s, int nModule, int nSetPort, int nDefaultPort, const char *pName, int nProtocol, bool bTryAny); 199 | 200 | static void OpenSocketInternal(int nModule, int nSetPort, int nDefaultPort, const char *pName, int nProtocol, bool bTryAny) 201 | { 202 | char dof[128]; 203 | unsigned int writelen; 204 | //sys_tty_write(0, dof, snprintf(dof, 128, "OpenSocketInternal %i %i %i %s %i\n", nModule, nSetPort, nDefaultPort, pName, nProtocol), &writelen); 205 | int iVar8 = nDefaultPort; 206 | if (nSetPort != 0) { 207 | iVar8 = nSetPort; 208 | } 209 | int* piVar7; 210 | int iVar2 = nModule * 0x4; 211 | int* net_sockets = *(int**)((*(byte*)(enginebase + 0x01ffe73)) * 0x10000 - 0x59e0); 212 | if (nProtocol == 6) 213 | { 214 | piVar7 = (int*)(net_sockets + iVar2 + 0xc); 215 | } 216 | else 217 | { 218 | piVar7 = (int*)(net_sockets + iVar2 + 8); 219 | } 220 | *piVar7 = NET_OpenSocket("localhost", iVar8, nProtocol); 221 | funnysocket[nModule] = *piVar7; 222 | *(int*)(net_sockets + iVar2) = iVar8; 223 | //sys_tty_write(0, dof, snprintf(dof, 128, "addr: %x\n", net_sockets + iVar2), &writelen); 224 | /*(**(LOL**)(**(int **)(enginebase+0x0055f84c) + 0xc)) 225 | ((enginebase + 0x0055f84c), *piVar7, nModule, nSetPort, nDefaultPort, pName, 226 | nProtocol, bTryAny);*/ 227 | } 228 | DetourHook* CSteamSocketMgrSendtoHk; 229 | 230 | 231 | 232 | int CSteamSocketMgrSendto(unsigned long long thisptr, int s, const char * buf, int len, int flags, const ns_address &to) 233 | { 234 | struct sockaddr_in c; 235 | memset(&c, 0, sizeof(sockaddr_in)); 236 | c.sin_family = AF_INET; 237 | //c.sin_port = to.m_adr.port; 238 | if (s == 0) 239 | { 240 | c.sin_port = 27006; 241 | } 242 | else 243 | { 244 | c.sin_port = 27016; 245 | } 246 | c.sin_addr = *(in_addr *)&to.m_adr.ip; 247 | //c.sin_addr = ()0xc0a8011e; 248 | char dof[128]; 249 | unsigned int writelen; 250 | 251 | //int ret = sendto(s, (void*)buf, len, flags, (sockaddr*)&c, sizeof(c)); 252 | if (funnysocket[s] == -1) 253 | { 254 | funnysocket[s] = socket(AF_INET, SOCK_DGRAM, 17); 255 | bind(funnysocket[s], (struct sockaddr *)&c, sizeof(c)); 256 | unsigned int opt = 1; 257 | ioctl(funnysocket[s], SO_NBIO, (unsigned long*)&opt); 258 | } 259 | c.sin_port = to.m_adr.port; 260 | 261 | //sys_tty_write(0, dof, snprintf(dof, 128, "sendto: %i %i %i %x %x %i\n", funnysocket[s], s, len, flags, c.sin_addr.s_addr, c.sin_port), &writelen); 262 | return sendto(funnysocket[s], (void*)buf, len, flags, (sockaddr*)&c, sizeof(c)); 263 | } 264 | 265 | 266 | DetourHook* CSteamSocketMgrRecvfromHk; 267 | int CSteamSocketMgrRecvfrom(unsigned long long thisptr, int s, char * buf, int len, int flags, ns_address *from, ns_address *otherfrom) 268 | { 269 | if (funnysocket[s] == -1) 270 | { 271 | struct sockaddr_in c; 272 | memset(&c, 0, sizeof(sockaddr_in)); 273 | c.sin_family = AF_INET; 274 | //c.sin_port = to.m_adr.port; 275 | if (s == 2) 276 | { 277 | c.sin_port = 27005; 278 | } 279 | else 280 | { 281 | c.sin_port = 27015; 282 | } 283 | c.sin_addr.s_addr = INADDR_ANY; 284 | funnysocket[s] = socket(AF_INET, SOCK_DGRAM, 17); 285 | bind(funnysocket[s], (struct sockaddr *)&c, sizeof(c)); 286 | unsigned int opt = 1; 287 | ioctl(funnysocket[s], SO_NBIO, (unsigned long*)&opt); 288 | } 289 | char dof[128]; 290 | unsigned int writelen; 291 | //sys_tty_write(0, dof, snprintf(dof, 128, "recv: %i %i %i %x %x %i\n", funnysocket[s], s, len, flags, from->m_adr.ip, from->m_adr.port), &writelen); 292 | sockaddr sadrfrom; 293 | socklen_t fromlen = sizeof(sadrfrom); 294 | int iret = recvfrom(funnysocket[s], buf, len, flags, &sadrfrom, &fromlen); 295 | if (iret > 0) 296 | { 297 | //sys_tty_write(0, dof, snprintf(dof, 128, "from: %x | otherfrom: %x\n", from, otherfrom), &writelen); 298 | from->SetFromSockadr(&sadrfrom); 299 | return iret; 300 | } 301 | return 0; 302 | } 303 | 304 | DetourHook* Net_SendStreamHk; 305 | int NET_SendStream(int nSock, const char * buf, int len, int flags) 306 | { 307 | return send(nSock, buf, len, flags); 308 | } 309 | 310 | 311 | DetourHook* NET_CloseSocketHk; 312 | void NET_CloseSocket(int hSocket, int sock) 313 | { 314 | int ret = close(hSocket); 315 | } 316 | 317 | 318 | int getkbLen(char* str) 319 | { 320 | int nullCount = 0; 321 | int i = 0; //num of chars.. 322 | for (i = 0; i < 512; i++) 323 | { 324 | if (nullCount == 2) { break; } 325 | if (*(str + i) == 0x00) { nullCount++; } 326 | else { nullCount = 0; } 327 | } 328 | return i; 329 | } 330 | void makekbStr(char* str, char* dest, int len) 331 | { 332 | int nulls = 0; 333 | for (int i = 0; i < len; i++) 334 | { 335 | if (*(str + i) == 0x00) { nulls++; } 336 | else { *(dest + i - nulls) = *(str + i); } 337 | } 338 | *(dest + len + 1 - nulls) = 0x00; //make sure its nulled... 339 | } 340 | bool pressedtriangle = false; 341 | bool keyboardon = false; 342 | 343 | void sysutil_callback(uint64_t status, uint64_t param, void *userdata) 344 | { 345 | unsigned int writelen; 346 | if (status != CELL_SYSUTIL_OSKDIALOG_FINISHED) 347 | { 348 | if (status == CELL_SYSUTIL_OSKDIALOG_UNLOADED) 349 | { 350 | keyboardon = false; 351 | } 352 | return; 353 | } 354 | CellOskDialogCallbackReturnParam OutputInfo; 355 | OutputInfo.result = CELL_OSKDIALOG_INPUT_FIELD_RESULT_OK; 356 | OutputInfo.numCharsResultString = CELL_OSKDIALOG_STRING_SIZE; 357 | uint16_t Result_Text_Buffer[CELL_OSKDIALOG_STRING_SIZE + 1]; 358 | OutputInfo.pResultString = Result_Text_Buffer; 359 | cellOskDialogUnloadAsync(&OutputInfo); 360 | int len = getkbLen((char*)(Result_Text_Buffer)); 361 | char command[CELL_OSKDIALOG_STRING_SIZE + 1] = { 0 }; 362 | makekbStr((char*)(Result_Text_Buffer), command, len); 363 | client->ClientCmd(command); 364 | keyboardon = false; 365 | } 366 | 367 | void ConsoleKeyboardInputThread(uint64_t IDK) 368 | { 369 | //sys_timer_usleep(3000000); 370 | //client->ClientCmd("killserver"); 371 | for (;;) 372 | { 373 | sys_timer_usleep(90000); 374 | if (!keyboardon) 375 | { 376 | CellPadData data; 377 | cellPadGetData(0, &data); 378 | if (!pressedtriangle && (data.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_TRIANGLE)) 379 | { 380 | int ret; 381 | CellOskDialogInputFieldInfo inputFieldInfo; 382 | inputFieldInfo.message = (uint16_t*)"\x00e\x00n\x00t\x00e\x00r\x00 \x00c\x00o\x00m\x00m\x00a\x00n\x00d\x00\x00"; 383 | inputFieldInfo.init_text = (uint16_t*)""; 384 | inputFieldInfo.limit_length = CELL_OSKDIALOG_STRING_SIZE; 385 | 386 | ret = cellOskDialogSetKeyLayoutOption(CELL_OSKDIALOG_10KEY_PANEL | CELL_OSKDIALOG_FULLKEY_PANEL); 387 | 388 | 389 | CellOskDialogPoint pos; 390 | pos.x = 0.0; pos.y = 0.0; 391 | int32_t LayoutMode = CELL_OSKDIALOG_LAYOUTMODE_X_ALIGN_CENTER | CELL_OSKDIALOG_LAYOUTMODE_Y_ALIGN_TOP; 392 | ret = cellOskDialogSetLayoutMode(LayoutMode); 393 | 394 | 395 | CellOskDialogParam dialogParam; 396 | dialogParam.allowOskPanelFlg = CELL_OSKDIALOG_PANELMODE_ALPHABET | 397 | CELL_OSKDIALOG_PANELMODE_NUMERAL | 398 | CELL_OSKDIALOG_PANELMODE_ENGLISH; 399 | //E Panel to display first 400 | dialogParam.firstViewPanel = CELL_OSKDIALOG_PANELMODE_ALPHABET; 401 | // E Initial display position of the on-screen keyboard dialog 402 | dialogParam.controlPoint = pos; 403 | //E Prohibited operation flag(s) (ex. CELL_OSKDIALOG_NO_SPACE) 404 | dialogParam.prohibitFlgs = 0; 405 | cellSysutilRegisterCallback(0, sysutil_callback, NULL); 406 | cellOskDialogLoadAsync(SYS_MEMORY_CONTAINER_ID_INVALID, &dialogParam, &inputFieldInfo); 407 | pressedtriangle = true; 408 | keyboardon = true; 409 | 410 | } 411 | else if (pressedtriangle && !(data.button[CELL_PAD_BTN_OFFSET_DIGITAL2] & CELL_PAD_CTRL_TRIANGLE)) 412 | { 413 | pressedtriangle = false; 414 | } 415 | } 416 | } 417 | } 418 | 419 | uint64_t GetModuleAddress(const char* name, uint64_t defaultaddr) 420 | { 421 | sys_prx_id_t sprx = sys_prx_get_module_id_by_name(name, 0, 0); 422 | if (sprx < 0) 423 | { 424 | //cellMsgDialogOpen2(ok_message, "RPCS3 does not support sys_prx_get_module_id_by_name which means that\nyou wont get built-in source engine console, sorry!\n(cry about it to rpcs3 devs)", closethedialog, NULL, NULL); 425 | return defaultaddr; 426 | } 427 | else 428 | { 429 | sys_prx_module_info_t info{}; 430 | static sys_prx_segment_info_t segments[10]{}; 431 | static char filename[SYS_PRX_MODULE_FILENAME_SIZE]{}; 432 | 433 | memset(segments, 0, sizeof(segments)); 434 | memset(filename, 0, sizeof(filename)); 435 | 436 | info.size = sizeof(info); 437 | info.segments = segments; 438 | info.segments_num = sizeof(segments) / sizeof(sys_prx_segment_info_t); 439 | info.filename = filename; 440 | info.filename_size = sizeof(filename); 441 | sys_prx_get_module_info(sprx, 0, &info); 442 | return info.segments->base; 443 | } 444 | } 445 | 446 | bool CEmptyServerPlugin::Load(CreateInterfaceFn interfaceFactory, CreateInterfaceFn gameServerFactory) 447 | { 448 | client = (IVEngineClient*)interfaceFactory("VEngineClient013", NULL); 449 | 450 | enginebase = GetModuleAddress("engine_rel", 0x670000); 451 | if (enginebase) 452 | { 453 | OpenSocketInternalHk = new DetourHook(enginebase + 0x001febcc, (uintptr_t)OpenSocketInternal); 454 | CSteamSocketMgrSendtoHk = new DetourHook(enginebase + 0x002051c8, (uintptr_t)CSteamSocketMgrSendto); 455 | CSteamSocketMgrRecvfromHk = new DetourHook(enginebase + 0x0020555c, (uintptr_t)CSteamSocketMgrRecvfrom); 456 | Net_SendStreamHk = new DetourHook(enginebase + 0x001fa790, (uintptr_t)NET_SendStream); 457 | NET_CloseSocketHk = new DetourHook(enginebase + 0x001fa538, (uintptr_t)NET_CloseSocket); 458 | write_mem(enginebase + 0x0017c1d0, 0x4800000c); 459 | write_mem(enginebase + 0x0017f18c, 0x48000038); 460 | write_mem(enginebase + 0x0017f1e8, 0x48000038); 461 | write_mem(enginebase + 0x0017fe60, 0x42800090); 462 | 463 | 464 | 465 | } 466 | uint64_t serverbase = GetModuleAddress("server_rel", 0x2910000); 467 | if (serverbase) 468 | { 469 | write_mem(serverbase + 0x00539d50, 0x38600020); 470 | write_mem(serverbase + 0x00539d5c, 0x3bc00020); 471 | write_mem(serverbase + 0x001cda80, 0x480000b0); 472 | write_mem(serverbase + 0x0016f938, 0x48000070); 473 | 474 | } 475 | sys_ppu_thread_t keyboardppulmao; 476 | sys_ppu_thread_create(&keyboardppulmao, ConsoleKeyboardInputThread, 0, 420, 0x5000, SYS_PPU_THREAD_CREATE_JOINABLE, "If_you_see_this_KEEP_YOURSELF_SAFE"); 477 | return true; 478 | } 479 | 480 | void CEmptyServerPlugin::Unload(void) 481 | { 482 | 483 | } 484 | 485 | void CEmptyServerPlugin::Pause(void) 486 | { 487 | 488 | } 489 | 490 | void CEmptyServerPlugin::UnPause(void) 491 | { 492 | 493 | } 494 | 495 | const char *CEmptyServerPlugin::GetPluginDescription(void) 496 | { 497 | return "Epic plugin for Portal 2"; 498 | } 499 | 500 | void CEmptyServerPlugin::LevelInit(char const *pMapName) 501 | { 502 | 503 | } 504 | 505 | void CEmptyServerPlugin::ServerActivate(edict_t *pEdictList, int edictCount, int clientMax) 506 | { 507 | 508 | } 509 | 510 | void CEmptyServerPlugin::GameFrame(bool simulating) 511 | { 512 | 513 | } 514 | 515 | void CEmptyServerPlugin::LevelShutdown(void) // !!!!this can get called multiple times per map change 516 | { 517 | 518 | } 519 | 520 | void CEmptyServerPlugin::ClientActive(edict_t *pEntity) 521 | { 522 | 523 | } 524 | 525 | void CEmptyServerPlugin::ClientDisconnect(edict_t *pEntity) 526 | { 527 | 528 | } 529 | 530 | void CEmptyServerPlugin::ClientPutInServer(edict_t *pEntity, char const *playername) 531 | { 532 | 533 | } 534 | 535 | void CEmptyServerPlugin::SetCommandClient(int index) 536 | { 537 | 538 | } 539 | 540 | void CEmptyServerPlugin::ClientSettingsChanged(edict_t *pEdict) 541 | { 542 | 543 | } 544 | 545 | PLUGIN_RESULT CEmptyServerPlugin::ClientConnect(bool *bAllowConnect, edict_t *pEntity, const char *pszName, const char *pszAddress, char *reject, int maxrejectlen) 546 | { 547 | return PLUGIN_CONTINUE; 548 | } 549 | 550 | PLUGIN_RESULT CEmptyServerPlugin::ClientCommand(edict_t *pEntity, const void* &args) 551 | { 552 | return PLUGIN_CONTINUE; 553 | } 554 | 555 | PLUGIN_RESULT CEmptyServerPlugin::NetworkIDValidated(const char *pszUserName, const char *pszNetworkID) 556 | { 557 | return PLUGIN_CONTINUE; 558 | } 559 | 560 | void CEmptyServerPlugin::OnQueryCvarValueFinished(void* iCookie, edict_t *pPlayerEntity, void* eStatus, const char *pCvarName, const char *pCvarValue) 561 | { 562 | 563 | } 564 | 565 | void CEmptyServerPlugin::OnEdictAllocated(edict_t *edict) 566 | { 567 | 568 | } 569 | 570 | void CEmptyServerPlugin::OnEdictFreed(const edict_t *edict) 571 | { 572 | 573 | } 574 | 575 | void CEmptyServerPlugin::FireGameEvent(KeyValues * event) 576 | { 577 | 578 | } 579 | --------------------------------------------------------------------------------