1
0
Fork 0
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:
Robin Appelman 2017-08-13 21:32:18 +02:00
commit 89b9c3b25c
10 changed files with 276 additions and 77 deletions

View file

@ -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},

View file

@ -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);

View file

@ -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);
}

View file

@ -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,
};
}

View file

@ -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);
}
}
}
}