diff --git a/data/function.cpp b/data/function.cpp deleted file mode 100644 index 2da4222..0000000 --- a/data/function.cpp +++ /dev/null @@ -1,32 +0,0 @@ -#include "function.h" - -// Virtual Function Information -template -__VirtualFunctionInfo::__VirtualFunctionInfo(T *const addr_, void *const parent_): - addr(addr_), - parent(parent_) {} -template -bool __VirtualFunctionInfo::can_overwrite() const { - return ((void *) *addr) != parent; -} - -// Function Information -template -__Function::__Function(const char *const name_, const __Function::ptr_type func_, const __Function::ptr_type thunk_): - is_virtual(false), - func(func_), - enabled(true), - name(name_), - backup(func_), - thunk(thunk_) {} -template -__Function::__Function(const char *const name_, __Function::ptr_type *const func_, void *const parent, const __Function::ptr_type thunk_): - is_virtual(true), - func(__VirtualFunctionInfo(func_, parent)), - enabled(std::get<__VirtualFunctionInfo>(func).can_overwrite()), - name(name_), - backup(*get_vtable_addr()), - thunk(thunk_) {} - -// Thunks -thunk_enabler_t thunk_enabler; \ No newline at end of file diff --git a/data/function.h b/data/function.h deleted file mode 100644 index ea66971..0000000 --- a/data/function.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include - -// Virtual Function Information -template -class __Function; -template -class __VirtualFunctionInfo { - __VirtualFunctionInfo(T *addr_, void *parent_); - [[nodiscard]] bool can_overwrite() const; - T *const addr; - void *const parent; - friend class __Function>; -}; - -// Thunks -typedef void *(*thunk_enabler_t)(void *target, void *thunk); -extern thunk_enabler_t thunk_enabler; - -// Function Information -template -class __Function { -public: - // Types - using ptr_type = Ret (*)(Args...); - using type = std::function; - using overwrite_type = std::function; - - // Normal Function - __Function(const char *name_, ptr_type func_, ptr_type thunk_); - // Virtual Function - __Function(const char *name_, ptr_type *func_, void *parent, ptr_type thunk_); - - // Overwrite Function - [[nodiscard]] bool overwrite(overwrite_type target) { - // Check If Enabled - if (!enabled) { - return false; - } - // Enable Thunk - enable_thunk(); - // Overwrite - type original = get_thunk_target(); - thunk_target = [original, target](Args... args) { - return target(original, std::forward(args)...); - }; - return true; - } - - // Getters - [[nodiscard]] ptr_type get(bool result_will_be_stored) { - if (!enabled) { - return nullptr; - } else { - if (result_will_be_stored) { - enable_thunk(); - } - if (is_virtual) { - return *get_vtable_addr(); - } else { - return std::get(func); - } - } - } - [[nodiscard]] ptr_type *get_vtable_addr() const { - if (is_virtual) { - return std::get<__VirtualFunctionInfo>(func).addr; - } else { - return nullptr; - } - } - [[nodiscard]] type get_thunk_target() const { - if (thunk_target) { - return thunk_target; - } else { - return backup; - } - } - -private: - // Current Function - const bool is_virtual; - std::variant> func; - -public: - // State - const bool enabled; - const char *const name; - - // Backup Of Original Function Pointer - const ptr_type backup; - -private: - // Thunk - const ptr_type thunk; - type thunk_target; - bool thunk_enabled = false; - void enable_thunk() { - if (!thunk_enabled && enabled) { - ptr_type real_thunk = (ptr_type) thunk_enabler((void *) backup, (void *) thunk); - if (!is_virtual) { - func = real_thunk; - } - thunk_enabled = true; - } - } -}; \ No newline at end of file diff --git a/data/out.cpp b/data/out.cpp index c7260cb..d355907 100644 --- a/data/out.cpp +++ b/data/out.cpp @@ -1,15 +1,9 @@ #include "{{ headerPath }}" -#include "{{ data }}/function.cpp" -// Thunks -template -struct __Thunk; -template -struct __Thunk { - template <__Function *const *func> - static Ret call(Args... args) { - return (*func)->get_thunk_target()(std::forward(args)...); - } -}; +// Thunk Template +template +decltype(auto) __thunk(auto... args) { + return (*func)->get_thunk_target()(std::forward(args)...); +} {{ main }} \ No newline at end of file diff --git a/data/out.h b/data/out.h index 671c56c..97f861d 100644 --- a/data/out.h +++ b/data/out.h @@ -6,7 +6,8 @@ #endif // Headers -#include "{{ data }}/function.h" +#include +#include #include #include #include @@ -14,6 +15,123 @@ #include #include +// Virtual Function Information +template +class __Function; +template +class __VirtualFunctionInfo { + __VirtualFunctionInfo(T *const addr_, void *const parent_): + addr(addr_), + parent(parent_) {} + [[nodiscard]] bool can_overwrite() const { + return ((void *) *addr) != parent; + } + T *const addr; + void *const parent; + friend class __Function>; +}; + +// Thunks +typedef void *(*thunk_enabler_t)(void *target, void *thunk); +extern thunk_enabler_t thunk_enabler; + +// Function Information +template +class __Function { +public: + // Types + using ptr_type = Ret (*)(Args...); + using type = std::function; + using overwrite_type = std::function; + + // Normal Function + __Function(const char *const name_, const ptr_type func_, const ptr_type thunk_): + func(func_), + enabled(true), + name(name_), + backup(func_), + thunk(thunk_) {} + // Virtual Function + __Function(const char *const name_, ptr_type *const func_, void *const parent, const ptr_type thunk_): + func(__VirtualFunctionInfo(func_, parent)), + enabled(std::get<__VirtualFunctionInfo>(func).can_overwrite()), + name(name_), + backup(*get_vtable_addr()), + thunk(thunk_) {} + + // Overwrite Function + [[nodiscard]] bool overwrite(overwrite_type target) { + // Check If Enabled + if (!enabled) { + return false; + } + // Enable Thunk + enable_thunk(); + // Overwrite + type original = get_thunk_target(); + thunk_target = [original, target](Args... args) { + return target(original, std::forward(args)...); + }; + return true; + } + + // Getters + [[nodiscard]] ptr_type get(bool result_will_be_stored) { + if (!enabled) { + return nullptr; + } else { + if (result_will_be_stored) { + enable_thunk(); + } + if (is_virtual()) { + return *get_vtable_addr(); + } else { + return std::get(func); + } + } + } + [[nodiscard]] ptr_type *get_vtable_addr() const { + return std::get<__VirtualFunctionInfo>(func).addr; + } + [[nodiscard]] type get_thunk_target() const { + if (thunk_target) { + return thunk_target; + } else { + return backup; + } + } + +private: + // Current Function + std::variant> func; + [[nodiscard]] bool is_virtual() const { + return func.index() == 1; + } + +public: + // State + const bool enabled; + const char *const name; + + // Backup Of Original Function Pointer + const ptr_type backup; + +private: + // Thunk + const ptr_type thunk; + type thunk_target; + bool thunk_enabled = false; + void enable_thunk() { + if (!thunk_enabled && enabled) { + ptr_type real_thunk = (ptr_type) thunk_enabler((void *) backup, (void *) thunk); + if (!is_virtual()) { + func = real_thunk; + } + thunk_enabled = true; + } + } +}; + // Shortcuts typedef unsigned char uchar; typedef unsigned short ushort; diff --git a/src/index.ts b/src/index.ts index 61f3463..f71b10f 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,5 @@ import * as fs from 'node:fs'; +import * as path from 'node:path'; import { STRUCTURE_FILES, EXTENSION, formatFile, getDataDir } from './common'; import { getStructure } from './map'; import { Struct } from './struct'; @@ -14,9 +15,8 @@ function invalidFileType(file: string) { throw new Error(`Invalid File Type: ${file}`); } const sourceOutput = process.argv.shift()!; -if (!sourceOutput.endsWith('.cpp')) { - invalidFileType(sourceOutput); -} +fs.rmSync(sourceOutput, {force: true, recursive: true}); +fs.mkdirSync(sourceOutput, {recursive: true}); const headerOutput = process.argv.shift()!; if (!headerOutput.endsWith('.h')) { invalidFileType(headerOutput); @@ -151,28 +151,38 @@ function makeMainHeader(output: string) { makeMainHeader(headerOutput); // Generate Compiled Code -function makeCompiledCode(output: string) { +function makeCompiledCode(outputDir: string) { // Load Symbols const structureObjects = loadSymbols(); // Generate - let declarations = ''; + let first = true; for (const structure of structureObjects) { + // Thunks + let declarations = ''; + if (first) { + first = false; + declarations += '// Thunk Enabler\n'; + declarations += 'thunk_enabler_t thunk_enabler;\n\n'; + } + + // Structure const name = structure.getName(); declarations += `// ${name}\n`; try { - declarations += structure.generateCode(); + declarations += structure.generateCode().trim(); } catch (e) { console.log(`Error Generating Code: ${name}: ${e instanceof Error ? e.stack : e}`); process.exit(1); } declarations += '\n'; - } - // Write - const headerPath = fs.realpathSync(headerOutput); - const main = declarations.trim(); - const result = formatFile('out.cpp', {headerPath, main, data: getDataDir()}); - fs.writeFileSync(output, result); + // Write + const headerPath = fs.realpathSync(headerOutput); + const main = declarations.trim(); + const result = formatFile('out.cpp', {headerPath, main, data: getDataDir()}); + const output = path.join(outputDir, name + '.cpp'); + fs.writeFileSync(output, result); + } } makeCompiledCode(sourceOutput); diff --git a/src/method.ts b/src/method.ts index 83342fb..1dd87df 100644 --- a/src/method.ts +++ b/src/method.ts @@ -66,7 +66,7 @@ export class Method { } else { out += `${this.getWrapperType()}::ptr_type(${toHex(this.address)})`; } - out += `, ${INTERNAL}Thunk<${this.#getSignature()}>::call<&${this.getName()}>)`; + out += `, ${INTERNAL}thunk<&${this.getName()}>)`; } out += ';\n'; if (!code) { diff --git a/src/struct.ts b/src/struct.ts index 8545ce0..c0f354f 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -139,27 +139,23 @@ export class Struct { // Generate C++ Method Shortcuts #generateMethod(method: Method, isVirtual: boolean) { - // Generate Method Call - let call = ''; - if (isVirtual) { - call += `this->vtable->${method.shortName}`; - } else { - call += `${method.getName()}->get(false)`; - } - call += '('; - if (!method.isStatic) { - call += `(${method.self} *) this, `; - } - call += 'std::forward(args)...)'; - // Generate Shortcut let out = ''; - out += `${INDENT}template \n`; out += INDENT; if (method.isStatic) { out += 'static '; } - out += `auto ${method.shortName}(Args&&... args) -> decltype(${call}) {\n`; - out += `${INDENT}${INDENT}return ${call};\n`; + out += `decltype(auto) ${method.shortName}(auto&&... args) {\n`; + out += `${INDENT}${INDENT}return `; + if (isVirtual) { + out += `this->vtable->${method.shortName}`; + } else { + out += `${method.getName()}->get(false)`; + } + out += '('; + if (!method.isStatic) { + out += `(${method.self} *) this, `; + } + out += 'std::forward(args)...);\n'; out += `${INDENT}}\n`; return out; }