diff --git a/.vscodeignore b/.vscodeignore
index 16021362..f227a4a6 100644
--- a/.vscodeignore
+++ b/.vscodeignore
@@ -5,5 +5,4 @@ vsc-extension-quickstart.md
**/jsconfig.json
**/*.map
**/.eslintrc.json
-**/update_notes.txt
-node_modules/**
\ No newline at end of file
+**/update_notes.txt
\ No newline at end of file
diff --git a/commands/convertVideoToGif.js b/commands/convertVideoToGif.js
new file mode 100644
index 00000000..37acb059
--- /dev/null
+++ b/commands/convertVideoToGif.js
@@ -0,0 +1,143 @@
+const vscode = require('vscode');
+const fs = require('fs');
+const path = require('path');
+const ffmpeg = require('fluent-ffmpeg');
+const ffmpegPath = require('ffmpeg-static');
+
+const { getConfig } = require('../support_files/config');
+const { rootModPath } = getConfig();
+
+console.log("FFmpeg Path:", ffmpegPath);
+console.log("Type of FFmpeg Path:", typeof ffmpegPath);
+
+if (typeof ffmpegPath === 'string') {
+ ffmpeg.setFfmpegPath(ffmpegPath);
+} else {
+ console.error("FFmpeg Path is not a string");
+}
+
+// Command to open the video conversion webview
+const convertVideoToGifCommand = vscode.commands.registerCommand('bg3-mod-helper.convertVideoToGif', async function () {
+ const panel = vscode.window.createWebviewPanel(
+ 'videoConversion',
+ 'Video Conversion',
+ vscode.ViewColumn.One,
+ { enableScripts: true }
+ );
+
+ const videoFiles = await getAllVideoFiles(rootModPath);
+
+ panel.webview.html = getWebviewContent(videoFiles);
+
+ // Handle messages from the webview
+ panel.webview.onDidReceiveMessage(async message => {
+ switch (message.command) {
+ case 'convert':
+ await convertVideoToGifFile(message.videoPath, message.gifPath);
+ break;
+ case 'convertAll':
+ for (const video of videoFiles) {
+ const gifPath = video.replace(path.extname(video), '.gif');
+ await convertVideoToGifFile(video, gifPath);
+ }
+ break;
+ case 'selectFile':
+ const options = {
+ canSelectMany: false,
+ openLabel: 'Select a video file',
+ filters: { 'Video files': ['mp4', 'mkv', 'avi', 'mov'] }
+ };
+ const fileUri = await vscode.window.showOpenDialog(options);
+ if (fileUri && fileUri[0]) {
+ const videoPath = fileUri[0].fsPath;
+ const gifPath = videoPath.replace(path.extname(videoPath), '.gif');
+ await convertVideoToGifFile(videoPath, gifPath);
+ }
+ break;
+ }
+ });
+});
+
+async function getAllVideoFiles(dir) {
+ let files = [];
+ const items = await fs.promises.readdir(dir, { withFileTypes: true });
+ for (const item of items) {
+ const fullPath = path.join(dir, item.name);
+ if (item.isDirectory()) {
+ files = files.concat(await getAllVideoFiles(fullPath));
+ } else if (/\.(mp4|mkv|avi|mov)$/i.test(item.name)) {
+ files.push(fullPath);
+ }
+ }
+ return files;
+}
+
+function getWebviewContent(videoFiles) {
+ const videoFileItems = videoFiles.map(file => `
+
+
${path.basename(file)}
+
${file}
+
+
+ `).join('');
+
+ return `
+
+
+
Video Conversion
+
+
+
Name
+
Path
+
Action
+
+ ${videoFileItems}
+
+
+
+
+
+
+ `;
+}
+
+async function convertVideoToGifFile(inputPath, outputPath) {
+ return new Promise((resolve, reject) => {
+ const normalizedInput = path.normalize(inputPath);
+ const normalizedOutput = path.normalize(outputPath);
+
+ console.log("Normalized Input Path:", normalizedInput);
+ console.log("Normalized Output Path:", normalizedOutput);
+
+ ffmpeg(normalizedInput)
+ .outputOptions([
+ '-vf', 'fps=24', // Increase fps for smoother animation
+ '-gifflags', 'transdiff',
+ '-y', // Overwrite output files without asking
+ '-q:v', '5' // Set quality level (lower is better, 0 is the best quality)
+ ])
+ .save(normalizedOutput)
+ .on('end', () => {
+ vscode.window.showInformationMessage(`GIF created: ${normalizedOutput}`);
+ resolve();
+ })
+ .on('error', (err) => {
+ vscode.window.showErrorMessage(`Error: ${err.message}`);
+ reject(err);
+ });
+ });
+}
+
+
+module.exports = { convertVideoToGifCommand };
diff --git a/commands/folderShortcuts.js b/commands/folderShortcuts.js
index 928ee2ff..3b6ae1ee 100644
--- a/commands/folderShortcuts.js
+++ b/commands/folderShortcuts.js
@@ -49,4 +49,16 @@ const openWorkspaceFolderCommand = vscode.commands.registerCommand('bg3-mod-help
}
});
-module.exports = { openModsFolderCommand, openGameFolderCommand, openLogsFolderCommand, openWorkspaceFolderCommand };
\ No newline at end of file
+const openPlayerProfilesFolderCommand = vscode.commands.registerCommand('bg3-mod-helper.openPlayerProfilesFolder', async () => {
+ const appDataPath = process.env.LOCALAPPDATA;
+ const playerProfilesPath = path.join(appDataPath, 'Larian Studios', "Baldur's Gate 3", 'PlayerProfiles');
+
+ try {
+ await fs.access(playerProfilesPath);
+ vscode.env.openExternal(vscode.Uri.file(playerProfilesPath));
+ } catch (error) {
+ vscode.window.showInformationMessage('PlayerProfiles folder not found.');
+ }
+});
+
+module.exports = { openModsFolderCommand, openGameFolderCommand, openLogsFolderCommand, openWorkspaceFolderCommand, openPlayerProfilesFolderCommand };
\ No newline at end of file
diff --git a/commands/indentXmlFiles.js b/commands/indentXmlFiles.js
new file mode 100644
index 00000000..740cd4ec
--- /dev/null
+++ b/commands/indentXmlFiles.js
@@ -0,0 +1,64 @@
+const vscode = require('vscode');
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+const { raiseError, raiseInfo } = require('../support_files/log_utils');
+const { getConfig, loadConfigFile, setModName, setConfig } = require('../support_files/config');
+const builder = require('xmlbuilder');
+
+async function indentXmlFiles() {
+ const config = getConfig();
+ const rootModPath = config.rootModPath;
+ const indentLevel = await vscode.window.showInputBox({
+ prompt: 'Enter the number of spaces for indentation',
+ validateInput: (value) => {
+ const num = parseInt(value, 10);
+ return isNaN(num) || num < 0 ? 'Please enter a valid non-negative number' : null;
+ }
+ });
+
+ if (!indentLevel) {
+ return; // User cancelled the input box
+ }
+
+ const findFiles = (dir, extensions, fileList = []) => {
+ fs.readdirSync(dir).forEach(file => {
+ const filePath = path.join(dir, file);
+ if (fs.statSync(filePath).isDirectory()) {
+ // Skip the Localization folder
+ if (path.basename(filePath).toLowerCase() !== 'localization') {
+ fileList = findFiles(filePath, extensions, fileList);
+ }
+ } else if (extensions.some(ext => filePath.endsWith(ext))) {
+ fileList.push(filePath);
+ }
+ });
+ return fileList;
+ };
+
+ const files = findFiles(rootModPath, ['.lsx', '.lsj']);
+
+ for (const filePath of files) {
+ let fileContent = fs.readFileSync(filePath, 'utf-8');
+ // Remove BOM if it exists
+ if (fileContent.charCodeAt(0) === 0xFEFF) {
+ fileContent = fileContent.slice(1);
+ }
+ try {
+ const doc = builder.create(fileContent, { headless: true });
+ const formattedContent = doc.end({ pretty: true, indent: ' '.repeat(parseInt(indentLevel, 10)) });
+ fs.writeFileSync(filePath, formattedContent, 'utf-8');
+ raiseInfo(`File ${filePath} has been formatted with an indent level of ${indentLevel} spaces.`);
+ } catch (error) {
+ raiseError(`Failed to process file ${filePath}: ${error.message}`);
+ }
+ }
+
+ raiseInfo('XML files formatting process completed.');
+}
+
+const indentXmlFilesCommand = vscode.commands.registerCommand('bg3-mod-helper.indentXmlFilesCommand', async function () {
+ await indentXmlFiles();
+});
+
+module.exports = { indentXmlFilesCommand };
diff --git a/commands/insertHandleUUID.js b/commands/insertHandleUUID.js
index 0951354b..0fdc6571 100644
--- a/commands/insertHandleUUID.js
+++ b/commands/insertHandleUUID.js
@@ -94,6 +94,14 @@ let handleDisposable = vscode.commands.registerCommand('bg3-mod-helper.insertHan
// Update localization files with the handle
await updateLocaXmlFiles(changes);
+
+ // Save the updated localization files
+ const locaFiles = await vscode.workspace.findFiles('**/Localization/**/*.xml');
+ for (const locaFile of locaFiles) {
+ const document = await vscode.workspace.openTextDocument(locaFile);
+ await document.save(); // Save each localization XML file directly
+ }
+
console.log(`Handle ${handle} created with initial value: ${userText}`);
}
}
@@ -103,7 +111,60 @@ let handleDisposable = vscode.commands.registerCommand('bg3-mod-helper.insertHan
vscode.window.showInformationMessage('Handles inserted and localization files updated successfully.');
} else {
console.error('Apply Edit failed:', workspaceEdit);
- vscode.window.showErrorMessage('Failed to replace the UUIDs.');
+ vscode.window.showErrorMessage('Failed to replace the handle.');
+ }
+});
+
+// Command to generate and replace all handles
+let handleReplaceDisposable = vscode.commands.registerCommand('bg3-mod-helper.generateReplaceAllHandles', async function () {
+ const editor = vscode.window.activeTextEditor;
+ if (!editor) {
+ vscode.window.showInformationMessage('You need to open an editor window to use this command');
+ return;
+ }
+
+ const document = editor.document;
+ const selection = editor.selection;
+ const selectedText = document.getText(selection);
+
+ // Validate the selected text as handle
+ const handleRegex = /^h[0-9a-fA-Fg]{32}[0-9a-fA-Fg]{4}$/i;
+ console.log(handleRegex)
+ console.log(selectedText)
+ if (!handleRegex.test(selectedText)) {
+ vscode.window.showErrorMessage('The selected text is not a valid handle.');
+ return;
+ }
+
+ const newHandle = generateHandle();
+ const globalRegex = new RegExp(selectedText, 'gi'); // Global case-insensitive search
+ const workspaceEdit = new vscode.WorkspaceEdit();
+ let documentsToSave = new Set();
+
+ // Search across all text files in the workspace
+ const files = await vscode.workspace.findFiles('**/*.{txt,lsx,lsj,xml}');
+ for (const file of files) {
+ const textDoc = await vscode.workspace.openTextDocument(file);
+ const text = textDoc.getText();
+ let match;
+ while ((match = globalRegex.exec(text)) !== null) {
+ const startPos = textDoc.positionAt(match.index);
+ const endPos = textDoc.positionAt(match.index + match[0].length);
+ const range = new vscode.Range(startPos, endPos);
+ workspaceEdit.replace(file, range, newHandle);
+ documentsToSave.add(textDoc);
+ }
+ }
+
+ // Apply/save all collected edits
+ if (await vscode.workspace.applyEdit(workspaceEdit)) {
+
+ for (const doc of documentsToSave) {
+ await doc.save();
+ }
+ vscode.window.showInformationMessage(`Replaced all occurrences of the handle '${selectedText}' with '${newHandle}' and saved changes. Use undo keyboard shortcut to revert all changes back at once (dont forget to save the files if you do).`);
+ } else {
+ vscode.window.showErrorMessage('Failed to replace the handles.');
}
});
@@ -122,7 +183,7 @@ async function updateLocaXmlFiles(changes) {
}
const locaFilePattern = new vscode.RelativePattern(workspaceFolder, '**/Localization/**/*.xml');
- const locaFiles = await vscode.workspace.findFiles(locaFilePattern, '**/node_modules/**');
+ const locaFiles = await vscode.workspace.findFiles(locaFilePattern);
if (locaFiles.length === 0) {
vscode.window.showWarningMessage(`No .xml files found under Localization/. You can create one with the 'Create BG3 File' command.`, 'Create BG3 File').then(selection => {
if (selection === 'Create BG3 File') {
diff --git a/commands/launchGame.js b/commands/launchGame.js
index 56a25584..70bf1db5 100644
--- a/commands/launchGame.js
+++ b/commands/launchGame.js
@@ -6,15 +6,17 @@ const { getConfig } = require('../support_files/config');
const launchGameCommand = vscode.commands.registerCommand('bg3-mod-helper.launchGame', function () {
- const { launchContinueGame, gameInstallLocation } = getConfig();
+ const { launchContinueGame, gameInstallLocation, laucherAPI } = getConfig();
if (!gameInstallLocation || gameInstallLocation === "") {
vscode.window.showErrorMessage('Game installation location is not set. Please configure it correctly in settings.');
return; // Stop execution if the path is not set
}
+ const executableName = laucherAPI === 'DirectX' ? 'bg3_dx11.exe' : 'bg3.exe';
+
// Construct the path to the executable
const binLocation = path.join(gameInstallLocation, 'bin');
- const gamePath = path.join(binLocation, "bg3.exe");
+ const gamePath = path.join(binLocation, executableName);
const args = launchContinueGame ? ["-continueGame"] : [];
const game = spawn(gamePath, args, { cwd: binLocation });
diff --git a/commands/organizeDataFiles.js b/commands/organizeDataFiles.js
new file mode 100644
index 00000000..1e65ac89
--- /dev/null
+++ b/commands/organizeDataFiles.js
@@ -0,0 +1,115 @@
+const vscode = require('vscode');
+const fs = require('fs');
+const path = require('path');
+const os = require('os');
+const { raiseError, raiseInfo } = require('../support_files/log_utils');
+const { getConfig, loadConfigFile, setModName, setConfig } = require('../support_files/config');
+
+function sortEntriesInFile(filePath) {
+ if (!fs.existsSync(filePath)) {
+ return null;
+ }
+
+ const fileContent = fs.readFileSync(filePath, 'utf-8');
+ const entries = fileContent.split(/(?:\r\n|\r|\n)(?=new entry )/).filter(entry => entry.trim() !== '');
+
+ const sortedEntries = entries.map(entry => {
+ const lines = entry.split(/(?:\r\n|\r|\n)/).filter(line => line.trim() !== '');
+ const newEntry = lines.shift(); // Extract "new entry" line
+ const typeLine = lines.shift(); // Extract "type" line
+ let usingLine = '';
+ const dataLines = lines.filter(line => {
+ if (line.startsWith('using ')) {
+ usingLine = line;
+ return false;
+ }
+ return true;
+ }).sort(); // Sort data lines alphabetically
+
+ // Construct the sorted entry
+ return `${newEntry}\r\n${typeLine}\r\n${usingLine ? usingLine + '\r\n' : ''}${dataLines.join('\r\n')}`;
+ });
+
+ const sortedContent = sortedEntries.join('\r\n\r\n');
+
+ // Write sorted content to a temporary file
+ const tempFilePath = path.join(os.tmpdir(), path.basename(filePath));
+ fs.writeFileSync(tempFilePath, sortedContent);
+
+ return { sortedContent, tempFilePath };
+}
+
+async function autoSortFiles() {
+ const config = getConfig();
+ const rootModPath = config.rootModPath;
+ const modName = config.modName;
+ let saveAll = false;
+ let closeRemaining = false;
+
+ const statsPath = path.join(rootModPath, 'Public', modName, 'Stats', 'Generated', 'Data');
+
+ if (!fs.existsSync(statsPath)) {
+ raiseError(`Data directory does not exist: ${statsPath}`);
+ vscode.window.showErrorMessage(`Data directory does not exist: ${statsPath}`);
+ return;
+ }
+
+ fs.readdir(statsPath, async (err, files) => {
+ if (err) {
+ raiseError(`Failed to read directory: ${err.message}`);
+ vscode.window.showErrorMessage(`Failed to read directory: ${err.message}`);
+ return;
+ }
+
+ for (const file of files) {
+ if (closeRemaining) {
+ break;
+ }
+
+ const filePath = path.join(statsPath, file);
+ const result = sortEntriesInFile(filePath);
+
+ if (result) {
+ const { sortedContent, tempFilePath } = result;
+ const leftUri = vscode.Uri.file(filePath);
+ const rightUri = vscode.Uri.file(tempFilePath);
+ const title = `Compare: ${path.basename(filePath)}`;
+
+ await vscode.commands.executeCommand('vscode.diff', leftUri, rightUri, title);
+
+ if (saveAll) {
+ fs.writeFileSync(filePath, sortedContent);
+ raiseInfo(`File ${filePath} has been overwritten with sorted content.`);
+ } else {
+ const confirm = await vscode.window.showInformationMessage(
+ `Do you want to overwrite the original file with the sorted content for ${path.basename(filePath)}?`,
+ 'Save', 'Close', 'Save Remaining', 'Close Remaining'
+ );
+
+ if (confirm === 'Save') {
+ fs.writeFileSync(filePath, sortedContent);
+ raiseInfo(`File ${filePath} has been overwritten with sorted content.`);
+ } else if (confirm === 'Save Remaining') {
+ saveAll = true;
+ fs.writeFileSync(filePath, sortedContent);
+ raiseInfo(`File ${filePath} has been overwritten with sorted content.`);
+ } else if (confirm === 'Close Remaining') {
+ closeRemaining = true;
+ raiseInfo(`Process terminated by user.`);
+ return;
+ } else {
+ raiseInfo(`File ${filePath} was not overwritten.`);
+ }
+ }
+ }
+ }
+
+ raiseInfo('File sorting process completed.');
+ });
+}
+
+const organizeDataFiles = vscode.commands.registerCommand('bg3-mod-helper.organizeDataFilesCommand', async function () {
+ await autoSortFiles();
+});
+
+module.exports = { organizeDataFiles };
diff --git a/commands/packMod.js b/commands/packMod.js
index f79827b4..2e871d1b 100644
--- a/commands/packMod.js
+++ b/commands/packMod.js
@@ -14,42 +14,45 @@ const { convert } = require('../support_files/conversion_junction.js');
const { getFormats } = require('../support_files/lslib_utils.js');
const { pak } = getFormats();
+let hasPromptedForModDestPath = false;
-const packModZipCommand = vscode.commands.registerCommand('bg3-mod-helper.packModZip', async function () {
- // You can call the existing packMod function with additional parameters
- await vscode.commands.executeCommand('bg3-mod-helper.packMod', { packAsZip: true });
-});
-
-// i think we should separate out the functions here if possible- maybe put some of them in helper_functions?
-const packModCommand = vscode.commands.registerCommand('bg3-mod-helper.packMod', async function (options = {}) {
+const packModCommand = vscode.commands.registerCommand('bg3-mod-helper.packMod', async function (action) {
bg3mh_logger.info("pack button clicked", false);
- const { rootModPath, modDestPath, lslibPath, autoLaunchOnPack } = getConfig();
+ const { rootModPath, modDestPath, lslibPath } = getConfig();
const modName = await getModName();
const modsDirPath = path.join(rootModPath, "Mods");
const metaPath = path.join(modsDirPath, modName, "meta.lsx");
- // Check if BG3 is running
- const isRunning = await isGameRunning();
+ // Check if BG3 is running might not need anymore i cant rememebr
+ const gameRunning = await handleGameRunning();
- if (isRunning) {
- vscode.window.showErrorMessage('Baldur\'s Gate 3 is currently running. Please close the game before packing the mod.');
+ if (gameRunning) {
+ vscode.window.showErrorMessage('Baldur\'s Gate 3 is currently running. Please close the game before packing the mod or enable autoclose in settings.');
return; // Stop further execution
}
- // Check if modDestPath is blank
- if (!modDestPath.includes(path.join("Larian Studios", path.sep, "Baldur's Gate 3", path.sep, "Mods"))) {
- const useStandardPath = await vscode.window.showInformationMessage(
- 'The Mods destination path does not seem to be the standard Baldur\'s Gate 3 Mods folder. Do you want to change it?',
- 'Change to Standard', 'Keep Current'
- );
- if (useStandardPath === 'Change to Standard') {
- const standardPath = path.join(process.env.LOCALAPPDATA, (path.join("Larian Studios", path.sep, "Baldur's Gate 3", path.sep, "Mods")));
- const modDestPath = standardPath
- await vscode.workspace.getConfiguration('bg3ModHelper').update('modDestPath', standardPath, vscode.ConfigurationTarget.Global);
+ const workspaceState = vscode.workspace.getConfiguration('bg3ModHelper');
+ const promptedForModDestPath = workspaceState.get('promptedForModDestPath', false);
+
+ // Only show the prompt the first time
+ if (!hasPromptedForModDestPath) {
+ if (!modDestPath.includes(path.join("Larian Studios", "Baldur's Gate 3", "Mods"))) {
+ const useStandardPath = await vscode.window.showInformationMessage(
+ 'The Mods destination path does not seem to be the standard Baldur\'s Gate 3 Mods folder. Do you want to change it?',
+ 'Change to Standard (REQUIRES RELOAD OF VSCODE)', 'Keep Current'
+ );
+ if (useStandardPath === 'Change to Standard (REQUIRES RELOAD OF VSCODE)') {
+ const standardPath = path.join(process.env.LOCALAPPDATA, "Larian Studios", "Baldur's Gate 3", "Mods");
+ await vscode.workspace.getConfiguration('bg3ModHelper').update('modDestPath', standardPath, vscode.ConfigurationTarget.Global);
+ }
+
+ // Set the flag to true to prevent the prompt from appearing again
+ hasPromptedForModDestPath = true;
}
}
+
bg3mh_logger.info("Grabbed mod name %s from %s.", modName, rootModPath);
if (!fs.existsSync(metaPath)) {
@@ -87,9 +90,11 @@ const packModCommand = vscode.commands.registerCommand('bg3-mod-helper.packMod',
}
// send the directory to the convert() function, and let it know it's a pak
- await convert(rootModPath, pak);
+ await convert(rootModPath, pak, action);
- if (autoLaunchOnPack) {
+ console.log(action)
+ if (action === 'packAndPlay') {
+ console.log('rrr')
vscode.commands.executeCommand('bg3-mod-helper.launchGame');
}
});
@@ -123,20 +128,41 @@ function createVersion64(major, minor, build, revision) {
}
-function isGameRunning() {
+function handleGameRunning() {
return new Promise((resolve, reject) => {
- exec('tasklist', (error, stdout, stderr) => {
+ exec('tasklist', async (error, stdout, stderr) => {
if (error || stderr) {
- bg3mh_logger.error("Error checking running processes" + error || stderr);
+ bg3mh_logger.error("Error checking running processes: " + (error || stderr));
resolve(false); // Assuming game is not running in case of error
return;
}
-
- // need to add a check for linux as well
- const isRunning = stdout.toLowerCase().includes('bg3.exe');
- resolve(isRunning);
+ const { autoCloseBG3, laucherAPI } = getConfig();
+ const exeName = laucherAPI === 'DirectX' ? 'bg3_dx11.exe' : 'bg3.exe';
+
+ // Check if BG3 is running (add Linux check if necessary)
+ const isRunning = stdout.toLowerCase().includes(exeName);
+
+ if (isRunning) {
+ if (autoCloseBG3) {
+ exec(`taskkill /F /IM ${exeName}`, (killError, killStdout, killStderr) => {
+ if (killError || killStderr) {
+ bg3mh_logger.error("Error closing Baldur's Gate 3: " + (killError || killStderr));
+ resolve(false); // Return false if there was an error closing the game
+ return;
+ }
+
+ vscode.window.showInformationMessage('Baldur\'s Gate 3 was closed to pack the mod.');
+ bg3mh_logger.info("Baldur's Gate 3 was successfully closed.");
+ resolve(false);
+ });
+ } else {
+ resolve(true); // Game is running, but user opted not to auto-close
+ }
+ } else {
+ resolve(false); // Game is not running
+ }
});
});
}
-module.exports = { packModZipCommand, packModCommand };
\ No newline at end of file
+module.exports = { packModCommand };
diff --git a/commands/symlinker.js b/commands/symlinker.js
index c2e072e4..767b0b32 100644
--- a/commands/symlinker.js
+++ b/commands/symlinker.js
@@ -29,9 +29,11 @@ const symlinkCommand = vscode.commands.registerCommand('bg3-mod-helper.symlinker
// Handle localization directories
const localizationPath = path.join(rootModPath, 'Localization');
- const locDirectories = fs.readdirSync(localizationPath, { withFileTypes: true })
- .filter(dirent => dirent.isDirectory())
- .map(dirent => dirent.name);
+ const locDirectories = fs.existsSync(localizationPath)
+ ? fs.readdirSync(localizationPath, { withFileTypes: true })
+ .filter(dirent => dirent.isDirectory())
+ .map(dirent => dirent.name)
+ : [];
const selectedLocDirs = await vscode.window.showQuickPick(locDirectories, {
canPickMany: true,
@@ -47,7 +49,7 @@ const symlinkCommand = vscode.commands.registerCommand('bg3-mod-helper.symlinker
console.log("Paths to create symlinks for:", paths);
- let anyExists = Object.values(paths).some(p => fs.existsSync(p));
+ let anyExists = Object.values(paths).some(p => fs.existsSync(p) && !p.includes('GustavDev'));
if (anyExists) {
const response = await vscode.window.showWarningMessage(
@@ -59,7 +61,7 @@ const symlinkCommand = vscode.commands.registerCommand('bg3-mod-helper.symlinker
return;
} else if (response === 'Remove All' || response === 'Replace All') {
Object.values(paths).forEach(p => {
- if (fs.existsSync(p)) {
+ if (fs.existsSync(p) && !p.includes('GustavDev')) {
console.log("Removing existing path:", p);
fs.rmdirSync(p, { recursive: true });
}
@@ -81,6 +83,13 @@ const symlinkCommand = vscode.commands.registerCommand('bg3-mod-helper.symlinker
} catch (error) {
vscode.window.showErrorMessage(`Failed to create symlinks: ${error.message}`);
}
+
+ // Special handling for the GustavDev folder
+ const modsPath = path.join(rootModPath, 'Mods');
+ const gustavDevPath = path.join(modsPath, 'GustavDev');
+ if (fs.existsSync(gustavDevPath)) {
+ console.log("Ignoring GustavDev folder in Mods directory.");
+ }
});
module.exports = symlinkCommand;
diff --git a/extension.js b/extension.js
index a766422a..be29ea3c 100644
--- a/extension.js
+++ b/extension.js
@@ -34,6 +34,12 @@ let packModImport,
openGameFolderCommand,
openLogsFolderCommand,
openWorkspaceFolderCommand,
+ openPlayerProfilesFolderCommand,
+ organizeDataFilesCommand,
+ xmlMergerCommand,
+ symlinkerCommand,
+ indentXmlFilesCommand,
+ convertVideoToGifCommand,
debugCommand,
debug2Command,
unpackGameDataCommand,
@@ -82,6 +88,7 @@ function setCommands() {
openGameFolderCommand = require('./commands/folderShortcuts').openGameFolderCommand;
openLogsFolderCommand = require('./commands/folderShortcuts').openLogsFolderCommand;
openWorkspaceFolderCommand = require('./commands/folderShortcuts').openWorkspaceFolderCommand;
+ openPlayerProfilesFolderCommand = require('./commands/folderShortcuts').openPlayerProfilesFolderCommand;
// image commands
resizeImageTooltip = require('./commands/resizeImage').resizeImageTooltip;
@@ -97,6 +104,13 @@ function setCommands() {
// launch the game
launchGameImport = require('./commands/launchGame');
+ // Need to be placed somewhere
+ xmlMergerCommand = require('./commands/xmlMerger');
+ organizeDataFilesCommand = require('./commands/organizeDataFiles');
+ symlinkerCommand = require('./commands/symlinker');
+ indentXmlFilesCommand = require('./commands/indentXmlFiles');
+ convertVideoToGifCommand = require('./commands/convertVideoToGif');
+
// debug commands
debugCommand = require('./commands/debug');
debug2Command = require('./commands/debug2');
@@ -186,7 +200,7 @@ function activate(context) {
let createModTemplateCommand = vscode.commands.registerCommand('bg3-mod-helper.createModTemplate', createModTemplateImport);
context.subscriptions.push(vscode.commands.registerCommand('bg3-mod-helper.addToExcludeList', addToExcludeList));
context.subscriptions.push(vscode.commands.registerCommand('bg3-mod-helper.removeFromExcludeList', removeFromExcludeList));
- context.subscriptions.push(uuidsHandlesHoverProvider, functionsHoverProvider, DDSToPNGCommand, PNGToDDSCommand, resizeTooltipCommand, resizeControllerCommand, resizeHotbarCommand, resizeCustomCommand, createModTemplateCommand, addIconBackgroundCommand, openConverterCommand, versionGeneratorCommand, rotationToolCommand, openModsFolderCommand, openGameFolderCommand, openLogsFolderCommand, openWorkspaceFolderCommand);
+ context.subscriptions.push(uuidsHandlesHoverProvider, functionsHoverProvider, DDSToPNGCommand, PNGToDDSCommand, resizeTooltipCommand, resizeControllerCommand, resizeHotbarCommand, resizeCustomCommand, createModTemplateCommand, addIconBackgroundCommand, openConverterCommand, versionGeneratorCommand, rotationToolCommand, openModsFolderCommand, openGameFolderCommand, openLogsFolderCommand, openWorkspaceFolderCommand, openPlayerProfilesFolderCommand);
}
@@ -209,13 +223,14 @@ function aSimpleDataProvider() {
{ label: 'Pack/Unpacking Tool (Click arrow for quick actions, or text to open the tool[tool is in development])', id: 'packer' },
{ label: 'Conversion Tool (Click arrow for quick actions, or text to open the tool)', command: 'bg3-mod-helper.openConverter', id: 'conversion' },
{ label: 'Configuration Options', id: 'config' },
+ { label: 'File Formatting/Cleaning', id: 'formatting' },
{ label: 'Launch Game', command: 'bg3-mod-helper.launchGame' },
{ label: 'Generate Folder Structure', command: 'bg3-mod-helper.createModTemplate' },
{ label: 'Atlas Generator (Supply a folder of icons to make an atlas and its corresponding .dds with those icons, as well as its merged.lsx)', command: 'bg3-mod-helper.createAtlas' },
{ label: 'BBCode/Markdown Editor ', command: 'bg3-mod-helper.textEditorTool'},
+ { label: 'Convert Video to GIF', command: 'bg3-mod-helper.convertVideoToGif' },
{ label: 'Version Generator', command: 'bg3-mod-helper.versionGenerator' },
{ label: 'Merge Xmls', command: 'bg3-mod-helper.xmlMerger' },
- { label: 'Add Dependencies to Meta via modsettings.lsx', command: 'bg3-mod-helper.addDependencies'},
{ label: 'Add/Remove Symlink (in development)', command: 'bg3-mod-helper.symlinker' },
{ label: 'Rotation Tool (in development)', command: 'bg3-mod-helper.rotationTool' },
{ label: 'DDS Viewer (in development)', command: 'bg3-mod-helper.DDSViewer' },
@@ -226,11 +241,17 @@ function aSimpleDataProvider() {
]);
} else if (element.id === 'packer') {
return Promise.resolve([
- { label: 'Pack Mod', command: 'bg3-mod-helper.packMod' },
- { label: 'Pack Mod and ZIP(gz)', command: 'bg3-mod-helper.packModZip' },
+ { label: 'Pack Mod', command: 'bg3-mod-helper.packMod', arguments: 'pack' },
+ { label: 'Pack and Play Mod', command: 'bg3-mod-helper.packMod', arguments: 'packAndPlay' },
+ { label: 'Pack and Zip Mod', command: 'bg3-mod-helper.packMod', arguments: 'packAndZip' },
{ label: 'Unpack Mod', command: 'bg3-mod-helper.unpackMod' },
{ label: 'Unpack Game Data (in development)', command: 'bg3-mod-helper.unpackGameDataCommand' }
]);
+ } else if (element.id === 'formatting') {
+ return Promise.resolve([
+ { label: 'Organize Data Files (Alphabetically)', command: 'bg3-mod-helper.organizeDataFilesCommand' },
+ { label: 'Mass Indent LSX Files (Not working)', command: 'bg3-mod-helper.indentXmlFilesCommand'}
+ ]);
} else if (element.id === 'config') {
return Promise.resolve([
{ label: 'Reload Window', command: 'workbench.action.reloadWindow' },
@@ -250,6 +271,7 @@ function aSimpleDataProvider() {
{ label: 'Workspace Folder', command: 'bg3-mod-helper.openWorkspaceFolder' },
{ label: 'Extension Logs Folder', command: 'bg3-mod-helper.openLogsFolder' },
{ label: 'Game Data Folder', command: 'bg3-mod-helper.openGameFolder' },
+ { label: 'Player Profiles Folder', command: 'bg3-mod-helper.openPlayerProfilesFolder' },
]);
} else {
return Promise.resolve([]);
diff --git a/package-lock.json b/package-lock.json
index 5d04be4c..7ac82e18 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,14 +1,16 @@
{
"name": "bg3-mod-helper",
- "version": "2.2.50",
+ "version": "2.2.57",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bg3-mod-helper",
- "version": "2.2.50",
+ "version": "2.2.57",
"license": "LGPL-3.0-or-later",
"dependencies": {
+ "ffmpeg-static": "^5.2.0",
+ "fluent-ffmpeg": "^2.1.3",
"jszip": "^3.10.1",
"log4js": "^6.9.1",
"magickwand.js": "^1.1.0",
@@ -23,6 +25,20 @@
"vscode": "^1.86.0"
}
},
+ "node_modules/@derhuerst/http-basic": {
+ "version": "8.2.4",
+ "resolved": "https://registry.npmjs.org/@derhuerst/http-basic/-/http-basic-8.2.4.tgz",
+ "integrity": "sha512-F9rL9k9Xjf5blCz8HsJRO4diy111cayL2vkY2XE4r4t3n0yPXVYy3KD3nJ1qbrSn9743UWSXH4IwuCa/HWlGFw==",
+ "dependencies": {
+ "caseless": "^0.12.0",
+ "concat-stream": "^2.0.0",
+ "http-response-object": "^3.0.1",
+ "parse-cache-control": "^1.0.1"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/@emnapi/runtime": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.2.0.tgz",
@@ -200,6 +216,11 @@
"node": ">=14"
}
},
+ "node_modules/@types/node": {
+ "version": "10.17.60",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz",
+ "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw=="
+ },
"node_modules/abbrev": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -269,6 +290,11 @@
"node": ">=10"
}
},
+ "node_modules/async": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
+ "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ=="
+ },
"node_modules/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -284,6 +310,11 @@
"concat-map": "0.0.1"
}
},
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
+ },
"node_modules/cacache": {
"version": "18.0.2",
"resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.2.tgz",
@@ -376,6 +407,11 @@
"node": ">=16 || 14 >=14.17"
}
},
+ "node_modules/caseless": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
+ "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw=="
+ },
"node_modules/chalk": {
"version": "5.3.0",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-5.3.0.tgz",
@@ -435,6 +471,20 @@
"integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
"license": "MIT"
},
+ "node_modules/concat-stream": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-2.0.0.tgz",
+ "integrity": "sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==",
+ "engines": [
+ "node >= 6.0"
+ ],
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "inherits": "^2.0.3",
+ "readable-stream": "^3.0.2",
+ "typedarray": "^0.0.6"
+ }
+ },
"node_modules/console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -598,11 +648,54 @@
"node": "^12.20 || >= 14.13"
}
},
+ "node_modules/ffmpeg-static": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ffmpeg-static/-/ffmpeg-static-5.2.0.tgz",
+ "integrity": "sha512-WrM7kLW+do9HLr+H6tk7LzQ7kPqbAgLjdzNE32+u3Ff11gXt9Kkkd2nusGFrlWMIe+XaA97t+I8JS7sZIrvRgA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "@derhuerst/http-basic": "^8.2.0",
+ "env-paths": "^2.2.0",
+ "https-proxy-agent": "^5.0.0",
+ "progress": "^2.0.3"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/flatted": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
"integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
},
+ "node_modules/fluent-ffmpeg": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/fluent-ffmpeg/-/fluent-ffmpeg-2.1.3.tgz",
+ "integrity": "sha512-Be3narBNt2s6bsaqP6Jzq91heDgOEaDCJAXcE3qcma/EJBSy5FB4cvO31XBInuAuKBx8Kptf8dkhjK0IOru39Q==",
+ "dependencies": {
+ "async": "^0.2.9",
+ "which": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/fluent-ffmpeg/node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="
+ },
+ "node_modules/fluent-ffmpeg/node_modules/which": {
+ "version": "1.3.1",
+ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
+ "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "which": "bin/which"
+ }
+ },
"node_modules/foreground-child": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
@@ -762,6 +855,14 @@
"node": ">= 14"
}
},
+ "node_modules/http-response-object": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz",
+ "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==",
+ "dependencies": {
+ "@types/node": "^10.0.3"
+ }
+ },
"node_modules/https-proxy-agent": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
@@ -1431,6 +1532,11 @@
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
},
+ "node_modules/parse-cache-control": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz",
+ "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg=="
+ },
"node_modules/path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
@@ -1484,6 +1590,14 @@
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
},
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
"node_modules/promise-retry": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
@@ -1785,6 +1899,11 @@
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
"license": "0BSD"
},
+ "node_modules/typedarray": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
+ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
+ },
"node_modules/unique-filename": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz",
diff --git a/package.json b/package.json
index 0f46c59c..2e191857 100644
--- a/package.json
+++ b/package.json
@@ -3,7 +3,7 @@
"displayName": "bg3_mod_helper",
"publisher": "ghostboats",
"description": "This extension is designed to help you make mods in Baldur's Gate 3 by creating UUIDs and handles for you, as well as updating your .loca.xml files as well should they exist. And more to come in the future.",
- "version": "2.2.50",
+ "version": "2.2.58",
"icon": "media/marketplace_icon.png",
"engines": {
"vscode": "^1.86.0"
@@ -221,8 +221,8 @@
"category": "BG3 Mod Helper"
},
{
- "command": "bg3-mod-helper.packModZip",
- "title": "Pack Mod and ZIP",
+ "command": "bg3-mod-helper.generateReplaceAllHandles",
+ "title": "Generate and Replace All Handles",
"category": "BG3 Mod Helper"
},
{
@@ -254,11 +254,6 @@
"default": true,
"description": "Enable or disable hover feature."
},
- "bg3ModHelper.hoverShowPath": {
- "type": "boolean",
- "default": true,
- "description": "Toggle file path in hover message (not working in current update)."
- },
"bg3ModHelper.rootModPath": {
"type": "string",
"default": "",
@@ -274,16 +269,6 @@
"default": "",
"description": "(Requires reload- Configuration Options > Reload Window) Path where your LSLib.dll file lives, ie [C:\\Documents\\ExportTool-v1.19.5\\Packed]:"
},
- "bg3ModHelper.autoLaunchOnPack": {
- "type": "boolean",
- "default": false,
- "description": "(Requires reload- Configuration Options > Reload Window) Automatically launch the game after you pack your mod"
- },
- "bg3ModHelper.zipOnPack": {
- "type": "boolean",
- "default": false,
- "description": "Zip up your mod in a .gz (.gzip) format, for distribution on other platforms."
- },
"bg3ModHelper.launchContinueGame": {
"type": "boolean",
"default": false,
@@ -291,7 +276,7 @@
},
"bg3ModHelper.excludedFiles": {
"type": "array",
- "description": "List of files to exclude from conversion. You can quick add items to this via right click on a file in the file tree -> Add to Conversion Exclusion List. Example: [c:/path/to/ur/file.lsx]",
+ "description": "When you pack a mod, the bg3 mod helper will autoconvert your lsx and xml files. If you wish to exclude an lxs/xml for some reason from auto convertin, add it to the list below. You can quick add items to this via right click on a file in the file tree -> Add to Conversion Exclusion List. Example: [c:/path/to/ur/file.lsx]",
"default": [],
"items": {
"type": "string"
@@ -315,7 +300,18 @@
"bg3ModHelper.excludeHidden": {
"type": "boolean",
"default": true,
- "description": "If, when packing mods, files and folders starting with a '.' should be excluded, ie the '.vscode' folder or a .gitignore file."
+ "description": "If, when packing mods, files and folders starting with a '.' should be excluded, ie the '.vscode' folder or a .gitignore file (not enabled atm)."
+ },
+ "bg3ModHelper.autoCloseBG3": {
+ "type": "boolean",
+ "default": false,
+ "description": "Automatically close Baldur's Gate 3 if it is running before packing a mod."
+ },
+ "bg3ModHelper.laucherAPI": {
+ "type": "string",
+ "default": "DirectX",
+ "enum": ["DirectX", "Vulkan"],
+ "description": "Lauch using directx or vulkan"
}
}
},
@@ -439,6 +435,11 @@
"when": "editorTextFocus && editorHasSelection",
"command": "bg3-mod-helper.generateReplaceAllUUIDs",
"group": "navigation@0"
+ },
+ {
+ "when": "editorTextFocus && editorHasSelection",
+ "command": "bg3-mod-helper.generateReplaceAllHandles",
+ "group": "navigation@0"
}
],
"bg3ModHelper.exportToolsSubmenu": [
@@ -514,6 +515,8 @@
"dependencies": "npm install"
},
"dependencies": {
+ "ffmpeg-static": "^5.2.0",
+ "fluent-ffmpeg": "^2.1.3",
"jszip": "^3.10.1",
"log4js": "^6.9.1",
"magickwand.js": "^1.1.0",
diff --git a/support_files/config.js b/support_files/config.js
index 3ec6aaab..2aeac71d 100644
--- a/support_files/config.js
+++ b/support_files/config.js
@@ -91,20 +91,19 @@ function getConfig() {
return {
hoverMaxFiles: config.get('hoverMaxFiles'),
hoverEnabled: config.get('hoverEnabled'),
- hoverShowPath: config.get('hoverShowPath'),
maxCacheSize: config.get('maxCacheSize'),
rootModPath: path.normalize(config.get('rootModPath')),
modDestPath: path.normalize(config.get('modDestPath')),
lslibPath: path.normalize(config.get('lslibPath')),
- autoLaunchOnPack: config.get('autoLaunchOnPack'),
- zipOnPack: config.get('zipOnPack'),
launchContinueGame: config.get('launchContinueGame'),
addHandlesToAllLocas: config.get('addHandlesToAllLocas'),
excludedFiles: normalizeExcludedFiles(config.get('excludedFiles')),
gameInstallLocation: path.normalize(config.get('gameInstallLocation')),
modName: config.get('modName'),
excludeHidden: config.get('excludeHidden'),
- packingPriority: config.get('packingPriority')
+ packingPriority: config.get('packingPriority'),
+ autoCloseBG3: config.get('autoCloseBG3'),
+ laucherAPI: config.get('laucherAPI')
};
}
diff --git a/support_files/conversion_junction.js b/support_files/conversion_junction.js
index 950493a1..a26742a7 100644
--- a/support_files/conversion_junction.js
+++ b/support_files/conversion_junction.js
@@ -152,7 +152,7 @@ function getDynamicPath(filePath) {
// at the moment this has all the functionality i planned for it, ie lsf, loca, and paks. for any other conversions we can make separate functions
-async function convert(convertPath, targetExt = path.extname(getDynamicPath(convertPath))) {
+async function convert(convertPath, targetExt = path.extname(getDynamicPath(convertPath)), action = null) {
let rootModPath = getConfig.rootModPath;
// checks if the convertPath was undefined and halts the function before it goes any further
@@ -171,14 +171,14 @@ async function convert(convertPath, targetExt = path.extname(getDynamicPath(conv
await convert(rootModPath, lsx)
.then(() => bg3mh_logger.info(`lsx conversion done`, false));
- processPak(rootModPath);
+ processPak(rootModPath, action);
}
// has a check for main thread here because when a worker thread calls this function, it's batch unpacking and has a specific place it needs the files inside to go, but can't rely on vscode module functions to get them
else if (fs.statSync(convertPath).isFile()) {
if (isMainThread) {
- processPak(convertPath);
+ processPak(convertPath, action);
} else {
- processPak(convertPath, workerData.jobDestPath);
+ processPak(convertPath, action, workerData.jobDestPath);
}
}
}
diff --git a/support_files/gzip_functions.js b/support_files/gzip_functions.js
deleted file mode 100644
index e9bb9780..00000000
--- a/support_files/gzip_functions.js
+++ /dev/null
@@ -1,61 +0,0 @@
-const path = require('path');
-const fs = require('fs');
-
-const { isMainThread, workerData } = require('worker_threads');
-
-const { createGzip } = require('zlib');
-const { pipeline } = require('stream');
-const { promisify } = require('util');
-const streamPipeline = promisify(pipeline);
-
-
-
-const { pak } = require('./lslib_utils').getFormats();
-
-const { CREATE_LOGGER } = require('./log_utils');
-const bg3mh_logger = CREATE_LOGGER();
-
-
-let vscode,
- getConfig;
-
-if (isMainThread) {
- vscode = require('vscode');
- getConfig = require('./config.js').getConfig();
-
-} else {
- getConfig = workerData.workerConfig;
-}
-
-let zipOnPack = getConfig.zipOnPack;
-
-async function zipUpPak(zipPak = zipOnPack) {
- let rootModPath = getConfig.rootModPath;
-
- const temp_folder = "\\temp_folder";
- const lastFolderName = path.basename(rootModPath);
- const rootParentPath = path.dirname(rootModPath);
- const temp_path = path.join(rootParentPath, temp_folder);
- const modTempDestPath = path.join(temp_path, lastFolderName + pak);
-
- if (zipPak == true) {
- console.log('zipping');
- const zipPath = path.join(rootModPath, `${lastFolderName}.pak.gz`);
- const gzip = createGzip();
- const source = fs.createReadStream(modTempDestPath);
- const destination = fs.createWriteStream(zipPath);
-
- await streamPipeline(source, gzip, destination);
- bg3mh_logger.info(`Gzip file has been created at ${zipPath}`, false);
-
- if (isMainThread) {
- vscode.window.showInformationMessage(`${lastFolderName}.pak.gz created`);
- }
- } else {
- bg3mh_logger.info('not zipping');
- }
-}
-
-module.exports = { zipUpPak }
-
-
diff --git a/support_files/process_pak.js b/support_files/process_pak.js
index ec680774..4b1d6914 100644
--- a/support_files/process_pak.js
+++ b/support_files/process_pak.js
@@ -4,7 +4,7 @@ const fs = require('fs');
const { getFormats, moveFileAcrossDevices, compatRootModPath, LOAD_LSLIB } = require('./lslib_utils');
const { pak } = getFormats();
-const { zipUpPak } = require('./gzip_functions');
+const { zipUpPak } = require('./zip_functions');
const { xmlUpdate } = require('./xml_functions');
const { isMainThread, workerData } = require('node:worker_threads');
@@ -52,14 +52,13 @@ function prepareTempDir(movedPak = false) {
// btw, sometimes this will log things before others because it's async.
-async function processPak(modPath, unpackLocation = path.join(path.dirname(modPath), path.basename(modPath, pak))) {
+async function processPak(modPath, action, unpackLocation = path.join(path.dirname(modPath), path.basename(modPath, pak))) {
await lslib_load();
var build = new LSLIB.PackageBuildData();
var Packager = new LSLIB.Packager();
let rootModPath,
modDestPath,
- zipOnPack,
packingPriority;
if (isMainThread) {
@@ -71,7 +70,6 @@ async function processPak(modPath, unpackLocation = path.join(path.dirname(modPa
rootModPath = getConfig.rootModPath;
modDestPath = getConfig.modDestPath;
- zipOnPack = getConfig.zipOnPack;
packingPriority = getConfig.packingPriority;
build.ExcludeHidden = getConfig.excludeHidden;
@@ -103,7 +101,9 @@ async function processPak(modPath, unpackLocation = path.join(path.dirname(modPa
vscode.window.showInformationMessage(`${lastFolderName + pak} packed`);
}
- zipUpPak(zipOnPack);
+ if (action === 'packAndZip') {
+ await zipUpPak();
+ }
moveFileAcrossDevices(modTempDestPath, modFinalDestPath);
prepareTempDir(true);
diff --git a/support_files/release_notes.js b/support_files/release_notes.js
index 7d786e49..73c7c971 100644
--- a/support_files/release_notes.js
+++ b/support_files/release_notes.js
@@ -1,14 +1,14 @@
const vscode = require('vscode');
-// This function checks for updates and shows the release notes if there's a new version. Make sure to update the webview
-//when making new releases
+const showAlways = false // Keep this false on release
+
function checkForUpdates(context) {
const extensionId = 'ghostboats.bg3-mod-helper';
const extension = vscode.extensions.getExtension(extensionId);
const currentVersion = extension.packageJSON.version;
const lastVersion = context.globalState.get('lastVersion');
- if (!lastVersion || lastVersion !== currentVersion) {
+ if (showAlways || !lastVersion || lastVersion !== currentVersion) {
showUpdateNotes(currentVersion, context);
context.globalState.update('lastVersion', currentVersion);
}
@@ -26,18 +26,92 @@ function showUpdateNotes(version, context) {
}
function getWebviewContent(version) {
- // Dynamically create the content based on the version here, maybe will use a file later in future
- const releaseNotes = `Here are the new features in version ${version}:
New feature 1
New feature 2
`;
+ const releaseNotes = generateReleaseNotes(version);
return `
- Update Info
+
+ Update Info
+
+
What's New in ${version}
-
${releaseNotes}
+ ${releaseNotes}
`;
}
+function generateReleaseNotes(version) {
+ const notes = [
+ {
+ version: version,
+ features: [
+ {
+ title: "Release Page Created",
+ details: [
+ "On launch of newly downloaded version of extension, launch release notes page showing newest updates, like this one :)"
+ ]
+ },
+ {
+ title: "Mod Setting Changes [IMPORTANT]",
+ details: [
+ "Choose to launch via directx or vulkan in the settings",
+ "Setting added to close the existing bg3 instance when you pack and play for quicker launch if you forgot to close. By default it is off.",
+ "If you have a unique mod destination path, the prompt will now only appear one time per session to avoid having to confirm each time on pack"
+ ]
+ },
+ {
+ title: "Zipping Fixes",
+ details: [
+ "Zipped files appear in correct location now (your set mod destination)",
+ "Pak file is deleted now if zip is made"
+ ]
+ },
+ {
+ title: "Minor Changes",
+ details: [
+ "Atlas Fix if .lsx file doenst exist yet",
+ "Generate and Replace Handle option added when highlighting a handle and rightclicking",
+ "Generate Handle will now correctly save the xml files it adds it to",
+ ]
+ },
+ {
+ title: "Rollback Instructions",
+ details: [
+ "If you encounter any issues with this update, you can roll back to a previous version of the extension by following these steps:",
+ "1. Open the Extensions view by clicking on the Extensions icon in the Activity Bar on the side of the window or by pressing `Ctrl+Shift+X`.",
+ "2. Search for 'BG3 Mod Helper' in the Extensions view.",
+ "3. Click the gear icon next to 'BG3 Mod Helper' and select 'Install Another Version...'.",
+ "4. Choose the previous version from the list to revert to that version."
+ ]
+ },
+ ]
+ }
+ ];
+
+ const currentNotes = notes.find(note => note.version === version);
+ if (!currentNotes) {
+ return `
No specific notes for version ${version}.
`;
+ }
+
+ return `
${currentNotes.features.map(feature => `
+
+ ${feature.title}
+ ${feature.details ? `
${feature.details.map(detail => `
${detail}
`).join('')}
` : ''}
+
`).join('')}
+
`;
+}
+
module.exports = {
checkForUpdates
};
diff --git a/support_files/zip_functions.js b/support_files/zip_functions.js
index c4ef9713..693f44d6 100644
--- a/support_files/zip_functions.js
+++ b/support_files/zip_functions.js
@@ -1,66 +1,55 @@
const path = require('path');
const fs = require('fs');
-
const { isMainThread, workerData } = require('worker_threads');
-
-const { createGzip } = require('zlib');
-const { pipeline } = require('stream');
-const { promisify } = require('util');
-const streamPipeline = promisify(pipeline);
-
const JSZip = require('jszip');
-
+const { promisify } = require('util');
const { pak } = require('./lslib_utils').getFormats();
-
const { CREATE_LOGGER } = require('./log_utils');
const bg3mh_logger = CREATE_LOGGER();
-
let vscode,
getConfig;
if (isMainThread) {
vscode = require('vscode');
getConfig = require('./config.js').getConfig();
-
} else {
getConfig = workerData.workerConfig;
}
-let zipOnPack = getConfig.zipOnPack;
-
let zip = new JSZip();
-async function zipUpPak(zipPak = zipOnPack) {
- if (zipPak) {
- let rootModPath = getConfig.rootModPath;
+async function zipUpPak(zipPak) {
+ let rootModPath = getConfig.rootModPath;
+ let modDestPath = getConfig.modDestPath;
+ let lastFolderName = path.basename(rootModPath);
+ let zipPath = path.join(modDestPath, `${lastFolderName}.zip`);
+
+ let temp_folder = path.join(path.sep, "temp_folder");
+ let pakFilePath = path.join(path.join(path.dirname(rootModPath), temp_folder), lastFolderName + pak);
+
+ if (fs.existsSync(pakFilePath)) {
+ let data = await promisify(fs.readFile)(pakFilePath);
+
+ zip.file(`${lastFolderName}.pak`, data);
+
+ let content = await zip.generateAsync({ type: 'nodebuffer' });
+ await promisify(fs.writeFile)(zipPath, content);
+
+ bg3mh_logger.info(`Zip file has been created at ${zipPath}`, false);
+
+ await promisify(fs.unlink)(pakFilePath);
+ bg3mh_logger.info(`Original .pak file has been deleted at ${pakFilePath}`, false);
- let temp_folder = path.join(path.sep, "temp_folder");
- let lastFolderName = path.basename(rootModPath);
-
- let zipPath = path.join(rootModPath, `${lastFolderName}.pak.gz`);
- let gzip = createGzip();
- let source = fs.createReadStream(
- path.join(
- path.join(
- path.dirname(rootModPath), temp_folder
- ),
- lastFolderName + pak
- )
- );
- let destination = fs.createWriteStream(zipPath);
-
- await streamPipeline(source, gzip, destination);
- bg3mh_logger.info(`Gzip file has been created at ${zipPath}`, false);
-
if (isMainThread) {
- vscode.window.showInformationMessage(`${lastFolderName}.pak.gz created`);
+ vscode.window.showInformationMessage(`${lastFolderName}.zip created`);
}
} else {
- bg3mh_logger.info('not zipping');
+ bg3mh_logger.error(`.pak file not found at ${pakFilePath}`);
+ if (isMainThread) {
+ vscode.window.showErrorMessage(`.pak file not found at ${pakFilePath}`);
+ }
}
}
-module.exports = { zipUpPak }
-
-
+module.exports = { zipUpPak };