You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and dots ('.'), can be up to 35 characters long. Letters must be lowercase.
		
		
		
		
		
			
		
			
				
					
					
						
							632 lines
						
					
					
						
							17 KiB
						
					
					
				
			
		
		
	
	
							632 lines
						
					
					
						
							17 KiB
						
					
					
				#include <assert.h> | 
						|
#include <stdio.h> | 
						|
#include <limits.h> | 
						|
#include <stdlib.h> | 
						|
 | 
						|
#define FAST_CHUNK   // disabling this enables the old, slower path that deblocks into a regular form | 
						|
 | 
						|
#include "cave_parse.h" | 
						|
 | 
						|
#include "stb_image.h" | 
						|
#include "stb.h" | 
						|
 | 
						|
#define NUM_CHUNKS_PER_REGION       32  // only on one axis | 
						|
#define NUM_CHUNKS_PER_REGION_LOG2   5 | 
						|
 | 
						|
#define NUM_COLUMNS_PER_CHUNK       16 | 
						|
#define NUM_COLUMNS_PER_CHUNK_LOG2   4 | 
						|
 | 
						|
uint32 read_uint32_be(FILE *f) | 
						|
{ | 
						|
   unsigned char data[4]; | 
						|
   fread(data, 1, 4, f); | 
						|
   return (data[0]<<24) + (data[1]<<16) + (data[2]<<8) + data[3]; | 
						|
} | 
						|
 | 
						|
typedef struct | 
						|
{ | 
						|
   uint8 *data; | 
						|
   size_t len; | 
						|
   int x,z; // chunk index | 
						|
   int refcount; // for multi-threading | 
						|
} compressed_chunk; | 
						|
 | 
						|
typedef struct | 
						|
{ | 
						|
   int x,z; | 
						|
   uint32 sector_data[NUM_CHUNKS_PER_REGION][NUM_CHUNKS_PER_REGION]; | 
						|
} region; | 
						|
 | 
						|
size_t cached_compressed=0; | 
						|
 | 
						|
FILE *last_region; | 
						|
int last_region_x; | 
						|
int last_region_z; | 
						|
int opened=0; | 
						|
 | 
						|
static void open_file(int reg_x, int reg_z) | 
						|
{ | 
						|
   if (!opened || last_region_x != reg_x || last_region_z != reg_z) { | 
						|
      char filename[256]; | 
						|
      if (last_region != NULL) | 
						|
         fclose(last_region); | 
						|
      sprintf(filename, "r.%d.%d.mca", reg_x, reg_z); | 
						|
      last_region = fopen(filename, "rb"); | 
						|
      last_region_x = reg_x; | 
						|
      last_region_z = reg_z; | 
						|
      opened = 1; | 
						|
   } | 
						|
} | 
						|
 | 
						|
static region *load_region(int reg_x, int reg_z) | 
						|
{ | 
						|
   region *r; | 
						|
   int x,z; | 
						|
 | 
						|
   open_file(reg_x, reg_z); | 
						|
 | 
						|
   r = malloc(sizeof(*r)); | 
						|
 | 
						|
   if (last_region == NULL) { | 
						|
      memset(r, 0, sizeof(*r)); | 
						|
   } else { | 
						|
      fseek(last_region, 0, SEEK_SET); | 
						|
      for (z=0; z < NUM_CHUNKS_PER_REGION; ++z) | 
						|
         for (x=0; x < NUM_CHUNKS_PER_REGION; ++x) | 
						|
            r->sector_data[z][x] = read_uint32_be(last_region); | 
						|
   } | 
						|
   r->x = reg_x; | 
						|
   r->z = reg_z; | 
						|
 | 
						|
   return r; | 
						|
} | 
						|
 | 
						|
void free_region(region *r) | 
						|
{ | 
						|
   free(r); | 
						|
} | 
						|
 | 
						|
#define MAX_MAP_REGIONS   64  // in one axis: 64 regions * 32 chunk/region * 16 columns/chunk = 16384 columns | 
						|
region *regions[MAX_MAP_REGIONS][MAX_MAP_REGIONS]; | 
						|
 | 
						|
static region *get_region(int reg_x, int reg_z) | 
						|
{ | 
						|
   int slot_x = reg_x & (MAX_MAP_REGIONS-1); | 
						|
   int slot_z = reg_z & (MAX_MAP_REGIONS-1); | 
						|
   region *r; | 
						|
 | 
						|
   r = regions[slot_z][slot_x]; | 
						|
 | 
						|
   if (r) { | 
						|
      if (r->x == reg_x && r->z == reg_z) | 
						|
         return r; | 
						|
      free_region(r); | 
						|
   } | 
						|
 | 
						|
   r = load_region(reg_x, reg_z); | 
						|
   regions[slot_z][slot_x] = r; | 
						|
 | 
						|
   return r; | 
						|
} | 
						|
 | 
						|
// about one region, so size should be ok | 
						|
#define NUM_CACHED_X 64 | 
						|
#define NUM_CACHED_Z 64 | 
						|
 | 
						|
// @TODO: is it really worth caching these? we probably can just | 
						|
// pull them from the disk cache nearly as efficiently. | 
						|
// Can test that by setting to 1x1? | 
						|
compressed_chunk *cached_chunk[NUM_CACHED_Z][NUM_CACHED_X]; | 
						|
 | 
						|
static void deref_compressed_chunk(compressed_chunk *cc) | 
						|
{ | 
						|
   assert(cc->refcount > 0); | 
						|
   --cc->refcount; | 
						|
   if (cc->refcount == 0) { | 
						|
      if (cc->data) | 
						|
         free(cc->data); | 
						|
      free(cc); | 
						|
   } | 
						|
} | 
						|
 | 
						|
static compressed_chunk *get_compressed_chunk(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   int slot_x = chunk_x & (NUM_CACHED_X-1); | 
						|
   int slot_z = chunk_z & (NUM_CACHED_Z-1); | 
						|
   compressed_chunk *cc = cached_chunk[slot_z][slot_x]; | 
						|
 | 
						|
   if (cc && cc->x == chunk_x && cc->z == chunk_z) | 
						|
      return cc; | 
						|
   else { | 
						|
      int reg_x = chunk_x >> NUM_CHUNKS_PER_REGION_LOG2; | 
						|
      int reg_z = chunk_z >> NUM_CHUNKS_PER_REGION_LOG2; | 
						|
      region *r = get_region(reg_x, reg_z); | 
						|
      if (cc) { | 
						|
         deref_compressed_chunk(cc); | 
						|
         cached_chunk[slot_z][slot_x] = NULL; | 
						|
      } | 
						|
      cc = malloc(sizeof(*cc)); | 
						|
      cc->x = chunk_x; | 
						|
      cc->z = chunk_z; | 
						|
      { | 
						|
         int subchunk_x = chunk_x & (NUM_CHUNKS_PER_REGION-1); | 
						|
         int subchunk_z = chunk_z & (NUM_CHUNKS_PER_REGION-1); | 
						|
         uint32 code = r->sector_data[subchunk_z][subchunk_x]; | 
						|
 | 
						|
         if (code & 255) { | 
						|
            open_file(reg_x, reg_z); | 
						|
            fseek(last_region, (code>>8)*4096, SEEK_SET); | 
						|
            cc->len = (code&255)*4096; | 
						|
            cc->data = malloc(cc->len); | 
						|
            fread(cc->data, 1, cc->len, last_region); | 
						|
         } else { | 
						|
            cc->len = 0; | 
						|
            cc->data = 0; | 
						|
         } | 
						|
      } | 
						|
      cc->refcount = 1; | 
						|
      cached_chunk[slot_z][slot_x] = cc; | 
						|
      return cc; | 
						|
   } | 
						|
} | 
						|
 | 
						|
 | 
						|
// NBT parser -- can automatically parse stuff we don't | 
						|
// have definitions for, but want to explicitly parse | 
						|
// stuff we do have definitions for. | 
						|
// | 
						|
// option 1: auto-parse everything into data structures, | 
						|
// then read those | 
						|
// | 
						|
// option 2: have a "parse next object" which | 
						|
// doesn't resolve whether it expands its children | 
						|
// yet, and then the user either says "expand" or | 
						|
// "skip" after looking at the name. Anything with | 
						|
// "children" without names can't go through this | 
						|
// interface. | 
						|
// | 
						|
// Let's try option 2. | 
						|
 | 
						|
 | 
						|
typedef struct | 
						|
{ | 
						|
   unsigned char *buffer_start; | 
						|
   unsigned char *buffer_end; | 
						|
   unsigned char *cur; | 
						|
   int nesting; | 
						|
   char temp_buffer[256]; | 
						|
} nbt; | 
						|
 | 
						|
enum { TAG_End=0, TAG_Byte=1, TAG_Short=2, TAG_Int=3, TAG_Long=4, | 
						|
       TAG_Float=5, TAG_Double=6, TAG_Byte_Array=7, TAG_String=8, | 
						|
       TAG_List=9, TAG_Compound=10, TAG_Int_Array=11 }; | 
						|
 | 
						|
static void nbt_get_string_data(unsigned char *data, char *buffer, size_t bufsize) | 
						|
{ | 
						|
   int len = data[0]*256 + data[1]; | 
						|
   int i; | 
						|
   for (i=0; i < len && i+1 < (int) bufsize; ++i) | 
						|
      buffer[i] = (char) data[i+2]; | 
						|
   buffer[i] = 0; | 
						|
} | 
						|
 | 
						|
static char *nbt_peek(nbt *n) | 
						|
{ | 
						|
   unsigned char type = *n->cur; | 
						|
   if (type == TAG_End) | 
						|
      return NULL; | 
						|
   nbt_get_string_data(n->cur+1, n->temp_buffer, sizeof(n->temp_buffer)); | 
						|
   return n->temp_buffer; | 
						|
} | 
						|
 | 
						|
static uint32 nbt_parse_uint32(unsigned char *buffer) | 
						|
{ | 
						|
   return (buffer[0] << 24) + (buffer[1]<<16) + (buffer[2]<<8) + buffer[3]; | 
						|
} | 
						|
 | 
						|
static void nbt_skip(nbt *n); | 
						|
 | 
						|
// skip an item that doesn't have an id or name prefix (usable in lists) | 
						|
static void nbt_skip_raw(nbt *n, unsigned char type) | 
						|
{ | 
						|
   switch (type) { | 
						|
      case TAG_Byte  : n->cur += 1; break; | 
						|
      case TAG_Short : n->cur += 2; break; | 
						|
      case TAG_Int   : n->cur += 4; break; | 
						|
      case TAG_Long  : n->cur += 8; break; | 
						|
      case TAG_Float : n->cur += 4; break; | 
						|
      case TAG_Double: n->cur += 8; break; | 
						|
      case TAG_Byte_Array: n->cur += 4 + 1*nbt_parse_uint32(n->cur); break; | 
						|
      case TAG_Int_Array : n->cur += 4 + 4*nbt_parse_uint32(n->cur); break; | 
						|
      case TAG_String    : n->cur += 2 + (n->cur[0]*256 + n->cur[1]); break; | 
						|
      case TAG_List      : { | 
						|
         unsigned char list_type = *n->cur++; | 
						|
         unsigned int list_len = nbt_parse_uint32(n->cur); | 
						|
         unsigned int i; | 
						|
         n->cur += 4; // list_len | 
						|
         for (i=0; i < list_len; ++i) | 
						|
            nbt_skip_raw(n, list_type); | 
						|
         break; | 
						|
      } | 
						|
      case TAG_Compound : { | 
						|
         while (*n->cur != TAG_End) | 
						|
            nbt_skip(n); | 
						|
         nbt_skip(n); // skip the TAG_end | 
						|
         break; | 
						|
      } | 
						|
   } | 
						|
   assert(n->cur <= n->buffer_end); | 
						|
} | 
						|
 | 
						|
static void nbt_skip(nbt *n) | 
						|
{ | 
						|
   unsigned char type = *n->cur++; | 
						|
   if (type == TAG_End) | 
						|
      return; | 
						|
   // skip name | 
						|
   n->cur += (n->cur[0]*256 + n->cur[1]) + 2; | 
						|
   nbt_skip_raw(n, type); | 
						|
} | 
						|
 | 
						|
// byteswap | 
						|
static void nbt_swap(unsigned char *ptr, int len) | 
						|
{ | 
						|
   int i; | 
						|
   for (i=0; i < (len>>1); ++i) { | 
						|
      unsigned char t = ptr[i]; | 
						|
      ptr[i] = ptr[len-1-i]; | 
						|
      ptr[len-1-i] = t; | 
						|
   } | 
						|
} | 
						|
 | 
						|
// pass in the expected type, fail if doesn't match | 
						|
// returns a pointer to the data, byteswapped if appropriate | 
						|
static void *nbt_get_fromlist(nbt *n, unsigned char type, int *len) | 
						|
{ | 
						|
   unsigned char *ptr; | 
						|
   assert(type != TAG_Compound); | 
						|
   assert(type != TAG_List); // we could support getting lists of primitives as if they were arrays, but eh | 
						|
   if (len) *len = 1; | 
						|
   ptr = n->cur; | 
						|
   switch (type) { | 
						|
      case TAG_Byte  : break; | 
						|
 | 
						|
      case TAG_Short : nbt_swap(ptr, 2); break; | 
						|
      case TAG_Int   : nbt_swap(ptr, 4); break; | 
						|
      case TAG_Long  : nbt_swap(ptr, 8); break; | 
						|
      case TAG_Float : nbt_swap(ptr, 4); break; | 
						|
      case TAG_Double: nbt_swap(ptr, 8); break; | 
						|
 | 
						|
      case TAG_Byte_Array: | 
						|
         *len = nbt_parse_uint32(ptr); | 
						|
         ptr += 4; | 
						|
         break; | 
						|
      case TAG_Int_Array: { | 
						|
         int i; | 
						|
         *len = nbt_parse_uint32(ptr); | 
						|
         ptr += 4; | 
						|
         for (i=0; i < *len; ++i) | 
						|
            nbt_swap(ptr + 4*i, 4); | 
						|
         break; | 
						|
      } | 
						|
 | 
						|
      default: assert(0); // unhandled case | 
						|
   } | 
						|
   nbt_skip_raw(n, type); | 
						|
   return ptr; | 
						|
} | 
						|
 | 
						|
static void *nbt_get(nbt *n, unsigned char type, int *len) | 
						|
{ | 
						|
   assert(n->cur[0] == type); | 
						|
   n->cur += 3 + (n->cur[1]*256+n->cur[2]); | 
						|
   return nbt_get_fromlist(n, type, len); | 
						|
} | 
						|
 | 
						|
static void nbt_begin_compound(nbt *n) // start a compound | 
						|
{ | 
						|
   assert(*n->cur == TAG_Compound); | 
						|
   // skip header | 
						|
   n->cur += 3 + (n->cur[1]*256 + n->cur[2]); | 
						|
   ++n->nesting; | 
						|
} | 
						|
 | 
						|
static void nbt_begin_compound_in_list(nbt *n) // start a compound | 
						|
{ | 
						|
   ++n->nesting; | 
						|
} | 
						|
 | 
						|
static void nbt_end_compound(nbt *n) // end a compound | 
						|
{ | 
						|
   assert(*n->cur == TAG_End); | 
						|
   assert(n->nesting != 0); | 
						|
   ++n->cur; | 
						|
   --n->nesting;    | 
						|
} | 
						|
 | 
						|
// @TODO no interface to get lists from lists | 
						|
static int nbt_begin_list(nbt *n, unsigned char type) | 
						|
{ | 
						|
   uint32 len; | 
						|
   unsigned char *ptr; | 
						|
 | 
						|
   ptr = n->cur + 3 + (n->cur[1]*256 + n->cur[2]); | 
						|
   if (ptr[0] != type) | 
						|
      return -1; | 
						|
   n->cur = ptr; | 
						|
   len = nbt_parse_uint32(n->cur+1); | 
						|
   assert(n->cur[0] == type); | 
						|
   // @TODO keep a stack with the count to make sure they do it right | 
						|
   ++n->nesting; | 
						|
   n->cur += 5; | 
						|
   return (int) len; | 
						|
} | 
						|
 | 
						|
static void nbt_end_list(nbt *n) | 
						|
{ | 
						|
   --n->nesting; | 
						|
} | 
						|
 | 
						|
// raw_block chunk is 16x256x16x4 = 2^(4+8+4+2) = 256KB | 
						|
// | 
						|
// if we want to process 64x64x256 at a time, that will be: | 
						|
//    4*4*256KB => 4MB per area in raw_block | 
						|
// | 
						|
// (plus we maybe need to decode adjacent regions) | 
						|
 | 
						|
 | 
						|
#ifdef FAST_CHUNK | 
						|
typedef fast_chunk parse_chunk; | 
						|
#else | 
						|
typedef chunk parse_chunk; | 
						|
#endif | 
						|
 | 
						|
static parse_chunk *minecraft_chunk_parse(unsigned char *data, size_t len) | 
						|
{ | 
						|
   char *s; | 
						|
   parse_chunk *c = NULL; | 
						|
 | 
						|
   nbt n_store, *n = &n_store; | 
						|
   n->buffer_start = data; | 
						|
   n->buffer_end   = data + len; | 
						|
   n->cur = n->buffer_start; | 
						|
   n->nesting = 0; | 
						|
 | 
						|
   nbt_begin_compound(n); | 
						|
   while ((s = nbt_peek(n)) != NULL) { | 
						|
      if (!strcmp(s, "Level")) { | 
						|
         int *height; | 
						|
         c = malloc(sizeof(*c)); | 
						|
         #ifdef FAST_CHUNK | 
						|
         memset(c, 0, sizeof(*c)); | 
						|
         c->pointer_to_free = data; | 
						|
         #else | 
						|
         c->rb[15][15][255].block = 0; | 
						|
         #endif | 
						|
         c->max_y = 0; | 
						|
 | 
						|
         nbt_begin_compound(n); | 
						|
         while ((s = nbt_peek(n)) != NULL) { | 
						|
            if (!strcmp(s, "xPos")) | 
						|
               c->xpos = *(int *) nbt_get(n, TAG_Int, 0); | 
						|
            else if (!strcmp(s, "zPos")) | 
						|
               c->zpos = *(int *) nbt_get(n, TAG_Int, 0); | 
						|
            else if (!strcmp(s, "Sections")) { | 
						|
               int count = nbt_begin_list(n, TAG_Compound), i; | 
						|
               if (count == -1) { | 
						|
                  // this not-a-list case happens in The End and I'm not sure | 
						|
                  // what it means... possibly one of those silly encodings | 
						|
                  // where it's not encoded as a list if there's only one? | 
						|
                  // not worth figuring out | 
						|
                  nbt_skip(n); | 
						|
                  count = -1; | 
						|
               } | 
						|
               for (i=0; i < count; ++i) { | 
						|
                  int yi, len; | 
						|
                  uint8 *light = NULL, *blocks = NULL, *data = NULL, *skylight = NULL; | 
						|
                  nbt_begin_compound_in_list(n); | 
						|
                  while ((s = nbt_peek(n)) != NULL) { | 
						|
                     if (!strcmp(s, "Y")) | 
						|
                        yi = * (uint8 *) nbt_get(n, TAG_Byte, 0); | 
						|
                     else if (!strcmp(s, "BlockLight")) { | 
						|
                        light = nbt_get(n, TAG_Byte_Array, &len); | 
						|
                        assert(len == 2048); | 
						|
                     } else if (!strcmp(s, "Blocks")) { | 
						|
                        blocks = nbt_get(n, TAG_Byte_Array, &len); | 
						|
                        assert(len == 4096); | 
						|
                     } else if (!strcmp(s, "Data")) { | 
						|
                        data = nbt_get(n, TAG_Byte_Array, &len); | 
						|
                        assert(len == 2048); | 
						|
                     } else if (!strcmp(s, "SkyLight")) { | 
						|
                        skylight = nbt_get(n, TAG_Byte_Array, &len); | 
						|
                        assert(len == 2048); | 
						|
                     } | 
						|
                  } | 
						|
                  nbt_end_compound(n); | 
						|
 | 
						|
                  assert(yi < 16); | 
						|
 | 
						|
                  #ifndef FAST_CHUNK | 
						|
 | 
						|
                  // clear data below current max_y | 
						|
                  { | 
						|
                     int x,z; | 
						|
                     while (c->max_y < yi*16) { | 
						|
                        for (x=0; x < 16; ++x) | 
						|
                           for (z=0; z < 16; ++z) | 
						|
                              c->rb[z][x][c->max_y].block = 0; | 
						|
                        ++c->max_y; | 
						|
                     } | 
						|
                  } | 
						|
 | 
						|
                  // now assemble the data | 
						|
                  { | 
						|
                     int x,y,z, o2=0,o4=0; | 
						|
                     for (y=0; y < 16; ++y) { | 
						|
                        for (z=0; z < 16; ++z) { | 
						|
                           for (x=0; x < 16; x += 2) { | 
						|
                              raw_block *rb = &c->rb[15-z][x][y + yi*16]; // 15-z because switching to z-up will require flipping an axis | 
						|
                              rb[0].block = blocks[o4]; | 
						|
                              rb[0].light = light[o2] & 15; | 
						|
                              rb[0].data  = data[o2] & 15; | 
						|
                              rb[0].skylight = skylight[o2] & 15; | 
						|
 | 
						|
                              rb[256].block = blocks[o4+1]; | 
						|
                              rb[256].light = light[o2] >> 4; | 
						|
                              rb[256].data  = data[o2] >> 4; | 
						|
                              rb[256].skylight = skylight[o2] >> 4; | 
						|
 | 
						|
                              o2 += 1; | 
						|
                              o4 += 2; | 
						|
                           } | 
						|
                        } | 
						|
                     } | 
						|
                     c->max_y += 16; | 
						|
                  } | 
						|
                  #else | 
						|
                  c->blockdata[yi] = blocks; | 
						|
                  c->data     [yi] = data; | 
						|
                  c->light    [yi] = light; | 
						|
                  c->skylight [yi] = skylight; | 
						|
                  #endif | 
						|
               } | 
						|
               //nbt_end_list(n); | 
						|
            } else if (!strcmp(s, "HeightMap")) { | 
						|
               height = nbt_get(n, TAG_Int_Array, &len); | 
						|
               assert(len == 256); | 
						|
            } else | 
						|
               nbt_skip(n); | 
						|
         } | 
						|
         nbt_end_compound(n); | 
						|
 | 
						|
      } else | 
						|
         nbt_skip(n); | 
						|
   } | 
						|
   nbt_end_compound(n); | 
						|
   assert(n->cur == n->buffer_end); | 
						|
   return c; | 
						|
} | 
						|
 | 
						|
#define MAX_DECODED_CHUNK_X  64 | 
						|
#define MAX_DECODED_CHUNK_Z  64 | 
						|
 | 
						|
typedef struct | 
						|
{ | 
						|
   int cx,cz; | 
						|
   fast_chunk *fc; | 
						|
   int valid; | 
						|
} decoded_buffer; | 
						|
 | 
						|
static decoded_buffer decoded_buffers[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; | 
						|
void lock_chunk_get_mutex(void); | 
						|
void unlock_chunk_get_mutex(void); | 
						|
 | 
						|
#ifdef FAST_CHUNK | 
						|
fast_chunk *get_decoded_fastchunk_uncached(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   unsigned char *decoded; | 
						|
   compressed_chunk *cc; | 
						|
   int inlen; | 
						|
   int len; | 
						|
   fast_chunk *fc; | 
						|
 | 
						|
   lock_chunk_get_mutex(); | 
						|
   cc = get_compressed_chunk(chunk_x, chunk_z); | 
						|
   if (cc->len != 0) | 
						|
      ++cc->refcount; | 
						|
   unlock_chunk_get_mutex(); | 
						|
 | 
						|
   if (cc->len == 0) | 
						|
      return NULL; | 
						|
 | 
						|
   assert(cc != NULL); | 
						|
 | 
						|
   assert(cc->data[4] == 2); | 
						|
 | 
						|
   inlen = nbt_parse_uint32(cc->data); | 
						|
   decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); | 
						|
   assert(decoded != NULL); | 
						|
   assert(len != 0); | 
						|
 | 
						|
   lock_chunk_get_mutex(); | 
						|
   deref_compressed_chunk(cc); | 
						|
   unlock_chunk_get_mutex(); | 
						|
 | 
						|
   #ifdef FAST_CHUNK | 
						|
   fc = minecraft_chunk_parse(decoded, len); | 
						|
   #else | 
						|
   fc = NULL; | 
						|
   #endif | 
						|
   if (fc == NULL) | 
						|
      free(decoded); | 
						|
   return fc; | 
						|
} | 
						|
 | 
						|
 | 
						|
decoded_buffer *get_decoded_buffer(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   decoded_buffer *db = &decoded_buffers[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; | 
						|
   if (db->valid) { | 
						|
      if (db->cx == chunk_x && db->cz == chunk_z) | 
						|
         return db; | 
						|
      if (db->fc) { | 
						|
         free(db->fc->pointer_to_free); | 
						|
         free(db->fc); | 
						|
      } | 
						|
   } | 
						|
 | 
						|
   db->cx = chunk_x; | 
						|
   db->cz = chunk_z; | 
						|
   db->valid = 1; | 
						|
   db->fc = 0; | 
						|
 | 
						|
   { | 
						|
      db->fc = get_decoded_fastchunk_uncached(chunk_x, chunk_z); | 
						|
      return db; | 
						|
   } | 
						|
} | 
						|
 | 
						|
fast_chunk *get_decoded_fastchunk(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   decoded_buffer *db = get_decoded_buffer(chunk_x, chunk_z); | 
						|
   return db->fc; | 
						|
} | 
						|
#endif | 
						|
 | 
						|
#ifndef FAST_CHUNK | 
						|
chunk *get_decoded_chunk_raw(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   unsigned char *decoded; | 
						|
   compressed_chunk *cc = get_compressed_chunk(chunk_x, chunk_z); | 
						|
   assert(cc != NULL); | 
						|
   if (cc->len == 0) | 
						|
      return NULL; | 
						|
   else { | 
						|
      chunk *ch; | 
						|
      int inlen = nbt_parse_uint32(cc->data); | 
						|
      int len; | 
						|
      assert(cc->data[4] == 2); | 
						|
      decoded = stbi_zlib_decode_malloc_guesssize(cc->data+5, inlen, inlen*3, &len); | 
						|
      assert(decoded != NULL); | 
						|
      #ifdef FAST_CHUNK | 
						|
      ch = NULL; | 
						|
      #else | 
						|
      ch = minecraft_chunk_parse(decoded, len); | 
						|
      #endif | 
						|
      free(decoded); | 
						|
      return ch; | 
						|
   } | 
						|
} | 
						|
 | 
						|
static chunk *decoded_chunks[MAX_DECODED_CHUNK_Z][MAX_DECODED_CHUNK_X]; | 
						|
chunk *get_decoded_chunk(int chunk_x, int chunk_z) | 
						|
{ | 
						|
   chunk *c = decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)]; | 
						|
   if (c && c->xpos == chunk_x && c->zpos == chunk_z) | 
						|
      return c; | 
						|
   if (c) free(c); | 
						|
   c = get_decoded_chunk_raw(chunk_x, chunk_z); | 
						|
   decoded_chunks[chunk_z&(MAX_DECODED_CHUNK_Z-1)][chunk_x&(MAX_DECODED_CHUNK_X-1)] = c; | 
						|
   return c; | 
						|
} | 
						|
#endif
 | 
						|
 |