mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: bkon read
This commit is contained in:
1 parent
559fb30da9
commit
43b6453fcb
5 files changed
+318
-3
No files matched your search
Vendored
+3
@@ -13,6 +13,7 @@
|
|||||||
"bepaint",
|
"bepaint",
|
||||||
"BINORMAL",
|
"BINORMAL",
|
||||||
"Binormals",
|
"Binormals",
|
||||||
|
"BKON",
|
||||||
"bkwd",
|
"bkwd",
|
||||||
"blitzstars",
|
"blitzstars",
|
||||||
"Camo",
|
"Camo",
|
||||||
@@ -100,6 +101,8 @@
|
|||||||
"statsfull",
|
"statsfull",
|
||||||
"steamapps",
|
"steamapps",
|
||||||
"Stringifying",
|
"Stringifying",
|
||||||
|
"struct",
|
||||||
|
"structs",
|
||||||
"strvk",
|
"strvk",
|
||||||
"subnation",
|
"subnation",
|
||||||
"Synx",
|
"Synx",
|
||||||
|
|||||||
@@ -0,0 +1,163 @@
|
|||||||
|
# Blitzkrieg Object Notation
|
||||||
|
|
||||||
|
A compact binary representation of JSON objects. This documentation uses a frankenstein version of C++ structs that I came up with. Deal with it.
|
||||||
|
|
||||||
|
## Primitives
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct String {
|
||||||
|
uint32 length;
|
||||||
|
ascii[length] value;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Reading
|
||||||
|
|
||||||
|
- Everything is little endian
|
||||||
|
- Magic is always `"BKON"`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct BlitzkriegObjectNotation {
|
||||||
|
ascii[4] magic;
|
||||||
|
Header header;
|
||||||
|
Body body;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Header
|
||||||
|
|
||||||
|
- Version is always `1`
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
struct Header {
|
||||||
|
uint16 version;
|
||||||
|
StringTable stringTable;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StringTable {
|
||||||
|
uint32 count;
|
||||||
|
uint32[count] ids;
|
||||||
|
String[count] strings;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Body
|
||||||
|
|
||||||
|
```cpp
|
||||||
|
// identical to ValueObject except with no type
|
||||||
|
struct Body {
|
||||||
|
uint32 count;
|
||||||
|
(ValueString | ValueFastString)[count] keys;
|
||||||
|
Value[count] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ValueType {
|
||||||
|
Null,
|
||||||
|
Boolean,
|
||||||
|
Uint8,
|
||||||
|
Uint16,
|
||||||
|
Uint32,
|
||||||
|
Uint64,
|
||||||
|
Int8,
|
||||||
|
Int16,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Float32,
|
||||||
|
Float64,
|
||||||
|
String,
|
||||||
|
FastString,
|
||||||
|
Array,
|
||||||
|
Object,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Value {
|
||||||
|
uint8 type;
|
||||||
|
// what comes next depends on type
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueNull {
|
||||||
|
ValueType.Null type;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueBoolean {
|
||||||
|
ValueType.Boolean type;
|
||||||
|
uint8 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueUint8 {
|
||||||
|
ValueType.Uint8 type;
|
||||||
|
uint8 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueUint16 {
|
||||||
|
ValueType.Uint16 type;
|
||||||
|
uint16 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueUint32 {
|
||||||
|
ValueType.Uint32 type;
|
||||||
|
uint32 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueUint64 {
|
||||||
|
ValueType.Uint64 type;
|
||||||
|
uint64 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueInt8 {
|
||||||
|
ValueType.Int8 type;
|
||||||
|
int8 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueInt16 {
|
||||||
|
ValueType.Int16 type;
|
||||||
|
int16 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueInt32 {
|
||||||
|
ValueType.Int32 type;
|
||||||
|
int32 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueInt64 {
|
||||||
|
ValueType.Int64 type;
|
||||||
|
int64 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueFloat32 {
|
||||||
|
ValueType.Float32 type;
|
||||||
|
float32 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueFloat64 {
|
||||||
|
ValueType.Float64 type;
|
||||||
|
float64 value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueString {
|
||||||
|
ValueType.String type;
|
||||||
|
String value;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueFastString {
|
||||||
|
ValueType.FastString type;
|
||||||
|
uint32 id;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueArray {
|
||||||
|
ValueType.Array type;
|
||||||
|
uint32 count;
|
||||||
|
Value[count] values;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ValueObject {
|
||||||
|
ValueType.Object type;
|
||||||
|
uint32 count;
|
||||||
|
(ValueString | ValueFastString)[count] keys;
|
||||||
|
Value[count] values;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Writing
|
||||||
|
|
||||||
|
It's pretty obvious how to write. Remember that a `FastString` should only be applied to the string table if the string is greater than 4 characters and shows up more than once in the body. Otherwise, there's no point and `String` should be used.
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
import { times } from 'lodash';
|
||||||
|
import { BufferStream } from './buffer';
|
||||||
|
|
||||||
|
enum ValueType {
|
||||||
|
Null,
|
||||||
|
Boolean,
|
||||||
|
Uint8,
|
||||||
|
Uint16,
|
||||||
|
Uint32,
|
||||||
|
Uint64,
|
||||||
|
Int8,
|
||||||
|
Int16,
|
||||||
|
Int32,
|
||||||
|
Int64,
|
||||||
|
Float32,
|
||||||
|
Float64,
|
||||||
|
String,
|
||||||
|
FastString,
|
||||||
|
Array,
|
||||||
|
Object,
|
||||||
|
}
|
||||||
|
|
||||||
|
type Value =
|
||||||
|
| null
|
||||||
|
| boolean
|
||||||
|
| number
|
||||||
|
| bigint
|
||||||
|
| string
|
||||||
|
| Value[]
|
||||||
|
| { [key: string]: Value };
|
||||||
|
|
||||||
|
export class BkonStream extends BufferStream {
|
||||||
|
bkon() {
|
||||||
|
this.magic();
|
||||||
|
const header = this.header();
|
||||||
|
const body = this.body(header.stringTable);
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
magic() {
|
||||||
|
if (this.ascii(4) !== 'BKON') {
|
||||||
|
throw new Error('Invalid BKON magic number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
header() {
|
||||||
|
return {
|
||||||
|
version: this.uint16(),
|
||||||
|
stringTable: this.stringTable(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
body(stringTable: Record<number, string>) {
|
||||||
|
const count = this.uint32();
|
||||||
|
const keys = times(count, () => this.value(stringTable) as string);
|
||||||
|
const values = times(count, () => this.value(stringTable));
|
||||||
|
const body: Record<string, Value> = {};
|
||||||
|
|
||||||
|
keys.forEach((key, index) => (body[key] = values[index]));
|
||||||
|
|
||||||
|
return body;
|
||||||
|
}
|
||||||
|
|
||||||
|
stringTable() {
|
||||||
|
const count = this.uint32();
|
||||||
|
const ids = times(count, this.uint32);
|
||||||
|
const strings = times(count, this.string);
|
||||||
|
const table: Record<number, string> = {};
|
||||||
|
|
||||||
|
ids.forEach((id, index) => (table[id] = strings[index]));
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
string() {
|
||||||
|
const length = this.uint32();
|
||||||
|
const string = this.ascii(length);
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
|
||||||
|
value(stringTable: Record<number, string>): Value {
|
||||||
|
const type = this.uint8();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case ValueType.Null:
|
||||||
|
return null;
|
||||||
|
|
||||||
|
case ValueType.Boolean:
|
||||||
|
return this.boolean();
|
||||||
|
|
||||||
|
case ValueType.Uint8:
|
||||||
|
return this.uint8();
|
||||||
|
|
||||||
|
case ValueType.Uint16:
|
||||||
|
return this.uint16();
|
||||||
|
|
||||||
|
case ValueType.Uint32:
|
||||||
|
return this.uint32();
|
||||||
|
|
||||||
|
case ValueType.Uint64:
|
||||||
|
return this.uint64();
|
||||||
|
|
||||||
|
case ValueType.Int8:
|
||||||
|
return this.int8();
|
||||||
|
|
||||||
|
case ValueType.Int16:
|
||||||
|
return this.int16();
|
||||||
|
|
||||||
|
case ValueType.Int32:
|
||||||
|
return this.int32();
|
||||||
|
|
||||||
|
case ValueType.Int64:
|
||||||
|
return this.int64();
|
||||||
|
|
||||||
|
case ValueType.Float32:
|
||||||
|
return this.float32();
|
||||||
|
|
||||||
|
case ValueType.Float64:
|
||||||
|
return this.float64();
|
||||||
|
|
||||||
|
case ValueType.String:
|
||||||
|
return this.string();
|
||||||
|
|
||||||
|
case ValueType.FastString:
|
||||||
|
return stringTable[this.uint32()];
|
||||||
|
|
||||||
|
case ValueType.Array:
|
||||||
|
return times(this.uint32(), () => this.value(stringTable));
|
||||||
|
|
||||||
|
case ValueType.Object: {
|
||||||
|
const count = this.uint32();
|
||||||
|
const keys = times(count, () => this.value(stringTable) as string);
|
||||||
|
const values = times(count, () => this.value(stringTable));
|
||||||
|
const object: Record<string, Value> = {};
|
||||||
|
|
||||||
|
keys.forEach((string, index) => (object[string] = values[index]));
|
||||||
|
|
||||||
|
return object;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw new Error(`Invalid value type: ${type}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,9 +60,12 @@ export class BufferStream {
|
|||||||
uint64() {
|
uint64() {
|
||||||
return this.buffer.readBigUInt64LE(this.increment(8));
|
return this.buffer.readBigUInt64LE(this.increment(8));
|
||||||
}
|
}
|
||||||
float() {
|
float32() {
|
||||||
return this.buffer.readFloatLE(this.increment(4));
|
return this.buffer.readFloatLE(this.increment(4));
|
||||||
}
|
}
|
||||||
|
float64() {
|
||||||
|
return this.buffer.readDoubleLE(this.increment(8));
|
||||||
|
}
|
||||||
|
|
||||||
boolean() {
|
boolean() {
|
||||||
return this.buffer.readUInt8(this.increment(1)) === 1;
|
return this.buffer.readUInt8(this.increment(1)) === 1;
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export enum VertexAttribute {
|
|||||||
|
|
||||||
export class ScpgStream extends BufferStream {
|
export class ScpgStream extends BufferStream {
|
||||||
vectorN(size: number) {
|
vectorN(size: number) {
|
||||||
return times(size, () => this.float());
|
return times(size, () => this.float32());
|
||||||
}
|
}
|
||||||
vector2() {
|
vector2() {
|
||||||
return this.vectorN(2);
|
return this.vectorN(2);
|
||||||
@@ -107,7 +107,7 @@ export class ScpgStream extends BufferStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case KAType.FLOAT:
|
case KAType.FLOAT:
|
||||||
return this.float();
|
return this.float32();
|
||||||
|
|
||||||
case KAType.ARRAY: {
|
case KAType.ARRAY: {
|
||||||
const length = this.uint32();
|
const length = this.uint32();
|
||||||
|
|||||||
Reference in new issue
Block a user