/* * ----------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): * wrote this file. As long as you retain this notice you * can do whatever you want with this stuff. If we meet some day, and you think * this stuff is worth it, you can buy me a beer in return. Lukas Niederbremer. * ----------------------------------------------------------------------------- */ #include #include #include #include #include "endianness.h" #include "nbt.h" /* Initialization subroutine(s) */ int nbt_init(nbt_file **nbt) { if ((*nbt = malloc(sizeof(nbt_file))) == NULL) return NBT_EMEM; (*nbt)->root = NULL; return NBT_OK; } /* Parser */ int nbt_parse(nbt_file *nbt, const char *filename) { int code = 0; if ((nbt->fp = gzopen(filename, "rb")) == Z_NULL) return NBT_EGZ; nbt->root = malloc(sizeof(nbt_tag)); if (nbt->root == NULL) return NBT_EMEM; code = nbt_read_tag(nbt, &(nbt->root)); gzclose(nbt->fp); if (code < 0) return code; if (nbt->root == NULL) return NBT_ERR; return NBT_OK; } int nbt_read_tag(nbt_file *nbt, nbt_tag **parent) { nbt_type type = 0; int code = 0; /* Read the type */ if (gzread(nbt->fp, &type, 1) == -1) return NBT_EGZ; (*parent)->type = type; (*parent)->name = NULL; (*parent)->value = NULL; if (type != TAG_END) /* TAG_END has no name */ code = nbt_read_string(nbt, &((*parent)->name)); /* negative value indicates error */ if (code < 0) return code; code = nbt_read(nbt, type, &((*parent)->value)); if (code < 0) return code; return type; } int nbt_read(nbt_file *nbt, nbt_type type, void **parent) { int code = 0; switch (type) { case TAG_END: break; case TAG_BYTE: code = nbt_read_byte(nbt, (char **)parent); break; case TAG_SHORT: code = nbt_read_short(nbt, (int16_t **)parent); break; case TAG_INT: code = nbt_read_int(nbt, (int32_t **)parent); break; case TAG_LONG: code = nbt_read_long(nbt, (int64_t **)parent); break; case TAG_FLOAT: code = nbt_read_float(nbt, (float **)parent); break; case TAG_DOUBLE: code = nbt_read_double(nbt, (double **)parent); break; case TAG_STRING: ;; /* to make it shut up about the variable declaration */ char *string = NULL; code = nbt_read_string(nbt, &string); *parent = string; break; case TAG_BYTE_ARRAY: ;; /* ... */ unsigned char *bytestring; int32_t len = nbt_read_byte_array(nbt, &bytestring); if (len < 0) { code = len; break; } nbt_byte_array *t = malloc(sizeof(nbt_byte_array)); t->length = len; t->content = bytestring; *parent = t; break; case TAG_LIST: ;; char type; void **target; int32_t length = nbt_read_list(nbt, &type, &target); if (length < 0) { code = length; break; } nbt_list *l = malloc(sizeof(nbt_list)); l->length = length; l->type = type; l->content = target; *parent = l; break; case TAG_COMPOUND: ;; nbt_compound *c = malloc(sizeof(nbt_compound)); nbt_tag **tags = NULL; int32_t lc = nbt_read_compound(nbt, &tags); if (lc < 0) { code = lc; break; } c->tags = tags; c->length = lc; *parent = c; } if (code < 0) return code; return type; /* Use to abort looping in TAG_Read_compound on TAG_END */ } int nbt_read_byte(nbt_file *nbt, char **out) { char t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; *out = malloc(sizeof(char)); memcpy(*out, &t, sizeof(char)); return 0; } int nbt_read_short(nbt_file *nbt, int16_t **out) { int16_t t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swaps((uint16_t *)&t); *out = malloc(sizeof(int16_t)); memcpy(*out, &t, sizeof(int16_t)); return 0; } int nbt_read_int(nbt_file *nbt, int32_t **out) { int32_t t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swapi((uint32_t *)&t); *out = malloc(sizeof(int32_t)); memcpy(*out, &t, sizeof(int32_t)); return 0; } int nbt_read_long(nbt_file *nbt, int64_t **out) { int64_t t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swapl((uint64_t *)&t); *out = malloc(sizeof(int64_t)); memcpy(*out, &t, sizeof(int64_t)); return 0; } int nbt_read_float(nbt_file *nbt, float **out) { float t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) t = swapf(t); *out = malloc(sizeof(float)); memcpy(*out, &t, sizeof(float)); return 0; } int nbt_read_double(nbt_file *nbt, double **out) { double t; if (gzread(nbt->fp, &t, sizeof(t)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) t = swapd(t); *out = malloc(sizeof(double)); memcpy(*out, &t, sizeof(double)); return 0; } int nbt_read_byte_array(nbt_file *nbt, unsigned char **out) { int32_t len; if (gzread(nbt->fp, &len, sizeof(len)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swapi((uint32_t *)&len); if (len < 0) return NBT_ERR; *out = malloc(len); if (gzread(nbt->fp, *out, len) == -1) { free(*out); return NBT_EGZ; } return len; } int nbt_read_string(nbt_file *nbt, char **out) { int16_t len; if (gzread(nbt->fp, &len, sizeof(len)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swaps((uint16_t *)&len); if (len < 0) return NBT_ERR; *out = malloc(len + 1); memset(*out, 0, len + 1); if (gzread(nbt->fp, *out, len) == -1) { free(*out); return NBT_EGZ; } return len; } int32_t nbt_read_list(nbt_file *nbt, char *type_out, void ***target) { char type; int32_t len; int i; if (gzread(nbt->fp, &type, 1) == -1) return NBT_EGZ; *type_out = type; if (type > TAG_MAX) return NBT_ERR; if (gzread(nbt->fp, &len, sizeof(len)) == -1) return NBT_EGZ; if (get_endianness() == L_ENDIAN) swapi((uint32_t *)&len); if (len < 0) return NBT_ERR; *target = malloc(len * sizeof(void *)); for (i = 0; i < len; ++i) if (nbt_read(nbt, type, &((*target)[i])) == -1) { free(*target); return NBT_EGZ; } return len; } int32_t nbt_read_compound(nbt_file *nbt, nbt_tag ***listptr) { int32_t i; *listptr = malloc(sizeof(nbt_tag *)); for (i = 0;; ++i) { (*listptr)[i] = malloc(sizeof(nbt_tag)); nbt_type last = nbt_read_tag(nbt, &((*listptr)[i])); *listptr = realloc(*listptr, sizeof(nbt_tag *) * (i+2)); if (last == TAG_END) { //(*listptr)[++i] = NULL; free((*listptr)[i]); /* This is an ugly, UGLY hack, let's remove this ASAP! */ break; } } return i; } /* Cleanup subroutines */ int nbt_free(nbt_file *nbt) { if (nbt->root != NULL) nbt_free_tag(nbt->root); free(nbt); return NBT_OK; } int nbt_free_tag(nbt_tag *t) { free(t->name); nbt_free_type(t->type, t->value); free(t); return 0; } int nbt_free_type(nbt_type type, void *payload) { if (payload == NULL) return 0; switch (type) { case TAG_BYTE: case TAG_SHORT: case TAG_INT: case TAG_LONG: case TAG_FLOAT: case TAG_DOUBLE: case TAG_STRING: free(payload); break; case TAG_LIST: nbt_free_list(payload); break; case TAG_BYTE_ARRAY: nbt_free_byte_array(payload); break; case TAG_COMPOUND: nbt_free_compound(payload); break; case TAG_END: /* Why the hell? */ return 1; } return 0; } int nbt_free_list(nbt_list *l) { int i; for (i = 0; i < l->length; ++i) nbt_free_type(l->type, l->content[i]); free(l->content); free(l); return 0; } int nbt_free_byte_array(nbt_byte_array *a) { free(a->content); free(a); return 0; } int nbt_free_compound(nbt_compound *c) { int i; for (i = 0; i < c->length; ++i) { free(c->tags[i]->name); nbt_free_type(c->tags[i]->type, c->tags[i]->value); free(c->tags[i]); } free(c->tags); free(c); return 0; } char *nbt_type_to_string(nbt_type t) { static char *str; switch (t) { case TAG_END: str = "TAG_END"; break; case TAG_BYTE: str = "TAG_BYTE"; break; case TAG_SHORT: str = "TAG_SHORT"; break; case TAG_INT: str = "TAG_INT"; break; case TAG_LONG: str = "TAG_LONG"; break; case TAG_FLOAT: str = "TAG_FLOAT"; break; case TAG_DOUBLE: str = "TAG_DOUBLE"; break; case TAG_BYTE_ARRAY: str = "TAG_BYTE_ARRAY"; break; case TAG_STRING: str = "TAG_STRING"; break; case TAG_LIST: str = "TAG_LIST"; break; case TAG_COMPOUND: str = "TAG_COMPOUND"; break; default: str = "TAG_Unknown"; break; } return str; } void nbt_print_tag(nbt_tag *t, int indent) { if (t->type == TAG_END) return; nbt_print_indent(indent); printf("%s(\"%s\"): ", nbt_type_to_string(t->type), t->name); nbt_print_value(t->type, t->value, indent); } void nbt_print_indent(int lv) { int i = 0; for (i = 0; i < lv; ++i) printf(" "); return; } void nbt_print_value(nbt_type t, void *v, int n) { int i; int indent = n; char type = (char)t; //printf("%s", indentation); switch (type) { case TAG_BYTE: printf("0x%02X (%d)", *((char *)v), *((char *)v)); break; case TAG_SHORT: printf("%d", *((int16_t *)v)); break; case TAG_INT: ;; long t = *((int32_t *)v); printf("%ld", t); break; case TAG_LONG: ;; long long tl = *((int64_t *)v); printf("%lld", tl); break; case TAG_FLOAT: printf("%f", *((float *)v)); break; case TAG_DOUBLE: printf("%f", *((double *)v)); break; case TAG_STRING: printf("\"%s\"", (char *)v); break; case TAG_BYTE_ARRAY: ;; nbt_byte_array *arr = (nbt_byte_array *)v; nbt_print_byte_array(arr->content, arr->length); break; case TAG_COMPOUND: ;; nbt_compound *c = (nbt_compound *)v; printf("(%d entries) { \n", c->length); indent++; for (i = 0; i < c->length; ++i) nbt_print_tag(c->tags[i], indent); nbt_print_indent(--indent); printf("}\n"); break; case TAG_LIST: ;; nbt_list *l = (nbt_list *)v; printf("(%d entries) { \n", l->length); indent++; for (i = 0; i < l->length; ++i) { nbt_print_indent(indent); printf("%s: ", nbt_type_to_string(l->type)); void **content = l->content; nbt_print_value(l->type, content[i], indent); } nbt_print_indent(--indent); printf("}\n"); break; default: printf("", type); } printf("\n"); return; } void nbt_print_byte_array(unsigned char *ba, int len) { int i; printf("(%d entries) [", len); for (i = 0; i < len; ++i) { printf("%02X", ba[i]); if (i == (len - 1)) printf(" "); else printf(", "); } printf("]"); return; } int nbt_change_value(nbt_tag *tag, void *val, size_t size) { void *t = malloc(size); if (t != NULL) { nbt_free_type(tag->type, tag->value); memcpy(t, val, size); tag->value = t; return 0; } return 1; } int nbt_change_name(nbt_tag *tag, const char *newname) { char *tmp = malloc(strlen(newname) + 1); if (tmp != NULL) { strcpy(tmp, newname); free(tag->name); tag->name = tmp; return 0; } return 1; } void nbt_add_list_item(void *item, nbt_tag *parent) { nbt_list *l = NULL; if (parent->type != TAG_LIST) return; l = nbt_cast_list(parent); if ((l->content = realloc(l->content, sizeof(void *) * (l->length + 1))) != NULL) { l->content[l->length++] = item; } return; } nbt_tag *nbt_add_tag(nbt_tag *child, nbt_tag *parent) { nbt_compound *c = NULL; if (parent->type != TAG_COMPOUND) return NULL; c = nbt_cast_compound(parent); nbt_tag **tags_temp = NULL; tags_temp = realloc(c->tags, sizeof(nbt_tag *) * (c->length + 1)); if (tags_temp != NULL) { c->tags = tags_temp; c->length++; c->tags[c->length - 1] = child; return child; } return NULL; } void nbt_remove_tag(nbt_tag *target, nbt_tag *parent) { int i; int count = 0; nbt_tag **templist = NULL; nbt_compound *tmp = (nbt_compound *)parent->value; if (parent->type != TAG_COMPOUND) return; templist = malloc(sizeof(nbt_tag *)); for (i = 0; i < tmp->length; ++i) { if (tmp->tags[i] != target) { templist[count] = tmp->tags[i]; templist = realloc(templist, sizeof(nbt_tag *) * (count+2)); ++count; } else { nbt_free_tag(tmp->tags[i]); } } free(tmp->tags); tmp->tags = templist; tmp->length = count; return; } void nbt_remove_list_item(void *target, nbt_tag *parent) { if (parent->type == TAG_LIST) { nbt_list *target_list = nbt_cast_list(parent); void **new_list = malloc(sizeof(void *) * target_list->length); if (new_list != NULL) { int i, j = 0; memset(new_list, 0, sizeof(void *) * target_list->length); for (i = 0; i < target_list->length; ++i) if (target_list->content[i] != target) new_list[j++] = target_list->content[i]; nbt_set_list(parent, new_list, j, target_list->type); free(new_list); } } return; } nbt_tag *nbt_find_tag_by_name(const char *needle, nbt_tag *haystack) { if (haystack->type == TAG_COMPOUND) { nbt_compound *c = (nbt_compound *)haystack->value; int i; for (i = 0; i < c->length; ++i) if (strcmp(c->tags[i]->name, needle) == 0) return c->tags[i]; } return NULL; } int nbt_write(nbt_file *nbt, const char *filename) { if ((nbt->fp = gzopen(filename, "wb")) == Z_NULL) return NBT_EGZ; if (nbt->root != NULL) { int size = nbt_write_tag(nbt, nbt->root); gzclose(nbt->fp); return size; } return NBT_ERR; } int nbt_write_tag(nbt_file *nbt, nbt_tag *tag) { int size = 0; size += gzwrite(nbt->fp, &(tag->type), sizeof(char)); if (tag->type != TAG_END) { /* Every tag but TAG_END has a name */ size += nbt_write_string(nbt, tag->name); size += nbt_write_value(nbt, tag->type, tag->value); } return size; } int nbt_write_value(nbt_file *nbt, nbt_type t, void *value) { int written = 0; switch (t) { case TAG_END: /* WHY is this even in? */ break; case TAG_BYTE: written = nbt_write_byte(nbt, (char *)value); break; case TAG_SHORT: written = nbt_write_short(nbt, (int16_t *)value); break; case TAG_INT: written = nbt_write_int(nbt, (int32_t *)value); break; case TAG_LONG: written = nbt_write_long(nbt, (int64_t *)value); break; case TAG_FLOAT: written = nbt_write_float(nbt, (float *)value); break; case TAG_DOUBLE: written = nbt_write_double(nbt, (double *)value); break; case TAG_STRING: written = nbt_write_string(nbt, (char *)value); break; case TAG_BYTE_ARRAY: written = nbt_write_byte_array(nbt, (nbt_byte_array *)value); break; case TAG_LIST: written = nbt_write_list(nbt, (nbt_list *)value); break; case TAG_COMPOUND: written = nbt_write_compound(nbt, (nbt_compound *)value); break; default: /* Maybe moan about a very unknown tag? Not yet... */ break; } return written; } int nbt_write_byte(nbt_file *nbt, char *val) { /* bytes, simple enough */ return gzwrite(nbt->fp, val, sizeof(char)); } int nbt_write_short(nbt_file *nbt, int16_t *val) { int16_t temp = *val; /* Needs swapping first? */ if (get_endianness() == L_ENDIAN) swaps((uint16_t *)&temp); return gzwrite(nbt->fp, &temp, sizeof(int16_t)); } int nbt_write_int(nbt_file *nbt, int32_t *val) { int32_t temp = *val; if (get_endianness() == L_ENDIAN) swapi((uint32_t *)&temp); return gzwrite(nbt->fp, &temp, sizeof(int32_t)); } int nbt_write_long(nbt_file *nbt, int64_t *val) { int64_t temp = *val; if (get_endianness() == L_ENDIAN) swapl((uint64_t *)&temp); return gzwrite(nbt->fp, &temp, sizeof(int64_t)); } int nbt_write_float(nbt_file *nbt, float *val) { float temp = *val; if (get_endianness() == L_ENDIAN) temp = swapf(temp); return gzwrite(nbt->fp, &temp, sizeof(float)); } int nbt_write_double(nbt_file *nbt, double *val) { double temp = *val; if (get_endianness() == L_ENDIAN) temp = swapd(temp); return gzwrite(nbt->fp, &temp, sizeof(double)); } int nbt_write_string(nbt_file *nbt, char *val) { int size = 0; int16_t len = strlen(val); /* Write length first */ size += nbt_write_short(nbt, &len); /* Write content */ size += gzwrite(nbt->fp, val, len); return size; } int nbt_write_byte_array(nbt_file *nbt, nbt_byte_array *val) { int size = 0; /* Length first again, then content */ size += nbt_write_int(nbt, &(val->length)); size += gzwrite(nbt->fp, val->content, val->length); return size; } int nbt_write_list(nbt_file *nbt, nbt_list *val) { int i; int size = 0; /* Write type id first */ size += nbt_write_byte(nbt, (char *)&(val->type)); size += nbt_write_int(nbt, &(val->length)); for (i = 0; i < val->length; ++i) size += nbt_write_value(nbt, val->type, val->content[i]); return size; } int nbt_write_compound(nbt_file *nbt, nbt_compound *val) { int endtag = 0; int i; int size = 0; for (i = 0; i < val->length; ++i) size += nbt_write_tag(nbt, val->tags[i]); size += gzwrite(nbt->fp, &endtag, sizeof(char)); return size; } char *nbt_cast_byte(nbt_tag *t) { if (t->type != TAG_BYTE) return NULL; return (char *)t->value; } int16_t *nbt_cast_short(nbt_tag *t) { if (t->type != TAG_SHORT) return NULL; return (int16_t *)t->value; } int32_t *nbt_cast_int(nbt_tag *t) { if (t->type != TAG_INT) return NULL; return (int32_t *)t->value; } int64_t *nbt_cast_long(nbt_tag *t) { if (t->type != TAG_LONG) return NULL; return (int64_t *)t->value; } float *nbt_cast_float(nbt_tag *t) { if (t->type != TAG_FLOAT) return NULL; return (float *)t->value; } double *nbt_cast_double(nbt_tag *t) { if (t->type != TAG_DOUBLE) return NULL; return (double *)t->value; } char *nbt_cast_string(nbt_tag *t) { if (t->type != TAG_STRING) return NULL; return (char *)t->value; } nbt_list *nbt_cast_list(nbt_tag *t) { if (t->type != TAG_LIST) return NULL; return (nbt_list *)t->value; } nbt_byte_array *nbt_cast_byte_array(nbt_tag *t) { if (t->type != TAG_BYTE_ARRAY) return NULL; return (nbt_byte_array *)t->value; } nbt_compound *nbt_cast_compound(nbt_tag *t) { if (t->type != TAG_COMPOUND) return NULL; return (nbt_compound *)t->value; } int nbt_set_byte(nbt_tag *t, char v) { if (t->type != TAG_BYTE) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_short(nbt_tag *t, int16_t v) { if (t->type != TAG_SHORT) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_int(nbt_tag *t, int32_t v) { if (t->type != TAG_INT) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_long(nbt_tag *t, int64_t v) { if (t->type != TAG_LONG) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_float(nbt_tag *t, float v) { if (t->type != TAG_FLOAT) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_double(nbt_tag *t, double v) { if (t->type != TAG_DOUBLE) return 1; return nbt_change_value(t, &v, sizeof(v)); } int nbt_set_string(nbt_tag *t, char *v) { if (t->type != TAG_STRING) return 1; return nbt_change_value(t, v, strlen(v) + 1); } int nbt_set_list(nbt_tag *t, void **v, int len, nbt_type type) { nbt_list temp; if (t->type != TAG_LIST) return 1; temp.type = type; temp.length = len; temp.content = malloc(sizeof(void *) * len); if (temp.content == NULL) return 1; memcpy(temp.content, v, sizeof(void *) * len); return nbt_change_value(t, &temp, sizeof(temp)); } int nbt_set_byte_array(nbt_tag *t, unsigned char *v, int len) { nbt_byte_array temp; if (t->type != TAG_BYTE_ARRAY) return 1; temp.length = len; temp.content = malloc(sizeof(unsigned char) * len); if (temp.content == NULL) return 1; memcpy(temp.content, v, len); return nbt_change_value(t, &temp, sizeof(temp)); } int nbt_set_compound(nbt_tag *t, nbt_tag *tags, int len) { nbt_compound temp; if (t->type != TAG_COMPOUND) return 1; temp.length = len; temp.tags = malloc(sizeof(nbt_tag *) * len); if (temp.tags == NULL) return 1; memcpy(temp.tags, tags, len); return nbt_change_value(t, &temp, sizeof(temp)); } int nbt_get_length(nbt_tag *t) { if (t->type == TAG_BYTE_ARRAY) { nbt_byte_array *ba = nbt_cast_byte_array(t); if (ba != NULL) return ba->length; } else if (t->type == TAG_LIST) { nbt_list *l = nbt_cast_list(t); if (l != NULL) return l->length; } else if (t->type == TAG_COMPOUND) { nbt_compound *c = nbt_cast_compound(t); if (c != NULL) return c->length; } return -1; } int nbt_get_list_type(nbt_tag *t) { nbt_list *l = NULL; if (t->type != TAG_LIST) return NBT_ERR; l = nbt_cast_list(t); return l->type; } int nbt_new_tag(nbt_tag **d, nbt_type t, const char *name) { *d = malloc(sizeof(nbt_tag)); if (*d == NULL) return -1; (*d)->type = t; (*d)->value = NULL; (*d)->name = NULL; if (nbt_change_name(*d, name) != 0) return -1; return 0; } int nbt_new_byte(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_BYTE, name) != 0) return -1; return nbt_set_byte(*d, 0); } int nbt_new_short(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_SHORT, name) != 0) return -1; return nbt_set_short(*d, 0); } int nbt_new_int(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_INT, name) != 0) return -1; return nbt_set_int(*d, 0); } int nbt_new_long(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_LONG, name) != 0) return -1; return nbt_set_long(*d, 0); } int nbt_new_float(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_FLOAT, name) != 0) return -1; return nbt_set_float(*d, 0.0); } int nbt_new_double(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_DOUBLE, name) != 0) return -1; return nbt_set_double(*d, 0.0); } int nbt_new_string(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_STRING, name) != 0) return -1; return nbt_set_string(*d, ""); } int nbt_new_byte_array(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_BYTE_ARRAY, name) != 0) return -1; return nbt_set_byte_array(*d, NULL, 0); } int nbt_new_list(nbt_tag **d, const char *name, nbt_type type) { if (nbt_new_tag(d, TAG_LIST, name) != 0) return -1; return nbt_set_list(*d, NULL, 0, type); } int nbt_new_compound(nbt_tag **d, const char *name) { if (nbt_new_tag(d, TAG_COMPOUND, name) != 0) return -1; return nbt_set_compound(*d, NULL, 0); }