Pure Evil
This commit is contained in:
parent
842f05a288
commit
67c4adaa77
@ -164,3 +164,30 @@ export class Size {
|
||||
return this.#isExact;
|
||||
}
|
||||
}
|
||||
export function getArgNames(args: string) {
|
||||
// Remove Parentheses
|
||||
args = args.substring(1, args.length - 1);
|
||||
if (args.length === 0) {
|
||||
return [];
|
||||
}
|
||||
// Split
|
||||
const argsList = args.split(',');
|
||||
// Parse
|
||||
const out = [];
|
||||
for (let arg of argsList) {
|
||||
arg = arg.trim();
|
||||
// Remove Type
|
||||
const nameStart = Math.max(arg.lastIndexOf(' '), arg.lastIndexOf('*')) + 1;
|
||||
arg = arg.substring(nameStart);
|
||||
// Collect
|
||||
out.push(arg);
|
||||
}
|
||||
// Return
|
||||
return out;
|
||||
}
|
||||
export function prependArg(args: string, arg: string) {
|
||||
if (args !== '()') {
|
||||
arg += ', ';
|
||||
}
|
||||
return '(' + arg + args.substring(1);
|
||||
}
|
110
src/cpp.ts
110
src/cpp.ts
@ -1,110 +0,0 @@
|
||||
import { INDENT } from './common';
|
||||
import { SimpleProperty } from './property';
|
||||
|
||||
// List Of Supported C++ Types
|
||||
const CPP_TYPES = [
|
||||
'std::string',
|
||||
'std::vector<',
|
||||
'std::map<'
|
||||
];
|
||||
const CPP_HEADERS = [
|
||||
'string',
|
||||
'vector',
|
||||
'map'
|
||||
];
|
||||
|
||||
// Get Fake Type Name
|
||||
function isGeneric(type: string) {
|
||||
return type.endsWith('<');
|
||||
}
|
||||
function removeGeneric(type: string) {
|
||||
if (isGeneric(type)) {
|
||||
// Remove Generic Parameters
|
||||
type = type.substring(0, type.length - 1);
|
||||
}
|
||||
return type;
|
||||
}
|
||||
function getFakeName(type: string) {
|
||||
return removeGeneric(type).replaceAll('std::', '_std_');
|
||||
}
|
||||
|
||||
// Generate C Type Definition
|
||||
function generateStruct(type: string) {
|
||||
// Get Information
|
||||
const property = new SimpleProperty(0, type, 'fake');
|
||||
const size = property.propertySize();
|
||||
const alignment = property.propertyAlignment();
|
||||
// Generate
|
||||
let struct = '';
|
||||
struct += `typedef struct __attribute__((aligned(${alignment}))) __attribute__((packed)) {\n`;
|
||||
struct += `${INDENT}uchar data[${size}];\n`;
|
||||
struct += `} ${getFakeName(type)};\n`;
|
||||
return struct;
|
||||
}
|
||||
|
||||
// Turn A C++ Header Into A C/C++ Headeer
|
||||
export function fixupCpp(data: string) {
|
||||
// Replace Types
|
||||
for (const type of CPP_TYPES) {
|
||||
// Check If Type Is Generic
|
||||
if (isGeneric(type)) {
|
||||
// Generic
|
||||
while (data.includes(type)) {
|
||||
// Find Type
|
||||
let index = data.indexOf(type);
|
||||
// Replace Type
|
||||
data = data.substring(0, index) + getFakeName(type) + '(' + data.substring(index + type.length);
|
||||
// Replace > With (
|
||||
index = data.indexOf('>', index);
|
||||
if (index !== -1) {
|
||||
data = data.substring(0, index) + ')' + data.substring(index + 1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Normal
|
||||
data = data.replaceAll(type, getFakeName(type));
|
||||
}
|
||||
}
|
||||
|
||||
// Setup Macros
|
||||
let header = '';
|
||||
header += '// C++ Types\n';
|
||||
|
||||
// C++ Support
|
||||
header += '#ifdef __cplusplus\n';
|
||||
for (const headerFile of CPP_HEADERS) {
|
||||
header += `#include <${headerFile}>\n`;
|
||||
}
|
||||
for (const type of CPP_TYPES) {
|
||||
const fakeName = getFakeName(type);
|
||||
if (isGeneric(type)) {
|
||||
header += `#define ${fakeName}(...) ${type}__VA_ARGS__>\n`;
|
||||
} else {
|
||||
header += `#define ${fakeName} ${type}\n`;
|
||||
}
|
||||
}
|
||||
|
||||
// C Support
|
||||
header += '#else\n';
|
||||
for (let type of CPP_TYPES) {
|
||||
const fakeName = getFakeName(type);
|
||||
header += generateStruct(type);
|
||||
// Strip Generic Information
|
||||
if (isGeneric(type)) {
|
||||
type = removeGeneric(type);
|
||||
header += `#define ${fakeName}(...) ${fakeName}\n`;
|
||||
}
|
||||
}
|
||||
header += '#endif\n';
|
||||
|
||||
// Cleanup Macros
|
||||
let footer = '';
|
||||
footer += '// Cleanup C++ Types\n';
|
||||
for (const type of CPP_TYPES) {
|
||||
const fakeName = getFakeName(type);
|
||||
footer += `#undef ${fakeName}\n`;
|
||||
}
|
||||
|
||||
// Return
|
||||
return header + '\n' + data + '\n' + footer;
|
||||
}
|
43
src/index.ts
43
src/index.ts
@ -2,7 +2,6 @@ import * as fs from 'node:fs';
|
||||
import { STRUCTURE_FILES, EXTENSION, INDENT } from './common';
|
||||
import { getStructure } from './map';
|
||||
import { Struct } from './struct';
|
||||
import { fixupCpp } from './cpp';
|
||||
|
||||
// Arguments
|
||||
if (process.argv.length < 5) {
|
||||
@ -158,25 +157,19 @@ function makeMainHeader(output: string) {
|
||||
for (const file of extraHeaders) {
|
||||
result += fs.readFileSync(file, {encoding: 'utf8'});
|
||||
}
|
||||
let core = '';
|
||||
core += '// C/C++ Support\n';
|
||||
core += '#ifndef __cplusplus\n';
|
||||
core += '#include <stddef.h>\n';
|
||||
core += '#include <assert.h>\n';
|
||||
core += 'typedef uchar bool;\n';
|
||||
core += '#else\n';
|
||||
core += '#include <cstddef>\n';
|
||||
core += 'extern "C" {\n';
|
||||
core += '#pragma GCC diagnostic push\n';
|
||||
core += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\n';
|
||||
core += '#endif\n\n';
|
||||
core += makeHeaderPart();
|
||||
core += '\n// Cleanup C++ Support\n';
|
||||
core += '#ifdef __cplusplus\n';
|
||||
core += '#pragma GCC diagnostic pop\n';
|
||||
core += '}\n';
|
||||
core += '#endif\n';
|
||||
result += '\n' + fixupCpp(core);
|
||||
result += '\n// Headers\n';
|
||||
result += '#include <cstddef>\n';
|
||||
result += '#include <string>\n';
|
||||
result += '#include <vector>\n';
|
||||
result += '#include <map>\n';
|
||||
result += '\n// Warnings\n';
|
||||
result += '#pragma GCC diagnostic push\n';
|
||||
result += '#pragma GCC diagnostic ignored "-Winvalid-offsetof"\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);
|
||||
@ -219,6 +212,16 @@ 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);
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, readDefinition, safeParseInt, syntaxError } from './common';
|
||||
import { COMMENT, EXTENSION, getSelfArg, parseTypeAndName, prependArg, readDefinition, safeParseInt, syntaxError } from './common';
|
||||
import { Method } from './method';
|
||||
import { SimpleProperty, StaticProperty } from './property';
|
||||
import { Struct } from './struct';
|
||||
@ -27,7 +27,7 @@ function parseProperty(args: string) {
|
||||
}
|
||||
|
||||
// Parse Method
|
||||
function parseMethod(args: string, self: string, insertSelfArg: boolean) {
|
||||
function parseMethod(args: string, self: string, insertSelfArg: boolean, isInherited: boolean) {
|
||||
const argsStart = args.indexOf('(');
|
||||
if (argsStart === -1) {
|
||||
syntaxError('Cannot Find Arguments');
|
||||
@ -43,14 +43,11 @@ function parseMethod(args: string, self: string, insertSelfArg: boolean) {
|
||||
syntaxError('Invalid Method Arguments');
|
||||
}
|
||||
if (insertSelfArg) {
|
||||
let selfArg = `(${getSelfArg(self)}`;
|
||||
if (methodArgs !== '()') {
|
||||
selfArg += ', ';
|
||||
}
|
||||
methodArgs = selfArg + methodArgs.substring(1);
|
||||
const selfArg = getSelfArg(self);
|
||||
methodArgs = prependArg(methodArgs, selfArg);
|
||||
}
|
||||
const address = safeParseInt(end[1]!);
|
||||
return new Method(self, name, type, methodArgs, address);
|
||||
return new Method(self, name, type, methodArgs, address, isInherited);
|
||||
}
|
||||
|
||||
// Load Structure
|
||||
@ -103,6 +100,9 @@ export function load(target: Struct, name: string, isExtended: boolean) {
|
||||
switch (command) {
|
||||
case 'extends': {
|
||||
// Load Parent
|
||||
if (!isExtended) {
|
||||
target.setDirectParent(args);
|
||||
}
|
||||
load(target, args, true);
|
||||
break;
|
||||
}
|
||||
@ -148,20 +148,20 @@ export function load(target: Struct, name: string, isExtended: boolean) {
|
||||
}
|
||||
case 'method': {
|
||||
// Add Method
|
||||
const method = parseMethod(args, target.getName(), true);
|
||||
const method = parseMethod(args, target.getName(), true, isExtended);
|
||||
target.addMethod(method, false);
|
||||
break;
|
||||
}
|
||||
case 'virtual-method': {
|
||||
// Add Virtual Method
|
||||
const method = parseMethod(args, target.getName(), true);
|
||||
const method = parseMethod(args, target.getName(), true, isExtended);
|
||||
target.addMethod(method, true);
|
||||
break;
|
||||
}
|
||||
case 'static-method': {
|
||||
// Add Static Method
|
||||
if (!isExtended) {
|
||||
const method = parseMethod(args, target.getName(), false);
|
||||
const method = parseMethod(args, target.getName(), false, false);
|
||||
target.addMethod(method, false);
|
||||
}
|
||||
break;
|
||||
@ -178,11 +178,16 @@ export function load(target: Struct, name: string, isExtended: boolean) {
|
||||
data += '_';
|
||||
}
|
||||
data += args;
|
||||
const method = parseMethod(data, target.getName(), true);
|
||||
const method = parseMethod(data, target.getName(), true, false);
|
||||
target.addMethod(method, false);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'vtable-destructor-offset': {
|
||||
// Set VTable Destructor Offset
|
||||
target.setVTableDestructorOffset(safeParseInt(args));
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error(`Invalid Command: ${command}`);
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { formatType } from './common';
|
||||
import { INDENT, formatType, getArgNames } from './common';
|
||||
|
||||
export class Method {
|
||||
readonly self: string;
|
||||
@ -6,19 +6,24 @@ export class Method {
|
||||
readonly returnType: string;
|
||||
readonly args: string;
|
||||
readonly address: number;
|
||||
readonly isInherited: boolean;
|
||||
|
||||
// Constructor
|
||||
constructor(self: string, name: string, returnType: string, args: string, address: number) {
|
||||
constructor(self: string, name: string, returnType: string, args: string, address: number, isInherited: boolean) {
|
||||
this.self = self;
|
||||
this.shortName = name;
|
||||
this.returnType = returnType;
|
||||
this.args = args;
|
||||
this.address = address;
|
||||
this.isInherited = isInherited;
|
||||
}
|
||||
|
||||
// Get Type
|
||||
getNameWithCustomSelf(self: string) {
|
||||
return `${self}_${this.shortName}`;
|
||||
}
|
||||
getName() {
|
||||
return `${this.self}_${this.shortName}`;
|
||||
return this.getNameWithCustomSelf(this.self);
|
||||
}
|
||||
getType() {
|
||||
return `${this.getName()}_t`;
|
||||
@ -26,12 +31,39 @@ export class Method {
|
||||
|
||||
// Generate Type Definition
|
||||
generateTypedef() {
|
||||
let out = '';
|
||||
|
||||
// Normal Definition
|
||||
const returnType = formatType(this.returnType);
|
||||
return `typedef ${returnType}(*${this.getType()})${this.args};\n`;
|
||||
out += `typedef ${returnType}(*${this.getType()})${this.args};\n`;
|
||||
|
||||
// Fancy Overwrite Does Not Support Varargs
|
||||
if (!this.args.includes('...')) {
|
||||
// Overwrite Helper
|
||||
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`;
|
||||
}
|
||||
|
||||
// Return
|
||||
return out;
|
||||
}
|
||||
|
||||
// Generate Variable Definition
|
||||
generateDefinition(nameSuffix?: string) {
|
||||
return `${this.getType()} ${this.getName()}${nameSuffix !== undefined ? nameSuffix : ''};\n`;
|
||||
}
|
||||
|
||||
// Generate "New Method" Test
|
||||
generateNewMethodTest(parent: string | null, prefix: string, suffix: string) {
|
||||
let out = `#define _is_new_method_${this.getName()}() (`;
|
||||
if (!this.isInherited) {
|
||||
out += 'true';
|
||||
} else {
|
||||
out += `((void *) ${prefix}${this.getName()}${suffix}) != ((void *) ${prefix}${this.getNameWithCustomSelf(parent!)}${suffix})`;
|
||||
}
|
||||
out += ')\n';
|
||||
return out;
|
||||
}
|
||||
}
|
@ -11,6 +11,8 @@ export class Struct {
|
||||
readonly #size: Size;
|
||||
readonly #dependencies: string[];
|
||||
readonly #staticProperties: StaticProperty[];
|
||||
#directParent: string | null;
|
||||
#vtableDestructorOffset: number;
|
||||
|
||||
// Constructor
|
||||
constructor(name: string) {
|
||||
@ -21,6 +23,8 @@ export class Struct {
|
||||
this.#size = new Size(false);
|
||||
this.#dependencies = [];
|
||||
this.#staticProperties = [];
|
||||
this.#directParent = null;
|
||||
this.#vtableDestructorOffset = 0;
|
||||
}
|
||||
|
||||
// Dependencies
|
||||
@ -34,11 +38,19 @@ export class Struct {
|
||||
// Ensure VTable Exists
|
||||
ensureVTable() {
|
||||
if (this.#vtable === null) {
|
||||
this.#vtable = new VTable(this.#name);
|
||||
this.#vtable = new VTable(this.#name, this.#vtableDestructorOffset);
|
||||
this.addProperty(this.#vtable);
|
||||
}
|
||||
}
|
||||
|
||||
// Set VTable Destructor Offset
|
||||
setVTableDestructorOffset(offset: number) {
|
||||
if (this.#vtable) {
|
||||
throw new Error('VTable Already Created');
|
||||
}
|
||||
this.#vtableDestructorOffset = offset;
|
||||
}
|
||||
|
||||
// Setters
|
||||
setSize(size: number, isExact: boolean) {
|
||||
this.#size.set(size, isExact);
|
||||
@ -200,11 +212,12 @@ export class Struct {
|
||||
for (const method of this.#methods) {
|
||||
out += method.generateTypedef();
|
||||
out += `extern ${method.generateDefinition()}`;
|
||||
out += method.generateNewMethodTest(this.#directParent, '', '');
|
||||
}
|
||||
|
||||
// VTable
|
||||
if (this.#vtable !== null) {
|
||||
out += this.#vtable.generate();
|
||||
out += this.#vtable.generate(this.#directParent);
|
||||
}
|
||||
|
||||
// Structure
|
||||
@ -294,4 +307,18 @@ export class Struct {
|
||||
// Return
|
||||
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;
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@ export class VTable implements Property {
|
||||
readonly #methods: Method[];
|
||||
|
||||
// Constructor
|
||||
constructor(name: string) {
|
||||
constructor(name: string, destructorOffset: number) {
|
||||
this.#name = name;
|
||||
this.#address = null;
|
||||
this.#size = new Size(true);
|
||||
@ -17,8 +17,8 @@ export class VTable implements Property {
|
||||
// Add Destructors (https://stackoverflow.com/a/17960941)
|
||||
const destructor_return = `${name} *`;
|
||||
const destructor_args = `(${getSelfArg(name)})`;
|
||||
this.add(new Method(name, 'destructor_complete', destructor_return, destructor_args, 0x0));
|
||||
this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4));
|
||||
this.add(new Method(name, 'destructor_complete', destructor_return, destructor_args, 0x0 + destructorOffset, false));
|
||||
this.add(new Method(name, 'destructor_deleting', destructor_return, destructor_args, 0x4 + destructorOffset, false));
|
||||
}
|
||||
|
||||
// Property Information
|
||||
@ -82,7 +82,7 @@ export class VTable implements Property {
|
||||
}
|
||||
|
||||
// Generate Code
|
||||
generate() {
|
||||
generate(directParent: string | null) {
|
||||
let out = '';
|
||||
|
||||
// Check
|
||||
@ -126,7 +126,7 @@ export class VTable implements Property {
|
||||
if (info) {
|
||||
const type = `${info.getType()} *`;
|
||||
out += `extern ${type}${info.getName()}_vtable_addr;\n`;
|
||||
out += `extern ${info.generateDefinition('_non_virtual')}`;
|
||||
out += info.generateNewMethodTest(directParent, '*', '_vtable_addr');
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,8 +161,6 @@ export class VTable implements Property {
|
||||
const type = `${info.getType()} *`;
|
||||
init += `${INDENT}${info.getName()}_vtable_addr = (${type}) ${toHex(vtableAddress)};\n`;
|
||||
declarations += `${type}${info.getName()}_vtable_addr;\n`;
|
||||
init += `${INDENT}${info.getName()}_non_virtual = *${info.getName()}_vtable_addr;\n`;
|
||||
declarations += info.generateDefinition('_non_virtual');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2,7 +2,7 @@ syntax def "\.def$"
|
||||
comment "//"
|
||||
|
||||
# Commands
|
||||
color magenta "\<(extends|size|vtable(-size)?|property|static-property(-array)?|((static|virtual)-)?method|constructor)\>"
|
||||
color magenta "\<(extends|size|vtable(-size|-destructor-offset)?|property|static-property(-array)?|((static|virtual)-)?method|constructor)\>"
|
||||
|
||||
# Types
|
||||
color green "\<(char|uchar|short|ushort|int|uint|float|bool|void|std::(string|vector|map))\>"
|
||||
|
@ -13,6 +13,7 @@
|
||||
<item>virtual-method</item>
|
||||
<item>static-method</item>
|
||||
<item>constructor</item>
|
||||
<item>vtable-destructor-offset</item>
|
||||
</list>
|
||||
<list name="types">
|
||||
<item>char</item>
|
||||
|
Loading…
Reference in New Issue
Block a user