Commit 59dcd5c1 authored by nanahira's avatar nanahira

support new replay recover

parent 6c459522
......@@ -33,9 +33,12 @@ var __importStar = (this && this.__importStar) || (function () {
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.Replay = exports.ReplayHeader = void 0;
exports.Replay = exports.ReplayHeader = exports.REPLAY_ID_YRP2 = exports.REPLAY_ID_YRP1 = exports.SEED_COUNT = void 0;
const fs = __importStar(require("fs"));
const lzma = __importStar(require("lzma"));
exports.SEED_COUNT = 8;
exports.REPLAY_ID_YRP1 = 0x31707279;
exports.REPLAY_ID_YRP2 = 0x32707279;
/**
* Metadata stored at the beginning of every replay file.
*/
......@@ -52,12 +55,14 @@ class ReplayHeader {
dataSizeRaw = [];
hash = 0;
props = [];
seedSequence = [];
headerVersion = 0;
value1 = 0;
value2 = 0;
value3 = 0;
/** Decompressed size as little‑endian 32‑bit */
get dataSize() {
return (this.dataSizeRaw[0] +
this.dataSizeRaw[1] * 0x100 +
this.dataSizeRaw[2] * 0x10000 +
this.dataSizeRaw[3] * 0x1000000);
return Buffer.from(this.dataSizeRaw).readUInt32LE(0);
}
get isTag() {
return (this.flag & ReplayHeader.REPLAY_TAG_FLAG) !== 0;
......@@ -112,6 +117,12 @@ class ReplayReader {
readInt32() {
return this.advance(4, () => this.buffer.readInt32LE(this.pointer));
}
readUInt16() {
return this.advance(2, () => this.buffer.readUInt16LE(this.pointer));
}
readUInt32() {
return this.advance(4, () => this.buffer.readUInt32LE(this.pointer));
}
readAll() {
return this.buffer.slice(this.pointer);
}
......@@ -162,6 +173,12 @@ class ReplayWriter {
writeInt32(val) {
this.advance(() => this.buffer.writeInt32LE(val, this.pointer), 4);
}
writeUInt16(val) {
this.advance(() => this.buffer.writeUInt16LE(val, this.pointer), 2);
}
writeUInt32(val) {
this.advance(() => this.buffer.writeUInt32LE(val, this.pointer), 4);
}
writeAll(buf) {
this.buffer = Buffer.concat([this.buffer, buf]);
}
......@@ -207,8 +224,8 @@ class Replay {
return this.header?.isTag ?? false;
}
/* ------------------ Static helpers ------------------ */
static fromFile(path) {
return Replay.fromBuffer(fs.readFileSync(path));
static async fromFile(path) {
return Replay.fromBuffer(await fs.promises.readFile(path));
}
static fromBuffer(buffer) {
const headerReader = new ReplayReader(buffer);
......@@ -227,13 +244,22 @@ class Replay {
}
static readHeader(reader) {
const h = new ReplayHeader();
h.id = reader.readInt32();
h.version = reader.readInt32();
h.flag = reader.readInt32();
h.seed = reader.readInt32();
h.id = reader.readUInt32();
h.version = reader.readUInt32();
h.flag = reader.readUInt32();
h.seed = reader.readUInt32();
h.dataSizeRaw = reader.readByteArray(4);
h.hash = reader.readInt32();
h.hash = reader.readUInt32();
h.props = reader.readByteArray(8);
if (h.id === exports.REPLAY_ID_YRP2) {
for (let i = 0; i < exports.SEED_COUNT; i++) {
h.seedSequence.push(reader.readUInt32());
}
h.headerVersion = reader.readUInt32();
h.value1 = reader.readUInt32();
h.value2 = reader.readUInt32();
h.value3 = reader.readUInt32();
}
return h;
}
static readReplay(header, reader) {
......@@ -313,17 +339,26 @@ class Replay {
}
return Buffer.concat([headerWriter.buffer, body]);
}
writeToFile(path) {
fs.writeFileSync(path, this.toBuffer());
async writeToFile(path) {
await fs.promises.writeFile(path, this.toBuffer());
}
writeHeader(w) {
w.writeInt32(this.header.id);
w.writeInt32(this.header.version);
w.writeInt32(this.header.flag);
w.writeInt32(this.header.seed);
w.writeUInt32(this.header.id);
w.writeUInt32(this.header.version);
w.writeUInt32(this.header.flag);
w.writeUInt32(this.header.seed);
w.writeByteArray(this.header.dataSizeRaw);
w.writeInt32(this.header.hash);
w.writeUInt32(this.header.hash);
w.writeByteArray(this.header.props);
if (this.header.id === exports.REPLAY_ID_YRP2) {
for (let i = 0; i < exports.SEED_COUNT; i++) {
w.writeUInt32(this.header.seedSequence[i]);
}
w.writeUInt32(this.header.headerVersion);
w.writeUInt32(this.header.value1);
w.writeUInt32(this.header.value2);
w.writeUInt32(this.header.value3);
}
}
writeContent(w) {
w.writeString(this.hostName, 40);
......
......@@ -7,6 +7,10 @@ export interface DeckObject {
ex: number[];
}
export const SEED_COUNT = 8;
export const REPLAY_ID_YRP1 = 0x31707279;
export const REPLAY_ID_YRP2 = 0x32707279;
/**
* Metadata stored at the beginning of every replay file.
*/
......@@ -24,15 +28,16 @@ export class ReplayHeader {
dataSizeRaw: number[] = [];
hash = 0;
props: number[] = [];
seedSequence: number[] = [];
headerVersion = 0;
value1 = 0;
value2 = 0;
value3 = 0;
/** Decompressed size as little‑endian 32‑bit */
get dataSize(): number {
return (
this.dataSizeRaw[0] +
this.dataSizeRaw[1] * 0x100 +
this.dataSizeRaw[2] * 0x10000 +
this.dataSizeRaw[3] * 0x1000000
);
return Buffer.from(this.dataSizeRaw).readUInt32LE(0);
}
get isTag(): boolean {
......@@ -94,6 +99,14 @@ class ReplayReader {
return this.advance(4, () => this.buffer.readInt32LE(this.pointer));
}
readUInt16(): number {
return this.advance(2, () => this.buffer.readUInt16LE(this.pointer));
}
readUInt32(): number {
return this.advance(4, () => this.buffer.readUInt32LE(this.pointer));
}
readAll(): Buffer {
return this.buffer.slice(this.pointer);
}
......@@ -149,6 +162,14 @@ class ReplayWriter {
this.advance(() => this.buffer.writeInt32LE(val, this.pointer), 4);
}
writeUInt16(val: number): void {
this.advance(() => this.buffer.writeUInt16LE(val, this.pointer), 2);
}
writeUInt32(val: number): void {
this.advance(() => this.buffer.writeUInt32LE(val, this.pointer), 4);
}
writeAll(buf: Buffer): void {
this.buffer = Buffer.concat([this.buffer, buf]);
}
......@@ -202,8 +223,8 @@ export class Replay {
/* ------------------ Static helpers ------------------ */
static fromFile(path: string): Replay {
return Replay.fromBuffer(fs.readFileSync(path));
static async fromFile(path: string): Promise<Replay> {
return Replay.fromBuffer(await fs.promises.readFile(path));
}
static fromBuffer(buffer: Buffer): Replay {
......@@ -227,13 +248,22 @@ export class Replay {
private static readHeader(reader: ReplayReader): ReplayHeader {
const h = new ReplayHeader();
h.id = reader.readInt32();
h.version = reader.readInt32();
h.flag = reader.readInt32();
h.seed = reader.readInt32();
h.id = reader.readUInt32();
h.version = reader.readUInt32();
h.flag = reader.readUInt32();
h.seed = reader.readUInt32();
h.dataSizeRaw = reader.readByteArray(4);
h.hash = reader.readInt32();
h.hash = reader.readUInt32();
h.props = reader.readByteArray(8);
if (h.id === REPLAY_ID_YRP2) {
for(let i = 0; i < SEED_COUNT; i++) {
h.seedSequence.push(reader.readUInt32());
}
h.headerVersion = reader.readUInt32();
h.value1 = reader.readUInt32();
h.value2 = reader.readUInt32();
h.value3 = reader.readUInt32();
}
return h;
}
......@@ -331,18 +361,27 @@ export class Replay {
return Buffer.concat([headerWriter.buffer, body]);
}
writeToFile(path: string): void {
fs.writeFileSync(path, this.toBuffer());
async writeToFile(path: string): Promise<void> {
await fs.promises.writeFile(path, this.toBuffer());
}
private writeHeader(w: ReplayWriter): void {
w.writeInt32(this.header!.id);
w.writeInt32(this.header!.version);
w.writeInt32(this.header!.flag);
w.writeInt32(this.header!.seed);
w.writeUInt32(this.header!.id);
w.writeUInt32(this.header!.version);
w.writeUInt32(this.header!.flag);
w.writeUInt32(this.header!.seed);
w.writeByteArray(this.header!.dataSizeRaw);
w.writeInt32(this.header!.hash);
w.writeUInt32(this.header!.hash);
w.writeByteArray(this.header!.props);
if (this.header!.id === REPLAY_ID_YRP2) {
for (let i = 0; i < SEED_COUNT; i++) {
w.writeUInt32(this.header!.seedSequence[i]);
}
w.writeUInt32(this.header!.headerVersion);
w.writeUInt32(this.header!.value1);
w.writeUInt32(this.header!.value2);
w.writeUInt32(this.header!.value3);
}
}
private writeContent(w: ReplayWriter): void {
......
......@@ -1362,11 +1362,15 @@ class Room
@hostinfo.start_lp, @hostinfo.start_hand, @hostinfo.draw_count, @hostinfo.time_limit, @hostinfo.replay_mode]
if firstSeed
# first seed is number[8], so we have to make it base64
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4)
for i in [0...firstSeed.length]
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4)
param.push(firstSeedBuf.toString('base64'))
if Array.isArray(firstSeed)
# new replay with extended header and long seed
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4)
for i in [0...firstSeed.length]
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4)
param.push(firstSeedBuf.toString('base64'))
else
# old replay with short seed
param.push(firstSeed.toString())
try
@process = spawn './ygopro', param, {cwd: 'ygopro'}
......@@ -1511,7 +1515,7 @@ class Room
return false
try
@recover_replay = await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + @recover_duel_log.replayFileName)
@spawn(@recover_replay.header.seed) # TODO: refa header.seed
@spawn(if @recover_replay.header.seed_sequence.length then @recover_replay.header.seed_sequence else @recover_replay.header.seed)
return true
catch e
log.warn("LOAD RECOVER REPLAY FAIL", e.toString())
......
......@@ -564,7 +564,7 @@
long_resolve_cards = global.long_resolve_cards = (await loadJSONAsync('./data/long_resolve_cards.json'));
}
if (settings.modules.tournament_mode.enable_recover) {
ReplayParser = global.ReplayParser = require("./Replay.js");
ReplayParser = global.ReplayParser = (require("./Replay.js")).Replay;
}
if (settings.modules.athletic_check.enabled) {
AthleticChecker = require("./athletic-check.js").AthleticChecker;
......@@ -1771,12 +1771,17 @@
var e, firstSeedBuf, i, j, param, ref;
param = [0, this.hostinfo.lflist, this.hostinfo.rule, this.hostinfo.mode, this.hostinfo.duel_rule, (this.hostinfo.no_check_deck ? 'T' : 'F'), (this.hostinfo.no_shuffle_deck ? 'T' : 'F'), this.hostinfo.start_lp, this.hostinfo.start_hand, this.hostinfo.draw_count, this.hostinfo.time_limit, this.hostinfo.replay_mode];
if (firstSeed) {
// first seed is number[8], so we have to make it base64
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4);
for (i = j = 0, ref = firstSeed.length; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4);
if (Array.isArray(firstSeed)) {
// new replay with extended header and long seed
firstSeedBuf = Buffer.allocUnsafe(firstSeed.length * 4);
for (i = j = 0, ref = firstSeed.length; (0 <= ref ? j < ref : j > ref); i = 0 <= ref ? ++j : --j) {
firstSeedBuf.writeUInt32LE(firstSeed[i], i * 4);
}
param.push(firstSeedBuf.toString('base64'));
} else {
// old replay with short seed
param.push(firstSeed.toString());
}
param.push(firstSeedBuf.toString('base64'));
}
try {
this.process = spawn('./ygopro', param, {
......@@ -1992,7 +1997,7 @@
}
try {
this.recover_replay = (await ReplayParser.fromFile(settings.modules.tournament_mode.replay_path + this.recover_duel_log.replayFileName));
this.spawn(this.recover_replay.header.seed); // TODO: refa header.seed
this.spawn(this.recover_replay.header.seed_sequence.length ? this.recover_replay.header.seed_sequence : this.recover_replay.header.seed);
return true;
} catch (error1) {
e = error1;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment