Initial commit
Build check / build (push) Has been cancelled

This commit is contained in:
2026-05-07 19:31:21 -07:00
commit 02bfea8f02
110 changed files with 18550 additions and 0 deletions
+229
View File
@@ -0,0 +1,229 @@
import path from 'path';
import { screen } from 'electron';
import fs from 'fs-extra';
import Logger from 'electron-log/main';
import Preferences from '~main/modules/preferences';
import { ConfigWtfSchema, type PreferencesSchema } from '~common/schemas';
import { isNotUndef } from '~common/utils';
import { fetchFile } from '~main/modules/updater';
const Servers = {
live: {
realmList: 'octowow.st',
patchList: 'octowow.st',
realmName: 'OctoWoW'
},
ptr: {
realmList: 'octowow.st',
patchList: 'octowow.st',
realmName: 'OctoWoW PTR'
}
} as const;
type Tweak = { key: keyof PreferencesSchema['config']; default?: unknown; forced?: boolean } & (
| {
type: 'bytes';
tweaks: [number, number[]][];
}
| {
type: 'int8' | 'uint16' | 'float';
offset: number;
value?: number;
}
);
export const patchExecutable = async () => {
Logger.log('Patching WoW.exe...');
const { clientDir, config } = Preferences.data;
if (!clientDir) return;
const exePath = path.join(clientDir, 'WoW.exe');
try {
Logger.log('Fetching clean WoW.exe...');
const file = await fetchFile('WoW.exe');
const buffer = Buffer.from(file);
const Tweaks = [
{
key: 'largeAddress',
type: 'uint16',
offset: 0x126,
value: buffer.readUint16LE(0x126) | 0x20,
default: false
},
{ key: 'farClip', type: 'float', offset: 0x40fed8 },
{
key: 'fieldOfView',
type: 'float',
offset: 0x4089b4,
value: (config.fieldOfView ?? 1) * (Math.PI / 180),
default: 90
},
{ key: 'frillDistance', type: 'float', offset: 0x467958 },
{
key: 'soundInBackground',
type: 'int8',
offset: 0x3a4869,
value: config.soundInBackground ? 0x27 : 0x14,
default: false
},
{
key: 'alwaysAutoLoot',
type: 'bytes',
tweaks: [
[0x0c1ecf, [0x75]],
[0x0c2b25, [0x75]]
]
},
{ key: 'nameplateRange', type: 'float', offset: 0x40c448 },
{ key: 'cameraDistance', type: 'float', offset: 0x4089a4 },
{
key: 'crossFactionResurrect' as never,
type: 'bytes',
default: true,
tweaks: [
[0x006e5fb8, [0x006e5fb9]],
[0x006e62a8, [0x006e62a9]]
]
},
{
key: 'skillUiGateHijack' as never,
type: 'bytes',
default: true,
forced: true,
tweaks: [
[
0x002ddf90,
[
0x55, 0x8b, 0xec, 0x83, 0xec, 0x08, 0x53, 0x56,
0x57, 0x8b, 0x3d, 0x60, 0xab, 0xce, 0x00, 0x83,
0xff, 0xff, 0x89, 0x55, 0xfc, 0x89, 0x4d, 0xf8,
0x74, 0x79, 0x8b, 0x75, 0x08, 0x8b, 0x15, 0x58,
0xab, 0xce, 0x00, 0x8b, 0xc7, 0x23, 0xc6, 0x8d,
0x04, 0x40, 0x8b, 0x4c, 0x82, 0x08, 0xf6, 0xc1,
0x01, 0x8d, 0x44, 0x82, 0x04, 0x75, 0x04, 0x85,
0xc9, 0x75, 0x05, 0x33, 0xc9, 0x8d, 0x49, 0x00,
0xf6, 0xc1, 0x01, 0x75, 0x4e, 0x85, 0xc9, 0x74,
0x4a, 0x39, 0x31, 0x74, 0x13, 0x8b, 0xc7, 0x23,
0xc6, 0x8d, 0x04, 0x40, 0x8d, 0x04, 0x82, 0x8b,
0x00, 0x03, 0xc1, 0x8b, 0x48, 0x04, 0xeb, 0xe0,
0x8b, 0x59, 0x1c, 0x8b, 0x71, 0x18, 0x33, 0xff,
0x85, 0xdb, 0x7e, 0x27, 0x8d, 0x64, 0x24, 0x00,
0x8b, 0x4e, 0x0c, 0x8b, 0x56, 0x08, 0x6a, 0x00,
0x6a, 0x00, 0x51, 0x8b, 0x4d, 0xf8, 0x52, 0x8b,
0x55, 0xfc, 0xe8, 0xb9, 0xfd, 0xff, 0xff, 0x84,
0xc0, 0x75, 0x13, 0x47, 0x83, 0xc6, 0x20, 0x3b,
0xfb, 0x7c, 0xdd, 0x5f, 0x5e, 0x33, 0xc0, 0x5b,
0x8b, 0xe5, 0x5d, 0xc2, 0x04, 0x00, 0x5f, 0x8b,
0xc6, 0x5e, 0x5b, 0x8b, 0xe5, 0x5d, 0xc2, 0x04,
0x00, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90, 0x90
]
]
]
}
] satisfies Tweak[];
// Apply patches
Tweaks.forEach(t => {
const val =
config[t.key] ?? t.default ?? ConfigWtfSchema.parse({})[t.key];
Logger.log(`Applying "${t.key}" patch with value: ${val}`);
if (t.type === 'float') {
buffer.writeFloatLE(t.value ?? (val as never), t.offset);
} else if (t.type === 'int8') {
buffer.writeInt8(t.value ?? (val as never), t.offset);
} else if (t.type === 'uint16') {
if (!t.forced && !val) return;
buffer.writeUInt16LE(t.value ?? (val as never), t.offset);
} else if (t.type === 'bytes') {
if (!t.forced && !val) return;
t.tweaks.forEach(([offset, bytes]) =>
Buffer.from(bytes).copy(buffer, offset)
);
}
});
await fs.writeFile(exePath, buffer);
Logger.log('WoW.exe successfully patched');
} catch (e) {
Logger.error('Failed to patch WoW.exe', e);
}
};
export const patchConfig = async () => {
const { clientDir, server, config } = Preferences.data;
if (!clientDir) return;
const configPath = path.join(clientDir, 'WTF', 'Config.wtf');
await fs.ensureDir(path.dirname(configPath));
const raw = (await fs.pathExists(configPath))
? await fs.readFile(configPath, { encoding: 'utf-8' })
: '';
if (raw) await fs.remove(configPath);
const configWtf = Object.fromEntries(
raw
.split('\n')
.map(l => {
const [_, k, v] = l.match(/SET (\w+) "(.+)"/) ?? [];
return !k || !v ? undefined : [k, v];
})
.filter(isNotUndef)
);
const primaryDisplay = screen.getPrimaryDisplay();
const { width, height } = primaryDisplay.bounds;
const parsed = {
scriptMemory: 512000,
gxResolution: `${width}x${height}`,
gxColorBits: primaryDisplay.colorDepth,
gxDepthBits: primaryDisplay.colorDepth,
gxRefresh: 60,
gxMultisample: 8,
gxMultisampleQuality: 0,
gxTripleBuffer: 1,
anisotropic: 16,
frillDensity: 48,
fullAlpha: 1,
SmallCull: 0.01,
DistCull: 888.8,
shadowLevel: 0,
trilinear: 1,
specular: 1,
pixelShaders: 1,
M2UsePixelShaders: 1,
particleDensity: 1,
unitDrawDist: 300,
weatherDensity: 3,
movieSubtitle: 1,
minimapZoom: 0,
minimapInsideZoom: 0,
SoundZoneMusicNoDelay: 1,
patchList: configWtf['patchList'] ?? Servers[server].patchList,
realmName: configWtf['realmName'] ?? Servers[server].realmName,
gxWindow: configWtf['gxWindow'] ?? 1,
gxMaximize: configWtf['gxMaximize'] ?? 1,
gxCursor: configWtf['gxCursor'] ?? 1,
checkAddonVersion: configWtf['checkAddonVersion'] ?? 0,
...configWtf,
CameraDistanceMax: config.cameraDistance,
farClip: config.farClip,
realmList: Servers[server].realmList,
hwDetect: 0,
M2UseShaders: 1
};
await fs.writeFile(
configPath,
Object.entries(parsed)
.filter(v => v[1] !== undefined && v[1] !== null)
.map(l => `SET ${l[0]} "${l[1]}"`)
.join('\n')
);
Logger.log('Config.wtf successfully patched');
};