diff --git a/lib/modAssist_func_lib.js b/lib/modAssist_func_lib.js
index c7875f5a..d4477b94 100644
--- a/lib/modAssist_func_lib.js
+++ b/lib/modAssist_func_lib.js
@@ -1717,7 +1717,7 @@ const file = {
obj.dest_filename
)
- promises.push(fsPromise.cp(obj.source_file, destFile, { recursive : true }).then(() => {
+ promises.push(fsPromise.cp(obj.source_file, destFile, { recursive : true, force : true }).then(() => {
file.log.info('Copy succeed', obj.source_file, destFile)
results.push(file._result_ok('copy', obj.source_file, destFile))
}).catch((err) => {
diff --git a/lib/modCheckLib.js b/lib/modCheckLib.js
index 240c2316..dd421f16 100644
--- a/lib/modCheckLib.js
+++ b/lib/modCheckLib.js
@@ -1381,6 +1381,7 @@ class csvFileChecker {
* @class
*/
class saveFileChecker {
+ #silent = false
#fileName = null
#isFolder = false
#fileHandle = null
@@ -1400,12 +1401,13 @@ class saveFileChecker {
* @param {string} fileName full file and path of save
* @param {boolean} isFolder Expect a folder
*/
- constructor( fileName, isFolder ) {
+ constructor( fileName, isFolder, silent = false ) {
+ this.#silent = silent
this.#fileName = fileName
this.#isFolder = isFolder
this.#log = serveIPC.log.group(`savegame-${path.basename(fileName)}`)
- this.#log.info(`Adding Save: ${fileName}`)
+ if ( ! this.#silent ) { this.#log.info(`Adding Save: ${fileName}`) }
}
/**
@@ -1416,6 +1418,7 @@ class saveFileChecker {
return {
errorList : this.errorList,
farms : this.farms,
+ isValid : this.errorList.length === 0,
mapMod : this.mapMod,
mods : this.mods,
singleFarm : this.singleFarm,
@@ -1432,8 +1435,9 @@ class saveFileChecker {
const couldOpen = await this.#fileHandle.open()
if ( ! couldOpen ) {
- this.#log.danger(`Save open fail: ${this.#fileName}`)
+ if ( ! this.#silent ) { this.#log.danger(`Save open fail: ${this.#fileName}`) }
this.errorList.push(['SAVEGAME_UNREADABLE', this.#fileName])
+ this.isValid = false
return this.dataReturn
}
@@ -1450,7 +1454,7 @@ class saveFileChecker {
#util_readError(type) {
this.errorList.push(['SAVEGAME_PARSE_ERROR', `${type}.xml`])
- this.#log.warning(`Parsing ${type}.xml failed`)
+ if ( ! this.#silent ) { this.#log.warning(`Parsing ${type}.xml failed`) }
}
#util_iterateXML(data) {
diff --git a/lib/workerThreadLib.js b/lib/workerThreadLib.js
index 39278c65..fec9b010 100644
--- a/lib/workerThreadLib.js
+++ b/lib/workerThreadLib.js
@@ -1828,8 +1828,8 @@ class fileHandlerAsync {
}
return thisParsedXML
- } catch {
- this.#log.warning(`Caught unrecoverable XML Parse error : ${fileName}`)
+ } catch (err) {
+ this.#log.warning(`Caught unrecoverable XML Parse error : ${fileName} :: ${err}`)
return false
}
}).catch((err) => {
diff --git a/modAssist_main.js b/modAssist_main.js
index ffe49730..c331c0fe 100644
--- a/modAssist_main.js
+++ b/modAssist_main.js
@@ -119,13 +119,23 @@ ipcMain.handle('files:drop', async (_, files) => {
})
return
} else if ( files.length === 1 && files[0].endsWith('.json') ) {
- // TODO: HANDLE JSON IMPORT
+ // HANDLE JSON IMPORT
funcLib.general.importJSON_process(files[0])
+ return
} else if ( files.length === 1 && files[0].endsWith('.zip') ) {
+ // Handles ZIP packs, ZIP save game, and finally single ZIP add
+ const saveResult = await new saveFileChecker(files[0], false, true).getInfo()
+
+ if ( saveResult.isValid ) {
+ serveIPC.windowLib.createNamedWindow('save', {
+ collectKey : null,
+ thisSaveGame : saveResult,
+ })
+ return
+ }
return getCopyMoveDelete('import', null, null, files, await new modPackChecker(files[0]).getInfo())
- } else {
- return getCopyMoveDelete('import', null, null, files, false)
}
+ return getCopyMoveDelete('import', null, null, files, false)
})
function sendCopyMoveDelete(operation, modIDS) {
@@ -613,7 +623,7 @@ ipcMain.handle('gamelog:auto', () => {
ipcMain.handle('gamelog:open', () => {
return dialog.showOpenDialog(serveIPC.windowLib.win.gamelog, {
properties : ['openFile'],
- defaultPath : path.join(serveIPC.prefs.basePath(), 'log.txt'),
+ defaultPath : path.join(funcLib.prefs.basePath(), 'log.txt'),
filters : [{ name : 'Log Files', extensions : ['txt'] }],
}).then((result) => {
if ( ! result.canceled ) { funcLib.gameSet.setGameLog(result.filePaths[0]) }
@@ -698,6 +708,8 @@ ipcMain.on('cache:clean', () => {
}
}
+ serveIPC.log.info('cache-cleaner', `Removed ${md5Set.size} stale entries.`)
+
for ( const md5 of md5Set ) { delete serveIPC.storeCache.remMod(md5) }
serveIPC.storeCache.saveFile()
diff --git a/renderer/a_changelog.html b/renderer/a_changelog.html
index aaca0c48..c6740e9b 100644
--- a/renderer/a_changelog.html
+++ b/renderer/a_changelog.html
@@ -30,13 +30,11 @@
-
Version 4.0.0 Features
+
Version 5.0.0 Features
- - ADDED Malware Detector for Mods
- - CHANGED New ZIP Library (no more 2Gb limit)
- - ADDED game.xml / gameSettings.xml Backups
- - ADDED Collection Import and Export
+ - ADDED FS2025 Initial Support
+ - CHANGED Backend re-write
@@ -45,6 +43,14 @@
GitHub Known Issues & Changes
Added Features by Major Version
+ -
+ 5.0.0
+
+ - Another massive refactor for stability
+ - Add basic FS25 support
+ - Remove multi-version option, always on now
+
+
-
4.0.0
diff --git a/renderer/renderJS/importjson_ui.js b/renderer/renderJS/importjson_ui.js
index e9ba89c4..6dddb26b 100644
--- a/renderer/renderJS/importjson_ui.js
+++ b/renderer/renderJS/importjson_ui.js
@@ -97,7 +97,7 @@ class windowState {
node.classList.add('w-75', 'd-block', 'mx-auto', 'btn', 'btn-primary', 'btn-sm', 'disabled', 'btn-download', 'mb-2')
node.innerHTML = ` :: ${uri}`
- node.firstElementChild.addEventListener('click', () => {
+ node.addEventListener('click', () => {
node.firstElementChild.classList.remove('btn-primary')
node.firstElementChild.classList.add('btn-success', 'disabled')
window.importjson_IPC.doDownload(this.importKey, uri, isPack)
@@ -131,8 +131,8 @@ class LoaderLib {
MA.byId('loadOverlay_downloadCancel').clsShow()
MA.byId('loadOverlay_speed').clsShow()
}
- updateCount(count, inMB = false) {
- const thisCount = inMB ? DATA.bytesToMB(count, false) : count
+ async updateCount(count, inMB = false) {
+ const thisCount = inMB ? await DATA.bytesToMB(count, false) : count
const thisElement = MA.byId('loadOverlay_statusCurrent')
const thisProg = MA.byId('loadOverlay_statusProgBarInner')
const thisPercent = `${Math.max(Math.ceil((count / this.lastTotal) * 100), 0)}%`
@@ -145,7 +145,7 @@ class LoaderLib {
const perDone = Math.max(1, Math.ceil((count / this.lastTotal) * 100))
const perRem = 100 - perDone
const elapsedSec = (Date.now() - this.startTime) / 1000
- const estSpeed = DATA.bytesToMBCalc(count, false) / elapsedSec // MB/sec
+ const estSpeed = await DATA.bytesToMBCalc(count, false) / elapsedSec // MB/sec
const secRemain = elapsedSec / perDone * perRem
const prettyMinRemain = Math.floor(secRemain / 60)
@@ -170,9 +170,9 @@ class LoaderLib {
this.show()
}
- updateTotal(count, inMB = false) {
+ async updateTotal(count, inMB = false) {
if ( inMB ) { this.startTime = Date.now() }
- const thisCount = inMB ? DATA.bytesToMB(count) : count
+ const thisCount = inMB ? await DATA.bytesToMB(count) : count
MA.byIdText('loadOverlay_statusTotal', thisCount)
this.lastTotal = ( count < 1 ) ? 1 : count
}
diff --git a/renderer/renderJS/main_ui_lib.js b/renderer/renderJS/main_ui_lib.js
index d4b2d074..2e0c418c 100644
--- a/renderer/renderJS/main_ui_lib.js
+++ b/renderer/renderJS/main_ui_lib.js
@@ -1005,28 +1005,28 @@ class StateManager {
},
launchGame() {
const currentList = MA.byIdValue('collectionSelect')
- if ( currentList === this.flag.activeCollect ) {
+ if ( currentList === window.state.flag.activeCollect ) {
// Selected is active, no confirm
LEDLib.spinLED()
window.main_IPC.dispatch('game')
} else {
// Different, ask confirmation
- MA.byIdHTML('no_match_game_list', this.mapCollectionDropdown[this.flag.activeCollect])
+ MA.byIdHTML('no_match_game_list', this.mapCollectionDropdown[window.state.flag.activeCollect])
MA.byIdHTML('no_match_ma_list', this.mapCollectionDropdown[currentList])
LEDLib.fastBlinkLED()
- this.modal.mismatch.show()
+ window.state.modal.mismatch.show()
}
},
launchGame_FIX : () => {
- this.modal.mismatch.hide()
- this.action.collectActive().then(() => {
+ window.state.modal.mismatch.hide()
+ window.state.action.collectActive().then(() => {
window.main_IPC.dispatch('game')
})
},
launchGame_IGNORE : () => {
- this.modal.mismatch.hide()
+ window.state.modal.mismatch.hide()
LEDLib.spinLED()
- MA.byIdValue('collectionSelect', this.flag.activeCollect)
+ MA.byIdValue('collectionSelect', window.state.flag.activeCollect)
window.main_IPC.dispatch('game')
},
openModInfo : (mod) => {
@@ -1637,7 +1637,9 @@ class DragDropLib {
const fileList = []
for ( const thisFile of files ) { fileList.push(thisFile.path) }
window.main_IPC.files.drop(fileList).then((result) => {
- window.state.files.start_external('import', result)
+ if ( typeof result !== 'undefined' ) {
+ window.state.files.start_external('import', result)
+ }
})
}
@@ -1747,8 +1749,8 @@ class LoaderLib {
MA.byId('loadOverlay_downloadCancel').clsShow()
MA.byId('loadOverlay_speed').clsShow()
}
- updateCount(count, inMB = false) {
- const thisCount = inMB ? DATA.bytesToMB(count, false) : count
+ async updateCount(count, inMB = false) {
+ const thisCount = inMB ? await DATA.bytesToMB(count, false) : count
const thisElement = MA.byId('loadOverlay_statusCurrent')
const thisProg = MA.byId('loadOverlay_statusProgBarInner')
const thisPercent = `${Math.max(Math.ceil((count / this.lastTotal) * 100), 0)}%`
@@ -1761,7 +1763,7 @@ class LoaderLib {
const perDone = Math.max(1, Math.ceil((count / this.lastTotal) * 100))
const perRem = 100 - perDone
const elapsedSec = (Date.now() - this.startTime) / 1000
- const estSpeed = DATA.bytesToMBCalc(count, false) / elapsedSec // MB/sec
+ const estSpeed = await DATA.bytesToMBCalc(count, false) / elapsedSec // MB/sec
const secRemain = elapsedSec / perDone * perRem
const prettyMinRemain = Math.floor(secRemain / 60)
@@ -1786,9 +1788,9 @@ class LoaderLib {
this.show()
}
- updateTotal(count, inMB = false) {
+ async updateTotal(count, inMB = false) {
if ( inMB ) { this.startTime = Date.now() }
- const thisCount = inMB ? DATA.bytesToMB(count) : count
+ const thisCount = inMB ? await DATA.bytesToMB(count) : count
MA.byIdText('loadOverlay_statusTotal', thisCount)
this.lastTotal = ( count < 1 ) ? 1 : count
}
@@ -2026,7 +2028,7 @@ class FileLib {
`,
shortname : shortName,
- title : zipFiles === null ? path : [...zipFiles].join('
'),
+ title : zipFiles === null ? path.replaceAll('\\', '\\') : [...zipFiles].join('
'),
})
}
diff --git a/renderer/renderJS/mini_ui.js b/renderer/renderJS/mini_ui.js
index 3b3085e6..58068a92 100644
--- a/renderer/renderJS/mini_ui.js
+++ b/renderer/renderJS/mini_ui.js
@@ -112,10 +112,10 @@ class StateManager {
},
launchGame : () => {
const currentList = MA.byIdValue('collectionSelect')
- if ( currentList === this.track.activeCollect ) {
+ if ( currentList === this.flag.activeCollect ) {
// Selected is active, no confirm
LEDLib.spinLED()
- window.mods.startFarmSim()
+ window.mini_IPC.startFarmSim()
} else if ( this.flasherInterval === null ) {
// Different, ask confirmation
LEDLib.fastBlinkLED()
diff --git a/renderer/renderJS/resolve_ui.js b/renderer/renderJS/resolve_ui.js
index 8e1f3930..85d910aa 100644
--- a/renderer/renderJS/resolve_ui.js
+++ b/renderer/renderJS/resolve_ui.js
@@ -98,7 +98,7 @@ class windowState {
// MARK: copy button action
MA.byIdEventIfExists('copyButton', () => {
- window.fileOpOverlay.show()
+ this.overlay.show()
MA.byId('fileOpWorking').clsShow()
MA.byId('fileOpSuccess').clsHide()
MA.byId('fileOpDanger').clsHide()
diff --git a/renderer/renderJS/util/storeItems.js b/renderer/renderJS/util/storeItems.js
index 56c711cb..1998ead4 100644
--- a/renderer/renderJS/util/storeItems.js
+++ b/renderer/renderJS/util/storeItems.js
@@ -337,7 +337,7 @@ const ST = {
let nameString = thisProduction.name
- if ( thisProduction.params !== null ) {
+ if ( typeof thisProduction.params !== 'undefined' && thisProduction.params !== null ) {
const paramArray = thisProduction.params.split('|')
let matchNum = -1
nameString = nameString.replace(/%s/g, () => {
diff --git a/renderer/renderJS/version_ui.js b/renderer/renderJS/version_ui.js
index db072709..80842724 100644
--- a/renderer/renderJS/version_ui.js
+++ b/renderer/renderJS/version_ui.js
@@ -89,7 +89,7 @@ async function displayData(data) {
class : ['badge-mod-notmod'],
skip : !isDiff || thisItem.modhub === null,
}),
- I18N.buildBadgeBare({
+ I18N.buildBadge({
name : isDiff ? 'version_diff' : 'version_same',
class : [],
}),
diff --git a/renderer/resolve.html b/renderer/resolve.html
index ebb9fda7..eefbb837 100644
--- a/renderer/resolve.html
+++ b/renderer/resolve.html
@@ -103,7 +103,7 @@
shortName version
title
-
collectName
+
collectName