'use strict'; const path = require('path'); const fs = require('fs').promises; const { downloadGitHubRepoFile, downloadReleaseAsset } = require('./github'); function pad2(n) { return String(n).padStart(2, '0'); } function backupSuffix() { const d = new Date(); return `${d.getFullYear()}${pad2(d.getMonth() + 1)}${pad2(d.getDate())}-${pad2(d.getHours())}${pad2(d.getMinutes())}${pad2(d.getSeconds())}`; } function wowExePath(cfg) { const exe = (cfg.launch && cfg.launch.exe) || 'Wow.exe'; const parts = exe.replace(/\\/g, '/').split('/').filter(Boolean); return path.join(cfg.game_dir, ...parts); } function wowInstallValid(cfg) { if (!cfg.game_dir) return false; return require('fs').existsSync(wowExePath(cfg)); } async function installFile(cfg, entry) { const parts = String(entry.dest).replace(/\\/g, '/').split('/').filter(Boolean); const destAbs = path.join(cfg.game_dir, ...parts); if (entry.backup) { try { const st = await fs.stat(destAbs); if (st.isFile()) { const bak = `${destAbs}.bak-${backupSuffix()}`; await fs.rename(destAbs, bak); } } catch (e) { if (e.code !== 'ENOENT') throw e; } } else { try { await fs.unlink(destAbs); } catch (e) { if (e.code !== 'ENOENT') throw e; } } const tmp = destAbs + '.new'; if (entry.from_release) { await downloadReleaseAsset(cfg, entry.source, tmp); } else { await downloadGitHubRepoFile(cfg, entry.source, tmp); } await fs.rename(tmp, destAbs); } async function applyRealmlist(cfg) { if (!cfg.realmlist || !cfg.realmlist.enabled) return; let line = String(cfg.realmlist.line || '').trim(); if (!line) throw new Error('realmlist.line empty'); if (!line.toLowerCase().startsWith('set realmlist ')) { line = `set realmlist ${line}`; } const content = line + '\n'; let paths = cfg.realmlist.paths; if (!paths || !paths.length) paths = ['Data/enUS/realmlist.wtf']; for (const rel of paths) { const r = String(rel).trim().replace(/\\/g, '/'); if (!r) continue; const segs = r.split('/').filter(Boolean); const abs = path.join(cfg.game_dir, ...segs); await fs.mkdir(path.dirname(abs), { recursive: true }); await fs.writeFile(abs, content, 'utf8'); } } async function applyPatches(cfg, onStatus) { for (const f of cfg.files || []) { if (onStatus) onStatus(`Updating ${f.dest} …`); try { await installFile(cfg, f); } catch (e) { throw new Error(`sync ${f.dest}: ${e.message || e}`); } } if (cfg.realmlist && cfg.realmlist.enabled) { if (onStatus) onStatus('Applying realmlist …'); await applyRealmlist(cfg); } if (onStatus) onStatus('All patches applied.'); } async function doAuth(cfg, username, password) { if (!cfg.auth || !cfg.auth.enabled) return; const u = String(username || '').trim(); const p = String(password || ''); if (!u || !p) throw new Error('username and password required'); const body = { [cfg.auth.username_field || 'username']: u, [cfg.auth.password_field || 'password']: p, }; const res = await fetch(cfg.auth.url, { method: cfg.auth.method || 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, body: JSON.stringify(body), }); const t = await res.text(); if (res.status < 200 || res.status >= 300) { throw new Error(`login failed ${res.status}: ${t.slice(0, 400)}`); } } module.exports = { applyPatches, applyRealmlist, wowExePath, wowInstallValid, doAuth, };