forked from mirrors/NBTExplorer
1340 lines
26 KiB
C
1340 lines
26 KiB
C
/*
|
|
* -----------------------------------------------------------------------------
|
|
* "THE BEER-WARE LICENSE" (Revision 42):
|
|
* <webmaster@flippeh.de> 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 <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <zlib.h>
|
|
#include <string.h>
|
|
|
|
#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("<not implemented: 0x%02X>", 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);
|
|
}
|