forked from chmod222/cNBT
-
Notifications
You must be signed in to change notification settings - Fork 0
/
nbt.h
325 lines (275 loc) · 11.2 KB
/
nbt.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
/* * -----------------------------------------------------------------------------
* "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
} 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;
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