diff --git a/src/main/GameReader.ts b/src/main/GameReader.ts index c9e30145..918af7a2 100644 --- a/src/main/GameReader.ts +++ b/src/main/GameReader.ts @@ -4,6 +4,19 @@ import patcher from '../patcher'; import { GameState, AmongUsState, Player } from '../common/AmongUsState'; import { IOffsets } from './IOffsets'; +const extraStructronTypes = { + __proto__: null, + // HACK assuming the value in a 64bit uint is never too large + 'UINT64': { + read(buffer: Buffer, offset: number) { + return Number(buffer.readBigUInt64LE(offset)); + }, + write(value: number, buffer: Buffer, offset: number) { + buffer.writeBigUInt64LE(BigInt(value), offset); + }, + SIZE: 8 + } +}; interface ValueType { read(buffer: BufferSource, offset: number): T; @@ -87,7 +100,9 @@ export default class GameReader { break; } + // TODO pretty sure this does not read 64bit ptrs correctly const allPlayersPtr = this.readMemory('ptr', this.gameAssembly.modBaseAddr, this.offsets.allPlayersPtr) & 0xffffffff; + // TODO pretty sure this does not read 64bit ptrs correctly const allPlayers = this.readMemory('ptr', allPlayersPtr, this.offsets.allPlayers); const playerCount = this.readMemory('int' as const, allPlayersPtr, this.offsets.playerCount); let playerAddrPtr = allPlayers + this.offsets.playerAddrPtr; @@ -178,7 +193,8 @@ export default class GameReader { if (member.type === 'SKIP' && member.skip) { this.PlayerStruct = this.PlayerStruct.addMember(Struct.TYPES.SKIP(member.skip), member.name); } else { - this.PlayerStruct = this.PlayerStruct.addMember(Struct.TYPES[member.type] as ValueType, member.name); + const type = (extraStructronTypes[member.type as unknown as keyof typeof extraStructronTypes] ?? Struct.TYPES[member.type]) as ValueType; + this.PlayerStruct = this.PlayerStruct.addMember(type, member.name); } } @@ -199,7 +215,7 @@ export default class GameReader { if (!this.amongUs) throw 'Among Us not open? Weird error'; address = address & 0xffffffff; for (let i = 0; i < offsets.length - 1; i++) { - address = readMemoryRaw(this.amongUs.handle, address + offsets[i], 'uint32'); + address = readMemoryRaw(this.amongUs.handle, address + offsets[i], this.offsets.is64Bit ? 'uint64' : 'uint32'); if (address == 0) break; } @@ -208,8 +224,8 @@ export default class GameReader { } readString(address: number): string { if (address === 0 || !this.amongUs) return ''; - const length = readMemoryRaw(this.amongUs.handle, address + 0x8, 'int'); - const buffer = readBuffer(this.amongUs.handle, address + 0xC, length << 1); + const length = readMemoryRaw(this.amongUs.handle, address + (this.offsets.is64Bit ? 0x10 : 0x8), 'int'); + const buffer = readBuffer(this.amongUs.handle, address + (this.offsets.is64Bit ? 0x14 : 0xC), length << 1); return buffer.toString('utf8').replace(/\0/g, ''); } diff --git a/src/main/IOffsets.d.ts b/src/main/IOffsets.d.ts index 9d730e22..7ebd44ec 100644 --- a/src/main/IOffsets.d.ts +++ b/src/main/IOffsets.d.ts @@ -1,5 +1,6 @@ export interface IOffsets { + is64Bit?: boolean; meetingHud: number[]; meetingHudCachePtr: number[]; meetingHudState: number[]; diff --git a/src/main/baked-offsets.ts b/src/main/baked-offsets.ts new file mode 100644 index 00000000..b5695469 --- /dev/null +++ b/src/main/baked-offsets.ts @@ -0,0 +1,54 @@ +export const bundledOffsets: Record = { + // Windows Store UWP 2020.12.4.0 + '2020.12.4.0': ` +is64Bit: true +meetingHud: [0x21D03E0, 0xB8, 0] +meetingHudCachePtr: [0x8] +meetingHudState: [0xC0] +gameState: [0x21D0EA0, 0xB8, 0, 0xAC] +allPlayersPtr: [0x1BE0BB8, 0xB8, 0, 0x30] +allPlayers: [0x08] +playerCount: [0x0c] +playerAddrPtr: 0x10 +exiledPlayerId: [0xff, 0x21D03E0, 0xB8, 0, 0xE0, 0x10] +gameCode: [0x1D50138, 0xB8, 0, 0x40, 0x48] +player: + struct: + - type: SKIP + skip: 10 + name: unused + - type: UINT + name: id + - type: UINT64 + name: name + - type: UINT + name: color + - type: UINT + name: hat + - type: UINT + name: pet + - type: UINT + name: skin + - type: UINT + name: disconnected + - type: UINT64 + name: taskPtr + - type: BYTE + name: impostor + - type: BYTE + name: dead + - type: SKIP + skip: 2 + name: unused + - type: UINT64 + name: objectPtr + isLocal: [0x78] + localX: [0x90, 0x6C] + localY: [0x90, 0x70] + remoteX: [0x90, 0x58] + remoteY: [0x90, 0x5C] + bufferLength: 64 + offsets: [0, 0] + inVent: [0x3D] +` +}; \ No newline at end of file diff --git a/src/main/hook-ti.ts b/src/main/hook-ti.ts index fbd10bc7..f8412ed1 100644 --- a/src/main/hook-ti.ts +++ b/src/main/hook-ti.ts @@ -5,6 +5,7 @@ import * as t from 'ts-interface-checker'; // tslint:disable:object-literal-key-quotes export const IOffsets = t.iface([], { + 'is64Bit': t.opt('boolean'), 'meetingHud': t.array('number'), 'meetingHudCachePtr': t.array('number'), 'meetingHudState': t.array('number'), diff --git a/src/main/hook.ts b/src/main/hook.ts index 7c40ba13..4ad4ab4c 100644 --- a/src/main/hook.ts +++ b/src/main/hook.ts @@ -14,6 +14,7 @@ import { createCheckers } from 'ts-interface-checker'; import TI from './hook-ti'; import { existsSync, readFileSync } from 'fs'; import { IOffsets } from './IOffsets'; +import { bundledOffsets } from './baked-offsets'; const { IOffsets } = createCheckers(TI); interface IOHookEvent { @@ -28,6 +29,7 @@ interface IOHookEvent { } const store = new Store(); +let edition: 'steam' | 'windowsstore' = 'steam'; async function loadOffsets(event: Electron.IpcMainEvent): Promise { @@ -43,14 +45,37 @@ async function loadOffsets(event: Electron.IpcMainEvent): Promise { + if(edition === 'windowsstore') { + dialog.showErrorBox('Error', 'CrewLink thinks you are using the Windows Store / XBox edition of Among Us. You must launch Among Us manually.'); + } // Get steam path from registry const steamPath = enumerateValues(HKEY.HKEY_LOCAL_MACHINE, 'SOFTWARE\\WOW6432Node\\Valve\\Steam') diff --git a/src/main/index.ts b/src/main/index.ts index b271c0f6..6313cc44 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -18,16 +18,16 @@ function createMainWindow() { const mainWindowState = windowStateKeeper({}); const window = new BrowserWindow({ - width: 250, - height: 350, - maxWidth: 250, - minWidth: 250, - maxHeight: 350, - minHeight: 350, + width: 1000, + height: 1000, + // maxWidth: 1000, + // minWidth: 1000, + // maxHeight: 1000, + // minHeight: 1000, x: mainWindowState.x, y: mainWindowState.y, - resizable: false, + // resizable: false, frame: false, fullscreenable: false, maximizable: false,