Pure Evil

This commit is contained in:
TheBrokenRail 2024-04-03 03:18:51 -04:00
parent 842f05a288
commit 67c4adaa77
9 changed files with 139 additions and 156 deletions

View File

@ -163,4 +163,31 @@ export class Size {
isExact() {
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);
}

View File

@ -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;
}

View File

@ -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);

View File

@ -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}`);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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');
}
}
}

View File

@ -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))\>"

View File

@ -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>