'use strict'; const path = require('path'); const fs = require('fs').promises; const { normalizeWinGameDir } = require('./win-game-dir'); /** Sources no longer shipped; drop from merged files so old launcher.json does not keep fetching them. */ const DEPRECATED_FILE_SOURCES = new Set(['patch-Z.MPQ', 'Wow-patched.exe']); function mergeFilesList(defaults, user) { const dep = (e) => DEPRECATED_FILE_SOURCES.has(String(e && e.source ? e.source : '').trim()); if (Array.isArray(user.files) && user.files.length) { const filtered = user.files.map((e) => ({ ...e })).filter((e) => !dep(e)); if (filtered.length) return filtered; } const defList = Array.isArray(defaults.files) ? defaults.files : []; return defList.map((e) => ({ ...e })).filter((e) => !dep(e)); } function userFilesContainDeprecated(user) { const files = user && user.files; if (!Array.isArray(files)) return false; return files.some((e) => DEPRECATED_FILE_SOURCES.has(String(e && e.source ? e.source : '').trim())); } function mergeConfig(defaults, user) { return { ...defaults, ...user, update_feed_url: user.update_feed_url != null && user.update_feed_url !== '' ? user.update_feed_url : defaults.update_feed_url, launcher_updates_from_github: user.launcher_updates_from_github != null ? user.launcher_updates_from_github : defaults.launcher_updates_from_github, github: { ...defaults.github, ...(user.github || {}) }, gitea: { ...defaults.gitea, ...(user.gitea || {}) }, patch_manifest: { ...defaults.patch_manifest, ...(user.patch_manifest || {}) }, launch: { ...defaults.launch, ...(user.launch || {}) }, auth: user.auth != null ? { ...defaults.auth, ...user.auth } : defaults.auth, realmlist: user.realmlist != null ? { ...defaults.realmlist, ...user.realmlist } : defaults.realmlist, files: mergeFilesList(defaults, user), }; } /** Hardcoded Gitea host/repo (see lib/baked-gitea-channel.js). Non-empty baked values win. */ function applyBakedGitea(cfg) { let baked; try { baked = require('./baked-gitea-channel'); } catch { return cfg; } if (!baked || typeof baked !== 'object') return cfg; cfg.gitea = { ...(cfg.gitea || {}) }; for (const k of ['base_url', 'owner', 'repo', 'release_tag']) { const v = baked[k]; if (v != null && String(v).trim() !== '') cfg.gitea[k] = String(v).trim(); } return cfg; } function getConfigPath(app) { if (process.env.FRACTURED_LAUNCHER_CONFIG) return process.env.FRACTURED_LAUNCHER_CONFIG; if (app && app.isPackaged) { // AppImage (and macOS .app) run from a read-only mount — cannot write beside execPath. if (process.platform === 'linux' || process.platform === 'darwin') { return path.join(app.getPath('userData'), 'launcher.json'); } return path.join(path.dirname(process.execPath), 'launcher.json'); } return path.join(__dirname, '..', 'launcher.json'); } async function loadConfig(app) { const p = getConfigPath(app); const defPath = path.join(__dirname, '..', 'default-launcher.json'); const defaults = JSON.parse(await fs.readFile(defPath, 'utf8')); try { const user = JSON.parse(await fs.readFile(p, 'utf8')); const config = applyBakedGitea(mergeConfig(defaults, user)); if (userFilesContainDeprecated(user)) { await fs.writeFile(p, JSON.stringify(config, null, 2), 'utf8'); } return { configPath: p, config }; } catch (e) { if (e.code === 'ENOENT') { const initial = applyBakedGitea(mergeConfig(defaults, {})); await fs.writeFile(p, JSON.stringify(initial, null, 2), 'utf8'); return { configPath: p, config: JSON.parse(JSON.stringify(initial)) }; } throw e; } } async function saveGameDir(configPath, gameDir) { const defPath = path.join(__dirname, '..', 'default-launcher.json'); const defaults = JSON.parse(await fs.readFile(defPath, 'utf8')); const user = JSON.parse(await fs.readFile(configPath, 'utf8')); user.game_dir = gameDir; const merged = applyBakedGitea(mergeConfig(defaults, user)); await fs.writeFile(configPath, JSON.stringify(merged, null, 2), 'utf8'); return merged; } function resolveGameDir(cfg, configPath) { const gd = cfg.game_dir; if (!gd) return ''; const abs = path.isAbsolute(gd) ? path.normalize(gd) : path.normalize(path.join(path.dirname(configPath), gd)); if (process.platform === 'win32') return normalizeWinGameDir(abs); return abs; } module.exports = { getConfigPath, loadConfig, saveGameDir, resolveGameDir, mergeConfig, applyBakedGitea };