Skip to content

Commit

Permalink
Read Reviver
Browse files Browse the repository at this point in the history
I still need to implement `this` calling.

```js
JSON.parse('{ "heya": ["nice", "2342"] }', (key, value) => { console.log(key, value); return Array.isArray(value) && value[0] === "nice" ? BigInt(value[1]) : value });
```

#33
  • Loading branch information
Offroaders123 committed Nov 24, 2024
1 parent d8ffeea commit dd33301
Showing 1 changed file with 22 additions and 20 deletions.
42 changes: 22 additions & 20 deletions src/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ export interface ReadOptions {
rootCheck: boolean;
}

export type Reviver = (this: any, key: any, value: any) => Tag;

/**
* Converts an NBT buffer into an NBT object. Accepts an endian type, compression format, and file headers to read the data with.
*
* If a format option isn't specified, the function will attempt reading the data using all options until it either throws or returns successfully.
*/
export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | ArrayBufferLike | Blob, options: Partial<ReadOptions> = {}): Promise<NBTData<T>> {
export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | ArrayBufferLike | Blob, options: Partial<ReadOptions> = {}, reviver?: Reviver): Promise<NBTData<T>> {
if (data instanceof Blob) {
data = await data.arrayBuffer();
}
Expand Down Expand Up @@ -65,10 +67,10 @@ export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | A
case reader.hasZlibHeader(): compression = "deflate"; break compression;
}
try {
return await read<T>(data, { ...options, compression: null });
return await read<T>(data, { ...options, compression: null }, reviver);
} catch (error) {
try {
return await read<T>(data, { ...options, compression: "deflate-raw" });
return await read<T>(data, { ...options, compression: "deflate-raw" }, reviver);
} catch {
throw error;
}
Expand All @@ -79,13 +81,13 @@ export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | A

if (endian === undefined) {
try {
return await read<T>(data, { ...options, endian: "big" });
return await read<T>(data, { ...options, endian: "big" }, reviver);
} catch (error) {
try {
return await read<T>(data, { ...options, endian: "little" });
return await read<T>(data, { ...options, endian: "little" }, reviver);
} catch {
try {
return await read<T>(data, { ...options, endian: "little-varint" });
return await read<T>(data, { ...options, endian: "little-varint" }, reviver);
} catch {
throw error;
}
Expand All @@ -97,10 +99,10 @@ export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | A

if (rootName === undefined) {
try {
return await read<T>(data, { ...options, rootName: true });
return await read<T>(data, { ...options, rootName: true }, reviver);
} catch (error) {
try {
return await read<T>(data, { ...options, rootName: false });
return await read<T>(data, { ...options, rootName: false }, reviver);
} catch {
throw error;
}
Expand All @@ -117,7 +119,7 @@ export async function read<T extends RootTagLike = RootTag>(data: Uint8Array | A
bedrockLevel = reader.hasBedrockLevelHeader(endian);
}

return reader.readRoot<T>({ rootName, endian, compression, bedrockLevel, strict, rootCheck });
return reader.readRoot<T>({ rootName, endian, compression, bedrockLevel, strict, rootCheck }, reviver);
}

class NBTReader {
Expand Down Expand Up @@ -157,7 +159,7 @@ class NBTReader {
}
}

async readRoot<T extends RootTagLike = RootTag>({ rootName, endian, compression, bedrockLevel, strict, rootCheck }: ReadOptions): Promise<NBTData<T>> {
async readRoot<T extends RootTagLike = RootTag>({ rootName, endian, compression, bedrockLevel, strict, rootCheck }: ReadOptions, reviver: Reviver = (_key, value) => value): Promise<NBTData<T>> {
if (compression !== null) {
this.#data = await decompress(this.#data, compression);
this.#view = new DataView(this.#data.buffer);
Expand All @@ -179,7 +181,7 @@ class NBTReader {
throw new Error(`Expected root name '${rootName}', encountered '${rootNameV}'`);
}

const root: T = this.#readTag<T>(type);
const root: T = reviver("", this.#readTag<T>(type, reviver)) as T;

if (strict && this.#data.byteLength > this.#byteOffset) {
const remaining: number = this.#data.byteLength - this.#byteOffset;
Expand All @@ -195,9 +197,9 @@ class NBTReader {
return result;
}

#readTag<T extends Tag>(type: TAG): T;
#readTag<T extends RootTagLike>(type: TAG): T;
#readTag(type: TAG): Tag {
#readTag<T extends Tag>(type: TAG, reviver: Reviver): T;
#readTag<T extends RootTagLike>(type: TAG, reviver: Reviver): T;
#readTag(type: TAG, reviver: Reviver): Tag {
switch (type) {
case TAG.END: {
const remaining: number = this.#data.byteLength - this.#byteOffset;
Expand All @@ -211,8 +213,8 @@ class NBTReader {
case TAG.DOUBLE: return this.#readDouble();
case TAG.BYTE_ARRAY: return this.#readByteArray();
case TAG.STRING: return this.#readString();
case TAG.LIST: return this.#readList();
case TAG.COMPOUND: return this.#readCompound();
case TAG.LIST: return this.#readList(reviver);
case TAG.COMPOUND: return this.#readCompound(reviver);
case TAG.INT_ARRAY: return this.#readIntArray();
case TAG.LONG_ARRAY: return this.#readLongArray();
default: throw new Error(`Encountered unsupported tag type '${type}' at byte offset ${this.#byteOffset}`);
Expand Down Expand Up @@ -363,7 +365,7 @@ class NBTReader {
return value;
}

#readList(): ListTag<Tag> {
#readList(reviver: Reviver): ListTag<Tag> {
const type: TAG = this.#readTagType();
const length: number = this.#varint ? this.#readVarIntZigZag(true) : this.#readInt(true);
const value: ListTag<Tag> = [];
Expand All @@ -374,19 +376,19 @@ class NBTReader {
value: type
});
for (let i: number = 0; i < length; i++) {
const entry: Tag = this.#readTag(type);
const entry: Tag = reviver(i, this.#readTag(type, reviver));
value.push(entry);
}
return value;
}

#readCompound(): CompoundTag {
#readCompound(reviver: Reviver): CompoundTag {
const value: CompoundTag = {};
while (true) {
const type: TAG = this.#readTagType();
if (type === TAG.END) break;
const name: string = this.#readString();
const entry: Tag = this.#readTag(type);
const entry: Tag = reviver(name, this.#readTag(type, reviver));
value[name] = entry;
}
return value;
Expand Down

0 comments on commit dd33301

Please sign in to comment.