mirror of
https://github.com/blitzkit/blitzkit.git
synced 2026-10-23 19:49:50 +02:00
feat: more compression
This commit is contained in:
1 parent
afe041f969
commit
1575b803c0
11 files changed
+135
-59
No files matched your search
+11
-2
@@ -22,6 +22,7 @@ struct String {
|
|||||||
|
|
||||||
- Everything is little endian
|
- Everything is little endian
|
||||||
- Magic is always `"BKON"`
|
- Magic is always `"BKON"`
|
||||||
|
- The index of fast strings must be read as mentioned by the fast string format
|
||||||
|
|
||||||
```cpp
|
```cpp
|
||||||
struct BlitzkriegObjectNotation {
|
struct BlitzkriegObjectNotation {
|
||||||
@@ -38,11 +39,19 @@ struct BlitzkriegObjectNotation {
|
|||||||
```cpp
|
```cpp
|
||||||
struct Header {
|
struct Header {
|
||||||
uint16 version;
|
uint16 version;
|
||||||
|
FastStringFormat fastStringFormat;
|
||||||
StringTable stringTable;
|
StringTable stringTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// read as uint8
|
||||||
|
enum FastStringFormat {
|
||||||
|
Format8,
|
||||||
|
Format16,
|
||||||
|
Format32,
|
||||||
|
}
|
||||||
|
|
||||||
struct StringTable {
|
struct StringTable {
|
||||||
uint16 count;
|
(uint8 | uint16 | uint32) count;
|
||||||
String[count] strings;
|
String[count] strings;
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@@ -142,7 +151,7 @@ struct ValueString {
|
|||||||
|
|
||||||
struct ValueFastString {
|
struct ValueFastString {
|
||||||
ValueType.FastString type;
|
ValueType.FastString type;
|
||||||
uint16 index;
|
(uint8 | uint16 | uint32) index;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ValueArray {
|
struct ValueArray {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
ModelDefinitions,
|
ModelDefinitions,
|
||||||
} from '../../src/core/blitzkrieg/modelDefinitions';
|
} from '../../src/core/blitzkrieg/modelDefinitions';
|
||||||
import { ProvisionDefinitions } from '../../src/core/blitzkrieg/provisionDefinitions';
|
import { ProvisionDefinitions } from '../../src/core/blitzkrieg/provisionDefinitions';
|
||||||
|
import { superCompress } from '../../src/core/blitzkrieg/superCompress';
|
||||||
import {
|
import {
|
||||||
GunDefinition,
|
GunDefinition,
|
||||||
ShellType,
|
ShellType,
|
||||||
@@ -912,29 +913,29 @@ export async function definitions(production: boolean) {
|
|||||||
'definitions',
|
'definitions',
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
content: JSON.stringify(tankDefinitions),
|
content: superCompress(tankDefinitions),
|
||||||
encoding: 'utf-8',
|
encoding: 'base64',
|
||||||
path: 'definitions/tanks.json',
|
path: 'definitions/tanks.bkon.lz4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: JSON.stringify(modelDefinitions),
|
content: superCompress(modelDefinitions),
|
||||||
encoding: 'utf-8',
|
encoding: 'base64',
|
||||||
path: 'definitions/models.json',
|
path: 'definitions/models.bkon.lz4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: JSON.stringify(equipmentDefinitions),
|
content: superCompress(equipmentDefinitions),
|
||||||
encoding: 'utf-8',
|
encoding: 'base64',
|
||||||
path: 'definitions/equipment.json',
|
path: 'definitions/equipment.bkon.lz4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: JSON.stringify(consumableDefinitions),
|
content: superCompress(consumableDefinitions),
|
||||||
encoding: 'utf-8',
|
encoding: 'base64',
|
||||||
path: 'definitions/consumables.json',
|
path: 'definitions/consumables.bkon.lz4',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
content: JSON.stringify(provisionDefinitions),
|
content: superCompress(provisionDefinitions),
|
||||||
encoding: 'utf-8',
|
encoding: 'base64',
|
||||||
path: 'definitions/provisions.json',
|
path: 'definitions/provisions.bkon.lz4',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
production,
|
production,
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
|
import { fetchBkonLz4 } from './fetchBkonLz4';
|
||||||
|
|
||||||
export type TankFilterDefinitionCategory = 'clip';
|
export type TankFilterDefinitionCategory = 'clip';
|
||||||
|
|
||||||
@@ -33,9 +34,6 @@ export interface ConsumableDefinitions {
|
|||||||
[key: string]: ConsumableEntry;
|
[key: string]: ConsumableEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const consumableDefinitions = fetch(
|
export const consumableDefinitions = fetchBkonLz4<ConsumableDefinitions>(
|
||||||
asset('definitions/consumables.json'),
|
asset('definitions/consumables.bkon.lz4'),
|
||||||
{
|
);
|
||||||
cache: 'no-cache',
|
|
||||||
},
|
|
||||||
).then(async (response) => response.json() as Promise<ConsumableDefinitions>);
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
|
import { fetchBkonLz4 } from './fetchBkonLz4';
|
||||||
|
|
||||||
type EquipmentOptions = [number, number];
|
type EquipmentOptions = [number, number];
|
||||||
|
|
||||||
@@ -21,6 +22,6 @@ export interface EquipmentDefinitions {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const equipmentDefinitions = fetch(asset('definitions/equipment.json'), {
|
export const equipmentDefinitions = fetchBkonLz4<EquipmentDefinitions>(
|
||||||
cache: 'no-cache',
|
asset('definitions/equipment.bkon.lz4'),
|
||||||
}).then(async (response) => response.json() as Promise<EquipmentDefinitions>);
|
);
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
import { superDecompress } from './superDecompress';
|
||||||
|
|
||||||
|
export async function fetchBkonLz4<Type>(url: string) {
|
||||||
|
return await fetch(url, {
|
||||||
|
cache: 'no-cache',
|
||||||
|
}).then(async (response) => {
|
||||||
|
return superDecompress(Buffer.from(await response.arrayBuffer())) as Type;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { Vector3Tuple } from 'three';
|
import { Vector3Tuple } from 'three';
|
||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
|
import { fetchBkonLz4 } from './fetchBkonLz4';
|
||||||
|
|
||||||
export type ModelDefinitions = Record<number, ModelDefinition>;
|
export type ModelDefinitions = Record<number, ModelDefinition>;
|
||||||
|
|
||||||
@@ -53,6 +54,6 @@ export interface PitchLimits {
|
|||||||
transition?: number;
|
transition?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const modelDefinitions = fetch(asset('definitions/models.json'), {
|
export const modelDefinitions = fetchBkonLz4<ModelDefinitions>(
|
||||||
cache: 'no-cache',
|
asset('definitions/models.bkon.lz4'),
|
||||||
}).then(async (response) => response.json() as Promise<ModelDefinitions>);
|
);
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
import { TankFilterDefinition } from './consumableDefinitions';
|
import { TankFilterDefinition } from './consumableDefinitions';
|
||||||
|
import { fetchBkonLz4 } from './fetchBkonLz4';
|
||||||
|
|
||||||
export interface ProvisionEntry {
|
export interface ProvisionEntry {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -13,9 +14,6 @@ export interface ProvisionDefinitions {
|
|||||||
[key: string]: ProvisionEntry;
|
[key: string]: ProvisionEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const provisionDefinitions = fetch(
|
export const provisionDefinitions = fetchBkonLz4<ProvisionDefinitions>(
|
||||||
asset('definitions/provisions.json'),
|
asset('definitions/provisions.bkon.lz4'),
|
||||||
{
|
);
|
||||||
cache: 'no-cache',
|
|
||||||
},
|
|
||||||
).then(async (response) => response.json() as Promise<ProvisionDefinitions>);
|
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { encode } from 'lz4';
|
||||||
|
import { BkonWriteStream } from '../streams/bkon';
|
||||||
|
|
||||||
|
export function superCompress(object: any) {
|
||||||
|
const write = new BkonWriteStream().bkon(object);
|
||||||
|
const compressed = encode(write.buffer);
|
||||||
|
return compressed.toString('base64');
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
import { decode } from 'lz4';
|
||||||
|
import { BkonReadStream } from '../streams/bkon';
|
||||||
|
|
||||||
|
export function superDecompress<Type>(buffer: Buffer) {
|
||||||
|
const decompressed = decode(buffer);
|
||||||
|
const read = new BkonReadStream(decompressed);
|
||||||
|
return read.bkon() as Type;
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ import { deburr } from 'lodash';
|
|||||||
import { Vector2Tuple } from 'three';
|
import { Vector2Tuple } from 'three';
|
||||||
import { TankType, TreeType } from '../../components/Tanks';
|
import { TankType, TreeType } from '../../components/Tanks';
|
||||||
import { asset } from './asset';
|
import { asset } from './asset';
|
||||||
|
import { fetchBkonLz4 } from './fetchBkonLz4';
|
||||||
|
|
||||||
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
|
export type ShellType = 'ap' | 'ap_cr' | 'hc' | 'he';
|
||||||
|
|
||||||
@@ -117,9 +118,9 @@ export const GUN_TYPE_NAMES: Record<GunDefinition['type'], string> = {
|
|||||||
autoReloader: 'Auto reloader',
|
autoReloader: 'Auto reloader',
|
||||||
};
|
};
|
||||||
|
|
||||||
export const tankDefinitions = fetch(asset('definitions/tanks.json'), {
|
export const tankDefinitions = fetchBkonLz4<TankDefinitions>(
|
||||||
cache: 'no-cache',
|
asset('definitions/tanks.bkon.lz4'),
|
||||||
}).then(async (response) => response.json() as Promise<TankDefinitions>);
|
);
|
||||||
|
|
||||||
const entries = new Promise<TankDefinition[]>(async (resolve) => {
|
const entries = new Promise<TankDefinition[]>(async (resolve) => {
|
||||||
resolve(Object.entries(await tankDefinitions).map(([, entry]) => entry));
|
resolve(Object.entries(await tankDefinitions).map(([, entry]) => entry));
|
||||||
|
|||||||
+63
-21
@@ -20,20 +20,26 @@ enum ValueType {
|
|||||||
Object,
|
Object,
|
||||||
}
|
}
|
||||||
|
|
||||||
type Value =
|
enum FastStringFormat {
|
||||||
|
Format8,
|
||||||
|
Format16,
|
||||||
|
Format32,
|
||||||
|
}
|
||||||
|
|
||||||
|
export type BkonValue =
|
||||||
| null
|
| null
|
||||||
| boolean
|
| boolean
|
||||||
| number
|
| number
|
||||||
| bigint
|
| bigint
|
||||||
| string
|
| string
|
||||||
| Value[]
|
| BkonValue[]
|
||||||
| { [key: string]: Value };
|
| { [key: string]: BkonValue };
|
||||||
|
|
||||||
export class BkonReadStream extends ReadStream {
|
export class BkonReadStream extends ReadStream {
|
||||||
bkon() {
|
bkon() {
|
||||||
this.magic();
|
this.magic();
|
||||||
const header = this.header();
|
const header = this.header();
|
||||||
const body = this.body(header.stringTable);
|
const body = this.body(header.fastStringFormat, header.stringTable);
|
||||||
|
|
||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
@@ -47,12 +53,16 @@ export class BkonReadStream extends ReadStream {
|
|||||||
header() {
|
header() {
|
||||||
return {
|
return {
|
||||||
version: this.uint16(),
|
version: this.uint16(),
|
||||||
|
fastStringFormat: this.uint8() as FastStringFormat,
|
||||||
stringTable: this.stringTable(),
|
stringTable: this.stringTable(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
body(stringTable: Record<number, string>) {
|
body(
|
||||||
return this.value(stringTable);
|
fastStringFormat: FastStringFormat,
|
||||||
|
stringTable: Record<number, string>,
|
||||||
|
) {
|
||||||
|
return this.value(fastStringFormat, stringTable);
|
||||||
}
|
}
|
||||||
|
|
||||||
stringTable() {
|
stringTable() {
|
||||||
@@ -75,7 +85,10 @@ export class BkonReadStream extends ReadStream {
|
|||||||
return string;
|
return string;
|
||||||
}
|
}
|
||||||
|
|
||||||
value(stringTable: Record<number, string>): Value {
|
value(
|
||||||
|
fastStringFormat: FastStringFormat,
|
||||||
|
stringTable: Record<number, string>,
|
||||||
|
): BkonValue {
|
||||||
const type = this.uint8();
|
const type = this.uint8();
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
@@ -118,17 +131,34 @@ export class BkonReadStream extends ReadStream {
|
|||||||
case ValueType.String:
|
case ValueType.String:
|
||||||
return this.string();
|
return this.string();
|
||||||
|
|
||||||
case ValueType.FastString:
|
case ValueType.FastString: {
|
||||||
return stringTable[this.uint16()];
|
switch (fastStringFormat) {
|
||||||
|
case FastStringFormat.Format8:
|
||||||
|
return stringTable[this.uint8()];
|
||||||
|
|
||||||
|
case FastStringFormat.Format16:
|
||||||
|
return stringTable[this.uint16()];
|
||||||
|
|
||||||
|
case FastStringFormat.Format32:
|
||||||
|
return stringTable[this.uint32()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case ValueType.Array:
|
case ValueType.Array:
|
||||||
return times(this.uint32(), () => this.value(stringTable));
|
return times(this.uint32(), () =>
|
||||||
|
this.value(fastStringFormat, stringTable),
|
||||||
|
);
|
||||||
|
|
||||||
case ValueType.Object: {
|
case ValueType.Object: {
|
||||||
const count = this.uint32();
|
const count = this.uint32();
|
||||||
const keys = times(count, () => this.value(stringTable) as string);
|
const keys = times(
|
||||||
const values = times(count, () => this.value(stringTable));
|
count,
|
||||||
const object: Record<string, Value> = {};
|
() => this.value(fastStringFormat, stringTable) as string,
|
||||||
|
);
|
||||||
|
const values = times(count, () =>
|
||||||
|
this.value(fastStringFormat, stringTable),
|
||||||
|
);
|
||||||
|
const object: Record<string, BkonValue> = {};
|
||||||
|
|
||||||
keys.forEach((string, index) => (object[string] = values[index]));
|
keys.forEach((string, index) => (object[string] = values[index]));
|
||||||
|
|
||||||
@@ -142,18 +172,20 @@ export class BkonReadStream extends ReadStream {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class BkonWriteStream extends WriteStream {
|
export class BkonWriteStream extends WriteStream {
|
||||||
bkon(object: Value) {
|
bkon(object: BkonValue) {
|
||||||
this.magic();
|
this.magic();
|
||||||
const stringTable = this.header(object);
|
const stringTable = this.header(object);
|
||||||
this.body(object, stringTable);
|
this.body(object, stringTable);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
body(object: Value, stringTable: Map<string, number>) {
|
body(object: BkonValue, stringTable: Map<string, number>) {
|
||||||
this.value(object, stringTable);
|
this.value(object, stringTable);
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
value(object: Value, stringTable: Map<string, number>) {
|
value(object: BkonValue, stringTable: Map<string, number>) {
|
||||||
if (object === null) {
|
if (object === null || object === undefined) {
|
||||||
this.uint8(ValueType.Null);
|
this.uint8(ValueType.Null);
|
||||||
} else if (typeof object === 'boolean') {
|
} else if (typeof object === 'boolean') {
|
||||||
this.uint8(ValueType.Boolean);
|
this.uint8(ValueType.Boolean);
|
||||||
@@ -226,17 +258,27 @@ export class BkonWriteStream extends WriteStream {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
header(object: Value) {
|
header(object: BkonValue) {
|
||||||
this.uint16(1);
|
this.uint16(1);
|
||||||
return this.stringTable(object);
|
return this.stringTable(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
stringTable(object: Value) {
|
stringTable(object: BkonValue) {
|
||||||
const stringTable = this.buildStringTable(
|
const stringTable = this.buildStringTable(
|
||||||
this.collectStringCounts(object, new Map()),
|
this.collectStringCounts(object, new Map()),
|
||||||
);
|
);
|
||||||
|
|
||||||
this.uint16(stringTable.size);
|
if (stringTable.size <= 0xff) {
|
||||||
|
this.uint8(FastStringFormat.Format8);
|
||||||
|
this.uint8(stringTable.size);
|
||||||
|
} else if (stringTable.size <= 0xffff) {
|
||||||
|
this.uint8(FastStringFormat.Format16);
|
||||||
|
this.uint16(stringTable.size);
|
||||||
|
} else {
|
||||||
|
this.uint8(FastStringFormat.Format32);
|
||||||
|
this.uint32(stringTable.size);
|
||||||
|
}
|
||||||
|
|
||||||
stringTable.forEach((_index, string) => this.string(string));
|
stringTable.forEach((_index, string) => this.string(string));
|
||||||
|
|
||||||
return stringTable;
|
return stringTable;
|
||||||
@@ -252,7 +294,7 @@ export class BkonWriteStream extends WriteStream {
|
|||||||
return stringTable;
|
return stringTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
collectStringCounts(object: Value, stringCounts: Map<string, number>) {
|
collectStringCounts(object: BkonValue, stringCounts: Map<string, number>) {
|
||||||
if (typeof object === 'string') {
|
if (typeof object === 'string') {
|
||||||
if (stringCounts.has(object)) {
|
if (stringCounts.has(object)) {
|
||||||
stringCounts.set(object, stringCounts.get(object)! + 1);
|
stringCounts.set(object, stringCounts.get(object)! + 1);
|
||||||
|
|||||||
Reference in new issue
Block a user