Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 30 additions & 8 deletions src/ReplayReader.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/* eslint-disable class-methods-use-this */
import { CipherKey } from 'crypto';
import { promises as fs } from 'fs';
import BinaryReader from './BinaryReader';
import { queryAccounts } from './Http';
Expand All @@ -16,6 +17,7 @@ import {
AdditionGfp,
SafeZone,
PlayerPositions,
FLocalFileReplayCustomVersion,
} from './structs';

const halfMapSize = 131328;
Expand Down Expand Up @@ -53,9 +55,20 @@ class ReplayReader {
private parseMeta() {
const magic = this.reader.readUInt32();
const fileVersion = this.reader.readUInt32();

if(fileVersion > FLocalFileReplayCustomVersion.LatestVersion) {
process.emitWarning(`The replay file you provided is not fully supported and may cause errors. Please report any issues you encounter.`);
}

const lengthInMs = this.reader.readUInt32();
const networkVersion = this.reader.readUInt32();
const changelist = this.reader.readUInt32();

if(fileVersion >= FLocalFileReplayCustomVersion.CustomVersions) {
// With fileVersion 7 Epic has added some extra bytes, we need to skip them atm
this.reader.goto(44);
}

const name = this.reader.readString();
const isLive = this.reader.readBool();

Expand All @@ -73,7 +86,7 @@ class ReplayReader {
if (fileVersion >= 6) {
isEncrypted = this.reader.readBool();
const encryptionKeyLength = this.reader.readUInt32();
encryptionKey = Buffer.from(this.reader.readBytes(encryptionKeyLength));
encryptionKey = this.reader.readBytes(encryptionKeyLength);
}

if (!isLive && isEncrypted && encryptionKey.length === 0) throw new Error('Cannot read encrypted replay without encryption key');
Expand All @@ -85,7 +98,7 @@ class ReplayReader {
this.encryption.encryptionKey = encryptionKey;
}

this.meta = {
const meta = {
magic,
fileVersion,
lengthInMs,
Expand All @@ -96,9 +109,12 @@ class ReplayReader {
timestamp,
isCompressed,
};

this.meta = meta;
return meta;
}

private parseChunks() {
private parseChunks(meta: ReplayMeta) {
const chunksStartOffset = this.reader.offset;
while (this.reader.buffer.byteLength > this.reader.offset) {
const chunkType = this.reader.readUInt32();
Expand All @@ -108,7 +124,7 @@ class ReplayReader {
switch (chunkType) {
case 0:
if (!this.header) {
this.parseHeader();
this.parseHeader(meta);
this.reader.goto(chunksStartOffset);
}
break;
Expand All @@ -121,7 +137,7 @@ class ReplayReader {
}
}

private parseHeader() {
private parseHeader(meta: ReplayMeta) {
const magic = this.reader.readUInt32();
const networkVersion = this.reader.readUInt32();
const networkChecksum = this.reader.readUInt32();
Expand All @@ -134,6 +150,12 @@ class ReplayReader {
this.reader.skip(4);
const patch = this.reader.readUInt16();
const changelist = this.reader.readUInt32();

if(meta.fileVersion >= FLocalFileReplayCustomVersion.CustomVersions) {
// With fileVersion 7 Epic has added some extra bytes, we need to skip them atm
this.reader.skip(84);
}

const branch = this.reader.readString();

let fileVersionUE4;
Expand Down Expand Up @@ -188,7 +210,7 @@ class ReplayReader {
const size = this.reader.readUInt32();

const decryptedEventBuffer = this.encryption.encryptionKey
? this.reader.decryptBuffer(size, this.encryption.encryptionKey) : this.reader.readBytes(size);
? this.reader.decryptBuffer(size, this.encryption.encryptionKey as any as CipherKey) : this.reader.readBytes(size);

const eventReader = new BinaryReader(decryptedEventBuffer);

Expand Down Expand Up @@ -487,8 +509,8 @@ class ReplayReader {
const replayBuffer = Buffer.isBuffer(replay) ? replay : await fs.readFile(replay);

const reader = new ReplayReader(replayBuffer);
reader.parseMeta();
reader.parseChunks();
const meta = reader.parseMeta();
reader.parseChunks(meta);

if (config?.resolveAccountNames) await reader.resolveDisplayNames();

Expand Down
15 changes: 15 additions & 0 deletions src/structs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,18 @@ export interface PlayerPositions {
[time: number]: PlayerPosition;
},
}

export enum FLocalFileReplayCustomVersion {
BeforeCustomVersionWasAdded = 0,

FixedSizeFriendlyName,
CompressionSupport,
RecordingTimestamp,
StreamChunkTimes,
FriendlyNameCharEncoding,
EncryptionSupport,
CustomVersions,

VersionPlusOne,
LatestVersion = VersionPlusOne - 1,
}
Binary file added test/replays/client-33.11.replay
Binary file not shown.
Loading
Loading