mirror of
https://github.com/thepigeongenerator/mcaselector-lite.git
synced 2025-12-17 11:05:45 +01:00
138 lines
3.4 KiB
C
138 lines
3.4 KiB
C
// Copyright (c) 2025 Quinn
|
|
// Licensed under the MIT Licence. See LICENSE for details
|
|
#include "nbt.h"
|
|
|
|
#include <assert.h>
|
|
#include <stddef.h>
|
|
#include <string.h>
|
|
|
|
#include "../util/compat/endian.h"
|
|
#include "../util/intdef.h"
|
|
|
|
#define MAX_DEPTH 512
|
|
|
|
/* TODO: write test cases for this function:
|
|
* - list:compound...
|
|
* - non-existent type
|
|
* - compound:list:int32
|
|
* - string
|
|
*/
|
|
const u8 *nbt_nexttag(const u8 *restrict buf) {
|
|
const u8 *tag, *ptr;
|
|
u8 tags[MAX_DEPTH] = {0};
|
|
i32 lens[MAX_DEPTH] = {0};
|
|
uint dpt = 0;
|
|
u8 type;
|
|
|
|
tag = buf;
|
|
do {
|
|
ptr = tag;
|
|
if (lens[dpt]) {
|
|
type = tags[dpt];
|
|
lens[dpt]--;
|
|
dpt -= !lens[dpt];
|
|
} else {
|
|
type = *tag;
|
|
ptr += be16toh(*(u16 *)(tag + 1)) + 3;
|
|
}
|
|
|
|
switch (type) {
|
|
case NBT_I8: ptr += 1; break;
|
|
case NBT_I16: ptr += 2; break;
|
|
case NBT_I32: // fall through
|
|
case NBT_F32: ptr += 4; break;
|
|
case NBT_I64: // fall through
|
|
case NBT_F64: ptr += 8; break;
|
|
|
|
case NBT_ARR_I8: ptr += 4 + (i32)be32toh(*(u32 *)ptr) * 1; break;
|
|
case NBT_ARR_I32: ptr += 4 + (i32)be32toh(*(u32 *)ptr) * 4; break;
|
|
case NBT_ARR_I64: ptr += 4 + (i32)be32toh(*(u32 *)ptr) * 8; break;
|
|
case NBT_STR: ptr += 2 + (u16)be16toh(*(u16 *)ptr) * 1; break;
|
|
|
|
case NBT_END: dpt--; break;
|
|
case NBT_COMPOUND: dpt += (tags[dpt] && *tag) ? 1 : -1; break;
|
|
|
|
|
|
case NBT_LIST: {
|
|
tag = ptr; // temporarily store the tag to cache later
|
|
switch (*(ptr++)) {
|
|
case NBT_END: break;
|
|
case NBT_I8: ptr += 1 * (i32)be32toh(*(u32 *)ptr); break;
|
|
case NBT_I16: ptr += 2 * (i32)be32toh(*(u32 *)ptr); break;
|
|
case NBT_I32: // fall through
|
|
case NBT_F32: ptr += 4 * (i32)be32toh(*(u32 *)ptr); break;
|
|
case NBT_I64: // fall through
|
|
case NBT_F64: ptr += 8 * (i32)be32toh(*(u32 *)ptr); break;
|
|
default:
|
|
// TODO: handle out of bounds... Might not be required if we use flexible array member
|
|
dpt++;
|
|
tags[dpt] = *tag;
|
|
lens[dpt] = (i32)be32toh(*(u32 *)ptr);
|
|
break;
|
|
}
|
|
ptr += 4;
|
|
break;
|
|
}
|
|
|
|
default: return NULL; // unexpected value; buffer is likely corrupt
|
|
}
|
|
|
|
tag = ptr;
|
|
} while (dpt > 0);
|
|
return tag;
|
|
}
|
|
|
|
// TODO: not actually doing anything
|
|
/* readies the output data for export, returns the new buffer position, or `NULL` upon an error (may be out of bounds) */
|
|
static const u8 *nbt_proctag(const u8 *restrict buf, u16 slen) {
|
|
const u8 *ptr = buf + 3 + slen;
|
|
u8 dat[8];
|
|
size_t arrlen = 0;
|
|
|
|
switch (*buf) {
|
|
// integral types
|
|
case NBT_I8: *dat = *ptr; return ptr + 1;
|
|
case NBT_I16: *(u16 *)dat = be16toh(*(u16 *)ptr); return ptr + 2;
|
|
case NBT_I32: // fall through
|
|
case NBT_F32: *(u32 *)dat = be16toh(*(u32 *)ptr); return ptr + 4;
|
|
case NBT_I64: // fall through
|
|
case NBT_F64: *(u64 *)dat = be16toh(*(u64 *)ptr); return ptr + 8;
|
|
|
|
// arrays, handled differently
|
|
case NBT_LIST:
|
|
case NBT_ARR_I8:
|
|
case NBT_STR:
|
|
case NBT_ARR_I32:
|
|
case NBT_ARR_I64:
|
|
// TODO: arrlen = nbt_arrbsize(ptr);
|
|
break;
|
|
|
|
default: return NULL;
|
|
}
|
|
if (!arrlen) return NULL;
|
|
return ptr; // TODO: return end of array
|
|
}
|
|
|
|
struct nbt_procdat nbt_initproc(struct nbt_path const *restrict pats, uint npats) {
|
|
i16 mdpt = 0;
|
|
|
|
// acquire the maximum depth that we'll need to go (exclusive)
|
|
for (uint i = 0; i < npats; i++) {
|
|
int tmp = pats[i].len - mdpt;
|
|
mdpt += -(tmp > 0) & tmp;
|
|
}
|
|
assert(mdpt > 0);
|
|
|
|
// storing the segments of the current path
|
|
const char **cpat = (const char **)calloc(mdpt - 1, sizeof(void *));
|
|
|
|
// return the initialised structure.
|
|
return (struct nbt_procdat){
|
|
pats,
|
|
cpat,
|
|
npats,
|
|
0,
|
|
mdpt,
|
|
};
|
|
}
|