-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
818a801
commit 41f5643
Showing
61 changed files
with
17,033 additions
and
166 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,172 +1,132 @@ | ||
const vscode = require('vscode'); | ||
const { exec } = require('child_process'); | ||
const path = require('path'); | ||
const { v4: uuidv4 } = require('uuid'); | ||
const fs = require('fs'); | ||
const { getConfig } = require('../support_files/config'); | ||
const { getConfig, setConfig } = require('../support_files/config'); | ||
const { v4: uuidv4 } = require('uuid'); | ||
const sharp = require('sharp'); | ||
const xmlbuilder = require('xmlbuilder'); | ||
const { getModName } = require('../support_files/helper_functions.js'); | ||
|
||
function findDivineExe(lslibPath) { | ||
let divinePath = null; | ||
|
||
function searchDir(dir) { | ||
const files = fs.readdirSync(dir); | ||
for (const file of files) { | ||
const filePath = path.join(dir, file); | ||
const stat = fs.lstatSync(filePath); | ||
if (stat.isDirectory()) { | ||
searchDir(filePath); | ||
} else if (file === 'Divine.exe') { | ||
divinePath = filePath; | ||
return; | ||
} | ||
async function createAtlas(iconsDir, atlasPath, texturePath, textureUUID) { | ||
const iconSize = 64; // Assuming all icons are 64x64 | ||
const textureSize = 1024; // Final texture size | ||
const atlas = sharp({ | ||
create: { | ||
width: textureSize, | ||
height: textureSize, | ||
channels: 4, | ||
background: { r: 0, g: 0, b: 0, alpha: 0 } | ||
} | ||
}); | ||
|
||
const icons = fs.readdirSync(iconsDir).filter(file => file.endsWith('.png')); | ||
let iconXMLNodes = []; | ||
|
||
for (let i = 0; i < icons.length; i++) { | ||
const iconPath = path.join(iconsDir, icons[i]); | ||
const iconName = path.parse(icons[i]).name; | ||
const x = (i % (textureSize / iconSize)) * iconSize; | ||
const y = Math.floor(i / (textureSize / iconSize)) * iconSize; | ||
|
||
// Composite each icon onto the atlas | ||
await atlas.composite([{ input: iconPath, left: x, top: y }]); | ||
|
||
// Prepare XML node for this icon | ||
iconXMLNodes.push({ | ||
node: { | ||
'@id': 'IconUV', | ||
attribute: [ | ||
{ '@id': 'MapKey', '@value': iconName, '@type': 'FixedString' }, | ||
{ '@id': 'U1', '@value': x / textureSize, '@type': 'float' }, | ||
{ '@id': 'V1', '@value': y / textureSize, '@type': 'float' }, | ||
{ '@id': 'U2', '@value': (x + iconSize) / textureSize, '@type': 'float' }, | ||
{ '@id': 'V2', '@value': (y + iconSize) / textureSize, '@type': 'float' } | ||
] | ||
} | ||
}); | ||
} | ||
|
||
if (fs.existsSync(lslibPath) && fs.lstatSync(lslibPath).isDirectory()) { | ||
searchDir(lslibPath); | ||
} else { | ||
vscode.window.showErrorMessage('lslib directory not found.'); | ||
throw new Error('lslib directory not found.'); | ||
} | ||
|
||
if (!divinePath) { | ||
vscode.window.showErrorMessage('No divine.exe found in lslib directory.'); | ||
throw new Error('No divine.exe found in lslib directory.'); | ||
} | ||
// Save the final atlas image | ||
await atlas.toFile(texturePath); | ||
|
||
// Generate XML content | ||
const xmlContent = xmlbuilder.create({ | ||
save: { | ||
version: { | ||
'@major': '4', '@minor': '0', '@revision': '9', '@build': '322' | ||
}, | ||
region: [ | ||
{ | ||
'@id': 'IconUVList', | ||
node: { | ||
'@id': 'root', | ||
children: iconXMLNodes | ||
} | ||
}, | ||
{ | ||
'@id': 'TextureAtlasInfo', | ||
node: { | ||
'@id': 'root', | ||
children: { | ||
node: [ | ||
{ | ||
'@id': 'TextureAtlasIconSize', | ||
attribute: [ | ||
{ '@id': 'Height', '@value': iconSize, '@type': 'int32' }, | ||
{ '@id': 'Width', '@value': iconSize, '@type': 'int32' } | ||
] | ||
}, | ||
{ | ||
'@id': 'TextureAtlasPath', | ||
attribute: [ | ||
{ '@id': 'Path', '@value': texturePath, '@type': 'string' }, | ||
{ '@id': 'UUID', '@value': textureUUID, '@type': 'FixedString' } | ||
] | ||
} | ||
] | ||
} | ||
} | ||
} | ||
] | ||
} | ||
}, { encoding: 'utf-8' }).end({ pretty: true }); | ||
|
||
return divinePath; | ||
// Save XML to file | ||
fs.writeFileSync(atlasPath, xmlContent); | ||
} | ||
|
||
|
||
let createAtlasCommand = vscode.commands.registerCommand('bg3-mod-helper.createAtlas', async function () { // Made the function async | ||
let createAtlasCommand = vscode.commands.registerCommand('bg3-mod-helper.createAtlas', async function () { | ||
console.log('‾‾createAtlasCommand‾‾'); | ||
const { rootModPath, lslibPath } = getConfig(); | ||
|
||
const scriptPath = path.join(__dirname, '..', 'support_files', 'scripts', 'python', 'add_icons_to_atlas.py'); | ||
|
||
let import_test = false | ||
|
||
let divinePath_; | ||
try { | ||
divinePath_ = findDivineExe(lslibPath); | ||
} catch (error) { | ||
console.log(error) | ||
const { rootModPath } = getConfig(); | ||
const modName = await getModName(); // Assuming this function correctly fetches the mod's name | ||
const newUuid = uuidv4(); | ||
|
||
// Directories for icons, texture, and atlas XML | ||
const iconsDirPath = await vscode.window.showOpenDialog({ canSelectFolders: true, canSelectFiles: false }); | ||
if (!iconsDirPath) { | ||
vscode.window.showErrorMessage('Icons directory not selected.'); | ||
return; | ||
} | ||
|
||
const modName = await getModName(); | ||
const texturesDirPath = path.join(rootModPath, 'Public', modName, 'Assets', 'Textures', 'Icons'); | ||
const texturePath = path.join(texturesDirPath, `Icons_${modName}.dds`); | ||
const atlasDirPath = path.join(rootModPath, 'Public', modName, 'GUI'); | ||
const atlasPath = path.join(atlasDirPath, `Icons_${modName}.lsx`); | ||
|
||
function checkPythonImports(packages) { | ||
return new Promise((resolve, reject) => { | ||
let importCommands = packages.map(pkg => `import ${pkg}`).join(';'); | ||
exec(`python -c "${importCommands}"`, (error, stdout, stderr) => { | ||
if (error) { | ||
reject(`Missing package(s): ${packages.join(', ')}`); | ||
} else { | ||
resolve(); | ||
} | ||
}); | ||
}); | ||
} | ||
// Ensure directories exist | ||
[texturesDirPath, atlasDirPath].forEach(dir => { | ||
if (!fs.existsSync(dir)) { | ||
fs.mkdirSync(dir, { recursive: true }); | ||
} | ||
}); | ||
|
||
// Create atlas and texture | ||
try { | ||
await checkPythonImports(['PIL', 'numpy']); | ||
import_test = true; | ||
await createAtlas(iconsDirPath[0].fsPath, atlasPath, texturePath, newUuid); | ||
vscode.window.showInformationMessage('Atlas and texture created successfully.'); | ||
} catch (error) { | ||
const terminal = vscode.window.createTerminal('Package Install'); | ||
terminal.show(); | ||
terminal.sendText('pip install Pillow numpy', false); | ||
vscode.window.showInformationMessage(`${error}. Please run the command in the opened terminal by pressing Enter.`); | ||
return; | ||
} | ||
|
||
if (import_test === true) { | ||
const newUuid = uuidv4(); | ||
// Function to prompt user to select a directory | ||
async function selectDirectory() { | ||
const options = { | ||
canSelectMany: false, | ||
openLabel: 'Select', | ||
canSelectFolders: true, | ||
canSelectFiles: false | ||
}; | ||
const fileUri = await vscode.window.showOpenDialog(options); | ||
if (fileUri && fileUri[0]) { | ||
return fileUri[0].fsPath; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
// Prompt user to select the icons directory | ||
const iconsDirPath = await selectDirectory(); | ||
if (!iconsDirPath) { | ||
vscode.window.showErrorMessage('Icons directory not selected.'); | ||
return; | ||
} | ||
|
||
const texturesDirPath = path.join(rootModPath, 'Public', modName, 'Assets', 'Textures', 'Icons'); | ||
const texturesPath = path.join(texturesDirPath, `Icons_${modName}.dds`); | ||
const atlasDirPath = path.join(rootModPath, 'Public', modName, 'GUI'); | ||
const atlasPath = path.join(atlasDirPath, `Icons_${modName}.lsx`); | ||
const mergedDirPath = path.join(rootModPath, 'Public', modName, 'Content', 'UI', '[PAK]_UI'); | ||
const mergedPath = path.join(mergedDirPath, 'merged.lsx'); | ||
const args = [ | ||
'-i', `"${iconsDirPath}"`, | ||
'-a', `"${atlasPath}"`, | ||
'-t', `"${texturesPath}"`, | ||
'-u', `"${newUuid}"`, | ||
'--divine', `"${divinePath_}"` | ||
].join(' '); | ||
|
||
// Before writing the modified content to the new merged.lsx file | ||
if (!fs.existsSync(mergedDirPath)) { | ||
fs.mkdirSync(mergedDirPath, { recursive: true }); | ||
} | ||
|
||
const command = `python "${scriptPath}" ${args}`; | ||
|
||
exec(command, (error, stdout, stderr) => { | ||
if (error) { | ||
console.error(`Error: ${error.message}`); | ||
vscode.window.showErrorMessage(`Error: ${error.message}`); | ||
return; | ||
} | ||
if (stderr) { | ||
console.error(`stderr: ${stderr}`); | ||
vscode.window.showErrorMessage(`stderr: ${stderr}`); | ||
return; | ||
} | ||
console.log(`stdout: ${stdout}`); | ||
vscode.window.showInformationMessage('Python script executed successfully.'); | ||
}); | ||
console.log('#################################') | ||
// Check if merged.lsx exists | ||
if (fs.existsSync(mergedPath)) { | ||
const overwrite = await vscode.window.showInformationMessage('A merged.lsx file already exists. Do you want to overwrite it?', 'Yes', 'No'); | ||
if (overwrite === 'No') { | ||
return; // Stop execution if user chooses not to overwrite | ||
} | ||
} | ||
|
||
// Use a skeleton merged.lsx file as a template | ||
const skeletonMergedPath = path.join(__dirname, '../support_files/templates/long_skeleton_files/merged_atlas.lsx'); | ||
let mergedContent = fs.readFileSync(skeletonMergedPath, 'utf8'); | ||
|
||
// Replace placeholders in merged.lsx file | ||
mergedContent = mergedContent.replace(/\{uuid\}/g, newUuid); | ||
mergedContent = mergedContent.replace(/\{file_name\}/g, `Icons_${modName}`); | ||
mergedContent = mergedContent.replace(/\{file_path\}/g, `Public/${modName}/Assets/Textures/Icons/Icons_${modName}.dds`); | ||
|
||
// Write the modified content to the new merged.lsx file | ||
fs.writeFileSync(mergedPath, mergedContent, 'utf8'); | ||
vscode.window.showInformationMessage(`merged.lsx file created/updated successfully at ${mergedDirPath}`); | ||
|
||
} else { | ||
console.log('Pil not found, not running exe for atlas icon stuff') | ||
vscode.window.showErrorMessage(`Failed to create atlas or texture: ${error.message}`); | ||
} | ||
console.log('__createAtlasCommand__'); | ||
}); | ||
|
||
module.exports = createAtlasCommand; |
Oops, something went wrong.