You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
377 lines
11 KiB
377 lines
11 KiB
class gbxReplay {
|
|
|
|
CHUNKS_OFFSET = 17;
|
|
HEADER_OFFSET = 21;
|
|
UNASSIGNED = 0xFFFFFFFF;
|
|
|
|
mapUID = "";
|
|
environment = "";
|
|
author = "";
|
|
bestTime = this.UNASSIGNED;
|
|
gamerHandle = ""
|
|
login = ""
|
|
titleId = ""
|
|
|
|
calcChunkOffset(num) {
|
|
return (((num) * 8) + this.HEADER_OFFSET)
|
|
}
|
|
|
|
isNumber(id) { return (((id) & 0xC0000000) == 0)}
|
|
isString(id) { return (((id) & 0xC0000000) != 0)}
|
|
isUnassigned(id) {return ((id) == this.UNASSIGNED)}
|
|
getIndex(id) {return ((id) & 0x3FFFFFFF)}
|
|
|
|
parseReplayChunk(buff, chunkInfo) {
|
|
buff.seek(0+chunkInfo.dwOffset);
|
|
let version = buff.readUInt32();
|
|
let isVSK = version >= 9999 ? true : false;
|
|
|
|
let idList = { version: 0, index: 0, list:[]}
|
|
|
|
if( chunkInfo.dwSize <= 4) {
|
|
return;
|
|
}
|
|
|
|
if((!isVSK && version >= 3) || (isVSK && version >= 10000))
|
|
{
|
|
let result = this.readIdentifier(buff, idList);
|
|
if(result.read > 0)
|
|
{
|
|
this.mapUID = result.str;
|
|
console.log("MapUID:\t " + this.mapUID)
|
|
}
|
|
|
|
result = this.readIdentifier(buff, idList);
|
|
if(result.read > 0)
|
|
{
|
|
this.environment = result.str;
|
|
console.log("Environ:\t " + this.environment)
|
|
}
|
|
|
|
result = this.readIdentifier(buff, idList)
|
|
if (result.read > 0)
|
|
{
|
|
this.author = result.str;
|
|
console.log("Author:\t " + this.author)
|
|
}
|
|
|
|
this.bestTime = buff.readUInt32();
|
|
console.log("BestTime:\t " + this.bestTime);
|
|
|
|
result = buff.readNadeoString();
|
|
if (result.read > 0)
|
|
{
|
|
this.gamerHandle = result.str;
|
|
console.log("Gamer Handle:\t " + this.gamerHandle)
|
|
}
|
|
|
|
if((!isVSK && version >= 6) || (isVSK && version >= 10000))
|
|
{
|
|
result = buff.readNadeoString();
|
|
if (result.read > 0)
|
|
{
|
|
this.login = result.str;
|
|
console.log("Login:\t " + this.login)
|
|
}
|
|
|
|
if((!isVSK && version >= 8) || (isVSK && version >= 10000))
|
|
{
|
|
buff.seek(buff.readPos+1);
|
|
result = this.readIdentifier(buff, idList)
|
|
if (result.read > 0)
|
|
{
|
|
this.titleId = result.str;
|
|
console.log("titleId:\t " + this.titleId)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
parseAuthorChunk(buff, chunkInfo) {
|
|
|
|
}
|
|
|
|
parseCommunityChunk(buff, chunkInfo) {
|
|
|
|
}
|
|
|
|
parse(buff)
|
|
{
|
|
buff.seek(0+this.CHUNKS_OFFSET);
|
|
let numChunks = buff.readUInt32();
|
|
|
|
if(numChunks == 0) {
|
|
return true;
|
|
}
|
|
else if (numChunks > 0xff) {
|
|
return false;
|
|
}
|
|
|
|
let chunkId, chunkSize = 0;
|
|
let chunkOffset = this.calcChunkOffset(numChunks)
|
|
let chunkVersion = {}
|
|
let chunkCommunity = {}
|
|
let chunkAuthor = {}
|
|
|
|
for (let counter = 1; counter <= numChunks; counter++) {
|
|
chunkId = buff.readUInt32();
|
|
chunkSize = buff.readUInt32();
|
|
|
|
chunkSize &= 0x7FFFFFFF;
|
|
|
|
switch (chunkId){
|
|
case 0x03093000: // (TM)
|
|
case 0x2403F000: // (VSK, TM)
|
|
chunkVersion.dwId = chunkId;
|
|
chunkVersion.dwSize = chunkSize;
|
|
chunkVersion.dwOffset = chunkOffset;
|
|
chunkOffset += chunkSize;
|
|
break;
|
|
case 0x03093001: // (TM)
|
|
case 0x2403F001: // (VSK, TM)
|
|
chunkCommunity.dwId = chunkId;
|
|
chunkCommunity.dwSize = chunkSize;
|
|
chunkCommunity.dwOffset = chunkOffset;
|
|
chunkOffset += chunkSize;
|
|
//OutputTextFmt(hwndCtl, szOutput, _countof(szOutput), g_szChunk, dwCouter, dwChunkId, dwChunkSize);
|
|
break;
|
|
|
|
case 0x03093002: // (MP)
|
|
chunkAuthor.dwId = chunkId;
|
|
chunkAuthor.dwSize = chunkSize;
|
|
chunkAuthor.dwOffset = chunkOffset;
|
|
chunkOffset += chunkSize;
|
|
//OutputTextFmt(hwndCtl, szOutput, _countof(szOutput), g_szChunk, dwCouter, dwChunkId, dwChunkSize);
|
|
break;
|
|
|
|
default:
|
|
chunkOffset += chunkSize;
|
|
//OutputTextFmt(hwndCtl, szOutput, _countof(szOutput), g_szChunk, dwCouter, dwChunkId, dwChunkSize);
|
|
}
|
|
}
|
|
|
|
if (chunkVersion.dwSize > 0) {
|
|
this.parseReplayChunk(buff, chunkVersion);
|
|
}
|
|
|
|
if (chunkCommunity.dwSize > 0) {
|
|
this.parseCommunityChunk(buff, chunkCommunity);
|
|
}
|
|
|
|
if (chunkAuthor.dwSize > 0) {
|
|
this.parseAuthorChunk(buff, chunkAuthor);
|
|
}
|
|
}
|
|
|
|
readIdentifier(buff, idList)
|
|
{
|
|
if ( idList.version < 3 )
|
|
{
|
|
idList.version = buff.readUInt32();
|
|
|
|
if (idList.version < 2)
|
|
{
|
|
return {read:-1, str:""};
|
|
}
|
|
}
|
|
|
|
let id = buff.readUInt32();
|
|
if(this.isUnassigned(id))
|
|
{
|
|
return {read:0, str:""};
|
|
}
|
|
|
|
if(this.isNumber(id))
|
|
{
|
|
return this.getCollectionString(id);
|
|
}
|
|
|
|
if(idList.version == 2)
|
|
{
|
|
return buff.readNadeoString();
|
|
}
|
|
|
|
if(this.isString(id) && this.getIndex(id) == 0)
|
|
{
|
|
let result = buff.readNadeoString();
|
|
|
|
if (result.read > 0 && idList.index < 8)
|
|
{
|
|
idList.list[idList.index] = result.str;
|
|
idList.index++;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
let index = this.getIndex(id);
|
|
if( index == 0 || index > 8)
|
|
{
|
|
return {read:0, str:""};
|
|
}
|
|
|
|
let latest = idList.list[idList.index-1];
|
|
return {read:latest.length, str:latest};
|
|
}
|
|
|
|
getCollectionString(id)
|
|
{
|
|
let outString = ""
|
|
switch (id)
|
|
{
|
|
case 0: // Speed
|
|
outString = "Desert";
|
|
break;
|
|
case 1: // Alpine
|
|
outString = "Snow";
|
|
break;
|
|
case 2: // Rally
|
|
outString = "Rally";
|
|
break;
|
|
case 3: // Island
|
|
outString = "Island";
|
|
break;
|
|
case 4: // Bay
|
|
outString = "Bay";
|
|
break;
|
|
case 5: // Coast
|
|
outString = "Coast";
|
|
break;
|
|
case 6: // StadiumMP4
|
|
outString = "Stadium";
|
|
break;
|
|
case 7: // Basic
|
|
outString = "Basic";
|
|
break;
|
|
case 8: // Plain
|
|
outString = "Plain";
|
|
break;
|
|
case 9: // Moon
|
|
outString = "Moon";
|
|
break;
|
|
case 10: // Toy
|
|
outString = "Toy";
|
|
break;
|
|
case 11: // Valley
|
|
outString = "Valley";
|
|
break;
|
|
case 12: // Canyon
|
|
outString = "Canyon";
|
|
break;
|
|
case 13: // Lagoon
|
|
outString = "Lagoon";
|
|
break;
|
|
case 14: // Deprecated_Arena
|
|
outString = "Arena";
|
|
break;
|
|
case 15: // TMTest8
|
|
outString = "TMTest8";
|
|
break;
|
|
case 16: // TMTest9
|
|
outString = "TMTest9";
|
|
break;
|
|
case 17: // TMCommon
|
|
outString = "TMCommon";
|
|
break;
|
|
case 18: // Canyon4
|
|
outString = "Canyon4";
|
|
break;
|
|
case 19: // Canyon256
|
|
outString = "Canyon256";
|
|
break;
|
|
case 20: // Valley4
|
|
outString = "Valley4";
|
|
break;
|
|
case 21: // Valley256
|
|
outString = "Valley256";
|
|
break;
|
|
case 22: // Lagoon4
|
|
outString = "Lagoon4";
|
|
break;
|
|
case 23: // Lagoon256
|
|
outString = "Lagoon256";
|
|
break;
|
|
case 24: // Stadium4
|
|
outString = "Stadium4";
|
|
break;
|
|
case 25: // Stadium256
|
|
outString = "Stadium256";
|
|
break;
|
|
case 26: // Stadium
|
|
outString = "Stadium";
|
|
break;
|
|
case 27: // Voxel
|
|
outString = "Voxel";
|
|
break;
|
|
case 100: // History
|
|
outString = "History";
|
|
break;
|
|
case 101: // Society
|
|
outString = "Society";
|
|
break;
|
|
case 102: // Galaxy
|
|
outString = "Galaxy";
|
|
break;
|
|
case 103: // QMTest1
|
|
outString = "QMTest1";
|
|
break;
|
|
case 104: // QMTest2
|
|
outString = "QMTest2";
|
|
break;
|
|
case 105: // QMTest3
|
|
outString = "QMTest3";
|
|
break;
|
|
case 200: // Gothic
|
|
outString = "Gothic";
|
|
break;
|
|
case 201: // Paris
|
|
outString = "Paris";
|
|
break;
|
|
case 202: // Storm
|
|
outString = "Storm";
|
|
break;
|
|
case 203: // Cryo
|
|
outString = "Cryo";
|
|
break;
|
|
case 204: // Meteor
|
|
outString = "Meteor";
|
|
break;
|
|
case 205: // Meteor4
|
|
outString = "Meteor4";
|
|
break;
|
|
case 206: // Meteor256
|
|
outString = "Meteor256";
|
|
break;
|
|
case 207: // SMTest3
|
|
outString = "SMTest3";
|
|
break;
|
|
case 299: // SMCommon
|
|
outString = "SMCommon";
|
|
break;
|
|
case 10000: // Vehicles
|
|
outString = "Vehicles";
|
|
break;
|
|
case 10001: // Orbital
|
|
outString = "Orbital";
|
|
break;
|
|
case 10002: // Actors
|
|
outString = "Actors";
|
|
break;
|
|
case 10003: // Common
|
|
outString = "Common";
|
|
break;
|
|
case UNASSIGNED:
|
|
outString = "_Unassigned";
|
|
break;
|
|
default:
|
|
{
|
|
return {read:0, str:""};
|
|
}
|
|
}
|
|
|
|
return {read:outString.length, str:outString}
|
|
}
|
|
}
|
|
|
|
module.exports = gbxReplay;
|