mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-21 10:47:04 +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",
|
||||
"BINORMAL",
|
||||
"Binormals",
|
||||
"BKON",
|
||||
"bkwd",
|
||||
"blitzstars",
|
||||
"Camo",
|
||||
@@ -100,6 +101,8 @@
|
||||
"statsfull",
|
||||
"steamapps",
|
||||
"Stringifying",
|
||||
"struct",
|
||||
"structs",
|
||||
"strvk",
|
||||
"subnation",
|
||||
"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() {
|
||||
return this.buffer.readBigUInt64LE(this.increment(8));
|
||||
}
|
||||
float() {
|
||||
float32() {
|
||||
return this.buffer.readFloatLE(this.increment(4));
|
||||
}
|
||||
float64() {
|
||||
return this.buffer.readDoubleLE(this.increment(8));
|
||||
}
|
||||
|
||||
boolean() {
|
||||
return this.buffer.readUInt8(this.increment(1)) === 1;
|
||||
|
||||
@@ -57,7 +57,7 @@ export enum VertexAttribute {
|
||||
|
||||
export class ScpgStream extends BufferStream {
|
||||
vectorN(size: number) {
|
||||
return times(size, () => this.float());
|
||||
return times(size, () => this.float32());
|
||||
}
|
||||
vector2() {
|
||||
return this.vectorN(2);
|
||||
@@ -107,7 +107,7 @@ export class ScpgStream extends BufferStream {
|
||||
}
|
||||
|
||||
case KAType.FLOAT:
|
||||
return this.float();
|
||||
return this.float32();
|
||||
|
||||
case KAType.ARRAY: {
|
||||
const length = this.uint32();
|
||||
|
||||
Reference in new issue
Block a user