forked from OctoWoW/OctoLauncher
Fixed tweaks and mods, added localization, added antivirus walkthrough
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
import path from 'node:path';
|
||||
|
||||
import fs from 'fs-extra';
|
||||
import {
|
||||
SFileOpenArchive,
|
||||
SFileCloseArchive,
|
||||
SFileHasFile
|
||||
} from 'stormlib-node';
|
||||
import { STREAM_FLAG } from 'stormlib-node/dist/enums';
|
||||
import Logger from 'electron-log/main';
|
||||
|
||||
import Preferences from './preferences';
|
||||
|
||||
const PREFERRED = 'L';
|
||||
const LETTERS = 'BCDEFGHIJKLMNOPQRSTUVWXYZ'.split('');
|
||||
const MARKER = 'octolocale.marker';
|
||||
|
||||
const patchFile = (dataDir: string, letter: string) =>
|
||||
path.join(dataDir, `patch-${letter}.mpq`);
|
||||
|
||||
const prebuiltFor = (dataDir: string, locale: string) =>
|
||||
path.join(dataDir, locale, 'patch-L.mpq');
|
||||
|
||||
const isOurPatch = (mpqPath: string): boolean => {
|
||||
if (!fs.existsSync(mpqPath)) return false;
|
||||
try {
|
||||
const h = SFileOpenArchive(mpqPath, STREAM_FLAG.READ_ONLY);
|
||||
try {
|
||||
return SFileHasFile(h, MARKER);
|
||||
} finally {
|
||||
SFileCloseArchive(h);
|
||||
}
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
const usableSlot = (dataDir: string, letter: string): boolean => {
|
||||
if (fs.existsSync(path.join(dataDir, `patch-${letter}.MPQ`))) return false;
|
||||
const f = patchFile(dataDir, letter);
|
||||
return !fs.existsSync(f) || isOurPatch(f);
|
||||
};
|
||||
|
||||
const removeOurPatch = async (dataDir: string) => {
|
||||
for (const l of LETTERS) {
|
||||
const f = patchFile(dataDir, l);
|
||||
if (isOurPatch(f)) await fs.remove(f).catch(() => {});
|
||||
}
|
||||
if (Preferences.data.localePatchLetter || Preferences.data.localePatchLocale)
|
||||
Preferences.data = {
|
||||
localePatchLetter: undefined,
|
||||
localePatchLocale: undefined
|
||||
};
|
||||
};
|
||||
|
||||
export const applyLocalePatch = async (
|
||||
clientDir: string | undefined,
|
||||
locale: string | undefined
|
||||
): Promise<void> => {
|
||||
if (!clientDir) return;
|
||||
const dataDir = path.join(clientDir, 'Data');
|
||||
|
||||
const nextLocale = !locale || locale === 'enUS' ? undefined : locale;
|
||||
if (Preferences.data.localePatchLocale !== nextLocale)
|
||||
await fs.remove(path.join(clientDir, 'WDB')).catch(() => {});
|
||||
|
||||
if (!locale || locale === 'enUS') {
|
||||
await removeOurPatch(dataDir);
|
||||
return;
|
||||
}
|
||||
|
||||
const source = prebuiltFor(dataDir, locale);
|
||||
if (!(await fs.pathExists(source))) {
|
||||
Logger.warn(
|
||||
`Locale patch: no prebuilt patch-L for ${locale}; leaving UI as-is`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const tracked = Preferences.data.localePatchLetter;
|
||||
const letter =
|
||||
(tracked && usableSlot(dataDir, tracked) ? tracked : undefined) ??
|
||||
(usableSlot(dataDir, PREFERRED)
|
||||
? PREFERRED
|
||||
: LETTERS.find(l => usableSlot(dataDir, l)));
|
||||
if (!letter) {
|
||||
Logger.warn('Locale patch: no usable patch slot');
|
||||
return;
|
||||
}
|
||||
const target = patchFile(dataDir, letter);
|
||||
|
||||
try {
|
||||
if (
|
||||
Preferences.data.localePatchLocale === locale &&
|
||||
isOurPatch(target) &&
|
||||
fs.statSync(target).mtimeMs >= fs.statSync(source).mtimeMs
|
||||
)
|
||||
return;
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
|
||||
try {
|
||||
for (const l of LETTERS) {
|
||||
if (l === letter) continue;
|
||||
const f = patchFile(dataDir, l);
|
||||
if (isOurPatch(f)) await fs.remove(f).catch(() => {});
|
||||
}
|
||||
await fs.copy(source, target, { overwrite: true });
|
||||
Preferences.data = { localePatchLetter: letter, localePatchLocale: locale };
|
||||
Logger.log(
|
||||
`Locale patch: swapped prebuilt ${locale} -> patch-${letter}.mpq`
|
||||
);
|
||||
} catch (e) {
|
||||
Logger.error('Locale patch: failed to swap in prebuilt patch', e);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user