mirror of
https://github.com/demostf/demo.js
synced 2026-06-04 09:04:13 +02:00
propper skipping of TempEntities
This commit is contained in:
parent
3b805be013
commit
1fbb61f252
11 changed files with 255 additions and 163 deletions
|
|
@ -17,22 +17,23 @@ export class Match {
|
||||||
sendTables: SendTable[];
|
sendTables: SendTable[];
|
||||||
instanceBaselines: SendProp[][][];
|
instanceBaselines: SendProp[][][];
|
||||||
staticBaseLines: any[];
|
staticBaseLines: any[];
|
||||||
|
_classBits: number = 0
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
this.tick = 0;
|
this.tick = 0;
|
||||||
this.chat = [];
|
this.chat = [];
|
||||||
this.users = {};
|
this.users = {};
|
||||||
this.deaths = [];
|
this.deaths = [];
|
||||||
this.rounds = [];
|
this.rounds = [];
|
||||||
this.startTick = 0;
|
this.startTick = 0;
|
||||||
this.intervalPerTick = 0;
|
this.intervalPerTick = 0;
|
||||||
this.entities = [];
|
this.entities = [];
|
||||||
this.stringTables = [];
|
this.stringTables = [];
|
||||||
this.sendTables = [];
|
this.sendTables = [];
|
||||||
this.serverClasses = [];
|
this.serverClasses = [];
|
||||||
this.entities = [];
|
this.entities = [];
|
||||||
this.instanceBaselines = [[], []];
|
this.instanceBaselines = [[], []];
|
||||||
this.staticBaseLines = [];
|
this.staticBaseLines = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getSendTable(name) {
|
getSendTable(name) {
|
||||||
|
|
@ -55,11 +56,11 @@ export class Match {
|
||||||
|
|
||||||
getState() {
|
getState() {
|
||||||
return {
|
return {
|
||||||
'chat': this.chat,
|
'chat': this.chat,
|
||||||
'users': this.users,
|
'users': this.users,
|
||||||
'deaths': this.deaths,
|
'deaths': this.deaths,
|
||||||
'rounds': this.rounds,
|
'rounds': this.rounds,
|
||||||
'startTick': this.startTick,
|
'startTick': this.startTick,
|
||||||
'intervalPerTick': this.intervalPerTick
|
'intervalPerTick': this.intervalPerTick
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -88,11 +89,11 @@ export class Match {
|
||||||
if (packet.tables.userinfo) {
|
if (packet.tables.userinfo) {
|
||||||
for (var j = 0; j < packet.tables.userinfo.length; j++) {
|
for (var j = 0; j < packet.tables.userinfo.length; j++) {
|
||||||
if (packet.tables.userinfo[j].extraData) {
|
if (packet.tables.userinfo[j].extraData) {
|
||||||
var name = packet.tables.userinfo[j].extraData[0];
|
var name = packet.tables.userinfo[j].extraData[0];
|
||||||
var steamId = packet.tables.userinfo[j].extraData[2];
|
var steamId = packet.tables.userinfo[j].extraData[2];
|
||||||
var userId = packet.tables.userinfo[j].extraData[1].charCodeAt(0);
|
var userId = packet.tables.userinfo[j].extraData[1].charCodeAt(0);
|
||||||
userState = this.getUserState(userId);
|
userState = this.getUserState(userId);
|
||||||
userState.name = name;
|
userState.name = name;
|
||||||
userState.steamId = steamId;
|
userState.steamId = steamId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -113,24 +114,24 @@ export class Match {
|
||||||
packet.event.values.userid -= 256;
|
packet.event.values.userid -= 256;
|
||||||
}
|
}
|
||||||
this.deaths.push({
|
this.deaths.push({
|
||||||
killer: packet.event.values.attacker,
|
killer: packet.event.values.attacker,
|
||||||
assister: assister,
|
assister: assister,
|
||||||
victim: packet.event.values.userid,
|
victim: packet.event.values.userid,
|
||||||
weapon: packet.event.values.weapon,
|
weapon: packet.event.values.weapon,
|
||||||
tick: this.tick
|
tick: this.tick
|
||||||
});
|
});
|
||||||
break;
|
break;
|
||||||
case 'teamplay_round_win':
|
case 'teamplay_round_win':
|
||||||
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
if (packet.event.values.winreason !== 6) {// 6 = timelimit
|
||||||
this.rounds.push({
|
this.rounds.push({
|
||||||
winner: packet.event.values.team === 2 ? 'red' : 'blue',
|
winner: packet.event.values.team === 2 ? 'red' : 'blue',
|
||||||
length: packet.event.values.round_time,
|
length: packet.event.values.round_time,
|
||||||
end_tick: this.tick
|
end_tick: this.tick
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'player_spawn':
|
case 'player_spawn':
|
||||||
userId = packet.event.values.userid;
|
userId = packet.event.values.userid;
|
||||||
userState = this.getUserState(userId);
|
userState = this.getUserState(userId);
|
||||||
if (!userState.team) { //only register first spawn
|
if (!userState.team) { //only register first spawn
|
||||||
userState.team = packet.event.values.team === 2 ? 'red' : 'blue'
|
userState.team = packet.event.values.team === 2 ? 'red' : 'blue'
|
||||||
|
|
@ -154,8 +155,8 @@ export class Match {
|
||||||
}
|
}
|
||||||
if (!this.users[userId]) {
|
if (!this.users[userId]) {
|
||||||
this.users[userId] = {
|
this.users[userId] = {
|
||||||
name: null,
|
name: null,
|
||||||
userId: userId,
|
userId: userId,
|
||||||
steamId: null,
|
steamId: null,
|
||||||
classes: {}
|
classes: {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,20 @@ export class SendPropDefinition {
|
||||||
table: SendTable|null;
|
table: SendTable|null;
|
||||||
numElements: number|null;
|
numElements: number|null;
|
||||||
arrayProperty: SendPropDefinition|null;
|
arrayProperty: SendPropDefinition|null;
|
||||||
|
ownerTableName: string;
|
||||||
|
|
||||||
constructor(type, name, flags) {
|
constructor(type, name, flags, ownerTableName) {
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.flags = flags;
|
this.flags = flags;
|
||||||
this.excludeDTName = null;
|
this.excludeDTName = null;
|
||||||
this.lowValue = 0;
|
this.lowValue = 0;
|
||||||
this.highValue = 0;
|
this.highValue = 0;
|
||||||
this.bitCount = 0;
|
this.bitCount = 0;
|
||||||
this.table = null;
|
this.table = null;
|
||||||
this.numElements = null;
|
this.numElements = null;
|
||||||
this.arrayProperty = null;
|
this.arrayProperty = null;
|
||||||
|
this.ownerTableName = ownerTableName;
|
||||||
}
|
}
|
||||||
|
|
||||||
hasFlag(flag: SendPropFlag) {
|
hasFlag(flag: SendPropFlag) {
|
||||||
|
|
@ -33,11 +35,22 @@ export class SendPropDefinition {
|
||||||
}
|
}
|
||||||
|
|
||||||
inspect() {
|
inspect() {
|
||||||
return {
|
let data: any = {
|
||||||
name: this.name,
|
fromTable: this.ownerTableName,
|
||||||
type: SendPropType[this.type],
|
name: this.name,
|
||||||
flags: SendPropDefinition.formatFlags(this.flags)
|
type: SendPropType[this.type],
|
||||||
|
flags: SendPropDefinition.formatFlags(this.flags),
|
||||||
|
bitCount: this.bitCount
|
||||||
|
};
|
||||||
|
if (this.type === SendPropType.DPT_Float) {
|
||||||
|
data.lowValue = this.lowValue;
|
||||||
|
data.highValue = this.highValue;
|
||||||
}
|
}
|
||||||
|
if (this.type === SendPropType.DPT_DataTable && this.table) {
|
||||||
|
data.tableName = this.table.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
static formatFlags(flags: number) {
|
static formatFlags(flags: number) {
|
||||||
|
|
@ -67,27 +80,27 @@ export enum SendPropType {
|
||||||
|
|
||||||
|
|
||||||
export enum SendPropFlag {
|
export enum SendPropFlag {
|
||||||
SPROP_UNSIGNED = (1 << 0),// Unsigned integer data.
|
SPROP_UNSIGNED = (1 << 0),// Unsigned integer data.
|
||||||
SPROP_COORD = (1 << 1),// If this is set, the float/vector is treated like a world coordinate.
|
SPROP_COORD = (1 << 1),// If this is set, the float/vector is treated like a world coordinate.
|
||||||
// Note that the bit count is ignored in this case.
|
// Note that the bit count is ignored in this case.
|
||||||
SPROP_NOSCALE = (1 << 2),// For floating point, don't scale into range, just take value as is.
|
SPROP_NOSCALE = (1 << 2),// For floating point, don't scale into range, just take value as is.
|
||||||
SPROP_ROUNDDOWN = (1 << 3),// For floating point, limit high value to range minus one bit unit
|
SPROP_ROUNDDOWN = (1 << 3),// For floating point, limit high value to range minus one bit unit
|
||||||
SPROP_ROUNDUP = (1 << 4),// For floating point, limit low value to range minus one bit unit
|
SPROP_ROUNDUP = (1 << 4),// For floating point, limit low value to range minus one bit unit
|
||||||
SPROP_NORMAL = (1 << 5),// If this is set, the vector is treated like a normal (only valid for vectors)
|
SPROP_NORMAL = (1 << 5),// If this is set, the vector is treated like a normal (only valid for vectors)
|
||||||
SPROP_EXCLUDE = (1 << 6),// This is an exclude prop (not excludED, but it points at another prop to be excluded).
|
SPROP_EXCLUDE = (1 << 6),// This is an exclude prop (not excludED, but it points at another prop to be excluded).
|
||||||
SPROP_XYZE = (1 << 7),// Use XYZ/Exponent encoding for vectors.
|
SPROP_XYZE = (1 << 7),// Use XYZ/Exponent encoding for vectors.
|
||||||
SPROP_INSIDEARRAY = (1 << 8),// This tells us that the property is inside an array, so it shouldn't be put into the
|
SPROP_INSIDEARRAY = (1 << 8),// This tells us that the property is inside an array, so it shouldn't be put into the
|
||||||
// flattened property list. Its array will point at it when it needs to.
|
// flattened property list. Its array will point at it when it needs to.
|
||||||
SPROP_PROXY_ALWAYS_YES = (1 << 9),// Set for datatable props using one of the default datatable proxies like
|
SPROP_PROXY_ALWAYS_YES = (1 << 9),// Set for datatable props using one of the default datatable proxies like
|
||||||
// SendProxy_DataTableToDataTable that always send the data to all clients.
|
// SendProxy_DataTableToDataTable that always send the data to all clients.
|
||||||
SPROP_CHANGES_OFTEN = (1 << 10),// this is an often changed field, moved to head of sendtable so it gets a small index
|
SPROP_CHANGES_OFTEN = (1 << 10),// this is an often changed field, moved to head of sendtable so it gets a small index
|
||||||
SPROP_IS_A_VECTOR_ELEM = (1 << 11),// Set automatically if SPROP_VECTORELEM is used.
|
SPROP_IS_A_VECTOR_ELEM = (1 << 11),// Set automatically if SPROP_VECTORELEM is used.
|
||||||
SPROP_COLLAPSIBLE = (1 << 12),// Set automatically if it's a datatable with an offset of 0 that doesn't change the pointer
|
SPROP_COLLAPSIBLE = (1 << 12),// Set automatically if it's a datatable with an offset of 0 that doesn't change the pointer
|
||||||
// (ie: for all automatically-chained base classes).
|
// (ie: for all automatically-chained base classes).
|
||||||
// In this case, it can get rid of this SendPropDataTable altogether and spare the
|
// In this case, it can get rid of this SendPropDataTable altogether and spare the
|
||||||
// trouble of walking the hierarchy more than necessary.
|
// trouble of walking the hierarchy more than necessary.
|
||||||
SPROP_COORD_MP = (1 << 13),// Like SPROP_COORD, but special handling for multiplayer games
|
SPROP_COORD_MP = (1 << 13),// Like SPROP_COORD, but special handling for multiplayer games
|
||||||
SPROP_COORD_MP_LOWPRECISION = (1 << 14),// Like SPROP_COORD, but special handling for multiplayer games where the fractional component only gets a 3 bits instead of 5
|
SPROP_COORD_MP_LOWPRECISION = (1 << 14),// Like SPROP_COORD, but special handling for multiplayer games where the fractional component only gets a 3 bits instead of 5
|
||||||
SPROP_COORD_MP_INTEGRAL = (1 << 15),// SPROP_COORD_MP, but coordinates are rounded to integral boundaries
|
SPROP_COORD_MP_INTEGRAL = (1 << 15),// SPROP_COORD_MP, but coordinates are rounded to integral boundaries
|
||||||
SPROP_VARINT = (1 << 5)
|
SPROP_VARINT = (1 << 5)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,8 +6,8 @@ export class SendTable {
|
||||||
private _flattenedProps: SendPropDefinition[];
|
private _flattenedProps: SendPropDefinition[];
|
||||||
|
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.props = [];
|
this.props = [];
|
||||||
this._flattenedProps = [];
|
this._flattenedProps = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -16,8 +16,8 @@ export class SendTable {
|
||||||
}
|
}
|
||||||
|
|
||||||
flatten() {
|
flatten() {
|
||||||
let excludes = [];
|
let excludes: SendPropDefinition[] = [];
|
||||||
let props: SendPropDefinition[] = [];
|
let props: SendPropDefinition[] = [];
|
||||||
this.getAllProps(excludes, props);
|
this.getAllProps(excludes, props);
|
||||||
|
|
||||||
// sort often changed props before the others
|
// sort often changed props before the others
|
||||||
|
|
@ -25,8 +25,8 @@ export class SendTable {
|
||||||
for (let i = 0; i < props.length; i++) {
|
for (let i = 0; i < props.length; i++) {
|
||||||
if (props[i].hasFlag(SendPropFlag.SPROP_CHANGES_OFTEN)) {
|
if (props[i].hasFlag(SendPropFlag.SPROP_CHANGES_OFTEN)) {
|
||||||
if (i != start) {
|
if (i != start) {
|
||||||
const temp = props[i];
|
const temp = props[i];
|
||||||
props[i] = props[start];
|
props[i] = props[start];
|
||||||
props[start] = temp;
|
props[start] = temp;
|
||||||
}
|
}
|
||||||
start++;
|
start++;
|
||||||
|
|
@ -35,7 +35,7 @@ export class SendTable {
|
||||||
this._flattenedProps = props;
|
this._flattenedProps = props;
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllProps(excludes: SendTable[], props: SendPropDefinition[]) {
|
getAllProps(excludes: SendPropDefinition[], props: SendPropDefinition[]) {
|
||||||
let localProps = [];
|
let localProps = [];
|
||||||
this.getAllPropsIteratorProps(excludes, localProps, props);
|
this.getAllPropsIteratorProps(excludes, localProps, props);
|
||||||
for (let i = 0; i < localProps.length; i++) {
|
for (let i = 0; i < localProps.length; i++) {
|
||||||
|
|
@ -43,18 +43,23 @@ export class SendTable {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getAllPropsIteratorProps(excludes: SendTable[], props: SendPropDefinition[], childProps: SendPropDefinition[]) {
|
getAllPropsIteratorProps(excludes: SendPropDefinition[], props: SendPropDefinition[], childProps: SendPropDefinition[]) {
|
||||||
for (let i = 0; i < this.props.length; i++) {
|
for (let i = 0; i < this.props.length; i++) {
|
||||||
const prop = this.props[i];
|
const prop = this.props[i];
|
||||||
|
if (prop.hasFlag(SendPropFlag.SPROP_EXCLUDE) || excludes.indexOf(prop) !== -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (excludes.filter((exclude) => {
|
||||||
|
return prop.table && exclude.name == prop.name && exclude.excludeDTName == prop.table.name;
|
||||||
|
}).length > 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
if (prop.type === SendPropType.DPT_DataTable && prop.table) {
|
if (prop.type === SendPropType.DPT_DataTable && prop.table) {
|
||||||
if (prop.hasFlag(SendPropFlag.SPROP_EXCLUDE)) {
|
if (prop.hasFlag(SendPropFlag.SPROP_COLLAPSIBLE)) {
|
||||||
excludes.push(prop.table);
|
prop.table.getAllPropsIteratorProps(excludes, props, childProps);
|
||||||
} else if (excludes.indexOf(this) === -1) {
|
} else {
|
||||||
if (prop.hasFlag(SendPropFlag.SPROP_COLLAPSIBLE)) {
|
prop.table.getAllProps(excludes, childProps);
|
||||||
prop.table.getAllPropsIteratorProps(excludes, props, childProps);
|
|
||||||
} else {
|
|
||||||
prop.table.getAllProps(excludes, childProps);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (!prop.hasFlag(SendPropFlag.SPROP_EXCLUDE)) {
|
} else if (!prop.hasFlag(SendPropFlag.SPROP_EXCLUDE)) {
|
||||||
props.push(prop);
|
props.push(prop);
|
||||||
|
|
|
||||||
|
|
@ -8,22 +8,22 @@ export class DataTable extends Parser {
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_common_eng.cpp#L356
|
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_common_eng.cpp#L356
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_recv_eng.cpp#L310
|
// https://github.com/LestaD/SourceEngine2007/blob/43a5c90a5ada1e69ca044595383be67f40b33c61/src_main/engine/dt_recv_eng.cpp#L310
|
||||||
// https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
|
// https://github.com/PazerOP/DemoLib/blob/master/DemoLib/Commands/DemoDataTablesCommand.cs
|
||||||
var tables:SendTable[] = [];
|
let tables:SendTable[] = [];
|
||||||
var i, j;
|
let i, j;
|
||||||
while (this.stream.readBoolean()) {
|
while (this.stream.readBoolean()) {
|
||||||
var needsDecoder = this.stream.readBoolean();
|
const needsDecoder = this.stream.readBoolean();
|
||||||
var tableName = this.stream.readASCIIString();
|
const tableName = this.stream.readASCIIString();
|
||||||
var numProps = this.stream.readBits(10);
|
const numProps = this.stream.readBits(10);
|
||||||
var table = new SendTable(tableName);
|
const table = new SendTable(tableName);
|
||||||
|
|
||||||
// get props metadata
|
// get props metadata
|
||||||
var arrayElementProp;
|
let arrayElementProp;
|
||||||
for (i = 0; i < numProps; i++) {
|
for (i = 0; i < numProps; i++) {
|
||||||
var propType = this.stream.readBits(5);
|
const propType = this.stream.readBits(5);
|
||||||
var propName = this.stream.readASCIIString();
|
const propName = this.stream.readASCIIString();
|
||||||
var nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??)
|
const nFlagsBits = 16; // might be 11 (old?), 13 (new?), 16(networked) or 17(??)
|
||||||
var flags = this.stream.readBits(nFlagsBits);
|
const flags = this.stream.readBits(nFlagsBits);
|
||||||
var prop = new SendPropDefinition(propType, propName, flags);
|
const prop = new SendPropDefinition(propType, propName, flags, tableName);
|
||||||
if (propType === SendPropType.DPT_DataTable) {
|
if (propType === SendPropType.DPT_DataTable) {
|
||||||
prop.excludeDTName = this.stream.readASCIIString();
|
prop.excludeDTName = this.stream.readASCIIString();
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -57,6 +57,12 @@ export class DataTable extends Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prop.hasFlag(SendPropFlag.SPROP_INSIDEARRAY)) {
|
if (prop.hasFlag(SendPropFlag.SPROP_INSIDEARRAY)) {
|
||||||
|
if (arrayElementProp) {
|
||||||
|
throw new Error("array element already set");
|
||||||
|
}
|
||||||
|
if (prop.hasFlag(SendPropFlag.SPROP_CHANGES_OFTEN)) {
|
||||||
|
throw new Error("unexpected CHANGES_OFTEN prop in array");
|
||||||
|
}
|
||||||
arrayElementProp = prop;
|
arrayElementProp = prop;
|
||||||
} else {
|
} else {
|
||||||
table.addProp(prop);
|
table.addProp(prop);
|
||||||
|
|
@ -76,23 +82,23 @@ export class DataTable extends Parser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var serverClasses = this.stream.readUint16(); // short
|
const serverClasses = this.stream.readUint16(); // short
|
||||||
if (serverClasses <= 0) {
|
if (serverClasses <= 0) {
|
||||||
throw "expected one or more serverclasses";
|
throw "expected one or more serverclasses";
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < serverClasses; i++) {
|
for (i = 0; i < serverClasses; i++) {
|
||||||
var classId = this.stream.readUint16();
|
const classId = this.stream.readUint16();
|
||||||
if (classId > serverClasses) {
|
if (classId > serverClasses) {
|
||||||
throw "invalid class id";
|
throw "invalid class id";
|
||||||
}
|
}
|
||||||
var className = this.stream.readASCIIString();
|
const className = this.stream.readASCIIString();
|
||||||
var dataTable = this.stream.readASCIIString();
|
const dataTable = this.stream.readASCIIString();
|
||||||
this.match.serverClasses.push(new ServerClass(classId, className, dataTable));
|
this.match.serverClasses.push(new ServerClass(classId, className, dataTable));
|
||||||
}
|
}
|
||||||
|
|
||||||
var bitsLeft = (this.length * 8) - this.stream._index;
|
const bitsLeft = (this.length * 8) - this.stream._index;
|
||||||
if (bitsLeft > 7) {
|
if (bitsLeft > 7 || bitsLeft < 0) {
|
||||||
throw "unexpected remaining data in datatable (" + bitsLeft + " bits)";
|
throw "unexpected remaining data in datatable (" + bitsLeft + " bits)";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,11 @@ import {SetConVar} from '../Packet/SetConVar';
|
||||||
import {UpdateStringTable} from '../Packet/UpdateStringTable';
|
import {UpdateStringTable} from '../Packet/UpdateStringTable';
|
||||||
import {UserMessage} from '../Packet/UserMessage';
|
import {UserMessage} from '../Packet/UserMessage';
|
||||||
import {PacketParserMap} from '../Packet/Parser'
|
import {PacketParserMap} from '../Packet/Parser'
|
||||||
|
import {TempEntities} from '../Packet/TempEntities'
|
||||||
|
|
||||||
import {GameEventDefinitionMap} from "../../Data/GameEvent";
|
import {GameEventDefinitionMap} from "../../Data/GameEvent";
|
||||||
|
|
||||||
|
|
||||||
import {Packet as IPacket} from '../../Data/Packet';
|
import {Packet as IPacket} from '../../Data/Packet';
|
||||||
|
|
||||||
// https://code.google.com/p/coldemoplayer/source/browse/branches/2.0/compLexity+Demo+Player/CDP.Source/Messages/?r=219
|
// https://code.google.com/p/coldemoplayer/source/browse/branches/2.0/compLexity+Demo+Player/CDP.Source/Messages/?r=219
|
||||||
|
|
@ -23,15 +26,10 @@ import {Packet as IPacket} from '../../Data/Packet';
|
||||||
// https://github.com/LestaD/SourceEngine2007/blob/master/src_main/common/netmessages.cpp
|
// https://github.com/LestaD/SourceEngine2007/blob/master/src_main/common/netmessages.cpp
|
||||||
|
|
||||||
export class Packet extends Parser {
|
export class Packet extends Parser {
|
||||||
viewOrigin: any;
|
|
||||||
|
|
||||||
parse() {
|
parse() {
|
||||||
//var table = new PacketStringTable(this.stream);
|
|
||||||
//table.searchIds();
|
|
||||||
//return [];
|
|
||||||
|
|
||||||
let packets: IPacket[] = [];
|
let packets: IPacket[] = [];
|
||||||
let entities = [];
|
let entities = [];
|
||||||
|
let lastPacketType = 0;
|
||||||
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
while (this.bitsLeft > 6) { // last 6 bits for NOOP
|
||||||
const type = this.stream.readBits(6);
|
const type = this.stream.readBits(6);
|
||||||
if (type !== 0) {
|
if (type !== 0) {
|
||||||
|
|
@ -39,8 +37,9 @@ export class Packet extends Parser {
|
||||||
let packet = Packet.parsers[type].call(this, this.stream, Packet.gameEventMap, entities, this.match);
|
let packet = Packet.parsers[type].call(this, this.stream, Packet.gameEventMap, entities, this.match);
|
||||||
packets.push(packet);
|
packets.push(packet);
|
||||||
} else {
|
} else {
|
||||||
throw 'Unknown packet type ' + type;
|
throw new Error('Unknown packet type ' + type + " just parsed a " + PacketType[lastPacketType]);
|
||||||
}
|
}
|
||||||
|
lastPacketType = type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return packets;
|
return packets;
|
||||||
|
|
@ -51,13 +50,13 @@ export class Packet extends Parser {
|
||||||
}
|
}
|
||||||
|
|
||||||
static parsers: PacketParserMap = {
|
static parsers: PacketParserMap = {
|
||||||
2: ParserGenerator.make('file', 'transferId{32}fileName{s}requested{b}'),
|
2: ParserGenerator.make('file', 'transferId{32}fileName{s}requested{b}'),
|
||||||
3: ParserGenerator.make('netTick', 'tick{32}frameTime{16}stdDev{16}'),
|
3: ParserGenerator.make('netTick', 'tick{32}frameTime{16}stdDev{16}'),
|
||||||
4: ParserGenerator.make('stringCmd', 'command{s}'),
|
4: ParserGenerator.make('stringCmd', 'command{s}'),
|
||||||
5: SetConVar,
|
5: SetConVar,
|
||||||
6: ParserGenerator.make('sigOnState', 'state{8}count{32}'),
|
6: ParserGenerator.make('sigOnState', 'state{8}count{32}'),
|
||||||
7: ParserGenerator.make('print', 'value{s}'),
|
7: ParserGenerator.make('print', 'value{s}'),
|
||||||
8: ParserGenerator.make('serverInfo',
|
8: ParserGenerator.make('serverInfo',
|
||||||
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
'version{16}serverCount{32}stv{b}dedicated{b}maxCrc{32}maxClasses{16}' +
|
||||||
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
'mapHash{128}playerCount{8}maxPlayerCount{8}intervalPerTick{f32}platform{s1}' +
|
||||||
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
'game{s}map{s}skybox{s}serverName{s}replay{b}'),
|
||||||
|
|
@ -75,7 +74,7 @@ export class Packet extends Parser {
|
||||||
24: EntityMessage,
|
24: EntityMessage,
|
||||||
25: GameEvent,
|
25: GameEvent,
|
||||||
26: PacketEntities,
|
26: PacketEntities,
|
||||||
27: ParserGenerator.make('tempEntities', 'count{8}length{17}_{$length}'),
|
27: TempEntities,
|
||||||
28: ParserGenerator.make('preFetch', 'index{14}'),
|
28: ParserGenerator.make('preFetch', 'index{14}'),
|
||||||
29: ParserGenerator.make('menu', 'type{16}length{16}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}'),//length*8
|
29: ParserGenerator.make('menu', 'type{16}length{16}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}_{$length}'),//length*8
|
||||||
30: GameEventList,
|
30: GameEventList,
|
||||||
|
|
@ -85,3 +84,33 @@ export class Packet extends Parser {
|
||||||
|
|
||||||
static gameEventMap: GameEventDefinitionMap = {};
|
static gameEventMap: GameEventDefinitionMap = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum PacketType {
|
||||||
|
file = 2,
|
||||||
|
netTick = 3,
|
||||||
|
stringSmd = 4,
|
||||||
|
setConVar = 5,
|
||||||
|
sigOnState = 6,
|
||||||
|
print = 7,
|
||||||
|
serverInfo = 8,
|
||||||
|
classInfo = 10,
|
||||||
|
setPause = 11,
|
||||||
|
createStringTable = 12,
|
||||||
|
updateStringTable = 13,
|
||||||
|
voiceInit = 14,
|
||||||
|
voiceData = 15,
|
||||||
|
parseSounds = 17,
|
||||||
|
setView = 18,
|
||||||
|
fixAngle = 19,
|
||||||
|
bspDecal = 21,
|
||||||
|
userMessage = 23,
|
||||||
|
entityMessage = 24,
|
||||||
|
gameEvent = 25,
|
||||||
|
packetEntities = 26,
|
||||||
|
tempEntities = 27,
|
||||||
|
preFetch = 28,
|
||||||
|
menu = 29,
|
||||||
|
gameEventList = 30,
|
||||||
|
getCvarValue = 30,
|
||||||
|
cmdKeyValues = 32
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ export function BSPDecal(stream: BitStream): Packet { // 21: BSPDecal
|
||||||
}
|
}
|
||||||
const lowPriority = !!stream.readBits(1);
|
const lowPriority = !!stream.readBits(1);
|
||||||
return {
|
return {
|
||||||
packetType: 'BSPDecal',
|
packetType: 'bspDecal',
|
||||||
position: position,
|
position: position,
|
||||||
textureIndex: textureIndex,
|
textureIndex: textureIndex,
|
||||||
entIndex: entIndex,
|
entIndex: entIndex,
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {Packet} from "../../Data/Packet";
|
||||||
import {BitStream} from 'bit-buffer';
|
import {BitStream} from 'bit-buffer';
|
||||||
import {GameEventDefinition} from "../../Data/GameEvent";
|
import {GameEventDefinition} from "../../Data/GameEvent";
|
||||||
import {Match} from "../../Data/Match";
|
import {Match} from "../../Data/Match";
|
||||||
|
import {readUBitVar} from "../readBitVar";
|
||||||
|
|
||||||
enum PVS {
|
enum PVS {
|
||||||
PRESERVE = 0,
|
PRESERVE = 0,
|
||||||
|
|
@ -13,7 +14,7 @@ enum PVS {
|
||||||
DELETE = 4
|
DELETE = 4
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPVSType(stream: BitStream): number {
|
function readPVSType(stream: BitStream): PVS {
|
||||||
// https://github.com/skadistats/smoke/blob/a2954fbe2fa3936d64aee5b5567be294fef228e6/smoke/io/stream/entity.pyx#L24
|
// https://github.com/skadistats/smoke/blob/a2954fbe2fa3936d64aee5b5567be294fef228e6/smoke/io/stream/entity.pyx#L24
|
||||||
let pvs;
|
let pvs;
|
||||||
const hi = stream.readBoolean();
|
const hi = stream.readBoolean();
|
||||||
|
|
@ -30,7 +31,7 @@ function readPVSType(stream: BitStream): number {
|
||||||
return pvs;
|
return pvs;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readEnterPVS(stream: BitStream, entityId: number, match: Match, baseLine: number):Entity {
|
function readEnterPVS(stream: BitStream, entityId: number, match: Match, baseLine: number): Entity {
|
||||||
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs#L198
|
// https://github.com/PazerOP/DemoLib/blob/5f9467650f942a4a70f9ec689eadcd3e0a051956/TF2Net/NetMessages/NetPacketEntitiesMessage.cs#L198
|
||||||
const serverClass = match.serverClasses[stream.readBits(match.classBits)];
|
const serverClass = match.serverClasses[stream.readBits(match.classBits)];
|
||||||
console.log(serverClass);
|
console.log(serverClass);
|
||||||
|
|
@ -84,7 +85,7 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
const length = stream.readBits(20);
|
const length = stream.readBits(20);
|
||||||
const updatedBaseLine = stream.readBoolean();
|
const updatedBaseLine = stream.readBoolean();
|
||||||
const end = stream._index + length;
|
const end = stream._index + length;
|
||||||
let entityId = -1
|
let entityId = -1;
|
||||||
|
|
||||||
stream._index = end;
|
stream._index = end;
|
||||||
return {
|
return {
|
||||||
|
|
@ -106,23 +107,25 @@ export function PacketEntities(stream: BitStream, events: GameEventDefinition[],
|
||||||
const diff = readUBitVar(stream);
|
const diff = readUBitVar(stream);
|
||||||
entityId += 1 + diff;
|
entityId += 1 + diff;
|
||||||
const pvs = readPVSType(stream);
|
const pvs = readPVSType(stream);
|
||||||
|
console.log("entity: " + entityId, ", pvs " + PVS[pvs]);
|
||||||
if (pvs === PVS.ENTER) {
|
if (pvs === PVS.ENTER) {
|
||||||
const entity = readEnterPVS(stream, entityId, match, baseLine);
|
const entity = readEnterPVS(stream, entityId, match, baseLine);
|
||||||
applyEntityUpdate(entity, stream);
|
applyEntityUpdate(entity, stream);
|
||||||
match.entities[entityId] = entity;
|
match.entities[entityId] = entity;
|
||||||
|
|
||||||
if (updatedBaseLine) {
|
if (updatedBaseLine) {
|
||||||
const newBaseLine:SendProp[] = [];
|
const newBaseLine: SendProp[] = [];
|
||||||
newBaseLine.concat(entity.props);
|
newBaseLine.concat(entity.props);
|
||||||
match.instanceBaselines[baseLine][entityId] = newBaseLine;
|
match.instanceBaselines[baseLine][entityId] = newBaseLine;
|
||||||
}
|
}
|
||||||
entity.inPVS = true;
|
entity.inPVS = true;
|
||||||
|
// stream.readBits(1);
|
||||||
} else if (pvs === PVS.PRESERVE) {
|
} else if (pvs === PVS.PRESERVE) {
|
||||||
const entity = match.entities[entityId];
|
const entity = match.entities[entityId];
|
||||||
if (entity) {
|
if (entity) {
|
||||||
applyEntityUpdate(entity, stream);
|
applyEntityUpdate(entity, stream);
|
||||||
} else {
|
} else {
|
||||||
console.log(entityId, match.entities.length);
|
console.log( entityId, match.entities.length);
|
||||||
throw new Error("unknown entity");
|
throw new Error("unknown entity");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -157,13 +160,13 @@ const readFieldIndex = function (stream: BitStream, lastIndex: number): number {
|
||||||
};
|
};
|
||||||
|
|
||||||
const applyEntityUpdate = function (entity: Entity, stream: BitStream): Entity {
|
const applyEntityUpdate = function (entity: Entity, stream: BitStream): Entity {
|
||||||
let index = -1;
|
let index = -1;
|
||||||
const allProps = entity.sendTable.flattenedProps;
|
const allProps = entity.sendTable.flattenedProps;
|
||||||
|
let changedProps: SendProp[] = [];
|
||||||
while ((index = readFieldIndex(stream, index)) != -1) {
|
while ((index = readFieldIndex(stream, index)) != -1) {
|
||||||
if (index > 4096) {
|
if (index > 4096) {
|
||||||
throw new Error('prop index out of bounds');
|
throw new Error('prop index out of bounds');
|
||||||
}
|
}
|
||||||
console.log(index);
|
|
||||||
const propDefinition = allProps[index];
|
const propDefinition = allProps[index];
|
||||||
const existingProp = entity.getPropByDefinition(propDefinition);
|
const existingProp = entity.getPropByDefinition(propDefinition);
|
||||||
let prop;
|
let prop;
|
||||||
|
|
@ -172,26 +175,18 @@ const applyEntityUpdate = function (entity: Entity, stream: BitStream): Entity {
|
||||||
} else {
|
} else {
|
||||||
prop = new SendProp(propDefinition);
|
prop = new SendProp(propDefinition);
|
||||||
}
|
}
|
||||||
prop.value = SendPropParser.decode(propDefinition, stream);
|
// prop.value = SendPropParser.decode(propDefinition, stream);
|
||||||
console.log(prop);
|
// console.log(prop);
|
||||||
|
changedProps.push(prop);
|
||||||
|
|
||||||
if (!existingProp) {
|
if (!existingProp) {
|
||||||
entity.props.push(prop);
|
entity.props.push(prop);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (let i = 0; i < changedProps.length; i++) {
|
||||||
|
const prop = changedProps[i];
|
||||||
|
prop.value = SendPropParser.decode(prop.definition, stream);
|
||||||
|
console.log(prop);
|
||||||
|
}
|
||||||
return entity;
|
return entity;
|
||||||
};
|
};
|
||||||
|
|
||||||
const readUBitVar = function (stream: BitStream): number {
|
|
||||||
switch (stream.readBits(2)) {
|
|
||||||
case 0:
|
|
||||||
return stream.readBits(4);
|
|
||||||
case 1:
|
|
||||||
return stream.readBits(8);
|
|
||||||
case 2:
|
|
||||||
return stream.readBits(12);
|
|
||||||
case 3:
|
|
||||||
return stream.readBits(32);
|
|
||||||
}
|
|
||||||
throw new Error('Invalid var bit');
|
|
||||||
};
|
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,32 @@
|
||||||
import {Parser} from './Parser';
|
import {Parser} from './Parser';
|
||||||
|
|
||||||
export function make(name: string, definition: string): Parser {
|
export function make(name: string, definition: string): Parser {
|
||||||
var parts = definition.substr(0, definition.length - 1).split('}');//remove leading } to prevent empty part
|
const parts = definition.substr(0, definition.length - 1).split('}');//remove leading } to prevent empty part
|
||||||
var items = parts.map(function (part) {
|
const items = parts.map(function (part) {
|
||||||
return part.split('{');
|
return part.split('{');
|
||||||
});
|
});
|
||||||
return function (stream) {
|
return function (stream) {
|
||||||
var result = {
|
let result = {
|
||||||
'packetType': name
|
'packetType': name
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
for (var i = 0; i < items.length; i++) {
|
for (let i = 0; i < items.length; i++) {
|
||||||
var value = readItem(stream, items[i][1], result);
|
const value = readItem(stream, items[i][1], result);
|
||||||
if (items[i][0] !== '_') {
|
if (items[i][0] !== '_') {
|
||||||
result[items[i][0]] = value;
|
result[items[i][0]] = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
throw 'Failed reading pattern ' + definition + '. ' + e;
|
throw new Error('Failed reading pattern ' + definition + '. ' + e);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const readItem = function (stream, description, data) {
|
const readItem = function (stream, description, data) {
|
||||||
var length;
|
let length;
|
||||||
if (description[0] === 'b') {
|
if (description[0] === 'b') {
|
||||||
return !!stream.readBits(1);
|
return stream.readBoolean();
|
||||||
} else if (description[0] === 's') {
|
} else if (description[0] === 's') {
|
||||||
if (description.length === 1) {
|
if (description.length === 1) {
|
||||||
return stream.readUTF8String();
|
return stream.readUTF8String();
|
||||||
|
|
@ -40,8 +40,8 @@ const readItem = function (stream, description, data) {
|
||||||
length = parseInt(description.substr(1), 10);
|
length = parseInt(description.substr(1), 10);
|
||||||
return stream.readBits(length);
|
return stream.readBits(length);
|
||||||
} else if (description[0] === '$') {
|
} else if (description[0] === '$') {
|
||||||
var variable = description.substr(1);
|
const variable = description.substr(1);
|
||||||
return stream.readBits(variable);
|
return stream.readBits(data[variable]);
|
||||||
} else {
|
} else {
|
||||||
return stream.readBits(parseInt(description, 10), true);
|
return stream.readBits(parseInt(description, 10), true);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
26
src/Parser/Packet/TempEntities.ts
Normal file
26
src/Parser/Packet/TempEntities.ts
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
import {Packet} from "../../Data/Packet";
|
||||||
|
import {BitStream} from 'bit-buffer';
|
||||||
|
|
||||||
|
export function TempEntities(stream: BitStream): Packet { // 10: classInfo
|
||||||
|
const entityCount = stream.readBits(8);
|
||||||
|
const length = readVarInt(stream);
|
||||||
|
console.log(length);
|
||||||
|
stream._index += length;
|
||||||
|
|
||||||
|
return {
|
||||||
|
'packetType': 'tempEntities'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function readVarInt(stream: BitStream) {
|
||||||
|
let result = 0;
|
||||||
|
for (let run = 0; run < 35; run += 7) {
|
||||||
|
const byte = stream.readUint8();
|
||||||
|
result |= ((byte & 0x7F) << run);
|
||||||
|
|
||||||
|
if ((byte >> 7) == 0) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
@ -47,14 +47,14 @@ export class SendPropParser {
|
||||||
|
|
||||||
static readArray(propDefinition: SendPropDefinition, stream: BitStream): SendPropArrayValue[] {
|
static readArray(propDefinition: SendPropDefinition, stream: BitStream): SendPropArrayValue[] {
|
||||||
let maxElements = propDefinition.numElements;
|
let maxElements = propDefinition.numElements;
|
||||||
let numBits = 1;
|
let numBits = 1;
|
||||||
while ((maxElements >>= 1) != 0)
|
while ((maxElements >>= 1) != 0)
|
||||||
numBits++;
|
numBits++;
|
||||||
|
|
||||||
const count = stream.readBits(numBits);
|
const count = stream.readBits(numBits);
|
||||||
const values: SendPropArrayValue[] = [];
|
const values: SendPropArrayValue[] = [];
|
||||||
if (!propDefinition.arrayProperty) {
|
if (!propDefinition.arrayProperty) {
|
||||||
throw new Error('Array of undefniend type');
|
throw new Error('Array of undefined type');
|
||||||
}
|
}
|
||||||
for (let i = 0; i < count; i++) {
|
for (let i = 0; i < count; i++) {
|
||||||
const value = SendPropParser.decode(propDefinition.arrayProperty, stream);
|
const value = SendPropParser.decode(propDefinition.arrayProperty, stream);
|
||||||
|
|
@ -88,24 +88,24 @@ export class SendPropParser {
|
||||||
if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD)) {
|
if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD)) {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP)) {
|
||||||
return SendPropParser.readBitCoord(stream, false, false);
|
return SendPropParser.readBitCoord(propDefinition, stream, false, false);
|
||||||
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_LOWPRECISION)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_LOWPRECISION)) {
|
||||||
return SendPropParser.readBitCoord(stream, false, true);
|
return SendPropParser.readBitCoord(propDefinition, stream, false, true);
|
||||||
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_INTEGRAL)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_COORD_MP_INTEGRAL)) {
|
||||||
return SendPropParser.readBitCoord(stream, true, false);
|
return SendPropParser.readBitCoord(propDefinition, stream, true, false);
|
||||||
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NOSCALE)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NOSCALE)) {
|
||||||
return stream.readFloat32();
|
return stream.readFloat32();
|
||||||
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NORMAL)) {
|
} else if (propDefinition.hasFlag(SendPropFlag.SPROP_NORMAL)) {
|
||||||
throw new Error("not implemented");
|
throw new Error("not implemented");
|
||||||
} else {
|
} else {
|
||||||
const raw = stream.readBits(propDefinition.bitCount);
|
const raw = stream.readBits(propDefinition.bitCount);
|
||||||
const percentage = raw / ((1 << propDefinition.bitCount) - 1);
|
const percentage = raw / ((1 << propDefinition.bitCount) - 1);
|
||||||
return propDefinition.lowValue + (propDefinition.highValue - propDefinition.lowValue) * percentage;
|
return propDefinition.lowValue + (propDefinition.highValue - propDefinition.lowValue) * percentage;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static readBitCoord(stream: BitStream, isIntegral: boolean, isLowPrecision: boolean): number {
|
static readBitCoord(propDefinition: SendPropDefinition, stream: BitStream, isIntegral: boolean, isLowPrecision: boolean): number {
|
||||||
let value = 0;
|
let value = 0;
|
||||||
let isNegative = false;
|
let isNegative = false;
|
||||||
const inBounds = stream.readBoolean();
|
const inBounds = stream.readBoolean();
|
||||||
|
|
||||||
|
|
@ -131,6 +131,7 @@ export class SendPropParser {
|
||||||
} else {
|
} else {
|
||||||
value = stream.readBits(14) + 1;
|
value = stream.readBits(14) + 1;
|
||||||
if (value < (1 << 11)) {
|
if (value < (1 << 11)) {
|
||||||
|
console.log(propDefinition, value);
|
||||||
throw new Error("Something's fishy...");
|
throw new Error("Something's fishy...");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
16
src/Parser/readBitVar.ts
Normal file
16
src/Parser/readBitVar.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
import {BitStream} from "bit-buffer";
|
||||||
|
export function readBitVar(stream: BitStream, signed?: boolean): number {
|
||||||
|
switch (stream.readBits(2)) {
|
||||||
|
case 0:
|
||||||
|
return stream.readBits(4, signed);
|
||||||
|
case 1:
|
||||||
|
return stream.readBits(8, signed);
|
||||||
|
case 2:
|
||||||
|
return stream.readBits(12, signed);
|
||||||
|
case 3:
|
||||||
|
return stream.readBits(32, signed);
|
||||||
|
}
|
||||||
|
throw new Error('Invalid var bit');
|
||||||
|
}
|
||||||
|
|
||||||
|
export const readUBitVar = readBitVar;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue