diff --git a/commands/DDSViewer.js b/commands/DDSViewer.js
new file mode 100644
index 00000000..1e54d7df
--- /dev/null
+++ b/commands/DDSViewer.js
@@ -0,0 +1,192 @@
+const vscode = require('vscode');
+const fs = require('fs');
+const path = require('path');
+const { getConfig } = require('../support_files/config');
+const { CREATE_LOGGER } = require('../support_files/log_utils');
+
+const { modName, rootModPath } = getConfig();
+const modsDirPath = path.normalize(rootModPath + "\\Mods");
+const bg3mh_logger = CREATE_LOGGER();
+
+
+const DDS_MAGIC = 0x20534444;
+const DDSD_MIPMAPCOUNT = 0x20000;
+const DDPF_FOURCC = 0x4;
+const DDSCAPS2_CUBEMAP = 0x200;
+
+const headerLengthInt = 31;
+
+const off_magic = 0;
+const off_size = 1;
+const off_flags = 2;
+const off_height = 3;
+const off_width = 4;
+const off_mipmapCount = 7;
+const off_pfFlags = 20;
+const off_pfFourCC = 21;
+const off_caps2 = 28;
+
+
+// Additional parsing functions
+function fourCCToInt32(value) {
+ return value.charCodeAt(0) +
+ (value.charCodeAt(1) << 8) +
+ (value.charCodeAt(2) << 16) +
+ (value.charCodeAt(3) << 24);
+}
+
+function int32ToFourCC(value) {
+ return String.fromCharCode(
+ value & 0xff,
+ (value >> 8) & 0xff,
+ (value >> 16) & 0xff,
+ (value >> 24) & 0xff
+ );
+}
+
+function parseHeaders(arrayBuffer) {
+ var header = new Int32Array(arrayBuffer, 0, headerLengthInt);
+
+ if (header[off_magic] !== DDS_MAGIC) {
+ throw new Error('Invalid magic number in DDS header');
+ }
+
+ if (!(header[off_pfFlags] & DDPF_FOURCC)) {
+ throw new Error('Unsupported format, must contain a FourCC code');
+ }
+
+ var fourCC = header[off_pfFourCC];
+ var format, blockBytes;
+ switch (fourCC) {
+ case fourCCToInt32('DXT1'):
+ blockBytes = 8;
+ format = 'dxt1';
+ break;
+ case fourCCToInt32('DXT3'):
+ blockBytes = 16;
+ format = 'dxt3';
+ break;
+ case fourCCToInt32('DXT5'):
+ blockBytes = 16;
+ format = 'dxt5';
+ break;
+ default:
+ throw new Error('Unsupported FourCC code: ' + int32ToFourCC(fourCC));
+ }
+
+ var cubemap = (header[off_caps2] & DDSCAPS2_CUBEMAP) !== 0;
+
+ return {
+ format: format,
+ width: header[off_width],
+ height: header[off_height],
+ mipMapCount: header[off_mipmapCount],
+ isCubemap: cubemap
+ };
+}
+
+let DDSViewerCommand = vscode.commands.registerCommand('bg3-mod-helper.DDSViewer', async function () {
+ bg3mh_logger.info('‾‾DDSViewerCommand‾‾');
+ const panel = vscode.window.createWebviewPanel(
+ 'DDSViewer',
+ 'DDS Viewer',
+ vscode.ViewColumn.One,
+ {
+ enableScripts: true,
+ retainContextWhenHidden: true
+ }
+ );
+
+ const ddsFiles = await findDDSFiles(modsDirPath);
+ const ddsDetails = await Promise.all(ddsFiles.map(async file => {
+ const data = await fs.promises.readFile(file);
+ return {
+ path: file,
+ info: parseHeaders(data.buffer)
+ };
+ }));
+
+ panel.webview.html = getWebviewContent(ddsDetails);
+
+ panel.webview.onDidReceiveMessage(
+ async message => {
+ try {
+ switch (message.command) {
+ case 'openFile':
+ vscode.commands.executeCommand('vscode.open', vscode.Uri.file(message.path));
+ break;
+ }
+ } catch (err) {
+ panel.webview.postMessage({ command: 'alert', text: 'Error' });
+ }
+ }
+ );
+ bg3mh_logger.info('__DDSViewerCommand__');
+});
+
+async function findDDSFiles(directory) {
+ let files = [];
+ console.log("Searching in directory: ", directory);
+ const entries = await fs.promises.readdir(directory, { withFileTypes: true });
+ for (const entry of entries) {
+ const fullPath = path.join(directory, entry.name);
+ console.log(entry.isDirectory() ? "Directory: " + fullPath : "File: " + fullPath);
+ if (entry.isDirectory()) {
+ const subFiles = await findDDSFiles(fullPath);
+ files = files.concat(subFiles);
+ } else {
+ const ext = path.extname(entry.name).toLowerCase();
+ console.log("File extension for " + entry.name + " is " + ext);
+ if (ext === '.dds') {
+ files.push(fullPath);
+ console.log("DDS file found: ", fullPath);
+ }
+ }
+ }
+ if (files.length === 0) {
+ console.log("No DDS files found in: ", directory);
+ }
+ return files;
+}
+
+
+function getWebviewContent(ddsDetails) {
+ let content = '
DDS File Viewer
';
+ content += `
+ File Path |
+ Format |
+ Dimensions |
+ MipMaps |
+ Cubemap |
+
`;
+
+ if (ddsDetails.length === 0) {
+ content += 'No DDS files found. |
';
+ } else {
+ ddsDetails.forEach(details => {
+ content += `
+ ${details.path} |
+ ${details.info.format} |
+ ${details.info.width} x ${details.info.height} |
+ ${details.info.mipMapCount} |
+ ${details.info.isCubemap ? 'Yes' : 'No'} |
+
`;
+ });
+ }
+ content += '
';
+
+ content += `
+ `;
+
+ return content;
+}
+
+module.exports = DDSViewerCommand;
\ No newline at end of file
diff --git a/commands/rotationTool.js b/commands/rotationTool.js
new file mode 100644
index 00000000..406ee8e9
--- /dev/null
+++ b/commands/rotationTool.js
@@ -0,0 +1,103 @@
+const vscode = require('vscode');
+const fs = require('fs');
+const path = require('path');
+const { getConfig } = require('../support_files/config');
+const { CREATE_LOGGER } = require('../support_files/log_utils.js');
+const { modName, rootModPath } = getConfig();
+const modsDirPath = path.normalize(rootModPath + "\\Mods");
+const metaPath = path.normalize(modsDirPath + "\\" + modName + "\\meta.lsx");
+const bg3mh_logger = CREATE_LOGGER();
+
+let rotationToolCommand = vscode.commands.registerCommand('bg3-mod-helper.rotationTool', function () {
+ bg3mh_logger.info('‾‾rotationToolCommand‾‾');
+ const panel = vscode.window.createWebviewPanel(
+ 'rotationTool',
+ 'Rotation Tool',
+ vscode.ViewColumn.One,
+ { enableScripts: true }
+ );
+
+ panel.webview.html = getWebviewContent();
+
+ bg3mh_logger.info('__rotationToolCommand__');
+});
+
+
+function getWebviewContent() {
+ return `
+
+
+
+
+
+ Rotation Tool
+
+
+
+ Rotation Tool
+
+
+
+
+
+ Input Vector (Y treated as angle in degrees)
+
+
+
+
+
+
+ Output (Quaternion)
+
+
+
+
+
+
+
+
+
+ `;
+}
+
+
+
+module.exports = rotationToolCommand;
diff --git a/extension.js b/extension.js
index 7571d5ba..fc861e37 100644
--- a/extension.js
+++ b/extension.js
@@ -20,6 +20,8 @@ const { xmlToLocaCommand, locaToXmlCommand, lsxToLsfCommand, lsfToLsxCommand } =
const openConverterCommand = require('./commands/openConverter');
const versionGeneratorCommand = require('./commands/versionGenerator');
+const rotationToolCommand = require('./commands/rotationTool');
+const DDSViewerCommand = require('./commands/DDSViewer');
const AutoCompleteProvider = require('./autocomplete/autoCompleteProvider');
@@ -33,10 +35,7 @@ const { resizeImageTooltip, resizeImageController, resizeImageHotbar, resizeImag
const { getFullPath } = require('./support_files/helper_functions');
-/**
- * Adds a file to the exclusion list.
- * @param {vscode.Uri} fileUri - The URI of the file to be excluded.
- */
+
async function addToExcludeList(fileUri) {
const config = vscode.workspace.getConfiguration('bg3ModHelper');
let excludedFiles = config.get('excludedFiles') || [];
@@ -52,6 +51,19 @@ async function addToExcludeList(fileUri) {
}
}
+async function removeFromExcludeList(fileUri) {
+ const config = vscode.workspace.getConfiguration('bg3ModHelper');
+ let excludedFiles = config.get('excludedFiles') || [];
+ const filePath = fileUri.fsPath.replace(/\\/g, '/');
+
+ if (excludedFiles.includes(filePath)) {
+ excludedFiles = excludedFiles.filter(p => p !== filePath); // Remove the file from the list
+ await config.update('excludedFiles', excludedFiles, vscode.ConfigurationTarget.Global);
+ vscode.window.showInformationMessage(`${filePath} removed from conversion exclusion list.`);
+ } else {
+ vscode.window.showWarningMessage(`${filePath} not in the exclusion list.`);
+ }
+}
/**
* @param {vscode.ExtensionContext} context
@@ -144,7 +156,8 @@ 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(uuidsHandlesHoverProvider, functionsHoverProvider, DDSToPNG, PNGToDDS, resizeTooltipCommand, resizeControllerCommand, resizeHotbarCommand, resizeCustomCommand, createModTemplateCommand, addIconBackgroundCommand, openConverterCommand, versionGeneratorCommand);
+ context.subscriptions.push(vscode.commands.registerCommand('bg3-mod-helper.removeFromExcludeList', removeFromExcludeList));
+ context.subscriptions.push(uuidsHandlesHoverProvider, functionsHoverProvider, DDSToPNG, PNGToDDS, resizeTooltipCommand, resizeControllerCommand, resizeHotbarCommand, resizeCustomCommand, createModTemplateCommand, addIconBackgroundCommand, openConverterCommand, versionGeneratorCommand, rotationToolCommand);
}
@@ -171,6 +184,8 @@ function aSimpleDataProvider() {
{ label: 'Generate Folder Structure', command: 'bg3-mod-helper.createModTemplate' },
{ label: 'Supply a folder of icons to make an atlas and its corresponding .dds with those icons', command: 'bg3-mod-helper.createAtlas' },
{ label: 'Version Generator', command: 'bg3-mod-helper.versionGenerator' },
+ { label: 'Rotation Tool (in development)', command: 'bg3-mod-helper.rotationTool' },
+ { label: 'DDS Viewer (in development)', command: 'bg3-mod-helper.DDSViewer' },
{ label: 'Debug Command', command: 'bg3-mod-helper.debugCommand' }
]);
} else if (element.id === 'conversion') {
diff --git a/node_modules/.package-lock.json b/node_modules/.package-lock.json
index a4831c63..26d04f0c 100644
--- a/node_modules/.package-lock.json
+++ b/node_modules/.package-lock.json
@@ -1,6 +1,6 @@
{
"name": "bg3-mod-helper",
- "version": "2.1.31",
+ "version": "2.1.32",
"lockfileVersion": 3,
"requires": true,
"packages": {
diff --git a/package-lock.json b/package-lock.json
index 88dec809..09d2e149 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "bg3-mod-helper",
- "version": "2.1.31",
+ "version": "2.1.32",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "bg3-mod-helper",
- "version": "2.1.31",
+ "version": "2.1.32",
"license": "LGPL-3.0-or-later",
"dependencies": {
"@img/sharp-win32-x64": "^0.33.3",
diff --git a/package.json b/package.json
index 60461526..484a4b63 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.1.31",
+ "version": "2.1.32",
"icon": "media/marketplace_icon.png",
"engines": {
"vscode": "^1.86.0"
@@ -138,11 +138,24 @@
"command": "bg3-mod-helper.versionGenerator",
"title": "Version Generator"
},
+ {
+ "command": "bg3-mod-helper.rotationTool",
+ "title": "Rotation Tool"
+ },
+ {
+ "command": "bg3-mod-helper.DDSViewer",
+ "title": "DDS Viewer"
+ },
{
"command": "bg3-mod-helper.addToExcludeList",
"title": "Add to Conversion Exclusion List",
"category": "BG3 Mod Helper"
},
+ {
+ "command": "bg3-mod-helper.removeFromExcludeList",
+ "title": "Remove from Conversion Exclusion List",
+ "category": "BG3 Mod Helper"
+ },
{
"command": "bg3-mod-helper.debugCommand",
"title": "For dev use, dont press the button"
@@ -277,9 +290,14 @@
"group": "navigation"
},
{
- "when": "resourceExtname == .lsx || resourceExtname == .loca || resourceExtname == .xml || resourceExtname == .lsf",
+ "when": "resourceExtname == .lsx || resourceExtname == .loca || resourceExtname == .xml || resourceExtname == .lsf && !isExcluded",
"command": "bg3-mod-helper.addToExcludeList",
"group": "navigation"
+ },
+ {
+ "when": "resourceExtname == .lsx || resourceExtname == .loca || resourceExtname == .xml || resourceExtname == .lsf && isExcluded",
+ "command": "bg3-mod-helper.removeFromExcludeList",
+ "group": "navigation"
}
],
"editor/context": [
diff --git a/support_files/config.js b/support_files/config.js
index 90ebbda6..c3d95ecf 100644
--- a/support_files/config.js
+++ b/support_files/config.js
@@ -8,6 +8,18 @@ function setConfig(newConfig) {
}
+function normalizeExcludedFiles(excludedFiles) {
+
+ if (Array.isArray(excludedFiles)) {
+ let normalizeExcludedFiles = excludedFiles.map((temp_file) => path.normalize(temp_file));
+ return normalizeExcludedFiles;
+ }
+ else {
+ return path.normalize(excludedFiles);
+ }
+}
+
+
function getConfig() {
const config = vscode.workspace.getConfiguration('bg3ModHelper');
return {
@@ -20,7 +32,7 @@ function getConfig() {
lslibPath: path.normalize(config.get('lslibPath')),
autoLaunchOnPack: config.get('autoLaunchOnPack'),
launchContinueGame: config.get('launchContinueGame'),
- excludedFiles: config.get('excludedFiles') || []
+ excludedFiles: normalizeExcludedFiles(config.get('excludedFiles'))
};
}
module.exports = { setConfig, getConfig };
diff --git a/support_files/conversion_junction.js b/support_files/conversion_junction.js
index c22a4269..763ca428 100644
--- a/support_files/conversion_junction.js
+++ b/support_files/conversion_junction.js
@@ -10,7 +10,7 @@ const { CREATE_LOGGER, raiseError, raiseInfo } = require('./log_utils');
const bg3mh_logger = CREATE_LOGGER();
const { getConfig } = require('./config.js');
-const { rootModPath, modName, modDestPath, excludedFiles } = getConfig();
+const { rootModPath, modName, modDestPath } = getConfig();
const { isLoca, processLoca, getLocaOutputPath } = require('./loca_convert');
const { isLsf, processLsf, getLsfOutputPath } = require('./lsf_convert');
@@ -46,15 +46,19 @@ function convert(convertPath, targetExt = path.extname(getDynamicPath(convertPat
if (targetExt === "empty") {
return;
}
-
+ // we can leave this here for the moment, might be a good logic bit to keep in the future.
+ /*
+ // changed this to a const so nothing bothers it while the conversion is taking place
+ const { excludedFiles } = getConfig();
const normalizedExcludedFiles = excludedFiles.map(file => path.normalize(file).replace(/^([a-zA-Z]):/, (match, drive) => drive.toUpperCase() + ':'));
- // bg3mh_logger.info(`Normalized Excluded Files: ${JSON.stringify(normalizedExcludedFiles, null, 2)}`);
-
+ bg3mh_logger.info(`Normalized Excluded Files: ${JSON.stringify(normalizedExcludedFiles, null, 2)}`);
+
const isExcluded = (file) => {
const normalizedFile = path.normalize(file).replace(/^([a-zA-Z]):/, (match, drive) => drive.toUpperCase() + ':');
return normalizedExcludedFiles.includes(normalizedFile);
};
+ */
if (targetExt === pak) {
prepareTempDir();
@@ -67,11 +71,12 @@ function convert(convertPath, targetExt = path.extname(getDynamicPath(convertPat
else if (Array.isArray(convertPath)) {
for (let i = 0; i < convertPath.length; i++) {
convert(convertPath[i], path.extname(convertPath[i]));
+ bg3mh_logger.info(`Excluded: ${convertPath[i]}`);
}
}
else if (fs.statSync(convertPath).isDirectory()) {
const filesToConvert = FIND_FILES(convertPath, targetExt);
- convert(filesToConvert, targetExt);
+ convert(filesToConvert);
}
else if (fs.statSync(convertPath).isFile()) {
if (isLoca(targetExt)) {
@@ -91,6 +96,9 @@ function convert(convertPath, targetExt = path.extname(getDynamicPath(convertPat
return;
}
}
+ }
+ else {
+ raiseInfo(`Excluded: ${convertPath}`, false);
}
}
diff --git a/support_files/lslib_utils.js b/support_files/lslib_utils.js
index bcc7c503..f28a171a 100644
--- a/support_files/lslib_utils.js
+++ b/support_files/lslib_utils.js
@@ -22,7 +22,7 @@ const { CREATE_LOGGER, raiseError, raiseInfo } = require('./log_utils.js')
const bg3mh_logger = CREATE_LOGGER();
const { getConfig } = require('./config.js');
-const { lslibPath, excludedFiles } = getConfig();
+const { lslibPath } = getConfig();
const compatRootModPath = path.join(getConfig().rootModPath + "\\");
const lslibToolsPath = path.join(getConfig().lslibPath, TOOL_SUBDIR);
@@ -149,8 +149,6 @@ function FIND_FILES(filesPath, targetExt = getFormats().lsf, isRecursive = true)
recursive: isRecursive
});
- bg3mh_logger.info(`Excluded Files: ${excludedFiles}`);
-
for (let i = 0; i < filesList.length; i++) {
const temp = filesList[i].toString();
@@ -164,12 +162,12 @@ function FIND_FILES(filesPath, targetExt = getFormats().lsf, isRecursive = true)
function FILTER_PATHS(filesPath) {
- //raiseInfo(excludedFiles);
+ let excludedFiles = getConfig().excludedFiles;
if (Array.isArray(filesPath)) {
let filteredPaths = [];
for (let i = 0; i < filesPath.length; i++) {
- let temp_path = FILTER_PATHS(filesPath[i]);
+ let temp_path = FILTER_PATHS(path.normalize(filesPath[i]));
if (temp_path) {
filteredPaths.push(temp_path);
@@ -183,7 +181,6 @@ function FILTER_PATHS(filesPath) {
for (let i = 0; i < temp_path.length; i++) {
if ((!excludedFiles.includes(filesPath) && convertDirs.includes(temp_path[i])) || (temp_ext === getFormats().dll && !illegalDlls.includes(path.basename(filesPath)))) {
- bg3mh_logger.info(`${filesPath} included`);
return filesPath;
}
}