diff --git a/src/common.ts b/src/common.ts index b228b95..56a2aa1 100644 --- a/src/common.ts +++ b/src/common.ts @@ -111,4 +111,13 @@ export function prependArg(args: string, arg: string) { arg += ', '; } return '(' + arg + args.substring(1); +} +export function removeFirstArg(args: string) { + let index = args.indexOf(','); + if (index === -1) { + index = args.indexOf(')'); + } else { + index++; + } + return '(' + args.substring(index).trim(); } \ No newline at end of file diff --git a/src/index.ts b/src/index.ts index ba98e5f..f3ff06a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -164,12 +164,11 @@ function makeMainHeader(output: string) { result += '#include \n'; result += '\n// Warnings\n'; result += '#pragma GCC diagnostic push\n'; - result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n\n'; + result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n'; + result += '#pragma GCC diagnostic ignored "-Wshadow"\n\n'; result += makeHeaderPart(); result += '\n// Cleanup Warnings\n'; result += '#pragma GCC diagnostic pop\n'; - result += '\n// Array Of All Method Symbols\n'; - result += 'extern void *_all_method_symbols[];\n'; fs.writeFileSync(output, result); } makeMainHeader(headerOutput); @@ -212,16 +211,6 @@ function makeCompiledCode(output: string) { result += init; result += '}\n\n'; result += declarations; - result += '\n// Setup Methods Array\n'; - result += 'void *_all_method_symbols[] = {\n'; - for (const structure of structureObjects) { - const methods = structure.getMethodSymbols(); - for (const method of methods) { - result += `${INDENT}&${method},\n`; - } - } - result += `${INDENT}nullptr\n`; - result += '};\n'; fs.writeFileSync(output, result); } makeCompiledCode(sourceOutput); diff --git a/src/loader.ts b/src/loader.ts index 497a395..8ef89be 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -47,7 +47,7 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean, isInher methodArgs = prependArg(methodArgs, selfArg); } const address = safeParseInt(end[1]!); - return new Method(self, name, type, methodArgs, address, isInherited); + return new Method(self, name, type, methodArgs, address, isInherited, !insertSelfArg); } // Load Structure @@ -144,7 +144,7 @@ export function load(target: Struct, name: string, isExtended: boolean) { } case 'method': { // Add Method - const method = parseMethod(args, target.getName(), true, isExtended); + const method = parseMethod(args, name, true, isExtended); target.addMethod(method, false); break; } diff --git a/src/method.ts b/src/method.ts index d8ec734..8f7eda6 100644 --- a/src/method.ts +++ b/src/method.ts @@ -7,15 +7,19 @@ export class Method { 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) { + 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 @@ -38,9 +42,9 @@ export class Method { out += `typedef ${returnType}(*${this.getType()})${this.args};\n`; // Fancy Overwrite Does Not Support Varargs - if (!this.args.includes('...')) { + if (!this.hasVarargs) { // Overwrite Helper - out += `#define _overwrite_helper_for_${this.getName()}(method, original) \\\n`; + out += `#define __overwrite_helper_for_${this.getName()}(method, original) \\\n`; out += `${INDENT}[]${this.args} { \\\n`; out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}method(${['original'].concat(getArgNames(this.args)).join(', ')}); \\\n`; out += `${INDENT}}\n`; @@ -57,7 +61,7 @@ export class Method { // Generate "New Method" Test generateNewMethodTest(parent: string | null, prefix: string, suffix: string) { - let out = `#define _is_new_method_${this.getName()}() (`; + let out = `#define __is_new_method_${this.getName()}() (`; if (!this.isInherited) { out += 'true'; } else { diff --git a/src/struct.ts b/src/struct.ts index 10f2d1a..7aa5801 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -1,4 +1,4 @@ -import { INDENT, STRUCTURE_FILES, toHex, assertSize } from './common'; +import { INDENT, STRUCTURE_FILES, toHex, assertSize, formatType, getArgNames, removeFirstArg } from './common'; import { Method } from './method'; import { Property, StaticProperty } from './property'; import { VTable } from './vtable'; @@ -58,13 +58,19 @@ export class Struct { // Add Method addMethod(method: Method, isVirtual: boolean) { - if (method.self !== this.#name) { - throw new Error(); + if (method.returnType !== this.#name && method.returnType in STRUCTURE_FILES) { + this.#addDependency(method.returnType); } if (isVirtual) { + if (method.self !== this.#name) { + throw new Error(); + } this.ensureVTable(); this.#vtable!.add(method); } else { + if (method.isInherited) { + this.#addDependency(method.self); + } this.#methods.push(method); } } @@ -131,6 +137,55 @@ export class Struct { return out; } + // Generate C++ Method Shortcuts + #generateMethods() { + let out = ''; + // Normal Methods + const getArgsOuter = (method: Method) => { + let out = method.args; + if (!method.isStatic) { + // Remove "self" + out = removeFirstArg(out); + } + return out; + }; + const getArgsInner = (method: Method) => { + const list = getArgNames(method.args); + if (!method.isStatic) { + // Replace "self" With "this" + list[0] = `(${method.self} *) this`; + } + return list.join(', '); + }; + for (const method of this.#methods) { + if (!method.hasVarargs) { + const returnType = method.returnType; + const shortName = method.shortName; + const fullName = method.getName(); + const args = getArgsOuter(method); + out += `${INDENT}inline ${formatType(returnType)}${shortName}${args} { \\\n`; + out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}${fullName}(${getArgsInner(method)});\n`; + out += `${INDENT}}\n`; + } + } + // Virtual Methods + if (this.#vtable !== null) { + const virtualMethods = this.#vtable.getMethods(); + for (const method of virtualMethods) { + if (method && !method.hasVarargs) { + const returnType = method.returnType; + const shortName = method.shortName; + const args = getArgsOuter(method); + out += `${INDENT}inline ${formatType(returnType)}${shortName}${args} { \\\n`; + out += `${INDENT}${INDENT}${returnType.trim() === 'void' ? '' : 'return '}this->vtable->${shortName}(${getArgsInner(method)});\n`; + out += `${INDENT}}\n`; + } + } + } + // Return + return out; + } + // Generate Header generate() { let out = ''; @@ -144,9 +199,11 @@ export class Struct { // Methods for (const method of this.#methods) { - out += method.generateTypedef(); - out += `extern ${method.generateDefinition()}`; - out += method.generateNewMethodTest(this.#directParent, '', ''); + if (!method.isInherited) { + out += method.generateTypedef(); + out += `extern ${method.generateDefinition()}`; + out += method.generateNewMethodTest(this.#directParent, '', ''); + } } // VTable @@ -162,6 +219,7 @@ export class Struct { // Structure out += `struct ${this.#name} {\n`; out += this.#generateProperties(); + out += this.#generateMethods(); out += `};\n`; // Sanity Check Offsets @@ -199,8 +257,10 @@ export class Struct { // Methods for (const method of this.#methods) { - init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`; - declarations += method.generateDefinition(); + if (!method.isInherited) { + init += `${INDENT}${method.getName()} = (${method.getType()}) ${toHex(method.address)};\n`; + declarations += method.generateDefinition(); + } } // VTable @@ -221,15 +281,6 @@ export class Struct { return {functions: declarations, init}; } - // Get Method Symbols - getMethodSymbols() { - const ret = []; - for (const method of this.#methods) { - ret.push(method.getName()); - } - return ret; - } - // Set Direct Parent (Used For "New Method" Testing) setDirectParent(directParent: string) { this.#directParent = directParent; diff --git a/src/vtable.ts b/src/vtable.ts index 9e3147a..2c6b777 100644 --- a/src/vtable.ts +++ b/src/vtable.ts @@ -71,15 +71,15 @@ export class VTable { } // Get Full Methods Table - #getMethods() { + getMethods() { // Copy Array const out = []; out.push(...this.#methods); // Add Destructors (https://stackoverflow.com/a/17960941) const destructor_return = `${this.#self} *`; const destructor_args = `(${getSelfArg(this.#self)})`; - this.#add(out, new Method(this.#self, 'destructor_complete', destructor_return, destructor_args, 0x0 + this.#destructorOffset, false)); - this.#add(out, new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false)); + this.#add(out, new Method(this.#self, 'destructor_complete', destructor_return, destructor_args, 0x0 + this.#destructorOffset, false, false)); + this.#add(out, new Method(this.#self, 'destructor_deleting', destructor_return, destructor_args, 0x4 + this.#destructorOffset, false, false)); // Return return out; } @@ -92,7 +92,7 @@ export class VTable { this.#check(); // Method Prototypes - const methods = this.#getMethods(); + const methods = this.getMethods(); for (let i = 0; i < methods.length; i++) { const info = methods[i]; if (info) { @@ -158,7 +158,7 @@ export class VTable { init += `${INDENT}${this.#getName()}_base = (${this.#getName()} *) ${toHex(this.#address)};\n`; declarations += `${this.#getName()} *${this.#getName()}_base;\n`; // Methods - const methods = this.#getMethods(); + const methods = this.getMethods(); for (let i = 0; i < methods.length; i++) { const info = methods[i]; if (info) {