-
Notifications
You must be signed in to change notification settings - Fork 1
/
index.ts
156 lines (147 loc) · 6.05 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
import { inflate } from "https://deno.land/x/compress@v0.3.3/mod.ts";
export enum Tag{ end, byte, short, int, long, float, double, byteArray, string, list, compound, intArray, longArray };
type Pair<Tg extends Tag, Type> = {type: Tg, value: Type};
type List<T extends Tag> = T extends Tag.end ? Pair<Tag.list, []> & { listType: Tag.end } : (Pair<Tag.list, Types[T][]> & { listType: T }) | List<Tag.end>;
export type Types = {
[Tag.end]: Pair<Tag.end, 0>;
[Tag.byte]: Pair<Tag.byte, number>;
[Tag.short]: Pair<Tag.short, number>;
[Tag.int]: Pair<Tag.int, number>;
[Tag.long]: Pair<Tag.long, bigint>;
[Tag.float]: Pair<Tag.float, number>;
[Tag.double]: Pair<Tag.double, number>;
[Tag.byteArray]: Pair<Tag.byteArray, number[]>;
[Tag.string]: Pair<Tag.string, string>;
[Tag.list]: List<Tag>;
[Tag.compound]: Pair<Tag.compound, {[key: string]: undefined | Types[Tag]}>;
[Tag.intArray]: Pair<Tag.intArray, number[]>;
[Tag.longArray]: Pair<Tag.longArray, bigint[]>;
}
class NBTReader{
data: DataView;
offset: number = 0;
constructor(data: Uint8Array){
this.data = new DataView(data.buffer);
}
[Tag.end](): Types[Tag.end] {
return { value: 0, type: Tag.end };
}
[Tag.byte](): Types[Tag.byte] {
const value = this.data.getInt8(this.offset);
this.offset +=1 ;
return { value, type: Tag.byte };
}
[Tag.short](): Types[Tag.short] {
const value = this.data.getInt16(this.offset);
this.offset += 2;
return { value, type: Tag.short };
}
[Tag.int](): Types[Tag.int] {
const value = this.data.getInt32(this.offset);
this.offset += 4;
return { value, type: Tag.int };
}
[Tag.long](): Types[Tag.long] {
const value = this.data.getBigInt64(this.offset);
this.offset += 8;
return { value, type: Tag.long };
}
[Tag.float](): Types[Tag.float] {
const value = this.data.getFloat32(this.offset);
this.offset += 4;
return { value, type: Tag.float };
}
[Tag.double](): Types[Tag.double] {
const value = this.data.getFloat64(this.offset);
this.offset += 8;
return { value, type: Tag.double };
}
[Tag.byteArray](): Types[Tag.byteArray] {
const len = this[Tag.int]().value;
const value: number[] = [];
for(let i = 0; i < len; i++) value.push(this[Tag.byte]().value);
return { value, type: Tag.byteArray };
}
[Tag.string](): Types[Tag.string] {
const len = this[Tag.short]().value;
const slice = this.data.buffer.slice(this.offset, this.offset + len);
this.offset += len;
return { value: (new TextDecoder('utf-8')).decode(slice), type: Tag.string };
}
[Tag.list](): Types[Tag.list] {
const type: Tag = this[Tag.byte]().value;
if(!isValidTagType(type)) throw new Error(`Invalid Tag Type! type: ${type}`);
const len = this[Tag.int]().value;
const value: Types[Tag][] = [];
for(let i = 0; i < len; i++) {
const cur = this[type]();
value.push(cur);
}
return { value, type: Tag.list, listType: type } as Types[Tag.list];
}
[Tag.compound](): Types[Tag.compound] {
const tag: {[key: string]: Types[Tag]} = {};
while(true){
const type: Tag = this[Tag.byte]().value;
if(!isValidTagType(type)) throw new Error(`Invalid Tag Type! type: ${type}`);
if(type === Tag.end) break;
const key = this[Tag.string]().value;
const value = this[type]();
tag[key] = value;
}
return { value: tag, type: Tag.compound };
}
[Tag.intArray](): Types[Tag.intArray] {
const len = this[Tag.int]().value;
const value: number[] = [];
for(let i = 0; i < len; i++) value.push(this[Tag.int]().value);
return { value, type: Tag.intArray };
}
[Tag.longArray](): Types[Tag.longArray] {
const len = this[Tag.int]().value;
const value: bigint[] = [];
for(let i = 0; i < len; i++) value.push(this[Tag.long]().value);
return { value, type: Tag.longArray };
}
}
export const isValidTagType = (tag: number): tag is Tag => tag in Tag;
export const parse = (data: Uint8Array) => {
if(data[0] === 0x1f && data[1] === 0x8b) data = inflate(data);
const reader = new NBTReader(data);
const type: Tag = reader[Tag.byte]().value;
if(type !== Tag.compound) throw new Error('Top tag should be a compoud');
reader[Tag.string]();
return reader[Tag.compound]();
}
export type SimplifiedType<T extends Types[Tag]> =
T extends Types[Tag.list]
? T["value"] extends (infer R)[] ? R extends Types[Tag] ? SimplifiedType<R>[] : never : never
: T extends Types[Tag.compound]
? { [K in keyof T['value']]: SimplifiedType<T['value'][K] extends Types[Tag] ? T['value'][K] : never> }
: T['value'];
export const simplify = <T extends Types[Tag]>(tag: T): SimplifiedType<T> => {
if(tag.type === Tag.compound)
return Object.fromEntries(Object.entries(tag.value).filter(([_, value]) => !!value).map(([key, value]) => {
return [key, simplify(value as Types[Tag])]
})) as any as SimplifiedType<T>;
else if(tag.type === Tag.list)
return (tag as any).value.map((v: any) => simplify(v)) as any as SimplifiedType<T>;
else
return tag.value as any as SimplifiedType<T>;
}
type CPair<T extends Exclude<Tag, Tag.list>, V> = V extends Types[T]['value'] ? { type: T, value: V } : never;
type CList<T extends Tag, V> = T extends Tag.end ? List<Tag.end> : V extends Types[T] ? { type: Tag.list, listType: T, value: V[] } | List<Tag.end> : never;
export type Constant<T extends Exclude<Tag, Tag.compound | Tag.list>, V> = V extends Types[T]['value'] ? [T, V] : never;
export type Blueprint = { [x: string]: Blueprint } | Tag | Blueprint[] | [Exclude<Tag, Tag.list>, Types[Tag]['value']] | undefined;
export type Create<T extends Blueprint> =
T extends [infer A, infer B]
? A extends Exclude<Tag, Tag.list> ? CPair<A, B> : never
: T extends Blueprint[]
? CList<T extends (infer A)[] ? A extends Tag ? A : A extends [infer B, any] ? B : Tag.list : Tag.compound, Create<T extends (infer I)[] ? I extends Blueprint ? I : never : never>>
: T extends { [x: string]: Blueprint }
? CPair<Tag.compound, { [K in keyof T]: Create<T[K]> }>
: T extends Tag
? Types[T]
: T extends undefined
? undefined
: never