diff --git a/package.json b/package.json index 6ef6089..5b1b6a0 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,11 @@ "command": "bg3-modding.generateHandle", "title": "Generate Handle and insert at selection", "category": "BG3" + }, + { + "command": "bg3-modding.convertVersionNumber", + "title": "Convert BG3 Version Number", + "category": "BG3" } ], "jsonValidation": [ diff --git a/src/commands/convertVersionNumber.ts b/src/commands/convertVersionNumber.ts new file mode 100644 index 0000000..6c5a7f6 --- /dev/null +++ b/src/commands/convertVersionNumber.ts @@ -0,0 +1,46 @@ +// Library +import * as vscode from 'vscode'; + +// Helpers +import { bg3, editor } from '../helpers'; + +/** Convert Int64 version number to string format or vice-versa */ +export async function convertVersionNumber() { + + // Prompt the user for the version number + const v = await vscode.window.showInputBox({ + title: "Version", + prompt: "Version Number", + placeHolder: 'Example: 1.0.0.0 or 36028797018963968', + + // Pre-fill the value with the currently selected text + value: editor.getSelection(), + + // Perform validation on the input value + validateInput: (value) => { + if (value.includes(".")) { // String version format (1.0.0.0) + if (!(/\d+\.\d+\.\d+\.\d+/.test(value))) { // Try parsing major.minor.revision.build + return "Invalid Version Number! Format: {Major}.{Minor}.{Revision}.{Build}"; + } + } else { // BigInt format (36028797018963968) + try { + BigInt(value); // Try parsing as BigInt ... + } catch (e) { // ... if that fails, the input is invalid as it doesn't match either format + return "Invalid Version Number! Example: 1.0.0.0 or 36028797018963968"; + } + } + } + }); + + // Return early if the input is empty + if (!v) { return; } + + // Convert the version from one format to the other + const result = v.includes(".") + ? new bg3.Version(v).toInt64().toString() + : new bg3.Version(BigInt(v)).toString(); + + // Insert the version at the cursor selection + editor.insertAtSelection(result); + +} diff --git a/src/commands/index.ts b/src/commands/index.ts index bba6afe..45f11aa 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -1,5 +1,6 @@ // Library import * as vscode from 'vscode'; +import * as constants from '../constants'; // ======== // COMMANDS @@ -7,15 +8,16 @@ import * as vscode from 'vscode'; import { generateUUID } from "./generateUUID"; import { generateHandle } from './generateHandle'; +import { convertVersionNumber } from './convertVersionNumber'; -/** Enumeration of the command IDs */ -enum Command { - GenerateUUID = "bg3-modding.generateUUID", - GenerateHandle = "bg3-modding.generateHandle", +/** Registers the command to VS Code */ +function registerCommand(command: (...args: any[]) => any): vscode.Disposable { + return vscode.commands.registerCommand(`${constants.EXTENSION_ID}.${command.name}`, command); } /** An array of disposables for the registered commands */ export const commands: vscode.Disposable[] = [ - vscode.commands.registerCommand(Command.GenerateUUID, generateUUID), - vscode.commands.registerCommand(Command.GenerateHandle, generateHandle) + registerCommand(generateUUID), + registerCommand(generateHandle), + registerCommand(convertVersionNumber), ]; diff --git a/src/constants.ts b/src/constants.ts new file mode 100644 index 0000000..a22599b --- /dev/null +++ b/src/constants.ts @@ -0,0 +1,6 @@ +// ========= +// CONSTANTS +// ========= + +/** ID of the extension. Must match the `package.json` */ +export const EXTENSION_ID = "bg3-modding"; diff --git a/src/helpers/bg3/Version.ts b/src/helpers/bg3/Version.ts index 1fce614..2887b73 100644 --- a/src/helpers/bg3/Version.ts +++ b/src/helpers/bg3/Version.ts @@ -13,18 +13,39 @@ export class Version { /** Regular expression to match the version line in `meta.lsx` */ public static readonly lsxRegex = //; - /** - * Parse the Version number - * @param num The long Int64 Version number as specified in the `meta.lsx` file - */ - constructor(num: bigint) { - this.major = Number(num >> 55n); - this.minor = Number((num >> 47n) & 0xFFn); - this.revision = Number((num >> 31n) & 0xFFFFn); - this.build = Number(num & 0xFFFFFFn); + /** Parse the Version number */ + constructor(x: bigint); + constructor(x: string); + constructor(x: bigint | string) { + + // Use the string constructor ... + if (typeof x === 'string') { + const v = x.split("."); + this.major = parseInt(v[0]); + this.minor = parseInt(v[1]); + this.revision = parseInt(v[2]); + this.build = parseInt(v[3]); + } + // ... else, use the bigint constructor. + else { + this.major = Number(x >> 55n); + this.minor = Number((x >> 47n) & 0xFFn); + this.revision = Number((x >> 31n) & 0xFFFFn); + this.build = Number(x & 0xFFFFFFn); + } + + } + + /** @returns Int64 representation of the version number */ + toInt64(): bigint { + const major = BigInt(this.major) << 55n; + const minor = (BigInt(this.minor) << 47n); + const revision = (BigInt(this.revision) << 31n); + const build = (BigInt(this.build)); + return major + minor + revision + build; } - /** Return the string representation of the Version number. (e.g `1.0.0.0`) */ + /** @returns String representation of the Version number. (e.g `1.0.0.0`) */ toString(): string { return `${this.major}.${this.minor}.${this.revision}.${this.build}`; } diff --git a/src/helpers/editor.ts b/src/helpers/editor.ts index 9341e17..fe3e4e8 100644 --- a/src/helpers/editor.ts +++ b/src/helpers/editor.ts @@ -23,3 +23,11 @@ export function insertAtSelection(text: string) { }); }); } + +/** + * Returns the currently selected text in the editor. Returns `undefined` + * if nothing has been selected. + */ +export function getSelection(): string | undefined { + return vscode.window.activeTextEditor?.document.getText(vscode.window.activeTextEditor.selection); +} diff --git a/src/test/extension.test.ts b/src/test/extension.test.ts index 1d234f5..8377ed2 100644 --- a/src/test/extension.test.ts +++ b/src/test/extension.test.ts @@ -43,24 +43,53 @@ suite('Extension Test Suite', () => { const actualHandle = bg3.convertToHandle(t.testUUID); assert.strictEqual(actualHandle, t.expectedHandle); } + }); test('Conversion of Version Numbers', () => { - const testSuite = [ + const testSuiteInt64 = [ { int64Version: 36028797018963968n, expected: "1.0.0.0" + }, + { + int64Version: 36310278438125572n, + expected: "1.2.3.4" + }, + { + int64Version: 36451024516415602n, + expected: "1.3.7.114" } - // TODO: Add more tests here ... ]; - for (const t of testSuite) { + for (const t of testSuiteInt64) { const version = new bg3.Version(t.int64Version); const actual = version.toString(); assert.strictEqual(actual, t.expected); } + const testSuiteStrings = [ + { + stringVersion: "1.0.0.0", + expected: 36028797018963968n + }, + { + stringVersion: "1.2.3.4", + expected: 36310278438125572n + }, + { + stringVersion: "1.3.7.114", + expected: 36451024516415602n + } + ]; + + for (const t of testSuiteStrings) { + const version = new bg3.Version(t.stringVersion); + const actual = version.toInt64(); + assert.strictEqual(actual, t.expected); + } + }); });