1
2 /*
3 * Copyright (c) 2017 Juliette Foucaut & Doug Binks
4 *
5 * This software is provided 'as-is', without any express or implied
6 * warranty. In no event will the authors be held liable for any damages
7 * arising from the use of this software.
8 *
9 * Permission is granted to anyone to use this software for any purpose,
10 * including commercial applications, and to alter it and redistribute it
11 * freely, subject to the following restrictions:
12 *
13 * 1. The origin of this software must not be misrepresented; you must not
14 * claim that you wrote the original software. If you use this software
15 * in a product, an acknowledgement in the product documentation would be
16 * appreciated but is not required.
17 * 2. Altered source versions must be plainly marked as such, and must not be
18 * misrepresented as being the original software.
19 * 3. This notice may not be removed or altered from any source distribution.
20 */
21
22 #include "enkimi.h"
23
24
25 static const uint32_t SECTOR_SIZE = 4096;
26
27 static const char* tagIdString[] =
28 {
29 "TAG_End",
30 "TAG_Byte",
31 "TAG_Short",
32 "TAG_Int",
33 "TAG_Long",
34 "TAG_Float",
35 "TAG_Double",
36 "TAG_Byte_Array",
37 "TAG_String",
38 "TAG_List",
39 "TAG_Compound",
40 "TAG_Int_Array",
41 };
42
43 static uint32_t minecraftPalette[] =
44 {
45 0xff000000, 0xff7d7d7d, 0xff4cb376, 0xff436086, 0xff7a7a7a, 0xff4e7f9c, 0xff256647, 0xff535353, 0xffdcaf70, 0xffdcaf70,
46 0xff135bcf, 0xff125ad4, 0xffa0d3db, 0xff7a7c7e, 0xff7c8b8f, 0xff7e8287, 0xff737373, 0xff315166, 0xff31b245, 0xff54c3c2,
47 0xfff4f0da, 0xff867066, 0xff894326, 0xff838383, 0xff9fd3dc, 0xff324364, 0xff3634b4, 0xff23c7f6, 0xff7c7c7c, 0xff77bf8e,
48 0xffdcdcdc, 0xff296595, 0xff194f7b, 0xff538ba5, 0xff5e96bd, 0xffdddddd, 0xffe5e5e5, 0xff00ffff, 0xff0d00da, 0xff415778,
49 0xff0d0fe1, 0xff4eecf9, 0xffdbdbdb, 0xffa1a1a1, 0xffa6a6a6, 0xff0630bc, 0xff0026af, 0xff39586b, 0xff658765, 0xff1d1214,
50 0xff00ffff, 0xff005fde, 0xff31271a, 0xff4e87a6, 0xff2a74a4, 0xff0000ff, 0xff8f8c81, 0xffd5db61, 0xff2e5088, 0xff17593c,
51 0xff335682, 0xff676767, 0xff00b9ff, 0xff5b9ab8, 0xff387394, 0xff345f79, 0xff5190b6, 0xff6a6a6a, 0xff5b9ab8, 0xff40596a,
52 0xff7a7a7a, 0xffc2c2c2, 0xff65a0c9, 0xff6b6b84, 0xff2d2ddd, 0xff000066, 0xff0061ff, 0xff848484, 0xfff1f1df, 0xffffad7d,
53 0xfffbfbef, 0xff1d830f, 0xffb0a49e, 0xff65c094, 0xff3b5985, 0xff42748d, 0xff1b8ce3, 0xff34366f, 0xff334054, 0xff45768f,
54 0xffbf0a57, 0xff2198f1, 0xffffffec, 0xffb2b2b2, 0xffb2b2b2, 0xffffffff, 0xff2d5d7e, 0xff7c7c7c, 0xff7a7a7a, 0xff7cafcf,
55 0xff78aaca, 0xff6a6c6d, 0xfff4efd3, 0xff28bdc4, 0xff69dd92, 0xff53ae73, 0xff0c5120, 0xff5287a5, 0xff2a4094, 0xff7a7a7a,
56 0xff75718a, 0xff767676, 0xff1a162c, 0xff1a162c, 0xff1a162c, 0xff2d28a6, 0xffb1c454, 0xff51677c, 0xff494949, 0xff343434,
57 0xffd18934, 0xffa5dfdd, 0xff0f090c, 0xff316397, 0xff42a0e3, 0xff4d84a1, 0xff49859e, 0xff1f71dd, 0xffa8e2e7, 0xff74806d,
58 0xff3c3a2a, 0xff7c7c7c, 0xff5a5a5a, 0xff75d951, 0xff345e81, 0xff84c0ce, 0xff455f88, 0xff868b8e, 0xffd7dd74, 0xff595959,
59 0xff334176, 0xff008c0a, 0xff17a404, 0xff5992b3, 0xffb0b0b0, 0xff434347, 0xff1d6b9e, 0xff70fdfe, 0xffe5e5e5, 0xff4c4a4b,
60 0xffbdc6bf, 0xffddedfb, 0xff091bab, 0xff4f547d, 0xff717171, 0xffdfe6ea, 0xffe3e8eb, 0xff41819b, 0xff747474, 0xffa1b2d1,
61 0xfff6f6f6, 0xff878787, 0xff395ab0, 0xff325cac, 0xff152c47, 0xff65c878, 0xff3534df, 0xffc7c7c7, 0xffa5af72, 0xffbec7ac,
62 0xff9fd3dc, 0xffcacaca, 0xff425c96, 0xff121212, 0xfff4bfa2, 0xff1474cf, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xff1d56ac,
63 0xff1d57ae, 0xff1d57ae, 0xff1d57ae, 0xff243c50, 0xff8dcddd, 0xff4d7aaf, 0xff0e2034, 0xff366bcf, 0xff355d7e, 0xff7bb8c7,
64 0xff5f86bb, 0xff1e2e3f, 0xff3a6bc5, 0xff30536e, 0xffe0f3f7, 0xff5077a9, 0xff2955aa, 0xff21374e, 0xffcdc5dc, 0xff603b60,
65 0xff856785, 0xffa679a6, 0xffaa7eaa, 0xffa879a8, 0xffa879a8, 0xffa879a8, 0xffaae6e1, 0xffaae6e1, 0xff457d98, 0xfff0f0f0,
66 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0,
67 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0,
68 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0,
69 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0,
70 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xfff0f0f0, 0xff242132 };
71
72
73 typedef struct SectionChunkInfo_s
74 {
75 uint8_t offset_0;
76 uint8_t offset_1;
77 uint8_t offset_2;
78 uint8_t sectorCount;
79 } SectionChunkInfo;
80
GetChunkLocation(SectionChunkInfo section_)81 static int32_t GetChunkLocation( SectionChunkInfo section_ )
82 {
83 return ( ( ( section_.offset_0 << 16 ) + ( section_.offset_1 << 8 ) + section_.offset_2 ) * SECTOR_SIZE );
84 }
85
86 typedef struct BigEndian4BytesTo32BitInt_s
87 {
88 uint8_t pos_0;
89 uint8_t pos_1;
90 uint8_t pos_2;
91 uint8_t pos_3;
92 } BigEndian4BytesTo32BitInt;
93
94
Get32BitInt(BigEndian4BytesTo32BitInt in_)95 static int32_t Get32BitInt( BigEndian4BytesTo32BitInt in_ )
96 {
97 return ( ( in_.pos_0 << 24 ) + ( in_.pos_1 << 16 ) + ( in_.pos_2 << 8 ) + in_.pos_3 );
98 }
99
100 typedef struct RegionHeader_s
101 {
102 SectionChunkInfo sectionChunksInfos[ ENKI_MI_REGION_CHUNKS_NUMBER ]; // chunks locations
103 BigEndian4BytesTo32BitInt sectionChunksTimestamps[ ENKI_MI_REGION_CHUNKS_NUMBER ]; // chunks timestamps
104 } RegionHeader;
105
enkiGetNBTTagIDAsString(uint8_t tagID_)106 const char * enkiGetNBTTagIDAsString( uint8_t tagID_ )
107 {
108 return tagIdString[ tagID_ ];
109 }
110
enkiGetNBTTagHeaderIDAsString(enkiNBTTagHeader tagheader_)111 const char * enkiGetNBTTagHeaderIDAsString( enkiNBTTagHeader tagheader_ )
112 {
113 return tagIdString[ tagheader_.tagId ];
114 }
115
enkiNBTInitFromMemoryUncompressed(enkiNBTDataStream * pStream_,uint8_t * pData_,uint32_t dataSize_)116 void enkiNBTInitFromMemoryUncompressed( enkiNBTDataStream* pStream_, uint8_t * pData_, uint32_t dataSize_)
117 {
118 memset( &pStream_->currentTag, 0, sizeof( enkiNBTTagHeader ) );
119 pStream_->pData = pData_;
120 pStream_->dataLength = dataSize_;
121 pStream_->pCurrPos = pStream_->pData;
122 pStream_->pDataEnd = pStream_->pData + pStream_->dataLength;
123 pStream_->pNextTag = pStream_->pCurrPos;
124 pStream_->level = -1;
125 pStream_->pAllocation = NULL;
126 }
127
enkiNBTInitFromMemoryCompressed(enkiNBTDataStream * pStream_,uint8_t * pCompressedData_,uint32_t compressedDataSize_,uint32_t uncompressedSizeHint_)128 int enkiNBTInitFromMemoryCompressed( enkiNBTDataStream* pStream_, uint8_t * pCompressedData_,
129 uint32_t compressedDataSize_, uint32_t uncompressedSizeHint_)
130 {
131 mz_ulong destLength = uncompressedSizeHint_;
132 if( destLength <= compressedDataSize_ )
133 {
134 destLength = compressedDataSize_ * 4 + 1024; // estimate uncompressed size
135 }
136 mz_ulong startDestLength = destLength;
137 uint8_t* dataUnCompressed = (uint8_t*)malloc( destLength );
138 int retval = uncompress( dataUnCompressed, &destLength, pCompressedData_, compressedDataSize_ );
139 if( retval == MZ_BUF_ERROR && startDestLength == destLength )
140 {
141 // failed to uncompress, buffer full
142 for( int attempts = 0; ( retval != MZ_OK ) && ( attempts < 3 ); ++attempts )
143 {
144 free( dataUnCompressed );
145 destLength *= 4 + 1024;
146 dataUnCompressed = (uint8_t*)malloc( destLength );
147 retval = uncompress( dataUnCompressed, &destLength, pCompressedData_, compressedDataSize_ );
148 }
149 }
150 if( retval != MZ_OK )
151 {
152 enkiNBTInitFromMemoryUncompressed( pStream_, NULL, 0 );
153 free( dataUnCompressed );
154 return 0;
155 }
156
157 dataUnCompressed = (uint8_t*)realloc( dataUnCompressed, destLength ); // reallocate to actual size
158 enkiNBTInitFromMemoryUncompressed( pStream_, dataUnCompressed, ( uint32_t )destLength );
159 pStream_->pAllocation = dataUnCompressed;
160 return 1;
161 }
162
enkiNBTFreeAllocations(enkiNBTDataStream * pStream_)163 void enkiNBTFreeAllocations( enkiNBTDataStream* pStream_ )
164 {
165 free( pStream_->pAllocation );
166 memset( pStream_, 0, sizeof(enkiNBTDataStream) );
167 }
168
enkiNBTReadInt8(enkiNBTDataStream * pStream_)169 int8_t enkiNBTReadInt8( enkiNBTDataStream* pStream_ )
170 {
171 int8_t retVal = pStream_->pCurrPos[ 0 ];
172 pStream_->pCurrPos += 1;
173 return retVal;
174 }
175
enkiNBTReadByte(enkiNBTDataStream * pStream_)176 int8_t enkiNBTReadByte( enkiNBTDataStream* pStream_ )
177 {
178 return enkiNBTReadInt8( pStream_ );
179 }
180
enkiNBTReadInt16(enkiNBTDataStream * pStream_)181 int16_t enkiNBTReadInt16( enkiNBTDataStream* pStream_ )
182 {
183 int16_t retVal = ( pStream_->pCurrPos[ 0 ] << 8 ) + pStream_->pCurrPos[ 1 ];
184 pStream_->pCurrPos += 2;
185 return retVal;
186 }
187
enkiNBTReadShort(enkiNBTDataStream * pStream_)188 int16_t enkiNBTReadShort( enkiNBTDataStream* pStream_ )
189 {
190 return enkiNBTReadInt16( pStream_ );
191 }
192
enkiNBTReadInt32(enkiNBTDataStream * pStream_)193 int32_t enkiNBTReadInt32( enkiNBTDataStream* pStream_ )
194 {
195 int32_t retVal = ( pStream_->pCurrPos[ 0 ] << 24 ) + ( pStream_->pCurrPos[ 1 ] << 16 ) + ( pStream_->pCurrPos[ 2 ] << 8 ) + pStream_->pCurrPos[ 3 ];
196 pStream_->pCurrPos += 4;
197 return retVal;
198 }
199
enkiNBTReadInt(enkiNBTDataStream * pStream_)200 int32_t enkiNBTReadInt( enkiNBTDataStream* pStream_ )
201 {
202 return enkiNBTReadInt32( pStream_ );
203 }
204
enkiNBTReadFloat(enkiNBTDataStream * pStream_)205 float enkiNBTReadFloat( enkiNBTDataStream* pStream_ )
206 {
207 int32_t iVal = ( pStream_->pCurrPos[ 0 ] << 24 ) + ( pStream_->pCurrPos[ 1 ] << 16 ) + ( pStream_->pCurrPos[ 2 ] << 8 ) + pStream_->pCurrPos[ 3 ];
208 float retVal = *( float* )&iVal;
209 pStream_->pCurrPos += 4;
210 return retVal;
211 }
212
enkiNBTReadInt64(enkiNBTDataStream * pStream_)213 int64_t enkiNBTReadInt64( enkiNBTDataStream* pStream_ )
214 {
215 int64_t retVal = ( ( int64_t )pStream_->pCurrPos[ 0 ] << 54 ) + ( ( int64_t )pStream_->pCurrPos[ 1 ] << 48 ) + ( ( int64_t )pStream_->pCurrPos[ 2 ] << 40 ) + ( ( int64_t )pStream_->pCurrPos[ 5 ] << 32 ) +
216 ( pStream_->pCurrPos[ 4 ] << 24 ) + ( pStream_->pCurrPos[ 5 ] << 16 ) + ( pStream_->pCurrPos[ 6 ] << 8 ) + pStream_->pCurrPos[ 7 ];
217 pStream_->pCurrPos += 8;
218 return retVal;
219 }
220
enkiNBTReadlong(enkiNBTDataStream * pStream_)221 int64_t enkiNBTReadlong( enkiNBTDataStream* pStream_ )
222 {
223 return enkiNBTReadInt64( pStream_ );
224 }
225
enkiNBTReadDouble(enkiNBTDataStream * pStream_)226 double enkiNBTReadDouble( enkiNBTDataStream* pStream_ )
227 {
228 int64_t iVal = ( ( int64_t )pStream_->pCurrPos[ 0 ] << 54 ) + ( ( int64_t )pStream_->pCurrPos[ 1 ] << 48 ) + ( ( int64_t )pStream_->pCurrPos[ 2 ] << 40 ) + ( ( int64_t )pStream_->pCurrPos[ 5 ] << 32 ) +
229 ( pStream_->pCurrPos[ 4 ] << 24 ) + ( pStream_->pCurrPos[ 5 ] << 16 ) + ( pStream_->pCurrPos[ 6 ] << 8 ) + pStream_->pCurrPos[ 7 ];
230 double retVal = *( double* )&iVal;
231 pStream_->pCurrPos += 8;
232 return retVal;
233 }
234
enkiNBTReadString(enkiNBTDataStream * pStream_)235 enkiNBTString enkiNBTReadString( enkiNBTDataStream* pStream_ )
236 {
237 enkiNBTString nbtString;
238 nbtString.size = enkiNBTReadInt16( pStream_ );
239 nbtString.pStrNotNullTerminated = (const char*)pStream_->pCurrPos;
240 return nbtString;
241 }
242
SkipDataToNextTag(enkiNBTDataStream * pStream_)243 static void SkipDataToNextTag( enkiNBTDataStream* pStream_ )
244 {
245 switch( pStream_->currentTag.tagId )
246 {
247 case enkiNBTTAG_End:
248 // no data, so do nothing.
249 break;
250 case enkiNBTTAG_Byte:
251 pStream_->pNextTag += 1; // 1 byte
252 break;
253 case enkiNBTTAG_Short:
254 pStream_->pNextTag += 2; // 2 bytes
255 break;
256 case enkiNBTTAG_Int:
257 pStream_->pNextTag += 4;
258 break;
259 case enkiNBTTAG_Long:
260 pStream_->pNextTag += 8;
261 break;
262 case enkiNBTTAG_Float:
263 pStream_->pNextTag += 4;
264 break;
265 case enkiNBTTAG_Double:
266 pStream_->pNextTag += 8;
267 break;
268 case enkiNBTTAG_Byte_Array:
269 {
270 int32_t length = enkiNBTReadInt32( pStream_ );
271 pStream_->pNextTag = pStream_->pCurrPos + length * 1; // array of bytes
272 break;
273 }
274 case enkiNBTTAG_String:
275 {
276 int32_t length = enkiNBTReadInt16( pStream_ );
277 pStream_->pNextTag = pStream_->pCurrPos + length;
278 break;
279 }
280 case enkiNBTTAG_List:
281 // read as a compound type
282 break;
283 case enkiNBTTAG_Compound:
284 // data is in standard format, so do nothing.
285 break;
286 case enkiNBTTAG_Int_Array:
287 {
288 int32_t length = enkiNBTReadInt32( pStream_ );
289 pStream_->pNextTag = pStream_->pCurrPos + length * 4; // array of ints (4 bytes)
290 break;
291 }
292 default:
293 assert( 0 );
294 break;
295 }
296 }
297
enkiNBTReadNextTag(enkiNBTDataStream * pStream_)298 int enkiNBTReadNextTag( enkiNBTDataStream* pStream_ )
299 {
300 if( ( enkiNBTTAG_Compound == pStream_->currentTag.tagId ) || ( enkiNBTTAG_List == pStream_->currentTag.tagId ) )
301 {
302 pStream_->level++;
303 pStream_->parentTags[ pStream_->level ] = pStream_->currentTag;
304 }
305 if( ( pStream_->level >= 0 ) && ( enkiNBTTAG_List == pStream_->parentTags[ pStream_->level ].tagId ) )
306 {
307 if( pStream_->parentTags[ pStream_->level ].listCurrItem == pStream_->parentTags[ pStream_->level ].listNumItems )
308 {
309 pStream_->level--;
310 }
311 else
312 {
313 pStream_->currentTag.tagId = pStream_->parentTags[ pStream_->level ].listItemTagId;
314 pStream_->currentTag.pName = NULL;
315 SkipDataToNextTag( pStream_ );
316 pStream_->parentTags[ pStream_->level ].listCurrItem++;
317 return 1;
318 }
319 }
320
321 if( pStream_->pNextTag >= pStream_->pDataEnd )
322 {
323 return 0;
324 }
325 pStream_->pCurrPos = pStream_->pNextTag;
326
327 // Get Tag Header
328 pStream_->currentTag.pName = NULL;
329 pStream_->currentTag.tagId = *(pStream_->pCurrPos++);
330 if( enkiNBTTAG_End != pStream_->currentTag.tagId )
331 {
332 if( 0xff == *(pStream_->pCurrPos) )
333 {
334 pStream_->pCurrPos++;
335 pStream_->currentTag.pName = ( char* )pStream_->pCurrPos;
336 while( *(pStream_->pCurrPos++) != 0 );
337 }
338 else
339 {
340 int32_t lengthOfName = enkiNBTReadInt16( pStream_ );
341 if( lengthOfName )
342 {
343 *( pStream_->pCurrPos - 2 ) = 0xff; // this value will not be seen as a length since it will be negative
344 pStream_->currentTag.pName = ( char* )( pStream_->pCurrPos - 1 );
345 memmove( pStream_->currentTag.pName, pStream_->pCurrPos, lengthOfName );
346 pStream_->pCurrPos += lengthOfName - 1;
347 pStream_->pCurrPos[ 0 ] = 0; // null terminator
348 pStream_->pCurrPos += 1;
349 }
350 }
351 }
352 if( enkiNBTTAG_List == pStream_->currentTag.tagId )
353 {
354 pStream_->currentTag.listItemTagId = *(pStream_->pCurrPos++);
355 pStream_->currentTag.listNumItems = enkiNBTReadInt32( pStream_ );
356 pStream_->currentTag.listCurrItem = 0;
357 }
358 pStream_->pNextTag = pStream_->pCurrPos;
359
360 SkipDataToNextTag( pStream_ );
361
362 if( ( pStream_->level >= 0 ) && ( enkiNBTTAG_End == pStream_->currentTag.tagId ) )
363 {
364 pStream_->level--;
365 }
366
367 return 1;
368 }
369
370
enkiNBTRewind(enkiNBTDataStream * pStream_)371 void enkiNBTRewind( enkiNBTDataStream* pStream_ )
372 {
373 memset( &(pStream_->currentTag), 0, sizeof( enkiNBTTagHeader ) );
374 pStream_->pCurrPos = pStream_->pData;
375 pStream_->level = -1;
376 pStream_->pNextTag = pStream_->pData;
377 }
378
enkiRegionFileInit(enkiRegionFile * pRegionFile_)379 void enkiRegionFileInit( enkiRegionFile* pRegionFile_ )
380 {
381 memset( pRegionFile_, 0, sizeof( enkiRegionFile ) );
382 }
383
384
enkiRegionFileLoad(FILE * fp_)385 enkiRegionFile enkiRegionFileLoad( FILE * fp_ )
386 {
387 enkiRegionFile regionFile;
388 enkiRegionFileInit( ®ionFile );
389 fseek( fp_, 0, SEEK_END );
390 regionFile.regionDataSize = ftell( fp_ );
391 fseek( fp_, 0, SEEK_SET ); // return to start position
392
393 // get the data in the chunks data section
394 regionFile.pRegionData = (uint8_t*)malloc( regionFile.regionDataSize );
395 fread( regionFile.pRegionData, 1, regionFile.regionDataSize, fp_ ); // note: because sectionDataChunks is an array of single bytes, sizeof( sectionDataChunks ) == sectionDataSize
396
397 return regionFile;
398 }
399
400
enkiInitNBTDataStreamForChunk(enkiRegionFile regionFile_,int32_t chunkNr_,enkiNBTDataStream * pStream_)401 void enkiInitNBTDataStreamForChunk( enkiRegionFile regionFile_, int32_t chunkNr_, enkiNBTDataStream* pStream_ )
402 {
403 RegionHeader* header = (RegionHeader*)regionFile_.pRegionData;
404 uint32_t locationOffset = GetChunkLocation( header->sectionChunksInfos[ chunkNr_ ] );
405 if( locationOffset > sizeof( RegionHeader ) )
406 {
407 uint32_t length = Get32BitInt( *( BigEndian4BytesTo32BitInt* )®ionFile_.pRegionData[ locationOffset ] );
408 uint8_t compression_type = regionFile_.pRegionData[ locationOffset + 4 ]; // we ignore this as unused for now
409 --length; // length includes compression_type
410 // get the data and decompress it
411 uint8_t* dataCompressed = ®ionFile_.pRegionData[ locationOffset + 5 ];
412 enkiNBTInitFromMemoryCompressed( pStream_, dataCompressed, length, 0 );
413 }
414 else
415 {
416 enkiNBTInitFromMemoryUncompressed( pStream_, NULL, 0 ); // clears stream
417 }
418 }
419
enkiGetTimestampForChunk(enkiRegionFile regionFile_,int32_t chunkNr_)420 int32_t enkiGetTimestampForChunk( enkiRegionFile regionFile_, int32_t chunkNr_ )
421 {
422 RegionHeader* header = (RegionHeader*)regionFile_.pRegionData;
423 return Get32BitInt( header->sectionChunksTimestamps[ chunkNr_ ] );
424 }
425
enkiRegionFileFreeAllocations(enkiRegionFile * pRegionFile_)426 void enkiRegionFileFreeAllocations(enkiRegionFile * pRegionFile_)
427 {
428 free( pRegionFile_->pRegionData );
429 memset( pRegionFile_, 0, sizeof(enkiRegionFile) );
430 }
431
enkiAreStringsEqual(const char * lhs_,const char * rhs_)432 int enkiAreStringsEqual( const char * lhs_, const char * rhs_ )
433 {
434 if( lhs_ && rhs_ )
435 {
436 if( 0 == strcmp( lhs_, rhs_ ) )
437 {
438 return 1;
439 }
440 }
441 return 0;
442 }
443
enkiChunkInit(enkiChunkBlockData * pChunk_)444 void enkiChunkInit( enkiChunkBlockData* pChunk_ )
445 {
446 memset( pChunk_, 0, sizeof( enkiChunkBlockData ) );
447 }
448
enkiNBTReadChunk(enkiNBTDataStream * pStream_)449 enkiChunkBlockData enkiNBTReadChunk( enkiNBTDataStream * pStream_ )
450 {
451 enkiChunkBlockData chunk;
452 enkiChunkInit( &chunk );
453 while( enkiNBTReadNextTag( pStream_ ) )
454 {
455 if( enkiAreStringsEqual( "Level", pStream_->currentTag.pName ) )
456 {
457 int foundXPos = 0;
458 int foundZPos = 0;
459 int foundSection = 0;
460 while( enkiNBTReadNextTag( pStream_ ) )
461 {
462 if( enkiAreStringsEqual( "xPos", pStream_->currentTag.pName ) )
463 {
464 foundXPos = 1;
465 chunk.xPos = enkiNBTReadInt32( pStream_ );
466 }
467 else if( enkiAreStringsEqual( "zPos", pStream_->currentTag.pName ) )
468 {
469 foundZPos = 1;
470 chunk.zPos = enkiNBTReadInt32( pStream_ );
471 }
472 else if( enkiAreStringsEqual( "Sections", pStream_->currentTag.pName ) )
473 {
474 foundSection = 1;
475 int32_t levelParent = pStream_->level;
476 int8_t sectionY = -1;
477 uint8_t* pBlocks = NULL;
478 do
479 {
480 if( 0 == enkiNBTReadNextTag( pStream_ ) )
481 {
482 break;
483 }
484 if( enkiNBTTAG_Compound == pStream_->currentTag.tagId )
485 {
486 chunk.countOfSections++;
487 }
488 if( enkiAreStringsEqual( "Blocks", pStream_->currentTag.pName ) )
489 {
490 pBlocks = pStream_->pCurrPos;
491 }
492 if( enkiAreStringsEqual( "Y", pStream_->currentTag.pName ) )
493 {
494 sectionY = enkiNBTReadInt8( pStream_ );
495 }
496 if( pBlocks && ( 0 <= sectionY ) )
497 {
498 chunk.sections[ sectionY ] = pBlocks;
499 sectionY = -1;
500 pBlocks = NULL;
501 }
502 } while( pStream_->level > levelParent );
503 }
504
505 if( foundXPos && foundZPos && foundSection )
506 {
507 return chunk;
508 }
509 }
510 }
511 }
512 // reset to empty
513 enkiChunkInit( &chunk );
514 return chunk;
515 }
516
enkiGetChunkOrigin(enkiChunkBlockData * pChunk_)517 enkiMICoordinate enkiGetChunkOrigin(enkiChunkBlockData * pChunk_)
518 {
519 enkiMICoordinate retVal;
520 retVal.x = pChunk_->xPos * ENKI_MI_SIZE_SECTIONS;
521 retVal.y = 0;
522 retVal.z = pChunk_->zPos * ENKI_MI_SIZE_SECTIONS;
523 return retVal;
524 }
525
enkiGetChunkSectionOrigin(enkiChunkBlockData * pChunk_,int32_t section_)526 enkiMICoordinate enkiGetChunkSectionOrigin(enkiChunkBlockData * pChunk_, int32_t section_)
527 {
528 enkiMICoordinate retVal;
529 retVal.x = pChunk_->xPos * ENKI_MI_SIZE_SECTIONS;
530 retVal.y = section_ * ENKI_MI_SIZE_SECTIONS;
531 retVal.z = pChunk_->zPos * ENKI_MI_SIZE_SECTIONS;
532 return retVal;
533 }
534
enkiGetChunkSectionVoxel(enkiChunkBlockData * pChunk_,int32_t section_,enkiMICoordinate sectionOffset_)535 uint8_t enkiGetChunkSectionVoxel(enkiChunkBlockData * pChunk_, int32_t section_, enkiMICoordinate sectionOffset_)
536 {
537 uint8_t retVal = 0;
538 uint8_t* pSection = pChunk_->sections[ section_ ];
539 uint8_t* pVoxel = pSection + sectionOffset_.y*ENKI_MI_SIZE_SECTIONS*ENKI_MI_SIZE_SECTIONS + sectionOffset_.z*ENKI_MI_SIZE_SECTIONS + sectionOffset_.x;
540 retVal = *pVoxel;
541 return retVal;
542 }
543
enkiGetMineCraftPalette()544 uint32_t* enkiGetMineCraftPalette()
545 {
546 return minecraftPalette;
547 }
548