Commit c3c66c53 authored by nanahira's avatar nanahira

add recover from replay

parent e592353c
lzma = require 'lzma'
fs = require 'fs'
# Deck = require './Deck.js'
class replayHeader
@replayCompressedFlag = 0x1
@replayTagFlag = 0x2
@replayDecodedFlag = 0x4
constructor: ->
@id = 0
@version = 0
@flag = 0
@seed = 0
@dataSizeRaw = []
@hash = 0
@props = []
getDataSize: ->
@dataSizeRaw[0] + @dataSizeRaw[1] * 0x100 + @dataSizeRaw[2] * 0x10000 + @dataSizeRaw[3] * 0x1000000
getIsTag: ->
@flag & replayHeader.replayTagFlag > 0
getIsCompressed: ->
@flag & replayHeader.replayCompressedFlag > 0
getLzmaHeader: ->
bytes = [].concat(@props[0..4], @dataSizeRaw, [0, 0, 0, 0])
Buffer.from(bytes)
Object.defineProperty replayHeader.prototype, 'dataSize', get: @getDataSize
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
Object.defineProperty replayHeader.prototype, 'isCompressed', get: @getIsCompressed
class ReplayReader
constructor: (buffer) ->
@pointer = 0
@buffer = buffer
readByte: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readByteArray: (length) ->
answer = []
answer.push @readByte() for i in [1..length]
answer
readInt8: ->
answer = @buffer.readInt8(@pointer)
@pointer += 1
answer
readUInt8: ->
answer = @buffer.readUInt8(@pointer)
@pointer += 1
answer
readInt16: ->
answer = @buffer.readInt16LE @pointer
@pointer += 2
answer
readInt32: ->
answer = @buffer.readInt32LE @pointer
@pointer += 4
answer
readAll: ->
answer = @buffer.slice(@pointer)
# @pointer = 0
answer
readString: (length) ->
if @pointer + length > @buffer.length
return null
full = @buffer.slice(@pointer, @pointer + length).toString('utf-16le')
answer = full.split("\u0000")[0]
@pointer += length
answer
readRaw: (length) ->
if @pointer + length > @buffer.length
return null
answer = @buffer.slice(@pointer, @pointer + length)
@pointer += length
answer
class Replay
constructor: ->
@header = null
@hostName = ""
@clientName = ""
@startLp = 0
@startHand = 0
@drawCount = 0
@opt = 0
@hostDeck = null
@clientDeck = null
@tagHostName = null
@tagClientName = null
@tagHostDeck = null
@tagClientDeck = null
@responses = null
getDecks: ->
if @isTag
[@hostDeck, @clientDeck, @tagHostDeck, @tagClientDeck]
else
[@hostDeck, @clientDeck]
getIsTag: ->
@header == null ? false : @header.isTag
@fromFile: (filePath) ->
Replay.fromBuffer fs.readFileSync filePath
@fromBuffer: (buffer) ->
reader = new ReplayReader buffer
header = Replay.readHeader reader
lzmaBuffer = Buffer.concat [header.getLzmaHeader(), reader.readAll()]
if header.isCompressed
decompressed = lzmaBuffer
else
decompressed = Buffer.from lzma.decompress lzmaBuffer
reader = new ReplayReader decompressed
replay = Replay.readReplay header, reader
replay
@readHeader: (reader) ->
header = new replayHeader()
header.id = reader.readInt32()
header.version = reader.readInt32()
header.flag = reader.readInt32()
header.seed = reader.readInt32()
header.dataSizeRaw = reader.readByteArray 4
header.hash = reader.readInt32()
header.props = reader.readByteArray 8
header
@readReplay: (header, reader) ->
replay = new Replay()
replay.header = header
replay.hostName = reader.readString(40)
replay.tagHostName = reader.readString(40) if header.isTag
replay.tagClientName = reader.readString(40) if header.isTag
replay.clientName = reader.readString(40)
replay.startLp = reader.readInt32()
replay.startHand = reader.readInt32()
replay.drawCount = reader.readInt32()
replay.opt = reader.readInt32()
replay.hostDeck = Replay.readDeck reader
replay.tagHostDeck = Replay.readDeck reader if header.isTag
replay.tagClientDeck = Replay.readDeck reader if header.isTag
replay.clientDeck = Replay.readDeck reader
replay.responses = Replay.readResponses reader
replay
@readDeck: (reader) ->
deck = {}
deck.main = Replay.readDeckPack reader
deck.ex = Replay.readDeckPack reader
deck
@readDeckPack: (reader) ->
length = reader.readInt32()
answer = []
answer.push reader.readInt32() for i in [1..length]
answer
@readResponses: (reader) ->
answer = []
while true
try
length = reader.readUInt8()
if length > 64
length = 64
single = reader.readRaw(length)
if !single
break
answer.push(single)
catch
break
answer
Object.defineProperty replayHeader.prototype, 'decks', get: @getDecks
Object.defineProperty replayHeader.prototype, 'isTag', get: @getIsTag
module.exports = Replay
// Generated by CoffeeScript 2.5.1
(function() {
var Replay, ReplayReader, fs, lzma, replayHeader;
lzma = require('lzma');
fs = require('fs');
replayHeader = (function() {
// Deck = require './Deck.js'
class replayHeader {
constructor() {
this.id = 0;
this.version = 0;
this.flag = 0;
this.seed = 0;
this.dataSizeRaw = [];
this.hash = 0;
this.props = [];
}
getDataSize() {
return this.dataSizeRaw[0] + this.dataSizeRaw[1] * 0x100 + this.dataSizeRaw[2] * 0x10000 + this.dataSizeRaw[3] * 0x1000000;
}
getIsTag() {
return this.flag & replayHeader.replayTagFlag > 0;
}
getIsCompressed() {
return this.flag & replayHeader.replayCompressedFlag > 0;
}
getLzmaHeader() {
var bytes;
bytes = [].concat(this.props.slice(0, 5), this.dataSizeRaw, [0, 0, 0, 0]);
return Buffer.from(bytes);
}
};
replayHeader.replayCompressedFlag = 0x1;
replayHeader.replayTagFlag = 0x2;
replayHeader.replayDecodedFlag = 0x4;
Object.defineProperty(replayHeader.prototype, 'dataSize', {
get: replayHeader.getDataSize
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: replayHeader.getIsTag
});
Object.defineProperty(replayHeader.prototype, 'isCompressed', {
get: replayHeader.getIsCompressed
});
return replayHeader;
}).call(this);
ReplayReader = class ReplayReader {
constructor(buffer) {
this.pointer = 0;
this.buffer = buffer;
}
readByte() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
}
readByteArray(length) {
var answer, i, j, ref;
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(this.readByte());
}
return answer;
}
readInt8() {
var answer;
answer = this.buffer.readInt8(this.pointer);
this.pointer += 1;
return answer;
}
readUInt8() {
var answer;
answer = this.buffer.readUInt8(this.pointer);
this.pointer += 1;
return answer;
}
readInt16() {
var answer;
answer = this.buffer.readInt16LE(this.pointer);
this.pointer += 2;
return answer;
}
readInt32() {
var answer;
answer = this.buffer.readInt32LE(this.pointer);
this.pointer += 4;
return answer;
}
readAll() {
var answer;
answer = this.buffer.slice(this.pointer);
// @pointer = 0
return answer;
}
readString(length) {
var answer, full;
if (this.pointer + length > this.buffer.length) {
return null;
}
full = this.buffer.slice(this.pointer, this.pointer + length).toString('utf-16le');
answer = full.split("\u0000")[0];
this.pointer += length;
return answer;
}
readRaw(length) {
var answer;
if (this.pointer + length > this.buffer.length) {
return null;
}
answer = this.buffer.slice(this.pointer, this.pointer + length);
this.pointer += length;
return answer;
}
};
Replay = (function() {
class Replay {
constructor() {
this.header = null;
this.hostName = "";
this.clientName = "";
this.startLp = 0;
this.startHand = 0;
this.drawCount = 0;
this.opt = 0;
this.hostDeck = null;
this.clientDeck = null;
this.tagHostName = null;
this.tagClientName = null;
this.tagHostDeck = null;
this.tagClientDeck = null;
this.responses = null;
}
getDecks() {
if (this.isTag) {
return [this.hostDeck, this.clientDeck, this.tagHostDeck, this.tagClientDeck];
} else {
return [this.hostDeck, this.clientDeck];
}
}
getIsTag() {
var ref;
return (ref = this.header === null) != null ? ref : {
false: this.header.isTag
};
}
static fromFile(filePath) {
return Replay.fromBuffer(fs.readFileSync(filePath));
}
static fromBuffer(buffer) {
var decompressed, header, lzmaBuffer, reader, replay;
reader = new ReplayReader(buffer);
header = Replay.readHeader(reader);
lzmaBuffer = Buffer.concat([header.getLzmaHeader(), reader.readAll()]);
if (header.isCompressed) {
decompressed = lzmaBuffer;
} else {
decompressed = Buffer.from(lzma.decompress(lzmaBuffer));
}
reader = new ReplayReader(decompressed);
replay = Replay.readReplay(header, reader);
return replay;
}
static readHeader(reader) {
var header;
header = new replayHeader();
header.id = reader.readInt32();
header.version = reader.readInt32();
header.flag = reader.readInt32();
header.seed = reader.readInt32();
header.dataSizeRaw = reader.readByteArray(4);
header.hash = reader.readInt32();
header.props = reader.readByteArray(8);
return header;
}
static readReplay(header, reader) {
var replay;
replay = new Replay();
replay.header = header;
replay.hostName = reader.readString(40);
if (header.isTag) {
replay.tagHostName = reader.readString(40);
}
if (header.isTag) {
replay.tagClientName = reader.readString(40);
}
replay.clientName = reader.readString(40);
replay.startLp = reader.readInt32();
replay.startHand = reader.readInt32();
replay.drawCount = reader.readInt32();
replay.opt = reader.readInt32();
replay.hostDeck = Replay.readDeck(reader);
if (header.isTag) {
replay.tagHostDeck = Replay.readDeck(reader);
}
if (header.isTag) {
replay.tagClientDeck = Replay.readDeck(reader);
}
replay.clientDeck = Replay.readDeck(reader);
replay.responses = Replay.readResponses(reader);
return replay;
}
static readDeck(reader) {
var deck;
deck = {};
deck.main = Replay.readDeckPack(reader);
deck.ex = Replay.readDeckPack(reader);
return deck;
}
static readDeckPack(reader) {
var answer, i, j, length, ref;
length = reader.readInt32();
answer = [];
for (i = j = 1, ref = length; (1 <= ref ? j <= ref : j >= ref); i = 1 <= ref ? ++j : --j) {
answer.push(reader.readInt32());
}
return answer;
}
static readResponses(reader) {
var answer, length, single;
answer = [];
while (true) {
try {
length = reader.readUInt8();
if (length > 64) {
length = 64;
}
single = reader.readRaw(length);
if (!single) {
break;
}
answer.push(single);
} catch (error) {
break;
}
}
return answer;
}
};
Object.defineProperty(replayHeader.prototype, 'decks', {
get: Replay.getDecks
});
Object.defineProperty(replayHeader.prototype, 'isTag', {
get: Replay.getIsTag
});
return Replay;
}).call(this);
module.exports = Replay;
}).call(this);
......@@ -168,7 +168,8 @@
"replay_safe": true,
"replay_path": "./replays/",
"replay_archive_tool": "7z",
"block_replay_to_player": true,
"block_replay_to_player": false,
"enable_recover": true,
"show_ip": false,
"show_info": true,
"log_save_path": "./config/",
......
......@@ -30,6 +30,9 @@
"cloud_replay_error": "Replay opening failed.",
"cloud_replay_playing": "Accessing cloud replay",
"cloud_replay_hint": "These are the recent saved replay codes, please enter the replay code at the password column to access it.",
"recover_replay_hint": "These are the recent duels, please enter the code RC[ID]%[TURN]#[ROOMNAME] at the password column to recover the duel.",
"recover_hint": "You entered a recover room. Please be ready with your deck on that duel.",
"recover_start_hint": "Started recovering...",
"blank_room_name": "Blank room name is unallowed, please fill in something.",
"loading_user_info": "Loading user info...",
"invalid_password_length": "Password invalid (Invalid Length)",
......@@ -342,6 +345,11 @@
"cloud_replay_error": "播放录像出错",
"cloud_replay_playing": "正在观看云录像",
"cloud_replay_hint": "以下是您近期的云录像,密码处输入 R#录像编号 即可观看",
"recover_replay_hint": "以下是您近期进行的决斗,密码处输入 RC决斗编号%回合数#房间号 即可创建复盘房间",
"recover_hint": "你进入了一个复盘房间,请使用复盘局的卡组准备。",
"recover_start_hint": "开始复盘...",
"recover_success": "复盘成功。请耐心等待跳到当前回合。",
"recover_fail": "复盘失败。",
"blank_room_name": "房间名不能为空,请在主机密码处填写房间名",
"loading_user_info": "正在读取用户信息...",
"invalid_password_length": "主机密码不正确 (Invalid Length)",
......
This diff is collapsed.
This diff is collapsed.
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