119 lines
4.9 KiB
TypeScript
119 lines
4.9 KiB
TypeScript
import { INDENT, OVERWRITE_TEST_SUFFIX, VTABLE_ADDR_SUFFIX, formatType, getArgNames, prependArg } from './common';
|
|
|
|
export class Method {
|
|
readonly self: string;
|
|
readonly shortName: string;
|
|
readonly returnType: string;
|
|
readonly args: string;
|
|
readonly address: number;
|
|
readonly isInherited: boolean;
|
|
readonly hasVarargs: boolean;
|
|
readonly isStatic: boolean;
|
|
|
|
// Constructor
|
|
constructor(self: string, name: string, returnType: string, args: string, address: number, isInherited: boolean, isStatic: boolean) {
|
|
this.self = self;
|
|
this.shortName = name;
|
|
this.returnType = returnType;
|
|
this.args = args;
|
|
this.address = address;
|
|
this.isInherited = isInherited;
|
|
this.hasVarargs = this.args.includes('...');
|
|
this.isStatic = isStatic;
|
|
}
|
|
|
|
// Get Type
|
|
getNameWithCustomSelf(self: string) {
|
|
return `${self}_${this.shortName}`;
|
|
}
|
|
getName() {
|
|
return this.getNameWithCustomSelf(this.self);
|
|
}
|
|
#getBaseType() {
|
|
return `${this.getName()}_t`;
|
|
}
|
|
getType() {
|
|
return `__raw_${this.#getBaseType()}`;
|
|
}
|
|
|
|
// Generate Type Definition
|
|
generateTypedef() {
|
|
let out = '';
|
|
|
|
// Normal Definition
|
|
out += `typedef ${formatType(this.returnType)}(*${this.getType()})${this.args};\n`;
|
|
|
|
// Return
|
|
return out;
|
|
}
|
|
|
|
// Overwrite Helper
|
|
#hasOverwriteHelper() {
|
|
// Fancy Overwrite Does Not Support Varargs
|
|
return !this.hasVarargs;
|
|
}
|
|
generateOverwriteHelper(code: boolean, isVirtual: boolean) {
|
|
let out = '';
|
|
if (this.#hasOverwriteHelper()) {
|
|
// Add Additional Typedefs
|
|
const baseType = this.#getBaseType();
|
|
const overwriteType = `__overwrite_${baseType}`;
|
|
const functionType = baseType;
|
|
const rawType = this.getType();
|
|
if (!code) {
|
|
out += `typedef std::function<std::remove_pointer<${rawType}>::type> ${functionType};\n`;
|
|
out += `typedef std::function<${this.returnType}${prependArg(this.args, functionType + ' original')}> ${overwriteType};\n`;
|
|
}
|
|
// Thunk
|
|
const name = this.getName();
|
|
const currentOverwriteName = '__current_overwrite_for_' + name;
|
|
const thunkName = '__overwrite_thunk_for_' + name;
|
|
if (code) {
|
|
out += `static ${functionType} &${currentOverwriteName} = *new ${functionType};\n`;
|
|
out += `static ${formatType(this.returnType)}${thunkName}${this.args} {\n`;
|
|
out += `${INDENT}${this.returnType.trim() === 'void' ? '' : 'return '}${currentOverwriteName}(${getArgNames(this.args).join(', ')});\n`;
|
|
out += `}\n`;
|
|
}
|
|
// Overwrite Helper
|
|
out += `std::string __set_overwrite_for_${name}(const ${overwriteType} &method, const std::function<void *(void *, void *)> &overwriter)`;
|
|
if (code) {
|
|
out += ' {\n';
|
|
if (isVirtual) {
|
|
// Check If Method Can Be Overwritten
|
|
out += `${INDENT}if (!${OVERWRITE_TEST_SUFFIX}${name}()) {\n`;
|
|
out += `${INDENT}${INDENT}return "Unable To Overwrite Virtual Method";\n`;
|
|
out += `${INDENT}}\n`;
|
|
}
|
|
// Redirect Method Calls To Thunk
|
|
out += `${INDENT}static bool enabled = false;\n`;
|
|
out += `${INDENT}if (!enabled) {\n`;
|
|
if (isVirtual) {
|
|
// Get Address Of Virtual Function
|
|
out += `${INDENT}${INDENT}${rawType} ${name} = *${name}${VTABLE_ADDR_SUFFIX};\n`;
|
|
}
|
|
out += `${INDENT}${INDENT}${rawType} &original = ${name};\n`;
|
|
out += `${INDENT}${INDENT}${currentOverwriteName} = original;\n`;
|
|
out += `${INDENT}${INDENT}original = (${rawType}) overwriter((void *) original, (void *) ${thunkName});\n`;
|
|
out += `${INDENT}${INDENT}enabled = true;\n`;
|
|
out += `${INDENT}}\n`;
|
|
// Bind Original Parameter
|
|
out += `${INDENT}${functionType} original = ${currentOverwriteName};\n`;
|
|
out += `${INDENT}${currentOverwriteName} = [method, original]${this.args} {\n`;
|
|
out += `${INDENT}${INDENT}${this.returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')});\n`;
|
|
out += `${INDENT}};\n`;
|
|
// Return Success
|
|
out += `${INDENT}return "";\n`;
|
|
out += `}`;
|
|
} else {
|
|
out += ';';
|
|
}
|
|
out += '\n';
|
|
}
|
|
return out;
|
|
}
|
|
|
|
// Generate Variable Definition
|
|
generateDefinition(nameSuffix?: string) {
|
|
return `${this.getType()} ${this.getName()}${nameSuffix !== undefined ? nameSuffix : ''}`;
|
|
}
|
|
} |