11import { parse , getTime } from "date-fns" ;
22import Registry from "winreg" ;
33import * as VDF from "@node-steam/vdf" ;
4- import * as fs from "fs/promises" ;
4+ import * as fsPromises from "fs/promises" ;
55import * as dumbfs from "fs" ;
66import appData from "./appData" ;
77import fetch from "electron-fetch" ;
88import { zonedTimeToUtc } from "date-fns-tz" ;
99import * as nodePath from "path" ;
1010import * as fsExtra from "fs-extra" ;
11+ import * as os from "os" ;
1112
1213export function fetchModData ( ids : string [ ] , cb : ( modData : ModData ) => void , log : ( msg : string ) => void ) {
1314 ids . forEach ( async ( workshopId ) => {
@@ -157,17 +158,17 @@ export async function getDataMod(filePath: string, log: (msg: string) => void):
157158 let lastChangedLocal = undefined ;
158159 let size = - 1 ;
159160 try {
160- [ lastChangedLocal , size ] = await fs . stat ( filePath ) . then ( ( stats ) => {
161+ [ lastChangedLocal , size ] = await fsPromises . stat ( filePath ) . then ( ( stats ) => {
161162 return [ stats . mtimeMs , stats . size ] ;
162163 } ) ;
163164 } catch ( err ) {
164165 log ( `ERROR: ${ err } ` ) ;
165166 }
166167
167168 let doesThumbnailExist = false ;
168- const thumbnailPath = ` ${ dataPath } \\ ${ fileName . replace ( / \. p a c k $ / , ".png" ) } ` ;
169+ const thumbnailPath = nodePath . join ( dataPath , fileName . replace ( / \. p a c k $ / , ".png" ) ) ;
169170 try {
170- await fs . access ( thumbnailPath , dumbfs . constants . R_OK ) ;
171+ await fsPromises . access ( thumbnailPath , dumbfs . constants . R_OK ) ;
171172 doesThumbnailExist = true ;
172173 // eslint-disable-next-line no-empty
173174 } catch { }
@@ -181,11 +182,13 @@ export async function getDataMod(filePath: string, log: (msg: string) => void):
181182 // eslint-disable-next-line no-empty
182183 } catch { }
183184
185+
186+ const linuxBit = process . platform === "linux" ? "Z:" : "" ;
184187 const mod : Mod = {
185188 humanName : "" ,
186189 name : fileName ,
187190 path : filePath ,
188- modDirectory : nodePath . dirname ( filePath ) ,
191+ modDirectory : linuxBit + nodePath . dirname ( filePath ) ,
189192 imgPath : doesThumbnailExist ? thumbnailPath : "" ,
190193 workshopId : fileName ,
191194 isEnabled : false ,
@@ -206,7 +209,7 @@ const getDataMods = async (gameDir: string, log: (msg: string) => void): Promise
206209 if ( ! dataPath ) throw new Error ( "Data folder not found" ) ;
207210
208211 const vanillaPacks : string [ ] = [ ] ;
209- return fs . readFile ( ` ${ gameDir } \\ data\\ manifest.txt` , "utf8" ) . then ( async ( data ) => {
212+ return fsPromises . readFile ( nodePath . join ( gameDir , " data" , " manifest.txt" ) , "utf8" ) . then ( async ( data ) => {
210213 const re = / ( [ ^ \s ] + ) / ;
211214 data . split ( "\n" ) . map ( ( line ) => {
212215 const found = line . match ( re ) ;
@@ -215,7 +218,7 @@ const getDataMods = async (gameDir: string, log: (msg: string) => void): Promise
215218 }
216219 } ) ;
217220
218- const files = await fs . readdir ( dataPath , { withFileTypes : true } ) ;
221+ const files = await fsPromises . readdir ( dataPath , { withFileTypes : true } ) ;
219222
220223 const dataModsPromises = files
221224 . filter (
@@ -238,36 +241,52 @@ const getDataMods = async (gameDir: string, log: (msg: string) => void): Promise
238241} ;
239242
240243const getFolderPaths = async ( log : ( msg : string ) => void ) => {
241- const regKey = new Registry ( {
242- hive : Registry . HKLM ,
243- key : "\\SOFTWARE\\Wow6432Node\\Valve\\Steam" ,
244- } ) ;
244+ let installPath = "" ;
245+ if ( process . platform === "win32" ) {
246+ const regKey = new Registry ( {
247+ hive : Registry . HKLM ,
248+ key : "\\SOFTWARE\\Wow6432Node\\Valve\\Steam" ,
249+ } ) ;
245250
246- const items = await regKeyValuesAsPromise ( regKey ) ;
247- const installPathObj = items . find ( ( x ) => x . name === "InstallPath" ) ;
248- if ( ! installPathObj ) return ;
251+ const items = await regKeyValuesAsPromise ( regKey ) ;
252+ const installPathObj = items . find ( ( x ) => x . name === "InstallPath" ) ;
253+ if ( ! installPathObj ) {
254+ log ( "Unable to find InstallPath in Windows registry" ) ;
255+ return ;
256+ }
257+ installPath = installPathObj . value ;
258+ }
259+ else if ( process . platform === "linux" ) {
260+ const steamPath = os . homedir ( ) + "/.steam/steam" ;
261+ if ( ! dumbfs . existsSync ( steamPath ) ) {
262+ log ( "Unable to find steam directory at " + steamPath ) ;
263+ return ;
264+ }
265+ installPath = steamPath ;
266+ }
249267
250- const installPath = installPathObj . value ;
251- const libFoldersPath = `${ installPath } \\steamapps\\libraryfolders.vdf` ;
268+ const libFoldersPath = nodePath . join ( installPath , "steamapps" , "libraryfolders.vdf" ) ;
269+ log ( `Check lib vdf at ${ libFoldersPath } ` ) ;
270+ if ( ! dumbfs . existsSync ( libFoldersPath ) ) return ;
252271 log ( `Found libraryfolders.vdf at ${ libFoldersPath } ` ) ;
253272
254- const data = await fs . readFile ( libFoldersPath , "utf8" ) ;
273+ const data = await fsPromises . readFile ( libFoldersPath , "utf8" ) ;
255274 const object = VDF . parse ( data ) . libraryfolders ;
256275 const paths = [ ] ;
257276 for ( const property in object ) {
258277 paths . push ( object [ property ] . path ) ;
259278 }
260279
261- for ( const path of paths ) {
262- const worshopFilePath = `${ path } \\steamapps\\appmanifest_1142710.acf` ;
280+ for ( const basepath of paths ) {
281+ const path = basepath . replaceAll ( "\\\\" , "\\" ) . replaceAll ( "//" , "/" ) ;
282+ const worshopFilePath = nodePath . join ( path , "steamapps" , "appmanifest_1142710.acf" ) ;
263283 try {
264- await fs . readFile ( worshopFilePath ) ;
284+ await fsPromises . readFile ( worshopFilePath ) ;
265285 log ( `Found appmanifest_1142710.acf at ${ worshopFilePath } ` ) ;
266- // log(worshopFilePath);
267- const contentFolder = `${ path } \\steamapps\\workshop\\content\\1142710` ;
268- appData . contentFolder = contentFolder . replaceAll ( "\\\\" , "\\" ) ;
269- appData . gamePath = `${ path } \\steamapps\\common\\Total War WARHAMMER III` . replaceAll ( "\\\\" , "\\" ) ;
270- appData . dataFolder = `${ appData . gamePath } \\data` . replaceAll ( "\\\\" , "\\" ) ;
286+ const contentFolder = nodePath . join ( path , "steamapps" , "workshop" , "content" , "1142710" ) ;
287+ appData . contentFolder = contentFolder ;
288+ appData . gamePath = nodePath . join ( path , "steamapps" , "common" , "Total War WARHAMMER III" ) ;
289+ appData . dataFolder = nodePath . join ( appData . gamePath , "data" ) ;
271290
272291 log ( `Content folder is at ${ appData . contentFolder } ` ) ;
273292 log ( `Game path is at ${ appData . gamePath } ` ) ;
@@ -295,7 +314,7 @@ export async function getContentModInFolder(
295314 if ( ! appData . contentFolder ) throw new Error ( "Content folder not found" ) ;
296315 const contentFolder = appData . contentFolder ;
297316
298- const files = await fs . readdir ( ` ${ contentFolder } \\ ${ contentSubFolderName } ` , { withFileTypes : true } ) ;
317+ const files = await fsPromises . readdir ( nodePath . join ( contentFolder , contentSubFolderName ) , { withFileTypes : true } ) ;
299318
300319 const pack = files . find ( ( file ) => file . name . endsWith ( ".pack" ) ) ;
301320 const img = files . find ( ( file ) => file . name . endsWith ( ".png" ) ) ;
@@ -305,8 +324,8 @@ export async function getContentModInFolder(
305324 let lastChangedLocal = undefined ;
306325 let size = - 1 ;
307326 try {
308- [ lastChangedLocal , size ] = await fs
309- . stat ( ` ${ contentFolder } \\ ${ contentSubFolderName } \\ ${ pack . name } ` )
327+ [ lastChangedLocal , size ] = await fsPromises
328+ . stat ( nodePath . join ( contentFolder , contentSubFolderName , pack . name ) )
310329 . then ( ( stats ) => {
311330 return [ stats . mtimeMs , stats . size ] ;
312331 } ) ;
@@ -315,14 +334,14 @@ export async function getContentModInFolder(
315334 }
316335
317336 // log(`Reading pack file ${contentFolder}\\${file.name}\\${pack.name}`);
318- const packPath = ` ${ contentFolder } \\ ${ contentSubFolderName } \\ ${ pack . name } ` ;
319- const imgPath = ( img && ` ${ contentFolder } \\ ${ contentSubFolderName } \\ ${ img . name } ` ) || "" ;
337+ const packPath = nodePath . join ( contentFolder , contentSubFolderName , pack . name ) ;
338+ const imgPath = ( img && nodePath . join ( contentFolder , contentSubFolderName , img . name ) || "" ) ;
320339 const mod : Mod = {
321340 author : "" ,
322341 humanName : "" ,
323342 name : pack . name ,
324343 path : packPath ,
325- modDirectory : ` ${ contentFolder } \\ ${ contentSubFolderName } ` ,
344+ modDirectory : nodePath . join ( contentFolder , contentSubFolderName ) ,
326345 imgPath : imgPath ,
327346 workshopId : contentSubFolderName ,
328347 isEnabled : false ,
@@ -348,7 +367,7 @@ export async function getMods(log: (msg: string) => void): Promise<Mod[]> {
348367 const dataMods = await getDataMods ( appData . gamePath , log ) ;
349368 mods . push ( ...dataMods ) ;
350369
351- const files = await fs . readdir ( contentFolder , { withFileTypes : true } ) ;
370+ const files = await fsPromises . readdir ( contentFolder , { withFileTypes : true } ) ;
352371 const newMods = files
353372 . filter ( ( file ) => file . isDirectory ( ) )
354373 . map ( async ( contentSubFolder ) => {
0 commit comments