166 lines
4.8 KiB
TypeScript
166 lines
4.8 KiB
TypeScript
import * as fs from 'node:fs';
|
|
import { isCppAllowed } from './map';
|
|
|
|
export const INDENT = ' ';
|
|
export const POINTER_SIZE = 4;
|
|
export const EXTENSION = '.def';
|
|
export const STRUCTURE_FILES: {[id: string]: string} = {};
|
|
export function readDefinition(name: string) {
|
|
if (!STRUCTURE_FILES[name]) {
|
|
throw new Error(`Missing Definition File: ${name}${EXTENSION}`);
|
|
}
|
|
return fs.readFileSync(STRUCTURE_FILES[name]!, {encoding: 'utf8'});
|
|
}
|
|
export function syntaxError(message?: string) {
|
|
throw new Error('Syntax Error' + (message ? `: ${message}` : ''));
|
|
}
|
|
export function parseTypeAndName(piece: string) {
|
|
// Split On Last Space
|
|
const index = piece.lastIndexOf(' ');
|
|
if (index === -1) {
|
|
syntaxError('Unable To Find Name/Type Divider');
|
|
}
|
|
let name = piece.substring(index + 1)!;
|
|
let type = piece.substring(0, index)!;
|
|
// Move Asterisks From Name To Type
|
|
while (name.startsWith('*')) {
|
|
name = name.substring(1);
|
|
if (!type.endsWith('*')) {
|
|
type += ' ';
|
|
}
|
|
type += '*';
|
|
}
|
|
// Return
|
|
return {type, name};
|
|
}
|
|
export const MIN_SIZE = 1;
|
|
export function toUpperSnakeCase(str: string) {
|
|
let wasUpper = false;
|
|
let nextIsUpper = false;
|
|
let out = '';
|
|
for (let i = 0; i < str.length; i++) {
|
|
let character = str.charAt(i);
|
|
if (character === '_') {
|
|
wasUpper = false;
|
|
nextIsUpper = true;
|
|
continue;
|
|
}
|
|
const isUpper = character === character.toUpperCase() || nextIsUpper;
|
|
nextIsUpper = false;
|
|
character = character.toUpperCase();
|
|
if (isUpper && i > 0 && !wasUpper) {
|
|
out += '_';
|
|
}
|
|
out += character;
|
|
wasUpper = isUpper;
|
|
}
|
|
return out;
|
|
}
|
|
export function formatType(type: string) {
|
|
if (!type.endsWith('*')) {
|
|
type += ' ';
|
|
}
|
|
return type;
|
|
}
|
|
export const COMMENT = '//';
|
|
export function toHex(x: number) {
|
|
return '0x' + x.toString(16);
|
|
}
|
|
export function getAssertFunction() {
|
|
return isCppAllowed() ? 'static_assert' : '_Static_assert';
|
|
}
|
|
export function assertSize(name: string, size: number, isDefined: boolean) {
|
|
let out = '';
|
|
// Define Size Macro
|
|
const macro = toUpperSnakeCase(name) + '_SIZE';
|
|
out += `#define ${macro} ${toHex(size)}\n`;
|
|
// Check Size
|
|
out += `${getAssertFunction()}(sizeof (${name}) == ${macro}, "Invalid Size");\n`;
|
|
// Hide Structure Size If The Real Size Is Unknown
|
|
if (!isDefined) {
|
|
out += `#undef ${macro}\n`;
|
|
}
|
|
// Return
|
|
return out;
|
|
}
|
|
export function safeParseInt(str: string) {
|
|
const x = parseInt(str);
|
|
if (isNaN(x)) {
|
|
throw new Error('Invalid Integer: ' + str);
|
|
}
|
|
return x;
|
|
}
|
|
export function stripArrayData(propertyName: string) {
|
|
const index = propertyName.indexOf('[');
|
|
if (index !== -1) {
|
|
propertyName = propertyName.substring(0, index);
|
|
}
|
|
return propertyName;
|
|
}
|
|
export function getSizeMultiplierFromArrayData(propertyName: string) {
|
|
let multiplier = 1;
|
|
// Check If Array Data Is Present
|
|
const index = propertyName.indexOf('[');
|
|
if (index === -1) {
|
|
return multiplier;
|
|
}
|
|
propertyName = propertyName.substring(index + 1);
|
|
// Strip Last ]
|
|
if (!propertyName.endsWith(']')) {
|
|
syntaxError('Expecting ]');
|
|
} else {
|
|
propertyName = propertyName.substring(0, propertyName.length - 1);
|
|
}
|
|
// Split
|
|
const parts = propertyName.split('][');
|
|
// Calculate Multiplier
|
|
for (const part of parts) {
|
|
multiplier *= safeParseInt(part);
|
|
}
|
|
// Return
|
|
return multiplier;
|
|
}
|
|
export function getSelfArg(type: string) {
|
|
return `${type} *self`;
|
|
}
|
|
export class Size {
|
|
#size: number | null;
|
|
#isExact: boolean;
|
|
readonly #pointerAligned: boolean;
|
|
|
|
constructor(pointerAligned: boolean) {
|
|
this.#size = null;
|
|
this.#isExact = false;
|
|
this.#pointerAligned = pointerAligned;
|
|
}
|
|
|
|
has() {
|
|
return this.#size !== null;
|
|
}
|
|
get() {
|
|
if (this.#size === null) {
|
|
throw new Error('No Size Specified');
|
|
}
|
|
return this.#size;
|
|
}
|
|
set(size: number, isExact: boolean) {
|
|
if (size < MIN_SIZE) {
|
|
throw new Error(`Size Too Small: ${toHex(size)}`);
|
|
}
|
|
if (this.#isExact) {
|
|
throw new Error('Cannot Change Size After Being Set By Base Structure');
|
|
}
|
|
if (this.#size !== null && size < this.#size) {
|
|
throw new Error('Cannot Decrease VTable Size');
|
|
}
|
|
this.#size = size;
|
|
this.#isExact = isExact;
|
|
if (this.#pointerAligned && (this.#size % POINTER_SIZE) !== 0) {
|
|
throw new Error(`Invalid Size: ${toHex(this.#size)}`);
|
|
}
|
|
}
|
|
// Whether This Is An Exact Limit Or Just A Lower Bound
|
|
isExact() {
|
|
return this.#isExact;
|
|
}
|
|
} |