diff --git a/src/common.ts b/src/common.ts index 36eb92d..7915320 100644 --- a/src/common.ts +++ b/src/common.ts @@ -3,9 +3,12 @@ import * as fs from 'node:fs'; export const INDENT = ' '; export const POINTER_SIZE = 4; export const EXTENSION = '.def'; +export const STRUCTURE_FILES: {[id: string]: string} = {}; export function readDefinition(name: string) { - const file = `${__dirname}/../symbols/${name}${EXTENSION}`; - return fs.readFileSync(file, {encoding: 'utf8'}); + if (!STRUCTURE_FILES[name]) { + throw new Error(`Missing Definition File: ${name}${EXTENSION}`); + } + return fs.readFileSync(STRUCTURE_FILES[name]!, {encoding: 'utf8'}); } export function parseTypeAndName(parts: string[]) { let type = parts[0]!; diff --git a/src/index.ts b/src/index.ts index 7b0083a..877fb00 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,29 +1,50 @@ import * as fs from 'node:fs'; -import { EXTENSION, INDENT } from './common'; +import { STRUCTURE_FILES, EXTENSION, INDENT } from './common'; import { getStructure, setCppAllowed } from './map'; -// Output Directory -let outputDirectory = process.argv[2]; -if (!outputDirectory) { - outputDirectory = `${__dirname}/../out`; +// Arguments +if (process.argv.length < 5) { + console.log('USAGE: npm run
'); + process.exit(1); +} +process.argv.shift(); +process.argv.shift(); +function invalidFileType(file: string) { + throw new Error(`Invalid File Type: ${file}`); +} +const sourceOutput = process.argv.shift()!; +if (!sourceOutput.endsWith('.cpp')) { + invalidFileType(sourceOutput); +} +const headerOutput = process.argv.shift()!; +if (!headerOutput.endsWith('.h')) { + invalidFileType(headerOutput); +} +const extraHeaders: string[] = []; +while (process.argv.length > 0) { + const file = process.argv.shift()!; + if (file.endsWith(EXTENSION)) { + // Get Class Name + const parts = file.split('/'); + let name = parts[parts.length - 1]!; + name = name.substring(0, name.length - EXTENSION.length); + // Store + STRUCTURE_FILES[name] = file; + } else if (file.endsWith('.h')) { + // Extra Headers + extraHeaders.push(file); + } else { + // Invalid File Type + invalidFileType(file); + } } -fs.rmSync(outputDirectory, {force: true, recursive: true}); -fs.mkdirSync(outputDirectory, {recursive: true}); -// Main +// Load And Sort Structures function loadSymbols() { // Load - const files = fs.readdirSync(`${__dirname}/../symbols`); const structureObjects = []; - for (const file of files) { - if (file.endsWith(EXTENSION)) { - // Get Name - const parts = file.split('/'); - let name = parts[parts.length - 1]!; - name = name.substring(0, name.length - EXTENSION.length); - // Structures - structureObjects.push(getStructure(name)); - } + for (const name in STRUCTURE_FILES) { + structureObjects.push(getStructure(name)); } // Sort structureObjects.sort((a, b) => { @@ -38,20 +59,15 @@ function loadSymbols() { // Return return structureObjects; } -function makeHeader(output: string, allowCpp: boolean) { + +// Generate Part Of Header +function makeHeaderPart(allowCpp: boolean) { // Set Mode setCppAllowed(allowCpp); // Load Symbols const structureObjects = loadSymbols(); - // Forward Declarations - let forwardDeclarations = '// Forward Declarations\n'; - for (const structure of structureObjects) { - const name = structure.getName(); - forwardDeclarations += `typedef struct ${name} ${name};\n`; - } - // Sort Structures By Dependency while (true) { let valid = true; @@ -84,9 +100,15 @@ function makeHeader(output: string, allowCpp: boolean) { // Generate Code let structures = ''; + let isFirst = true; for (const structure of structureObjects) { const name = structure.getName(); - structures += `\n// ${name}\n`; + if (isFirst) { + isFirst = false; + } else { + structures += '\n'; + } + structures += `// ${name}\n`; try { structures += structure.generate(); } catch (e) { @@ -95,22 +117,50 @@ function makeHeader(output: string, allowCpp: boolean) { } } - // Write - let result = ''; - result += '#pragma once\n'; - result += '\n'; - result += forwardDeclarations; - result += '\n// Init\n'; - result += 'void init_symbols();'; - result += '\n// Extra Definitions\n'; - result += fs.readFileSync(`${__dirname}/../symbols/extra.h`, {encoding: 'utf8'}).trim() + '\n'; - result += structures; - fs.writeFileSync(output, result); + // Return + return structures; } -// Run -makeHeader(`${outputDirectory}/minecraft_c.h`, false); -makeHeader(`${outputDirectory}/minecraft_cpp.h`, true); +// Create Main Header +function makeMainHeader(output: string) { + let result = ''; + result += '#pragma once\n'; + result += '\n// Suppress Warnings\n'; + result += '#pragma GCC diagnostic push\n'; + result += '#pragma GCC diagnostic ignored "-Wunused-variable"\n'; + result += '#pragma GCC diagnostic ignored "-Wunused-function"\n'; + result += '\n// Shortcuts\n'; + result += 'typedef unsigned char uchar;\n'; + result += 'typedef unsigned int uint;\n'; + result += '\n// Init\n'; + result += 'void init_symbols();\n'; + result += '\n// Forward Declarations\n'; + for (const name in STRUCTURE_FILES) { + result += `typedef struct ${name} ${name};\n`; + }result += '\n// Extra Headers\n'; + for (const file of extraHeaders) { + result += fs.readFileSync(file, {encoding: 'utf8'}); + } + result += '\n// Switch Mode\n'; + result += '#ifndef __cplusplus\n'; + result += '\n// C Mode\n'; + result += 'typedef uchar bool;\n\n'; + result += makeHeaderPart(false); + result += '\n// End C Mode\n'; + result += '#else\n'; + result += '\n// C++ Mode\n'; + result += '#include \n'; + result += '#include \n'; + result += 'extern "C" {\n\n'; + result += makeHeaderPart(true); + result += '\n// End C++ Mode\n'; + result += '}\n'; + result += '#endif\n'; + result += '\n// Stop Suppressing Warnings\n'; + result += '#pragma GCC diagnostic pop\n'; + fs.writeFileSync(output, result); +} +makeMainHeader(headerOutput); // Generate Compiled Code function makeCompiledCode(output: string) { @@ -122,10 +172,17 @@ function makeCompiledCode(output: string) { // Generate let declarations = ''; let init = ''; + let isFirst = true; for (const structure of structureObjects) { const name = structure.getName(); - declarations += `\n// ${name}\n`; - init += `\n${INDENT}// ${name}\n`; + if (isFirst) { + isFirst = false; + } else { + declarations += '\n'; + init += '\n'; + } + declarations += `// ${name}\n`; + init += `${INDENT}// ${name}\n`; try { const code = structure.generateCode(); declarations += code.functions; @@ -138,39 +195,13 @@ function makeCompiledCode(output: string) { // Write let result = ''; - result += '#include "minecraft.h"\n'; + result += `#include "${fs.realpathSync(headerOutput)}"\n`; + result += '\n#include \n'; result += '\n// Init\n'; - result += 'void init_symbols() {'; + result += 'void init_symbols() {\n'; result += init; - result += '}\n'; + result += '}\n\n'; result += declarations; fs.writeFileSync(output, result); } -makeCompiledCode(`${outputDirectory}/minecraft.cpp`); - -// Create Main Header -function makeMainHeader(output: string) { - let result = ''; - result += '#pragma once\n'; - result += '#pragma GCC diagnostic push\n'; - result += '#pragma GCC diagnostic ignored "-Wunused-variable"\n'; - result += '#pragma GCC diagnostic ignored "-Wunused-function"\n'; - result += 'typedef unsigned char uchar;\n'; - result += 'typedef unsigned int uint;\n'; - result += '#include \n'; - result += '#include \n'; - result += '#include \n'; - result += '#ifndef __cplusplus\n'; - result += 'typedef unsigned char bool;\n'; - result += '#include "minecraft_c.h"\n'; - result += '#else\n'; - result += '#include \n'; - result += '#include \n'; - result += 'extern "C" {\n'; - result += '#include "minecraft_cpp.h"\n'; - result += '}\n'; - result += '#endif\n'; - result += '#pragma GCC diagnostic pop\n'; - fs.writeFileSync(output, result); -} -makeMainHeader(`${outputDirectory}/minecraft.h`); \ No newline at end of file +makeCompiledCode(sourceOutput); \ No newline at end of file diff --git a/src/loader.ts b/src/loader.ts index 0f5f18a..88f7ba6 100644 --- a/src/loader.ts +++ b/src/loader.ts @@ -4,6 +4,7 @@ import { Method } from './method'; import { SimpleProperty, StaticProperty } from './property'; import { Struct } from './struct'; +// Error Handling function safeParseInt(str: string) { const x = parseInt(str); if (isNaN(x)) { @@ -14,7 +15,6 @@ function safeParseInt(str: string) { function syntaxError(message?: string) { throw new Error('Syntax Error' + (message ? `: ${message}` : '')); } - export class ErrorOnLine { readonly error: unknown; readonly file: string; @@ -199,14 +199,6 @@ export function load(target: Struct, name: string, isExtended: boolean) { } break; } - case 'requires': { - // Add Dependency - if (args.length === 0) { - syntaxError('Missing Dependency Name'); - } - target.addDependency(args); - break; - } default: { throw new Error(`Invalid Command: ${command}`); } diff --git a/src/map.ts b/src/map.ts index d08a23f..cfba07f 100644 --- a/src/map.ts +++ b/src/map.ts @@ -16,7 +16,7 @@ const structures: {[id: string]: Struct} = {}; // Get Or Load Structure export function getStructure(name: string) { - if (Object.prototype.hasOwnProperty.call(structures, name)) { + if (name in structures) { // Already Loaded return structures[name]!; } else { @@ -45,8 +45,6 @@ export function getStructure(name: string) { // Clear Loaded Structures function clearStructures() { for (const name in structures) { - if (Object.prototype.hasOwnProperty.call(structures, name)) { - delete structures[name]; - } + delete structures[name]; } } \ No newline at end of file diff --git a/src/struct.ts b/src/struct.ts index bcff7fa..7f3f392 100644 --- a/src/struct.ts +++ b/src/struct.ts @@ -1,4 +1,4 @@ -import { INDENT, MIN_SIZE, toUpperSnakeCase, formatType } from './common'; +import { INDENT, MIN_SIZE, toUpperSnakeCase, formatType, STRUCTURE_FILES } from './common'; import { isCppAllowed } from './map'; import { Method } from './method'; import { Property, StaticProperty } from './property'; @@ -25,7 +25,7 @@ export class Struct { } // Dependencies - addDependency(dependency: string) { + #addDependency(dependency: string) { this.#dependencies.push(dependency); } getDependencies() { @@ -94,6 +94,11 @@ export class Struct { // Add Property addProperty(property: Property) { this.#properties.push(property); + // Add Dependency If Needed + const type = property.propertyType(); + if (type in STRUCTURE_FILES) { + this.#addDependency(type); + } } // Add Static Property addStaticProperty(property: StaticProperty) {