mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 00:54:14 +02:00
encoder for createStringTable
This commit is contained in:
parent
79cd277fed
commit
89b9c3b25c
10 changed files with 276 additions and 77 deletions
|
|
@ -3,7 +3,7 @@ import {make} from '../Packet/ParserGenerator';
|
|||
import {ParseBSPDecal} from '../Packet/BSPDecal';
|
||||
import {EncodeClassInfo, ParseClassInfo} from '../Packet/ClassInfo';
|
||||
import {ParseCmdKeyValues} from '../Packet/CmdKeyValues';
|
||||
import {ParseCreateStringTable} from '../Packet/CreateStringTable';
|
||||
import {EncodeCreateStringTable, ParseCreateStringTable} from '../Packet/CreateStringTable';
|
||||
import {ParseEntityMessage} from '../Packet/EntityMessage';
|
||||
import {ParseGameEvent} from '../Packet/GameEvent';
|
||||
import {ParseGameEventList} from '../Packet/GameEventList';
|
||||
|
|
@ -41,7 +41,7 @@ export class Packet extends Parser {
|
|||
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
||||
10: {parser: ParseClassInfo, encoder: EncodeClassInfo},
|
||||
11: make('setPause', 'paused{b}'),
|
||||
12: {parser: ParseCreateStringTable, encoder: voidEncoder},
|
||||
12: {parser: ParseCreateStringTable, encoder: EncodeCreateStringTable},
|
||||
13: {parser: ParseUpdateStringTable, encoder: voidEncoder},
|
||||
14: {parser: ParseVoiceInit, encoder: voidEncoder},
|
||||
15: {parser: ParseVoiceData, encoder: voidEncoder},
|
||||
|
|
|
|||
|
|
@ -10,13 +10,13 @@ export class StringTable extends Parser {
|
|||
// tables: []
|
||||
// }];
|
||||
// https://github.com/StatsHelix/demoinfo/blob/3d28ea917c3d44d987b98bb8f976f1a3fcc19821/DemoInfo/ST/StringTableParser.cs
|
||||
const tableCount = this.stream.readUint8();
|
||||
const tableCount = this.stream.readUint8();
|
||||
const tables: StringTableObject[] = [];
|
||||
let extraDataLength;
|
||||
for (let i = 0; i < tableCount; i++) {
|
||||
const entries: StringTableEntry[] = [];
|
||||
const tableName = this.stream.readASCIIString();
|
||||
const entryCount = this.stream.readUint16();
|
||||
const tableName = this.stream.readASCIIString();
|
||||
const entryCount = this.stream.readUint16();
|
||||
for (let j = 0; j < entryCount; j++) {
|
||||
let entry: StringTableEntry;
|
||||
try {
|
||||
|
|
@ -45,7 +45,7 @@ export class StringTable extends Parser {
|
|||
}
|
||||
const table: StringTableObject = {
|
||||
entries,
|
||||
name: tableName,
|
||||
name: tableName,
|
||||
maxEntries: entryCount,
|
||||
};
|
||||
tables.push(table);
|
||||
|
|
|
|||
|
|
@ -1,27 +1,26 @@
|
|||
import {BitStream} from 'bit-buffer';
|
||||
import {StringTablePacket} from '../../Data/Packet';
|
||||
import {CreateStringTablePacket} from '../../Data/Packet';
|
||||
import {logBase2} from '../../Math';
|
||||
import {readVarInt} from '../readBitVar';
|
||||
import {readVarInt, writeVarInt} from '../readBitVar';
|
||||
|
||||
import {uncompress} from 'snappyjs';
|
||||
import {Match} from '../../Data/Match';
|
||||
import {StringTable} from '../../Data/StringTable';
|
||||
import {parseStringTable} from '../StringTableParser';
|
||||
import {encodeStringTableEntries, guessStringTableEntryLength, parseStringTableEntries} from '../StringTableParser';
|
||||
|
||||
export function ParseCreateStringTable(stream: BitStream, match: Match): StringTablePacket { // 12: createStringTable
|
||||
const tableName = stream.readASCIIString();
|
||||
const maxEntries = stream.readUint16();
|
||||
const encodeBits = logBase2(maxEntries);
|
||||
export function ParseCreateStringTable(stream: BitStream): CreateStringTablePacket { // 12: createStringTable
|
||||
const tableName = stream.readASCIIString();
|
||||
const maxEntries = stream.readUint16();
|
||||
const encodeBits = logBase2(maxEntries);
|
||||
const entityCount = stream.readBits(encodeBits + 1);
|
||||
|
||||
const bitCount = readVarInt(stream);
|
||||
|
||||
let userDataSize = 0;
|
||||
let userDataSize = 0;
|
||||
let userDataSizeBits = 0;
|
||||
|
||||
// userdata fixed size
|
||||
if (stream.readBoolean()) {
|
||||
userDataSize = stream.readBits(12);
|
||||
userDataSize = stream.readBits(12);
|
||||
userDataSizeBits = stream.readBits(4);
|
||||
}
|
||||
|
||||
|
|
@ -31,7 +30,7 @@ export function ParseCreateStringTable(stream: BitStream, match: Match): StringT
|
|||
|
||||
if (isCompressed) {
|
||||
const decompressedByteSize = data.readUint32();
|
||||
const compressedByteSize = data.readUint32();
|
||||
const compressedByteSize = data.readUint32();
|
||||
|
||||
const magic = data.readASCIIString(4);
|
||||
|
||||
|
|
@ -50,17 +49,45 @@ export function ParseCreateStringTable(stream: BitStream, match: Match): StringT
|
|||
}
|
||||
|
||||
const table: StringTable = {
|
||||
name: tableName,
|
||||
entries: [],
|
||||
name: tableName,
|
||||
entries: [],
|
||||
maxEntries,
|
||||
fixedUserDataSize: userDataSize,
|
||||
fixedUserDataSize: userDataSize,
|
||||
fixedUserDataSizeBits: userDataSizeBits,
|
||||
};
|
||||
|
||||
parseStringTable(data, table, entityCount, match);
|
||||
table.entries = parseStringTableEntries(data, table, entityCount);
|
||||
|
||||
return {
|
||||
packetType: 'stringTable',
|
||||
tables: [table],
|
||||
packetType: 'createStringTable',
|
||||
table: table,
|
||||
};
|
||||
}
|
||||
|
||||
export function EncodeCreateStringTable(packet: CreateStringTablePacket, stream: BitStream) {
|
||||
stream.writeASCIIString(packet.table.name);
|
||||
stream.writeUint16(packet.table.maxEntries);
|
||||
const encodeBits = logBase2(packet.table.maxEntries);
|
||||
stream.writeBits(packet.table.entries.length, encodeBits + 1);
|
||||
|
||||
const entryData = new BitStream(new ArrayBuffer(guessStringTableEntryLength(packet.table)));
|
||||
encodeStringTableEntries(entryData, packet.table);
|
||||
|
||||
const entryLength = entryData.index;
|
||||
entryData.index = 0;
|
||||
|
||||
writeVarInt(entryLength, stream);
|
||||
|
||||
if (packet.table.fixedUserDataSize && packet.table.fixedUserDataSizeBits) {
|
||||
stream.writeBoolean(true);
|
||||
stream.writeBits(packet.table.fixedUserDataSize, 12);
|
||||
stream.writeBits(packet.table.fixedUserDataSizeBits, 4);
|
||||
} else {
|
||||
stream.writeBoolean(false);
|
||||
}
|
||||
|
||||
// we never compress table data
|
||||
stream.writeBoolean(false);
|
||||
|
||||
stream.writeBitStream(entryData, entryLength);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,26 +1,32 @@
|
|||
import {BitStream} from 'bit-buffer';
|
||||
import {Match} from '../../Data/Match';
|
||||
import {StringTablePacket} from '../../Data/Packet';
|
||||
import {parseStringTable} from '../StringTableParser';
|
||||
import {UpdateStringTablePacket} from '../../Data/Packet';
|
||||
import {parseStringTableEntries} from '../StringTableParser';
|
||||
|
||||
export function ParseUpdateStringTable(stream: BitStream, match: Match): StringTablePacket { // 12: updateStringTable
|
||||
export function ParseUpdateStringTable(stream: BitStream, match: Match): UpdateStringTablePacket { // 12: updateStringTable
|
||||
const tableId = stream.readBits(5);
|
||||
|
||||
const multipleChanged = stream.readBoolean();
|
||||
const changedEntries = (multipleChanged) ? stream.readBits(16) : 1;
|
||||
const changedEntries = (multipleChanged) ? stream.readBits(16) : 1;
|
||||
|
||||
const bitCount = stream.readBits(20);
|
||||
const data = stream.readBitStream(bitCount);
|
||||
const data = stream.readBitStream(bitCount);
|
||||
|
||||
if (!match.stringTables[tableId]) {
|
||||
throw new Error('Table not found for update');
|
||||
}
|
||||
|
||||
const table = match.stringTables[tableId];
|
||||
parseStringTable(data, table, changedEntries, match);
|
||||
const updatedEntries = parseStringTableEntries(data, table, changedEntries, table.entries);
|
||||
|
||||
for (let i = 0; i < updatedEntries.length; i++) {
|
||||
if (updatedEntries[i]) {
|
||||
table.entries[i] = updatedEntries[i];
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
packetType: 'stringTable',
|
||||
tables: [table],
|
||||
packetType: 'updateStringTable',
|
||||
table: table,
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,18 +3,15 @@ import {Match} from '../Data/Match';
|
|||
import {StringTable, StringTableEntry} from '../Data/StringTable';
|
||||
import {logBase2} from '../Math';
|
||||
|
||||
export function parseStringTable(stream: BitStream, table: StringTable, entries: number, match: Match) {
|
||||
export function parseStringTableEntries(stream: BitStream, table: StringTable, entryCount: number, existingEntries: StringTableEntry[] = []): StringTableEntry[] {
|
||||
const entryBits = logBase2(table.maxEntries);
|
||||
let lastEntry = -1;
|
||||
const entries: StringTableEntry[] = [];
|
||||
let lastEntry = -1;
|
||||
|
||||
const history: StringTableEntry[] = [];
|
||||
|
||||
for (let i = 0; i < entries; i++) {
|
||||
let entryIndex = lastEntry + 1;
|
||||
|
||||
if (!stream.readBoolean()) {
|
||||
entryIndex = stream.readBits(entryBits);
|
||||
}
|
||||
for (let i = 0; i < entryCount; i++) {
|
||||
const entryIndex = (!stream.readBoolean()) ? stream.readBits(entryBits) : lastEntry + 1;
|
||||
|
||||
lastEntry = entryIndex;
|
||||
|
||||
|
|
@ -28,7 +25,7 @@ export function parseStringTable(stream: BitStream, table: StringTable, entries:
|
|||
const subStringCheck = stream.readBoolean();
|
||||
|
||||
if (subStringCheck) {
|
||||
const index = stream.readBits(5);
|
||||
const index = stream.readBits(5);
|
||||
const bytesToCopy = stream.readBits(5);
|
||||
|
||||
const restOfString = stream.readASCIIString();
|
||||
|
|
@ -43,19 +40,19 @@ export function parseStringTable(stream: BitStream, table: StringTable, entries:
|
|||
}
|
||||
}
|
||||
|
||||
let userData: BitStream|undefined;
|
||||
let userData: BitStream | undefined;
|
||||
|
||||
if (stream.readBoolean()) {
|
||||
if (table.fixedUserDataSize && table.fixedUserDataSizeBits) {
|
||||
userData = stream.readBitStream(table.fixedUserDataSizeBits);
|
||||
} else {
|
||||
const userDataBytes = stream.readBits(14);
|
||||
userData = stream.readBitStream(userDataBytes * 8);
|
||||
userData = stream.readBitStream(userDataBytes * 8);
|
||||
}
|
||||
}
|
||||
|
||||
if (table.entries[entryIndex]) {
|
||||
const existingEntry = table.entries[entryIndex];
|
||||
if (existingEntries[entryIndex]) {
|
||||
const existingEntry: StringTableEntry = {...existingEntries[entryIndex]};
|
||||
if (userData) {
|
||||
existingEntry.extraData = userData;
|
||||
}
|
||||
|
|
@ -63,16 +60,67 @@ export function parseStringTable(stream: BitStream, table: StringTable, entries:
|
|||
if (value) {
|
||||
existingEntry.text = value;
|
||||
}
|
||||
entries[entryIndex] = existingEntry;
|
||||
history.push(existingEntry);
|
||||
} else {
|
||||
table.entries[entryIndex] = {
|
||||
text: value,
|
||||
entries[entryIndex] = {
|
||||
text: value,
|
||||
extraData: userData,
|
||||
};
|
||||
history.push(table.entries[entryIndex]);
|
||||
console.log(entries[entryIndex]);
|
||||
history.push(entries[entryIndex]);
|
||||
}
|
||||
if (history.length > 32) {
|
||||
history.shift();
|
||||
}
|
||||
}
|
||||
|
||||
return entries;
|
||||
}
|
||||
|
||||
export function guessStringTableEntryLength(table: StringTable): number {
|
||||
// a rough guess of how many bytes are needed to encode the table entries
|
||||
const entryBytes = Math.ceil(logBase2(table.maxEntries) / 8);
|
||||
return table.entries.reduce((length: number, entry: StringTableEntry) => {
|
||||
return length +
|
||||
entryBytes +
|
||||
1 + // misc boolean
|
||||
entry.text.length + 1 + // +1 for null termination
|
||||
(entry.extraData ? Math.ceil(entry.extraData.length / 8) : 0);
|
||||
}, 1);
|
||||
}
|
||||
|
||||
export function encodeStringTableEntries(stream: BitStream, table: StringTable) {
|
||||
const entryBits = logBase2(table.maxEntries);
|
||||
let lastIndex = -1;
|
||||
for (let i = 0; i < table.entries.length; i++) {
|
||||
if (table.entries[i]) {
|
||||
const entry = table.entries[i];
|
||||
if (i !== lastIndex) {
|
||||
stream.writeBoolean(false);
|
||||
stream.writeBits(i, entryBits);
|
||||
} else {
|
||||
stream.writeBoolean(true);
|
||||
}
|
||||
|
||||
// we always encode a value
|
||||
stream.writeBoolean(true);
|
||||
// we don't encode substring optimizations
|
||||
stream.writeBoolean(false);
|
||||
|
||||
stream.writeASCIIString(entry.text);
|
||||
|
||||
if (entry.extraData) {
|
||||
stream.writeBoolean(true);
|
||||
|
||||
if (!table.fixedUserDataSizeBits) {
|
||||
stream.writeBits(Math.ceil(entry.extraData.length / 8), 14);
|
||||
}
|
||||
stream.writeBitStream(entry.extraData);
|
||||
entry.extraData.index = 0;
|
||||
} else {
|
||||
stream.writeBoolean(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue