| /* * ----------------------------------------------------------------------------- |
| * "THE BEER-WARE LICENSE" (Revision 42): |
| * Lukas Niederbremer <webmaster@flippeh.de> and Clark Gaebel <cg.wowus.cg@gmail.com> |
| * 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 us a beer in return. |
| * ----------------------------------------------------------------------------- |
| */ |
| |
| #ifndef NBT_H |
| #define NBT_H |
| |
| #ifdef __cplusplus |
| #define restrict __restrict__ |
| extern "C" { |
| #endif |
| |
| #include <stdbool.h> |
| #include <stddef.h> /* for size_t */ |
| #include <stdint.h> |
| #include <stdio.h> /* for FILE* */ |
| |
| #include "buffer.h" /* for struct buffer */ |
| #include "list.h" /* For struct list_entry etc. */ |
| |
| typedef enum { |
| NBT_OK = 0, /* No error. */ |
| NBT_ERR = -1, /* Generic error, most likely of the parsing variety. */ |
| NBT_EMEM = -2, /* Out of memory. */ |
| NBT_EIO = -3, /* IO error. */ |
| NBT_EZ = -4 /* Zlib compression/decompression error. */ |
| } nbt_status; |
| |
| typedef enum { |
| TAG_INVALID = 0, /* tag_end, but we don't use it in the in-memory representation. */ |
| TAG_BYTE = 1, /* char, 8 bits, signed */ |
| TAG_SHORT = 2, /* short, 16 bits, signed */ |
| TAG_INT = 3, /* long, 32 bits, signed */ |
| TAG_LONG = 4, /* long long, 64 bits, signed */ |
| TAG_FLOAT = 5, /* float, 32 bits, signed */ |
| TAG_DOUBLE = 6, /* double, 64 bits, signed */ |
| TAG_BYTE_ARRAY = 7, /* char *, 8 bits, unsigned, TAG_INT length */ |
| TAG_STRING = 8, /* char *, 8 bits, signed, TAG_SHORT length */ |
| TAG_LIST = 9, /* X *, X bits, TAG_INT length, no names inside */ |
| TAG_COMPOUND = 10, /* nbt_tag * */ |
| TAG_INT_ARRAY = 11, /* long *, 32 bits, signed, TAG_INT length */ |
| TAG_LONG_ARRAY = 12 /* long long *, 64 bits, signed, TAG_INT length */ |
| |
| } nbt_type; |
| |
| typedef enum { |
| STRAT_GZIP, /* Use a gzip header. Use this if you want your data to be |
| compressed like level.dat */ |
| |
| STRAT_INFLATE /* Use a zlib header. Use this if you want your data to be |
| compressed like a chunk. */ |
| } nbt_compression_strategy; |
| |
| struct nbt_node; |
| |
| /* |
| * Represents a single node in the tree. You should switch on `type' and ONLY |
| * access the union member it signifies. tag_compound and tag_list contain |
| * recursive nbt_node entries, so those will have to be switched on too. I |
| * recommended being VERY comfortable with recursion before traversing this |
| * beast, or at least sticking to the library routines provided. |
| */ |
| typedef struct nbt_node { |
| nbt_type type; |
| char* name; /* This may be NULL. Check your damn pointers. */ |
| |
| union { /* payload */ |
| |
| /* tag_end has no payload */ |
| int8_t tag_byte; |
| int16_t tag_short; |
| int32_t tag_int; |
| int64_t tag_long; |
| float tag_float; |
| double tag_double; |
| |
| struct nbt_byte_array { |
| unsigned char* data; |
| int32_t length; |
| } tag_byte_array; |
| |
| struct nbt_int_array { |
| int32_t* data; |
| int32_t length; |
| } tag_int_array; |
| |
| struct nbt_long_array { |
| int64_t* data; |
| int32_t length; |
| } tag_long_array; |
| |
| char* tag_string; /* TODO: technically, this should be a UTF-8 string */ |
| |
| /* |
| * Design addendum: we make tag_list a linked list instead of an array |
| * so that nbt_node can be a true recursive data structure. If we used |
| * an array, it would be incorrect to call free() on any element except |
| * the first one. By using a linked list, the context of the node is |
| * irrelevant. One tradeoff of this design is that we don't get tight |
| * list packing when memory is a concern and huge lists are created. |
| * |
| * For more information on using the linked list, see `list.h'. The API |
| * is well documented. |
| */ |
| struct nbt_list { |
| struct nbt_node* data; /* A single node's data. */ |
| struct list_head entry; |
| } * tag_list, |
| * tag_compound; |
| |
| /* |
| * The primary difference between a tag_list and a tag_compound is the |
| * use of the first (sentinel) node. |
| * |
| * In an nbt_list, the sentinel node contains a valid data pointer with |
| * only the type filled in. This is to deal with empty lists which |
| * still posess types. Therefore, the sentinel's data pointer must be |
| * deallocated. |
| * |
| * In the tag_compound, the only use of the sentinel is to get the |
| * beginning and end of the doubly linked list. The data pointer is |
| * unused and set to NULL. |
| */ |
| } payload; |
| } nbt_node; |
| |
| /***** High Level Loading/Saving Functions *****/ |
| |
| /* |
| * Loads a NBT tree from a compressed file. The file must have been opened with |
| * a mode of "rb". If an error occurs, NULL will be returned and errno will be |
| * set to the appropriate nbt_status. Check your danm pointers. |
| */ |
| nbt_node* nbt_parse_file(FILE* fp); |
| |
| /* |
| * The same as nbt_parse_file, but opens and closes the file for you. |
| */ |
| nbt_node* nbt_parse_path(const char* filename); |
| |
| /* |
| * Loads a NBT tree from a compressed block of memory (such as a chunk or a |
| * pre-loaded level.dat). If an error occurs, NULL will be returned and errno |
| * will be set to the appropriate nbt_status. Check your damn pointers. |
| * |
| * PROTIP: Memory map each individual region file, then call |
| * nbt_parse_compressed for chunks as needed. |
| */ |
| nbt_node* nbt_parse_compressed(const void* chunk_start, size_t length); |
| |
| /* |
| * Dumps a tree into a file. Check your damn error codes. This function should |
| * return NBT_OK. |
| * |
| * @see nbt_compression_strategy |
| */ |
| nbt_status nbt_dump_file(const nbt_node* tree, |
| FILE* fp, nbt_compression_strategy); |
| |
| /* |
| * Dumps a tree into a block of memory. If an error occurs, a buffer with a NULL |
| * `data' pointer will be returned, and errno will be set. |
| * |
| * 1) Check your damn pointers. |
| * 2) Don't forget to free buf->data. Memory leaks are bad, mkay? |
| * |
| * @see nbt_compression_strategy |
| */ |
| struct buffer nbt_dump_compressed(const nbt_node* tree, |
| nbt_compression_strategy); |
| |
| /***** Low Level Loading/Saving Functions *****/ |
| |
| /* |
| * Loads a NBT tree from memory. The tree MUST NOT be compressed. If an error |
| * occurs, NULL will be returned, and errno will be set to the appropriate |
| * nbt_status. Please check your damn pointers. |
| */ |
| nbt_node* nbt_parse(const void* memory, size_t length); |
| |
| /* |
| * Returns a NULL-terminated string as the ascii representation of the tree. If |
| * an error occurs, NULL will be returned and errno will be set. |
| * |
| * 1) Check your damn pointers. |
| * 2) Don't forget to free the returned pointer. Memory leaks are bad, mkay? |
| */ |
| char* nbt_dump_ascii(const nbt_node* tree); |
| |
| /* |
| * Returns a buffer representing the uncompressed tree in Notch's official |
| * binary format. Trees dumped with this function can be regenerated with |
| * nbt_parse. If an error occurs, a buffer with a NULL `data' pointer will be |
| * returned, and errno will be set. |
| * |
| * 1) Check your damn pointers. |
| * 2) Don't forget to free buf->data. Memory leaks are bad, mkay? |
| */ |
| struct buffer nbt_dump_binary(const nbt_node* tree); |
| |
| /***** Tree Manipulation Functions *****/ |
| |
| /* |
| * Clones an existing tree. Returns NULL on memory errors. |
| */ |
| nbt_node* nbt_clone(nbt_node*); |
| |
| /* |
| * Recursively deallocates a node and all its children. If this is used on a an |
| * entire tree, no memory will be leaked. |
| */ |
| void nbt_free(nbt_node*); |
| |
| /* |
| * Recursively frees all the elements of a list, and then frees the list itself. |
| */ |
| void nbt_free_list(struct nbt_list*); |
| |
| /* |
| * A visitor function to traverse the tree. Return true to keep going, false to |
| * stop. `aux' is an optional parameter which will be passed to your visitor |
| * from the parent function. |
| */ |
| typedef bool (*nbt_visitor_t)(nbt_node* node, void* aux); |
| |
| /* |
| * A function which directs the overall algorithm with its return type. |
| * `aux' is an optional parameter which will be passed to your predicate from |
| * the parent function. |
| */ |
| typedef bool (*nbt_predicate_t)(const nbt_node* node, void* aux); |
| |
| /* |
| * Traverses the tree until a visitor says stop or all elements are exhausted. |
| * Returns false if it was terminated by a visitor, true otherwise. In most |
| * cases this can be ignored. |
| * |
| * TODO: Is there a way to do this without expensive function pointers? Maybe |
| * something like list_for_each? |
| */ |
| bool nbt_map(nbt_node* tree, nbt_visitor_t, void* aux); |
| |
| /* |
| * Returns a new tree, consisting of a copy of all the nodes the predicate |
| * returned `true' for. If the new tree is empty, this function will return |
| * NULL. If an out of memory error occured, errno will be set to NBT_EMEM. |
| * |
| * TODO: What if I want to keep a tree and all of its children? Do I need to |
| * augment nbt_node with parent pointers? |
| */ |
| nbt_node* nbt_filter(const nbt_node* tree, nbt_predicate_t, void* aux); |
| |
| /* |
| * The exact same as nbt_filter, except instead of returning a new tree, the |
| * existing tree is modified in place, and then returned for convenience. |
| */ |
| nbt_node* nbt_filter_inplace(nbt_node* tree, nbt_predicate_t, void* aux); |
| |
| /* |
| * Returns the first node which causes the predicate to return true. If all |
| * nodes are rejected, NULL is returned. If you want to find every instance of |
| * something, consider using nbt_map with a visitor that keeps track. |
| * |
| * Since const-ing `tree' would require me const-ing the return value, you'll |
| * just have to take my word for it that nbt_find DOES NOT modify the tree. |
| * Feel free to cast as necessary. |
| */ |
| nbt_node* nbt_find(nbt_node* tree, nbt_predicate_t, void* aux); |
| |
| /* |
| * Returns the first node with the name `name'. If no node with that name is in |
| * the tree, returns NULL. |
| * |
| * If `name' is NULL, this function will find the first unnamed node. |
| * |
| * Since const-ing `tree' would require me const-ing the return value, you'll |
| * just have to take my word for it that nbt_find DOES NOT modify the tree. |
| * Feel free to cast as necessary. |
| */ |
| nbt_node* nbt_find_by_name(nbt_node* tree, const char* name); |
| |
| /* |
| * Returns the first node with the "path" in the tree of `path'. If no such node |
| * exists, returns NULL. If an element has no name, something like: |
| * |
| * root.subelement..data == "root" -> "subelement" -> "" -> "data" |
| * |
| * Remember, if multiple elements exist in a sublist which share the same name |
| * (including ""), the first one will be chosen. |
| */ |
| nbt_node* nbt_find_by_path(nbt_node* tree, const char* path); |
| |
| /* Returns the number of nodes in the tree. */ |
| size_t nbt_size(const nbt_node* tree); |
| |
| /* |
| * Returns the Nth item of a list |
| * Don't use this to iterate through a list, it would be very inefficient |
| */ |
| nbt_node* nbt_list_item(nbt_node* list, int n); |
| |
| /* TODO: More utilities as requests are made and patches contributed. */ |
| |
| /***** Utility Functions *****/ |
| |
| /* Returns true if the trees are identical. */ |
| bool nbt_eq(const nbt_node* restrict a, const nbt_node* restrict b); |
| |
| /* |
| * Converts a type to a print-friendly string. The string is statically |
| * allocated, and therefore does not have to be freed by the user. |
| */ |
| const char* nbt_type_to_string(nbt_type); |
| |
| /* |
| * Converts an error code into a print-friendly string. The string is statically |
| * allocated, and therefore does not have to be freed by the user. |
| */ |
| const char* nbt_error_to_string(nbt_status); |
| |
| #ifdef __cplusplus |
| } |
| #endif |
| |
| #endif |