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;