Bug Summary

File:Formats.c
Warning:line 868, column 35
The left operand of '!=' is a garbage value

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -triple amd64-unknown-openbsd6.8 -analyze -disable-free -disable-llvm-verifier -discard-value-names -main-file-name Formats.c -analyzer-store=region -analyzer-opt-analyze-nested-blocks -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mthread-model posix -mframe-pointer=all -relaxed-aliasing -fno-rounding-math -masm-verbose -mconstructor-aliases -munwind-tables -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -dwarf-column-info -fno-split-dwarf-inlining -debugger-tuning=gdb -resource-dir /usr/local/lib/clang/10.0.1 -I /usr/X11R6/include -I /usr/local/include -fdebug-compilation-dir /home/ben/Projects/ClassiCube/src -ferror-limit 19 -fmessage-length 0 -fwrapv -D_RET_PROTECTOR -ret-protector -fgnuc-version=4.2.1 -fobjc-runtime=gnustep -fdiagnostics-show-option -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -o /home/ben/Projects/ClassiCube/src/scan/2020-12-10-180422-33404-1 -x c Formats.c
1#include "Formats.h"
2#include "String.h"
3#include "World.h"
4#include "Deflate.h"
5#include "Block.h"
6#include "Entity.h"
7#include "Platform.h"
8#include "ExtMath.h"
9#include "Logger.h"
10#include "Game.h"
11#include "Server.h"
12#include "Event.h"
13#include "Funcs.h"
14#include "Errors.h"
15#include "Stream.h"
16#include "Chat.h"
17#include "Inventory.h"
18#include "TexturePack.h"
19
20
21/*########################################################################################################################*
22*--------------------------------------------------------General----------------------------------------------------------*
23*#########################################################################################################################*/
24static cc_result Map_ReadBlocks(struct Stream* stream) {
25 World.Volume = World.Width * World.Length * World.Height;
26 World.Blocks = (BlockRaw*)Mem_TryAlloc(World.Volume, 1);
27
28 if (!World.Blocks) return ERR_OUT_OF_MEMORY;
29 return Stream_Read(stream, World.Blocks, World.Volume);
30}
31
32static cc_result Map_SkipGZipHeader(struct Stream* stream) {
33 struct GZipHeader gzHeader;
34 cc_result res;
35 GZipHeader_Init(&gzHeader);
36
37 while (!gzHeader.done) {
38 if ((res = GZipHeader_Read(stream, &gzHeader))) return res;
39 }
40 return 0;
41}
42
43IMapImporter Map_FindImporter(const cc_string* path) {
44 static const cc_string cw = String_FromConst(".cw"){ ".cw", (sizeof(".cw") - 1), (sizeof(".cw") - 1)}, lvl = String_FromConst(".lvl"){ ".lvl", (sizeof(".lvl") - 1), (sizeof(".lvl") - 1)};
45 static const cc_string fcm = String_FromConst(".fcm"){ ".fcm", (sizeof(".fcm") - 1), (sizeof(".fcm") - 1)}, dat = String_FromConst(".dat"){ ".dat", (sizeof(".dat") - 1), (sizeof(".dat") - 1)};
46
47 if (String_CaselessEnds(path, &cw)) return Cw_Load;
48#ifndef CC_BUILD_WEB
49 if (String_CaselessEnds(path, &lvl)) return Lvl_Load;
50 if (String_CaselessEnds(path, &fcm)) return Fcm_Load;
51 if (String_CaselessEnds(path, &dat)) return Dat_Load;
52#endif
53
54 return NULL((void*)0);
55}
56
57void Map_LoadFrom(const cc_string* path) {
58 IMapImporter importer;
59 struct Stream stream;
60 cc_result res;
61 Game_Reset();
62
63 res = Stream_OpenFile(&stream, path);
64 if (res) { Logger_SysWarn2(res, "opening", path); return; }
65
66 importer = Map_FindImporter(path);
67 if (!importer) {
68 Logger_SysWarn2(ERR_NOT_SUPPORTED, "decoding", path);
69 } else if ((res = importer(&stream))) {
70 World_Reset();
71 Logger_SysWarn2(res, "decoding", path);
72 }
73
74 res = stream.Close(&stream);
75 if (res) { Logger_SysWarn2(res, "closing", path); }
76
77 World_SetNewMap(World.Blocks, World.Width, World.Height, World.Length);
78 LocalPlayer_MoveToSpawn();
79}
80
81
82/*########################################################################################################################*
83*--------------------------------------------------MCSharp level Format---------------------------------------------------*
84*#########################################################################################################################*/
85#define LVL_CUSTOMTILE163 163
86#define LVL_CHUNKSIZE16 16
87/* MCSharp* format is a GZIP compressed binary map format. All metadata is discarded.
88 U16 "Identifier" (must be 1874)
89 U16 "Width", "Length", "Height"
90 U16 "SpawnX", "SpawnZ", "SpawnY"
91 U8 "Yaw", "Pitch"
92 U16 "Build permissions" (ignored)
93 U8* "Blocks"
94
95 -- this data is only in MCGalaxy maps
96 U8 "Identifier" (0xBD for 'block definitions', i.e. custom blocks)
97 U8* "Data" (16x16x16 sparsely allocated chunks)
98}*/
99
100static const cc_uint8 Lvl_table[256] = {
101 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
102 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
103 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
104 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
105 64, 65, 0, 0, 0, 0, 39, 36, 36, 10, 46, 21, 22, 22, 22, 22,
106 4, 0, 22, 21, 0, 22, 23, 24, 22, 26, 27, 28, 30, 31, 32, 33,
107 34, 35, 36, 22, 20, 49, 45, 1, 4, 0, 9, 11, 4, 19, 5, 17,
108 10, 49, 20, 1, 18, 12, 5, 25, 46, 44, 17, 49, 20, 1, 18, 12,
109 5, 25, 36, 34, 0, 9, 11, 46, 44, 0, 9, 11, 8, 10, 22, 27,
110 22, 8, 10, 28, 17, 49, 20, 1, 18, 12, 5, 25, 46, 44, 11, 9,
111 0, 9, 11, 163, 0, 0, 9, 11, 0, 0, 0, 0, 0, 0, 0, 28,
112 22, 21, 11, 0, 0, 0, 46, 46, 10, 10, 46, 20, 41, 42, 11, 9,
113 0, 8, 10, 10, 8, 0, 22, 22, 0, 0, 0, 0, 0, 0, 0, 0,
114 0, 0, 0, 21, 10, 0, 0, 0, 0, 0, 22, 22, 42, 3, 2, 29,
115 47, 0, 0, 0, 0, 0, 27, 46, 48, 24, 22, 36, 34, 8, 10, 21,
116 29, 22, 10, 22, 22, 41, 19, 35, 21, 29, 49, 34, 16, 41, 0, 22
117};
118
119static cc_result Lvl_ReadCustomBlocks(struct Stream* stream) {
120 cc_uint8 chunk[LVL_CHUNKSIZE16 * LVL_CHUNKSIZE16 * LVL_CHUNKSIZE16];
121 cc_uint8 hasCustom;
122 int baseIndex, index, xx, yy, zz;
123 cc_result res;
124 int x, y, z, i;
125
126 /* skip bounds checks when we know chunk is entirely inside map */
127 int adjWidth = World.Width & ~0x0F;
128 int adjHeight = World.Height & ~0x0F;
129 int adjLength = World.Length & ~0x0F;
130
131 for (y = 0; y < World.Height; y += LVL_CHUNKSIZE16) {
132 for (z = 0; z < World.Length; z += LVL_CHUNKSIZE16) {
133 for (x = 0; x < World.Width; x += LVL_CHUNKSIZE16) {
134
135 if ((res = stream->ReadU8(stream, &hasCustom))) return res;
136 if (hasCustom != 1) continue;
137 if ((res = Stream_Read(stream, chunk, sizeof(chunk)))) return res;
138 baseIndex = World_Pack(x, y, z)(((y) * World.Length + (z)) * World.Width + (x));
139
140 if ((x + LVL_CHUNKSIZE16) <= adjWidth && (y + LVL_CHUNKSIZE16) <= adjHeight && (z + LVL_CHUNKSIZE16) <= adjLength) {
141 for (i = 0; i < sizeof(chunk); i++) {
142 xx = i & 0xF; yy = (i >> 8) & 0xF; zz = (i >> 4) & 0xF;
143
144 index = baseIndex + World_Pack(xx, yy, zz)(((yy) * World.Length + (zz)) * World.Width + (xx));
145 World.Blocks[index] = World.Blocks[index] == LVL_CUSTOMTILE163 ? chunk[i] : World.Blocks[index];
146 }
147 } else {
148 for (i = 0; i < sizeof(chunk); i++) {
149 xx = i & 0xF; yy = (i >> 8) & 0xF; zz = (i >> 4) & 0xF;
150 if ((x + xx) >= World.Width || (y + yy) >= World.Height || (z + zz) >= World.Length) continue;
151
152 index = baseIndex + World_Pack(xx, yy, zz)(((yy) * World.Length + (zz)) * World.Width + (xx));
153 World.Blocks[index] = World.Blocks[index] == LVL_CUSTOMTILE163 ? chunk[i] : World.Blocks[index];
154 }
155 }
156 }
157 }
158 }
159 return 0;
160}
161
162cc_result Lvl_Load(struct Stream* stream) {
163 cc_uint8 header[18];
164 cc_uint8* blocks;
165 cc_uint8 section;
166 cc_result res;
167 int i;
168
169 struct LocalPlayer* p = &LocalPlayer_Instance;
170 struct Stream compStream;
171 struct InflateState state;
172 Inflate_MakeStream2(&compStream, &state, stream);
173
174 if ((res = Map_SkipGZipHeader(stream))) return res;
175 if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res;
176 if (Stream_GetU16_LE(&header[0]) != 1874) return LVL_ERR_VERSION;
177
178 World.Width = Stream_GetU16_LE(&header[2]);
179 World.Length = Stream_GetU16_LE(&header[4]);
180 World.Height = Stream_GetU16_LE(&header[6]);
181
182 p->Spawn.X = Stream_GetU16_LE(&header[8]);
183 p->Spawn.Z = Stream_GetU16_LE(&header[10]);
184 p->Spawn.Y = Stream_GetU16_LE(&header[12]);
185 p->SpawnYaw = Math_Packed2Deg(header[14])((header[14]) * 360.0f / 256.0f);
186 p->SpawnPitch = Math_Packed2Deg(header[15])((header[15]) * 360.0f / 256.0f);
187 /* (2) pervisit, perbuild permissions */
188
189 if ((res = Map_ReadBlocks(&compStream))) return res;
190 blocks = World.Blocks;
191 /* Bulk convert 4 blocks at once */
192 for (i = 0; i < (World.Volume & ~3); i += 4) {
193 *blocks = Lvl_table[*blocks]; blocks++;
194 *blocks = Lvl_table[*blocks]; blocks++;
195 *blocks = Lvl_table[*blocks]; blocks++;
196 *blocks = Lvl_table[*blocks]; blocks++;
197 }
198 for (; i < World.Volume; i++) {
199 *blocks = Lvl_table[*blocks]; blocks++;
200 }
201
202 /* 0xBD section type is not present in older .lvl files */
203 res = compStream.ReadU8(&compStream, &section);
204 if (res == ERR_END_OF_STREAM) return 0;
205
206 if (res) return res;
207 return section == 0xBD ? Lvl_ReadCustomBlocks(&compStream) : 0;
208}
209
210
211/*########################################################################################################################*
212*----------------------------------------------------fCraft map format----------------------------------------------------*
213*#########################################################################################################################*/
214/* fCraft* format is a binary map format. All metadata is discarded.
215 U32 "Identifier" (must be FC2AF40)
216 U8 "Revision" (only '13' supported)
217 U16 "Width", "Height", "Length"
218 U32 "SpawnX", "SpawnY", "SpawnZ"
219 U8 "Yaw", "Pitch"
220 U32 "DateModified", "DateCreated" (ignored)
221 U8* "UUID"
222 U8 "Layers" (only maps with 1 layer supported)
223 U8* "LayersInfo" (ignored, assumes only layer is map blocks)
224 U32 "MetaCount"
225 METADATA { STR "Group", "Key", "Value" }
226 U8* "Blocks"
227}*/
228static cc_result Fcm_ReadString(struct Stream* stream) {
229 cc_uint8 data[2];
230 int len;
231 cc_result res;
232
233 if ((res = Stream_Read(stream, data, sizeof(data)))) return res;
234 len = Stream_GetU16_LE(data);
235
236 return stream->Skip(stream, len);
237}
238
239cc_result Fcm_Load(struct Stream* stream) {
240 cc_uint8 header[79];
241 cc_result res;
242 int i, count;
243
244 struct LocalPlayer* p = &LocalPlayer_Instance;
245 struct Stream compStream;
246 struct InflateState state;
247 Inflate_MakeStream2(&compStream, &state, stream);
248
249 if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
250 if (Stream_GetU32_LE(&header[0]) != 0x0FC2AF40UL) return FCM_ERR_IDENTIFIER;
251 if (header[4] != 13) return FCM_ERR_REVISION;
252
253 World.Width = Stream_GetU16_LE(&header[5]);
254 World.Height = Stream_GetU16_LE(&header[7]);
255 World.Length = Stream_GetU16_LE(&header[9]);
256
257 p->Spawn.X = ((int)Stream_GetU32_LE(&header[11])) / 32.0f;
258 p->Spawn.Y = ((int)Stream_GetU32_LE(&header[15])) / 32.0f;
259 p->Spawn.Z = ((int)Stream_GetU32_LE(&header[19])) / 32.0f;
260 p->SpawnYaw = Math_Packed2Deg(header[23])((header[23]) * 360.0f / 256.0f);
261 p->SpawnPitch = Math_Packed2Deg(header[24])((header[24]) * 360.0f / 256.0f);
262
263 /* header[25] (4) date modified */
264 /* header[29] (4) date created */
265 Mem_Copy(&World.Uuid, &header[33], WORLD_UUID_LEN16);
266 /* header[49] (26) layer index */
267 count = (int)Stream_GetU32_LE(&header[75]);
268
269 /* header isn't compressed, rest of data is though */
270 for (i = 0; i < count; i++) {
271 if ((res = Fcm_ReadString(&compStream))) return res; /* Group */
272 if ((res = Fcm_ReadString(&compStream))) return res; /* Key */
273 if ((res = Fcm_ReadString(&compStream))) return res; /* Value */
274 }
275
276 return Map_ReadBlocks(&compStream);
277}
278
279
280/*########################################################################################################################*
281*---------------------------------------------------------NBTFile---------------------------------------------------------*
282*#########################################################################################################################*/
283enum NbtTagType {
284 NBT_END, NBT_I8, NBT_I16, NBT_I32, NBT_I64, NBT_F32,
285 NBT_R64, NBT_I8S, NBT_STR, NBT_LIST, NBT_DICT
286};
287
288#define NBT_SMALL_SIZE64 STRING_SIZE64
289#define NBT_STRING_SIZE64 STRING_SIZE64
290#define NbtTag_IsSmall(tag)((tag)->dataSize <= 64) ((tag)->dataSize <= NBT_SMALL_SIZE64)
291struct NbtTag;
292
293struct NbtTag {
294 struct NbtTag* parent;
295 cc_uint8 type;
296 cc_string name;
297 cc_uint32 dataSize; /* size of data for arrays */
298
299 union {
300 cc_uint8 u8;
301 cc_int16 i16;
302 cc_uint16 u16;
303 cc_uint32 u32;
304 float f32;
305 cc_uint8 small[NBT_SMALL_SIZE64];
306 cc_uint8* big; /* malloc for big byte arrays */
307 struct { cc_string text; char buffer[NBT_STRING_SIZE64]; } str;
308 } value;
309 char _nameBuffer[NBT_STRING_SIZE64];
310 cc_result result;
311};
312
313static cc_uint8 NbtTag_U8(struct NbtTag* tag) {
314 if (tag->type != NBT_I8) Logger_Abort("Expected I8 NBT tag");
315 return tag->value.u8;
316}
317
318static cc_int16 NbtTag_I16(struct NbtTag* tag) {
319 if (tag->type != NBT_I16) Logger_Abort("Expected I16 NBT tag");
320 return tag->value.i16;
321}
322
323static cc_uint16 NbtTag_U16(struct NbtTag* tag) {
324 if (tag->type != NBT_I16) Logger_Abort("Expected I16 NBT tag");
325 return tag->value.u16;
326}
327
328static float NbtTag_F32(struct NbtTag* tag) {
329 if (tag->type != NBT_F32) Logger_Abort("Expected F32 NBT tag");
330 return tag->value.f32;
331}
332
333static cc_uint8* NbtTag_U8_Array(struct NbtTag* tag, int minSize) {
334 if (tag->type != NBT_I8S) Logger_Abort("Expected I8_Array NBT tag");
335 if (tag->dataSize < minSize) Logger_Abort("I8_Array NBT tag too small");
336
337 return NbtTag_IsSmall(tag)((tag)->dataSize <= 64) ? tag->value.small : tag->value.big;
338}
339
340static cc_string NbtTag_String(struct NbtTag* tag) {
341 if (tag->type != NBT_STR) Logger_Abort("Expected String NBT tag");
342 return tag->value.str.text;
343}
344
345static cc_result Nbt_ReadString(struct Stream* stream, cc_string* str) {
346 cc_uint8 buffer[NBT_STRING_SIZE64 * 4];
347 int len;
348 cc_result res;
349
350 if ((res = Stream_Read(stream, buffer, 2))) return res;
351 len = Stream_GetU16_BE(buffer);
352
353 if (len > Array_Elems(buffer)(sizeof(buffer) / sizeof(buffer[0]))) return CW_ERR_STRING_LEN;
354 if ((res = Stream_Read(stream, buffer, len))) return res;
355
356 String_AppendUtf8(str, buffer, len);
357 return 0;
358}
359
360typedef void (*Nbt_Callback)(struct NbtTag* tag);
361static cc_result Nbt_ReadTag(cc_uint8 typeId, cc_bool readTagName, struct Stream* stream, struct NbtTag* parent, Nbt_Callback callback) {
362 struct NbtTag tag;
363 cc_uint8 childType;
364 cc_uint8 tmp[5];
365 cc_result res;
366 cc_uint32 i, count;
367
368 if (typeId == NBT_END) return 0;
369 tag.type = typeId;
370 tag.parent = parent;
371 tag.dataSize = 0;
372 String_InitArray(tag.name, tag._nameBuffer)tag.name.buffer = tag._nameBuffer; tag.name.length = 0; tag.name
.capacity = sizeof(tag._nameBuffer);
;
373
374 if (readTagName) {
375 res = Nbt_ReadString(stream, &tag.name);
376 if (res) return res;
377 }
378
379 switch (typeId) {
380 case NBT_I8:
381 res = stream->ReadU8(stream, &tag.value.u8);
382 break;
383 case NBT_I16:
384 res = Stream_Read(stream, tmp, 2);
385 tag.value.u16 = Stream_GetU16_BE(tmp);
386 break;
387 case NBT_I32:
388 case NBT_F32:
389 res = Stream_ReadU32_BE(stream, &tag.value.u32);
390 break;
391 case NBT_I64:
392 case NBT_R64:
393 res = stream->Skip(stream, 8);
394 break; /* (8) data */
395
396 case NBT_I8S:
397 if ((res = Stream_ReadU32_BE(stream, &tag.dataSize))) break;
398
399 if (NbtTag_IsSmall(&tag)((&tag)->dataSize <= 64)) {
400 res = Stream_Read(stream, tag.value.small, tag.dataSize);
401 } else {
402 tag.value.big = (cc_uint8*)Mem_TryAlloc(tag.dataSize, 1);
403 if (!tag.value.big) return ERR_OUT_OF_MEMORY;
404
405 res = Stream_Read(stream, tag.value.big, tag.dataSize);
406 if (res) Mem_Free(tag.value.big);
407 }
408 break;
409 case NBT_STR:
410 String_InitArray(tag.value.str.text, tag.value.str.buffer)tag.value.str.text.buffer = tag.value.str.buffer; tag.value.str
.text.length = 0; tag.value.str.text.capacity = sizeof(tag.value
.str.buffer);
;
411 res = Nbt_ReadString(stream, &tag.value.str.text);
412 break;
413
414 case NBT_LIST:
415 if ((res = Stream_Read(stream, tmp, 5))) break;
416 childType = tmp[0];
417 count = Stream_GetU32_BE(&tmp[1]);
418
419 for (i = 0; i < count; i++) {
420 res = Nbt_ReadTag(childType, false0, stream, &tag, callback);
421 if (res) break;
422 }
423 break;
424
425 case NBT_DICT:
426 for (;;) {
427 if ((res = stream->ReadU8(stream, &childType))) break;
428 if (childType == NBT_END) break;
429
430 res = Nbt_ReadTag(childType, true1, stream, &tag, callback);
431 if (res) break;
432 }
433 break;
434
435 default: return NBT_ERR_UNKNOWN;
436 }
437
438 if (res) return res;
439 tag.result = 0;
440 callback(&tag);
441 /* NOTE: callback must set DataBig to NULL, if doesn't want it to be freed */
442 if (!NbtTag_IsSmall(&tag)((&tag)->dataSize <= 64)) Mem_Free(tag.value.big);
443 return tag.result;
444}
445#define IsTag(tag, tagName)(String_CaselessEqualsConst(&tag->name, tagName)) (String_CaselessEqualsConst(&tag->name, tagName))
446
447/*########################################################################################################################*
448*--------------------------------------------------ClassicWorld format----------------------------------------------------*
449*#########################################################################################################################*/
450/* ClassicWorld is a NBT tag based map format. Tags not listed below are discarded.
451COMPOUND "ClassicWorld" {
452 U8* "UUID"
453 U16 "X", "Y", "Z"
454 COMPOUND "Spawn" {
455 I16 "X", "Y", "Z"
456 U8 "H", "P"
457 }
458 U8* "BlockArray" (lower 8 bits, required)
459 U8* "BlockArray2" (upper 8 bits, optional)
460 COMPOUND "Metadata" {
461 COMPOUND "CPE" {
462 COMPOUND "ClickDistance" { U16 "Reach" }
463 COMPOUND "EnvWeatherType" { U8 "WeatherType" }
464 COMPOUND "EnvMapAppearance" {
465 U8 "SideBlock", "EdgeBlock"
466 I16 "SidesLevel"
467 STR "TextureURL"
468 }
469 COMPOUND "EnvColors" {
470 COMPOUND "Sky" { U16 "R", "G", "B" }
471 COMPOUND "Cloud" { U16 "R", "G", "B" }
472 COMPOUND "Fog" { U16 "R", "G", "B" }
473 COMPOUND "Sunlight" { U16 "R", "G", "B" }
474 COMPOUND "Ambient" { U16 "R", "G", "B" }
475 }
476 COMPOUND "BlockDefinitions" {
477 COMPOUND "Block_XYZ" { (name must start with 'Block')
478 U8 "ID", U16 "ID2"
479 STR "Name"
480 F32 "Speed"
481 U8 "CollideType", "BlockDraw"
482 U8 "TransmitsLight", "FullBright"
483 U8 "Shape" , "WalkSound"
484 U8* "Textures", "Fog", "Coords"
485 }
486 }
487 }
488 }
489}*/
490static BlockRaw* Cw_GetBlocks(struct NbtTag* tag) {
491 BlockRaw* ptr;
492 if (NbtTag_IsSmall(tag)((tag)->dataSize <= 64)) {
493 ptr = (BlockRaw*)Mem_Alloc(tag->dataSize, 1, ".cw map blocks");
494 Mem_Copy(ptr, tag->value.small, tag->dataSize);
495 } else {
496 ptr = tag->value.big;
497 tag->value.big = NULL((void*)0); /* So Nbt_ReadTag doesn't call Mem_Free on World.Blocks */
498 }
499 return ptr;
500}
501
502static void Cw_Callback_1(struct NbtTag* tag) {
503 if (IsTag(tag, "X")(String_CaselessEqualsConst(&tag->name, "X"))) { World.Width = NbtTag_U16(tag); return; }
504 if (IsTag(tag, "Y")(String_CaselessEqualsConst(&tag->name, "Y"))) { World.Height = NbtTag_U16(tag); return; }
505 if (IsTag(tag, "Z")(String_CaselessEqualsConst(&tag->name, "Z"))) { World.Length = NbtTag_U16(tag); return; }
506
507 if (IsTag(tag, "UUID")(String_CaselessEqualsConst(&tag->name, "UUID"))) {
508 if (tag->dataSize != WORLD_UUID_LEN16) {
509 tag->result = CW_ERR_UUID_LEN;
510 } else {
511 Mem_Copy(World.Uuid, tag->value.small, WORLD_UUID_LEN16);
512 }
513 return;
514 }
515
516 if (IsTag(tag, "BlockArray")(String_CaselessEqualsConst(&tag->name, "BlockArray"))) {
517 World.Volume = tag->dataSize;
518 World.Blocks = Cw_GetBlocks(tag);
519 }
520#ifdef EXTENDED_BLOCKS
521 if (IsTag(tag, "BlockArray2")(String_CaselessEqualsConst(&tag->name, "BlockArray2")
)
) World_SetMapUpper(Cw_GetBlocks(tag));
522#endif
523}
524
525static void Cw_Callback_2(struct NbtTag* tag) {
526 struct LocalPlayer* p = &LocalPlayer_Instance;
527 if (!IsTag(tag->parent, "Spawn")(String_CaselessEqualsConst(&tag->parent->name, "Spawn"
))
) return;
528
529 if (IsTag(tag, "X")(String_CaselessEqualsConst(&tag->name, "X"))) { p->Spawn.X = NbtTag_I16(tag); return; }
530 if (IsTag(tag, "Y")(String_CaselessEqualsConst(&tag->name, "Y"))) { p->Spawn.Y = NbtTag_I16(tag); return; }
531 if (IsTag(tag, "Z")(String_CaselessEqualsConst(&tag->name, "Z"))) { p->Spawn.Z = NbtTag_I16(tag); return; }
532 if (IsTag(tag, "H")(String_CaselessEqualsConst(&tag->name, "H"))) { p->SpawnYaw = Math_Packed2Deg(NbtTag_U8(tag))((NbtTag_U8(tag)) * 360.0f / 256.0f); return; }
533 if (IsTag(tag, "P")(String_CaselessEqualsConst(&tag->name, "P"))) { p->SpawnPitch = Math_Packed2Deg(NbtTag_U8(tag))((NbtTag_U8(tag)) * 360.0f / 256.0f); return; }
534}
535
536static BlockID cw_curID;
537static int cw_colR, cw_colG, cw_colB;
538static PackedCol Cw_ParseCol(PackedCol defValue) {
539 int r = cw_colR, g = cw_colG, b = cw_colB;
540 if (r > 255 || g > 255 || b > 255) return defValue;
541 return PackedCol_Make(r, g, b, 255)(((cc_uint8)(r) << 0) | ((cc_uint8)(g) << 8) | ((
cc_uint8)(b) << 16) | ((cc_uint8)(255) << 24))
;
542}
543
544static void Cw_Callback_4(struct NbtTag* tag) {
545 BlockID id = cw_curID;
546 struct LocalPlayer* p = &LocalPlayer_Instance;
547
548 if (!IsTag(tag->parent->parent, "CPE")(String_CaselessEqualsConst(&tag->parent->parent->
name, "CPE"))
) return;
549 if (!IsTag(tag->parent->parent->parent, "Metadata")(String_CaselessEqualsConst(&tag->parent->parent->
parent->name, "Metadata"))
) return;
550
551 if (IsTag(tag->parent, "ClickDistance")(String_CaselessEqualsConst(&tag->parent->name, "ClickDistance"
))
) {
552 if (IsTag(tag, "Distance")(String_CaselessEqualsConst(&tag->name, "Distance"))) { p->ReachDistance = NbtTag_U16(tag) / 32.0f; return; }
553 }
554 if (IsTag(tag->parent, "EnvWeatherType")(String_CaselessEqualsConst(&tag->parent->name, "EnvWeatherType"
))
) {
555 if (IsTag(tag, "WeatherType")(String_CaselessEqualsConst(&tag->name, "WeatherType")
)
) { Env.Weather = NbtTag_U8(tag); return; }
556 }
557
558 if (IsTag(tag->parent, "EnvMapAppearance")(String_CaselessEqualsConst(&tag->parent->name, "EnvMapAppearance"
))
) {
559 if (IsTag(tag, "SideBlock")(String_CaselessEqualsConst(&tag->name, "SideBlock"))) { Env.SidesBlock = NbtTag_U8(tag); return; }
560 if (IsTag(tag, "EdgeBlock")(String_CaselessEqualsConst(&tag->name, "EdgeBlock"))) { Env.EdgeBlock = NbtTag_U8(tag); return; }
561 if (IsTag(tag, "SideLevel")(String_CaselessEqualsConst(&tag->name, "SideLevel"))) { Env.EdgeHeight = NbtTag_I16(tag); return; }
562
563 if (IsTag(tag, "TextureURL")(String_CaselessEqualsConst(&tag->name, "TextureURL"))) {
564 cc_string url = NbtTag_String(tag);
565 if (url.length) Server_RetrieveTexturePack(&url);
566 return;
567 }
568 }
569
570 /* Callback for compound tag is called after all its children have been processed */
571 if (IsTag(tag->parent, "EnvColors")(String_CaselessEqualsConst(&tag->parent->name, "EnvColors"
))
) {
572 if (IsTag(tag, "Sky")(String_CaselessEqualsConst(&tag->name, "Sky"))) {
573 Env.SkyCol = Cw_ParseCol(ENV_DEFAULT_SKY_COL(((cc_uint8)(0x99) << 0) | ((cc_uint8)(0xCC) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
); return;
574 } else if (IsTag(tag, "Cloud")(String_CaselessEqualsConst(&tag->name, "Cloud"))) {
575 Env.CloudsCol = Cw_ParseCol(ENV_DEFAULT_CLOUDS_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
); return;
576 } else if (IsTag(tag, "Fog")(String_CaselessEqualsConst(&tag->name, "Fog"))) {
577 Env.FogCol = Cw_ParseCol(ENV_DEFAULT_FOG_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
); return;
578 } else if (IsTag(tag, "Sunlight")(String_CaselessEqualsConst(&tag->name, "Sunlight"))) {
579 Env_SetSunCol(Cw_ParseCol(ENV_DEFAULT_SUN_COL(((cc_uint8)(0xFF) << 0) | ((cc_uint8)(0xFF) << 8
) | ((cc_uint8)(0xFF) << 16) | ((cc_uint8)(0xFF) <<
24))
)); return;
580 } else if (IsTag(tag, "Ambient")(String_CaselessEqualsConst(&tag->name, "Ambient"))) {
581 Env_SetShadowCol(Cw_ParseCol(ENV_DEFAULT_SHADOW_COL(((cc_uint8)(0x9B) << 0) | ((cc_uint8)(0x9B) << 8
) | ((cc_uint8)(0x9B) << 16) | ((cc_uint8)(0xFF) <<
24))
)); return;
582 }
583 }
584
585 if (IsTag(tag->parent, "BlockDefinitions")(String_CaselessEqualsConst(&tag->parent->name, "BlockDefinitions"
))
&& Game_AllowCustomBlocks) {
586 static const cc_string blockStr = String_FromConst("Block"){ "Block", (sizeof("Block") - 1), (sizeof("Block") - 1)};
587 if (!String_CaselessStarts(&tag->name, &blockStr)) return;
588
589 /* hack for sprite draw (can't rely on order of tags when reading) */
590 if (Blocks.SpriteOffset[id] == 0) {
591 Blocks.SpriteOffset[id] = Blocks.Draw[id];
592 Blocks.Draw[id] = DRAW_SPRITE;
593 } else {
594 Blocks.SpriteOffset[id] = 0;
595 }
596
597 Block_DefineCustom(id);
598 Blocks.CanPlace[id] = true1;
599 Blocks.CanDelete[id] = true1;
600 Event_RaiseVoid(&BlockEvents.PermissionsChanged);
601
602 cw_curID = 0;
603 }
604}
605
606static void Cw_Callback_5(struct NbtTag* tag) {
607 BlockID id = cw_curID;
608 cc_uint8* arr;
609 cc_uint8 sound;
610
611 if (!IsTag(tag->parent->parent->parent, "CPE")(String_CaselessEqualsConst(&tag->parent->parent->
parent->name, "CPE"))
) return;
612 if (!IsTag(tag->parent->parent->parent->parent, "Metadata")(String_CaselessEqualsConst(&tag->parent->parent->
parent->parent->name, "Metadata"))
) return;
613
614 if (IsTag(tag->parent->parent, "EnvColors")(String_CaselessEqualsConst(&tag->parent->parent->
name, "EnvColors"))
) {
615 if (IsTag(tag, "R")(String_CaselessEqualsConst(&tag->name, "R"))) { cw_colR = NbtTag_U16(tag); return; }
616 if (IsTag(tag, "G")(String_CaselessEqualsConst(&tag->name, "G"))) { cw_colG = NbtTag_U16(tag); return; }
617 if (IsTag(tag, "B")(String_CaselessEqualsConst(&tag->name, "B"))) { cw_colB = NbtTag_U16(tag); return; }
618 }
619
620 if (IsTag(tag->parent->parent, "BlockDefinitions")(String_CaselessEqualsConst(&tag->parent->parent->
name, "BlockDefinitions"))
&& Game_AllowCustomBlocks) {
621 if (IsTag(tag, "ID")(String_CaselessEqualsConst(&tag->name, "ID"))) { cw_curID = NbtTag_U8(tag); return; }
622 if (IsTag(tag, "ID2")(String_CaselessEqualsConst(&tag->name, "ID2"))) { cw_curID = NbtTag_U16(tag); return; }
623 if (IsTag(tag, "CollideType")(String_CaselessEqualsConst(&tag->name, "CollideType")
)
) { Block_SetCollide(id, NbtTag_U8(tag)); return; }
624 if (IsTag(tag, "Speed")(String_CaselessEqualsConst(&tag->name, "Speed"))) { Blocks.SpeedMultiplier[id] = NbtTag_F32(tag); return; }
625 if (IsTag(tag, "TransmitsLight")(String_CaselessEqualsConst(&tag->name, "TransmitsLight"
))
) { Blocks.BlocksLight[id] = NbtTag_U8(tag) == 0; return; }
626 if (IsTag(tag, "FullBright")(String_CaselessEqualsConst(&tag->name, "FullBright"))) { Blocks.FullBright[id] = NbtTag_U8(tag) != 0; return; }
627 if (IsTag(tag, "BlockDraw")(String_CaselessEqualsConst(&tag->name, "BlockDraw"))) { Blocks.Draw[id] = NbtTag_U8(tag); return; }
628 if (IsTag(tag, "Shape")(String_CaselessEqualsConst(&tag->name, "Shape"))) { Blocks.SpriteOffset[id] = NbtTag_U8(tag); return; }
629
630 if (IsTag(tag, "Name")(String_CaselessEqualsConst(&tag->name, "Name"))) {
631 cc_string name = NbtTag_String(tag);
632 Block_SetName(id, &name);
633 return;
634 }
635
636 if (IsTag(tag, "Textures")(String_CaselessEqualsConst(&tag->name, "Textures"))) {
637 arr = NbtTag_U8_Array(tag, 6);
638 Block_Tex(id, FACE_YMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_YMAX)] = arr[0]; Block_Tex(id, FACE_YMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_YMIN)] = arr[1];
639 Block_Tex(id, FACE_XMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_XMIN)] = arr[2]; Block_Tex(id, FACE_XMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_XMAX)] = arr[3];
640 Block_Tex(id, FACE_ZMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_ZMIN)] = arr[4]; Block_Tex(id, FACE_ZMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_ZMAX)] = arr[5];
641
642 /* hacky way of storing upper 8 bits */
643 if (tag->dataSize >= 12) {
644 Block_Tex(id, FACE_YMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_YMAX)] |= arr[6] << 8; Block_Tex(id, FACE_YMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_YMIN)] |= arr[7] << 8;
645 Block_Tex(id, FACE_XMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_XMIN)] |= arr[8] << 8; Block_Tex(id, FACE_XMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_XMAX)] |= arr[9] << 8;
646 Block_Tex(id, FACE_ZMIN)Blocks.Textures[(id) * FACE_COUNT + (FACE_ZMIN)] |= arr[10] << 8; Block_Tex(id, FACE_ZMAX)Blocks.Textures[(id) * FACE_COUNT + (FACE_ZMAX)] |= arr[11] << 8;
647 }
648 return;
649 }
650
651 if (IsTag(tag, "WalkSound")(String_CaselessEqualsConst(&tag->name, "WalkSound"))) {
652 sound = NbtTag_U8(tag);
653 Blocks.DigSounds[id] = sound;
654 Blocks.StepSounds[id] = sound;
655 if (sound == SOUND_GLASS) Blocks.StepSounds[id] = SOUND_STONE;
656 return;
657 }
658
659 if (IsTag(tag, "Fog")(String_CaselessEqualsConst(&tag->name, "Fog"))) {
660 arr = NbtTag_U8_Array(tag, 4);
661 Blocks.FogDensity[id] = (arr[0] + 1) / 128.0f;
662 /* Fix for older ClassicalSharp versions which saved wrong fog density value */
663 if (arr[0] == 0xFF) Blocks.FogDensity[id] = 0.0f;
664 Blocks.FogCol[id] = PackedCol_Make(arr[1], arr[2], arr[3], 255)(((cc_uint8)(arr[1]) << 0) | ((cc_uint8)(arr[2]) <<
8) | ((cc_uint8)(arr[3]) << 16) | ((cc_uint8)(255) <<
24))
;
665 return;
666 }
667
668 if (IsTag(tag, "Coords")(String_CaselessEqualsConst(&tag->name, "Coords"))) {
669 arr = NbtTag_U8_Array(tag, 6);
670 Blocks.MinBB[id].X = (cc_int8)arr[0] / 16.0f; Blocks.MaxBB[id].X = (cc_int8)arr[3] / 16.0f;
671 Blocks.MinBB[id].Y = (cc_int8)arr[1] / 16.0f; Blocks.MaxBB[id].Y = (cc_int8)arr[4] / 16.0f;
672 Blocks.MinBB[id].Z = (cc_int8)arr[2] / 16.0f; Blocks.MaxBB[id].Z = (cc_int8)arr[5] / 16.0f;
673 return;
674 }
675 }
676}
677
678static void Cw_Callback(struct NbtTag* tag) {
679 struct NbtTag* tmp = tag->parent;
680 int depth = 0;
681 while (tmp) { depth++; tmp = tmp->parent; }
682
683 switch (depth) {
684 case 1: Cw_Callback_1(tag); return;
685 case 2: Cw_Callback_2(tag); return;
686 case 4: Cw_Callback_4(tag); return;
687 case 5: Cw_Callback_5(tag); return;
688 }
689 /* ClassicWorld -> Metadata -> CPE -> ExtName -> [values]
690 0 1 2 3 4 */
691}
692
693cc_result Cw_Load(struct Stream* stream) {
694 struct Stream compStream;
695 struct InflateState state;
696 cc_result res;
697 cc_uint8 tag;
698
699 Inflate_MakeStream2(&compStream, &state, stream);
700 if ((res = Map_SkipGZipHeader(stream))) return res;
701 if ((res = compStream.ReadU8(&compStream, &tag))) return res;
702
703 if (tag != NBT_DICT) return CW_ERR_ROOT_TAG;
704 return Nbt_ReadTag(NBT_DICT, true1, &compStream, NULL((void*)0), Cw_Callback);
705}
706
707
708/*########################################################################################################################*
709*-------------------------------------------------Minecraft .dat format---------------------------------------------------*
710*#########################################################################################################################*/
711/* .dat is a java serialised map format. Rather than bothering following this, I skip a lot of it.
712 Stream BlockData BlockDataTiny BlockDataLong
713|--------------| |---------------| |---------------| |---------------|
714| U16 Magic | |>BlockDataTiny | | TC_BLOCKDATA | | TC_BLOCKLONG |
715| U16 Version | |>BlockDataLong | | U8 Size | | U32 Size |
716| Content[var] | |_______________| | U8 Data[size] | | U8 Data[size] |
717|______________| |_______________| |_______________|
718
719 Content
720|--------------| |--------------|
721| >BlockData | | >NewString |
722| >Object | | >TC_RESET |
723|______________| | >TC_NULL |
724| >PrevObject |
725| >NewClass |
726| >NewEnum |
727
728}*/
729enum JTypeCode {
730 TC_NULL = 0x70, TC_REFERENCE = 0x71, TC_CLASSDESC = 0x72, TC_OBJECT = 0x73,
731 TC_STRING = 0x74, TC_ARRAY = 0x75, TC_ENDBLOCKDATA = 0x78
732};
733enum JFieldType {
734 JFIELD_I8 = 'B', JFIELD_F32 = 'F', JFIELD_I32 = 'I', JFIELD_I64 = 'J',
735 JFIELD_BOOL = 'Z', JFIELD_ARRAY = '[', JFIELD_OBJECT = 'L'
736};
737
738#define JNAME_SIZE48 48
739struct JFieldDesc {
740 cc_uint8 Type;
741 cc_uint8 FieldName[JNAME_SIZE48];
742 union {
743 cc_uint8 U8;
744 cc_int32 I32;
745 cc_uint32 U32;
746 float F32;
747 struct { cc_uint8* Ptr; cc_uint32 Size; } Array;
748 } Value;
749};
750
751struct JClassDesc {
752 cc_uint8 ClassName[JNAME_SIZE48];
753 int FieldsCount;
754 struct JFieldDesc Fields[22];
755};
756
757static cc_result Dat_ReadString(struct Stream* stream, cc_uint8* buffer) {
758 int len;
759 cc_result res;
760
761 if ((res = Stream_Read(stream, buffer, 2))) return res;
762 len = Stream_GetU16_BE(buffer);
763
764 Mem_Set(buffer, 0, JNAME_SIZE48);
765 if (len > JNAME_SIZE48) return DAT_ERR_JSTRING_LEN;
766 return Stream_Read(stream, buffer, len);
767}
768
769static cc_result Dat_ReadFieldDesc(struct Stream* stream, struct JFieldDesc* desc) {
770 cc_uint8 typeCode;
771 cc_uint8 className1[JNAME_SIZE48];
772 cc_result res;
773
774 if ((res = stream->ReadU8(stream, &desc->Type))) return res;
775 if ((res = Dat_ReadString(stream, desc->FieldName))) return res;
776
777 if (desc->Type == JFIELD_ARRAY || desc->Type == JFIELD_OBJECT) {
778 if ((res = stream->ReadU8(stream, &typeCode))) return res;
779
780 if (typeCode == TC_STRING) {
781 return Dat_ReadString(stream, className1);
782 } else if (typeCode == TC_REFERENCE) {
783 return stream->Skip(stream, 4); /* (4) handle */
784 } else {
785 return DAT_ERR_JFIELD_CLASS_NAME;
786 }
787 }
788 return 0;
789}
790
791static cc_result Dat_ReadClassDesc(struct Stream* stream, struct JClassDesc* desc) {
792 cc_uint8 typeCode;
793 cc_uint8 count[2];
794 struct JClassDesc superClassDesc;
795 cc_result res;
796 int i;
797
798 if ((res = stream->ReadU8(stream, &typeCode))) return res;
16
Assuming 'res' is 0
17
Taking false branch
72
Assuming 'res' is 0
73
Taking false branch
799 if (typeCode == TC_NULL) { desc->ClassName[0] = '\0'; desc->FieldsCount = 0; return 0; }
18
Assuming 'typeCode' is not equal to TC_NULL
19
Taking false branch
74
Assuming 'typeCode' is equal to TC_NULL
75
Taking true branch
800 if (typeCode != TC_CLASSDESC) return DAT_ERR_JCLASS_TYPE;
20
Assuming 'typeCode' is equal to TC_CLASSDESC
21
Taking false branch
801
802 if ((res = Dat_ReadString(stream, desc->ClassName))) return res;
22
Assuming 'res' is 0
23
Taking false branch
803 if ((res = stream->Skip(stream, 9))) return res; /* (8) serial version UID, (1) flags */
24
Assuming 'res' is 0
25
Taking false branch
804
805 if ((res = Stream_Read(stream, count, 2))) return res;
26
Assuming 'res' is 0
27
Taking false branch
806 desc->FieldsCount = Stream_GetU16_BE(count);
807 if (desc->FieldsCount > Array_Elems(desc->Fields)(sizeof(desc->Fields) / sizeof(desc->Fields[0]))) return DAT_ERR_JCLASS_FIELDS;
28
Assuming the condition is false
29
Taking false branch
808
809 for (i = 0; i < desc->FieldsCount; i++) {
30
Assuming 'i' is < field 'FieldsCount'
31
Loop condition is true. Entering loop body
34
Assuming 'i' is >= field 'FieldsCount'
35
Loop condition is false. Execution continues on line 813
810 if ((res = Dat_ReadFieldDesc(stream, &desc->Fields[i]))) return res;
32
Assuming 'res' is 0
33
Taking false branch
811 }
812
813 if ((res = stream->ReadU8(stream, &typeCode))) return res;
36
Assuming 'res' is 0
37
Taking false branch
814 if (typeCode != TC_ENDBLOCKDATA) return DAT_ERR_JCLASS_ANNOTATION;
38
Assuming 'typeCode' is equal to TC_ENDBLOCKDATA
39
Taking false branch
815
816 return Dat_ReadClassDesc(stream, &superClassDesc);
817}
818
819static cc_result Dat_ReadFieldData(struct Stream* stream, struct JFieldDesc* field) {
820 cc_uint8 typeCode;
821 cc_string fieldName;
822 cc_uint32 count;
823 struct JClassDesc arrayClassDesc;
824 cc_result res;
825
826 switch (field->Type) {
64
Control jumps to 'case JFIELD_ARRAY:' at line 857
827 case JFIELD_I8:
828 case JFIELD_BOOL:
829 return stream->ReadU8(stream, &field->Value.U8);
830 case JFIELD_F32:
831 case JFIELD_I32:
832 return Stream_ReadU32_BE(stream, &field->Value.U32);
833 case JFIELD_I64:
834 return stream->Skip(stream, 8); /* (8) data */
835
836 case JFIELD_OBJECT: {
837 /* Luckily for us, we only have to account for blockMap object */
838 /* Other objects (e.g. player) are stored after the fields we actually care about, so ignore them */
839 fieldName = String_FromRaw((char*)field->FieldName, JNAME_SIZE48);
840 if (!String_CaselessEqualsConst(&fieldName, "blockMap")) return 0;
841 if ((res = stream->ReadU8(stream, &typeCode))) return res;
842
843 /* Skip all blockMap data with awful hacks */
844 /* These offsets were based on server_level.dat map from original minecraft classic server */
845 if (typeCode == TC_OBJECT) {
846 if ((res = stream->Skip(stream, 315))) return res;
847 if ((res = Stream_ReadU32_BE(stream, &count))) return res;
848
849 if ((res = stream->Skip(stream, 17 * count))) return res;
850 if ((res = stream->Skip(stream, 152))) return res;
851 } else if (typeCode != TC_NULL) {
852 /* WoM maps have this field as null, which makes things easier for us */
853 return DAT_ERR_JOBJECT_TYPE;
854 }
855 } break;
856
857 case JFIELD_ARRAY: {
858 if ((res = stream->ReadU8(stream, &typeCode))) return res;
65
Assuming 'res' is 0
66
Taking false branch
859 /* NULL/empty array */
860 if (typeCode == TC_NULL) {
67
Assuming 'typeCode' is not equal to TC_NULL
68
Taking false branch
861 field->Value.Array.Size = 0;
862 field->Value.Array.Ptr = NULL((void*)0);
863 break;
864 }
865
866 if (typeCode != TC_ARRAY) return DAT_ERR_JARRAY_TYPE;
69
Assuming 'typeCode' is equal to TC_ARRAY
70
Taking false branch
867 if ((res = Dat_ReadClassDesc(stream, &arrayClassDesc))) return res;
71
Calling 'Dat_ReadClassDesc'
76
Returning from 'Dat_ReadClassDesc'
77
Assuming 'res' is 0
78
Taking false branch
868 if (arrayClassDesc.ClassName[1] != JFIELD_I8) return DAT_ERR_JARRAY_CONTENT;
79
The left operand of '!=' is a garbage value
869
870 if ((res = Stream_ReadU32_BE(stream, &count))) return res;
871 field->Value.Array.Size = count;
872 field->Value.Array.Ptr = (cc_uint8*)Mem_TryAlloc(count, 1);
873
874 if (!field->Value.Array.Ptr) return ERR_OUT_OF_MEMORY;
875 res = Stream_Read(stream, field->Value.Array.Ptr, count);
876 if (res) { Mem_Free(field->Value.Array.Ptr); return res; }
877 } break;
878 }
879 return 0;
880}
881
882static int Dat_I32(struct JFieldDesc* field) {
883 if (field->Type != JFIELD_I32) Logger_Abort("Field type must be Int32");
884 return field->Value.I32;
885}
886
887cc_result Dat_Load(struct Stream* stream) {
888 cc_uint8 header[10];
889 struct JClassDesc obj;
890 struct JFieldDesc* field;
891 cc_string fieldName;
892 cc_result res;
893 int i;
894
895 struct LocalPlayer* p = &LocalPlayer_Instance;
896 struct Stream compStream;
897 struct InflateState state;
898 Inflate_MakeStream2(&compStream, &state, stream);
899
900 if ((res = Map_SkipGZipHeader(stream))) return res;
1
Assuming 'res' is 0
2
Taking false branch
901 if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res;
3
Assuming 'res' is 0
4
Taking false branch
902 /* .dat header */
903 if (Stream_GetU32_BE(&header[0]) != 0x271BB788) return DAT_ERR_IDENTIFIER;
5
Assuming the condition is false
6
Taking false branch
904 if (header[4] != 0x02) return DAT_ERR_VERSION;
7
Assuming the condition is false
8
Taking false branch
905
906 /* Java seralisation headers */
907 if (Stream_GetU16_BE(&header[5]) != 0xACED) return DAT_ERR_JIDENTIFIER;
9
Assuming the condition is false
10
Taking false branch
908 if (Stream_GetU16_BE(&header[7]) != 0x0005) return DAT_ERR_JVERSION;
11
Assuming the condition is false
12
Taking false branch
909 if (header[9] != TC_OBJECT) return DAT_ERR_ROOT_TYPE;
13
Assuming the condition is false
14
Taking false branch
910 if ((res = Dat_ReadClassDesc(&compStream, &obj))) return res;
15
Calling 'Dat_ReadClassDesc'
40
Returning from 'Dat_ReadClassDesc'
41
Assuming 'res' is 0
42
Taking false branch
911
912 for (i = 0; i < obj.FieldsCount; i++) {
43
Assuming 'i' is < field 'FieldsCount'
44
Loop condition is true. Entering loop body
61
Assuming 'i' is < field 'FieldsCount'
62
Loop condition is true. Entering loop body
913 field = &obj.Fields[i];
914 if ((res = Dat_ReadFieldData(&compStream, field))) return res;
45
Assuming 'res' is 0
46
Taking false branch
63
Calling 'Dat_ReadFieldData'
915 fieldName = String_FromRaw((char*)field->FieldName, JNAME_SIZE48);
916
917 if (String_CaselessEqualsConst(&fieldName, "width")) {
47
Assuming the condition is false
48
Taking false branch
918 World.Width = Dat_I32(field);
919 } else if (String_CaselessEqualsConst(&fieldName, "height")) {
49
Assuming the condition is false
50
Taking false branch
920 World.Length = Dat_I32(field);
921 } else if (String_CaselessEqualsConst(&fieldName, "depth")) {
51
Assuming the condition is false
52
Taking false branch
922 World.Height = Dat_I32(field);
923 } else if (String_CaselessEqualsConst(&fieldName, "blocks")) {
53
Assuming the condition is false
54
Taking false branch
924 if (field->Type != JFIELD_ARRAY) Logger_Abort("Blocks field must be Array");
925 World.Blocks = field->Value.Array.Ptr;
926 World.Volume = field->Value.Array.Size;
927 } else if (String_CaselessEqualsConst(&fieldName, "xSpawn")) {
55
Assuming the condition is false
56
Taking false branch
928 p->Spawn.X = (float)Dat_I32(field);
929 } else if (String_CaselessEqualsConst(&fieldName, "ySpawn")) {
57
Assuming the condition is false
58
Taking false branch
930 p->Spawn.Y = (float)Dat_I32(field);
931 } else if (String_CaselessEqualsConst(&fieldName, "zSpawn")) {
59
Assuming the condition is false
60
Taking false branch
932 p->Spawn.Z = (float)Dat_I32(field);
933 }
934 }
935 return 0;
936}
937
938
939/*########################################################################################################################*
940*--------------------------------------------------ClassicWorld export----------------------------------------------------*
941*#########################################################################################################################*/
942#define CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
NBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0,
943
944static int Cw_WriteEndString(cc_uint8* data, const cc_string* text) {
945 cc_uint8* cur = data + 2;
946 int i, wrote, len = 0;
947
948 for (i = 0; i < text->length; i++) {
949 wrote = Convert_CP437ToUtf8(text->buffer[i], cur);
950 len += wrote; cur += wrote;
951 }
952
953 Stream_SetU16_BE(data, len);
954 *cur = NBT_END;
955 return len + 1;
956}
957
958static cc_uint8 cw_begin[131] = {
959NBT_DICT, 0,12, 'C','l','a','s','s','i','c','W','o','r','l','d',
960 NBT_I8, 0,13, 'F','o','r','m','a','t','V','e','r','s','i','o','n', 1,
961 NBT_I8S, 0,4, 'U','U','I','D', 0,0,0,16, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
962 NBT_I16, 0,1, 'X', 0,0,
963 NBT_I16, 0,1, 'Y', 0,0,
964 NBT_I16, 0,1, 'Z', 0,0,
965 NBT_DICT, 0,5, 'S','p','a','w','n',
966 NBT_I16, 0,1, 'X', 0,0,
967 NBT_I16, 0,1, 'Y', 0,0,
968 NBT_I16, 0,1, 'Z', 0,0,
969 NBT_I8, 0,1, 'H', 0,
970 NBT_I8, 0,1, 'P', 0,
971 NBT_END,
972 NBT_I8S, 0,10, 'B','l','o','c','k','A','r','r','a','y', 0,0,0,0,
973};
974static cc_uint8 cw_map2[18] = {
975 NBT_I8S, 0,11, 'B','l','o','c','k','A','r','r','a','y','2', 0,0,0,0,
976};
977static cc_uint8 cw_meta_cpe[303] = {
978 NBT_DICT, 0,8, 'M','e','t','a','d','a','t','a',
979 NBT_DICT, 0,3, 'C','P','E',
980 NBT_DICT, 0,13, 'C','l','i','c','k','D','i','s','t','a','n','c','e',
981 NBT_I16, 0,8, 'D','i','s','t','a','n','c','e', 0,0,
982 NBT_END,
983 NBT_DICT, 0,14, 'E','n','v','W','e','a','t','h','e','r','T','y','p','e',
984 NBT_I8, 0,11, 'W','e','a','t','h','e','r','T','y','p','e', 0,
985 NBT_END,
986 NBT_DICT, 0,9, 'E','n','v','C','o','l','o','r','s',
987 NBT_DICT, 0,3, 'S','k','y', CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
988 NBT_END,
989 NBT_DICT, 0,5, 'C','l','o','u','d', CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
990 NBT_END,
991 NBT_DICT, 0,3, 'F','o','g', CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
992 NBT_END,
993 NBT_DICT, 0,7, 'A','m','b','i','e','n','t', CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
994 NBT_END,
995 NBT_DICT, 0,8, 'S','u','n','l','i','g','h','t', CW_META_RGBNBT_I16,0,1,'R',0,0, NBT_I16,0,1,'G',0,0, NBT_I16,0,1,'B',0,0
,
996 NBT_END,
997 NBT_END,
998 NBT_DICT, 0,16, 'E','n','v','M','a','p','A','p','p','e','a','r','a','n','c','e',
999 NBT_I8, 0,9, 'S','i','d','e','B','l','o','c','k', 0,
1000 NBT_I8, 0,9, 'E','d','g','e','B','l','o','c','k', 0,
1001 NBT_I16, 0,9, 'S','i','d','e','L','e','v','e','l', 0,0,
1002 NBT_STR, 0,10, 'T','e','x','t','u','r','e','U','R','L', 0,0,
1003};
1004static cc_uint8 cw_meta_defs[19] = {
1005 NBT_DICT, 0,16, 'B','l','o','c','k','D','e','f','i','n','i','t','i','o','n','s',
1006};
1007static cc_uint8 cw_meta_def[189] = {
1008 NBT_DICT, 0,9, 'B','l','o','c','k','\0','\0','\0','\0',
1009 NBT_I8, 0,2, 'I','D', 0,
1010 /* It would be have been better to just change ID to be a I16 */
1011 /* Unfortunately this isn't backwards compatible with ClassicalSharp */
1012 NBT_I16, 0,3, 'I','D','2', 0,0,
1013 NBT_I8, 0,11, 'C','o','l','l','i','d','e','T','y','p','e', 0,
1014 NBT_F32, 0,5, 'S','p','e','e','d', 0,0,0,0,
1015 /* Ugly hack for supporting texture IDs over 255 */
1016 /* First 6 elements are lower 8 bits, next 6 are upper 8 bits */
1017 NBT_I8S, 0,8, 'T','e','x','t','u','r','e','s', 0,0,0,12, 0,0,0,0,0,0, 0,0,0,0,0,0,
1018 NBT_I8, 0,14, 'T','r','a','n','s','m','i','t','s','L','i','g','h','t', 0,
1019 NBT_I8, 0,9, 'W','a','l','k','S','o','u','n','d', 0,
1020 NBT_I8, 0,10, 'F','u','l','l','B','r','i','g','h','t', 0,
1021 NBT_I8, 0,5, 'S','h','a','p','e', 0,
1022 NBT_I8, 0,9, 'B','l','o','c','k','D','r','a','w', 0,
1023 NBT_I8S, 0,3, 'F','o','g', 0,0,0,4, 0,0,0,0,
1024 NBT_I8S, 0,6, 'C','o','o','r','d','s', 0,0,0,6, 0,0,0,0,0,0,
1025 NBT_STR, 0,4, 'N','a','m','e', 0,0,
1026};
1027static cc_uint8 cw_end[4] = {
1028 NBT_END,
1029 NBT_END,
1030 NBT_END,
1031NBT_END,
1032};
1033
1034
1035static cc_result Cw_WriteBockDef(struct Stream* stream, int b) {
1036 cc_uint8 tmp[512];
1037 cc_string name;
1038 int len;
1039
1040 cc_bool sprite = Blocks.Draw[b] == DRAW_SPRITE;
1041 union IntAndFloat speed;
1042 TextureLoc tex;
1043 cc_uint8 fog;
1044 PackedCol col;
1045 Vec3 minBB, maxBB;
1046
1047 Mem_Copy(tmp, cw_meta_def, sizeof(cw_meta_def));
1048 {
1049 /* Hacky unique tag name for each by using hex of block */
1050 name = String_Init((char*)&tmp[8], 0, 4);
1051 String_AppendHex(&name, b >> 8);
1052 String_AppendHex(&name, b);
1053
1054 tmp[17] = b;
1055 Stream_SetU16_BE(&tmp[24], b);
1056
1057 tmp[40] = Blocks.Collide[b];
1058 speed.f = Blocks.SpeedMultiplier[b];
1059 Stream_SetU32_BE(&tmp[49], speed.u);
1060
1061 /* Originally only up to 256 textures were supported, which used up 6 bytes total */
1062 /* Later, support for more textures was added, which requires 2 bytes per texture */
1063 /* For backwards compatibility, the lower byte of each texture is */
1064 /* written into first 6 bytes, then higher byte into next 6 bytes */
1065 tex = Block_Tex(b, FACE_YMAX)Blocks.Textures[(b) * FACE_COUNT + (FACE_YMAX)]; tmp[68] = (cc_uint8)tex; tmp[74] = (cc_uint8)(tex >> 8);
1066 tex = Block_Tex(b, FACE_YMIN)Blocks.Textures[(b) * FACE_COUNT + (FACE_YMIN)]; tmp[69] = (cc_uint8)tex; tmp[75] = (cc_uint8)(tex >> 8);
1067 tex = Block_Tex(b, FACE_XMIN)Blocks.Textures[(b) * FACE_COUNT + (FACE_XMIN)]; tmp[70] = (cc_uint8)tex; tmp[76] = (cc_uint8)(tex >> 8);
1068 tex = Block_Tex(b, FACE_XMAX)Blocks.Textures[(b) * FACE_COUNT + (FACE_XMAX)]; tmp[71] = (cc_uint8)tex; tmp[77] = (cc_uint8)(tex >> 8);
1069 tex = Block_Tex(b, FACE_ZMIN)Blocks.Textures[(b) * FACE_COUNT + (FACE_ZMIN)]; tmp[72] = (cc_uint8)tex; tmp[78] = (cc_uint8)(tex >> 8);
1070 tex = Block_Tex(b, FACE_ZMAX)Blocks.Textures[(b) * FACE_COUNT + (FACE_ZMAX)]; tmp[73] = (cc_uint8)tex; tmp[79] = (cc_uint8)(tex >> 8);
1071
1072 tmp[97] = Blocks.BlocksLight[b] ? 0 : 1;
1073 tmp[110] = Blocks.DigSounds[b];
1074 tmp[124] = Blocks.FullBright[b] ? 1 : 0;
1075 tmp[133] = sprite ? 0 : (cc_uint8)(Blocks.MaxBB[b].Y * 16);
1076 tmp[146] = sprite ? Blocks.SpriteOffset[b] : Blocks.Draw[b];
1077
1078 fog = (cc_uint8)(128 * Blocks.FogDensity[b] - 1);
1079 col = Blocks.FogCol[b];
1080 tmp[157] = Blocks.FogDensity[b] ? fog : 0;
1081 tmp[158] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[159] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[160] = PackedCol_B(col)((cc_uint8)(col >> 16));
1082
1083 minBB = Blocks.MinBB[b]; maxBB = Blocks.MaxBB[b];
1084 tmp[174] = (cc_uint8)(minBB.X * 16); tmp[175] = (cc_uint8)(minBB.Y * 16); tmp[176] = (cc_uint8)(minBB.Z * 16);
1085 tmp[177] = (cc_uint8)(maxBB.X * 16); tmp[178] = (cc_uint8)(maxBB.Y * 16); tmp[179] = (cc_uint8)(maxBB.Z * 16);
1086 }
1087
1088 name = Block_UNSAFE_GetName(b);
1089 len = Cw_WriteEndString(&tmp[187], &name);
1090 return Stream_Write(stream, tmp, sizeof(cw_meta_def) + len);
1091}
1092
1093cc_result Cw_Save(struct Stream* stream) {
1094 cc_uint8 tmp[768];
1095 PackedCol col;
1096 struct LocalPlayer* p = &LocalPlayer_Instance;
1097 cc_result res;
1098 int b, len;
1099
1100 Mem_Copy(tmp, cw_begin, sizeof(cw_begin));
1101 {
1102 Mem_Copy(&tmp[43], World.Uuid, WORLD_UUID_LEN16);
1103 Stream_SetU16_BE(&tmp[63], World.Width);
1104 Stream_SetU16_BE(&tmp[69], World.Height);
1105 Stream_SetU16_BE(&tmp[75], World.Length);
1106 Stream_SetU32_BE(&tmp[127], World.Volume);
1107
1108 /* TODO: Maybe keep real spawn too? */
1109 Stream_SetU16_BE(&tmp[89], (cc_uint16)p->Base.Position.X);
1110 Stream_SetU16_BE(&tmp[95], (cc_uint16)p->Base.Position.Y);
1111 Stream_SetU16_BE(&tmp[101], (cc_uint16)p->Base.Position.Z);
1112 tmp[107] = Math_Deg2Packed(p->SpawnYaw)((cc_uint8)((p->SpawnYaw) * 256.0f / 360.0f));
1113 tmp[112] = Math_Deg2Packed(p->SpawnPitch)((cc_uint8)((p->SpawnPitch) * 256.0f / 360.0f));
1114 }
1115 if ((res = Stream_Write(stream, tmp, sizeof(cw_begin)))) return res;
1116 if ((res = Stream_Write(stream, World.Blocks, World.Volume))) return res;
1117
1118 if (World.Blocks != World.Blocks2) {
1119 Mem_Copy(tmp, cw_map2, sizeof(cw_map2));
1120 Stream_SetU32_BE(&tmp[14], World.Volume);
1121
1122 if ((res = Stream_Write(stream, tmp, sizeof(cw_map2)))) return res;
1123 if ((res = Stream_Write(stream, World.Blocks2, World.Volume))) return res;
1124 }
1125
1126 Mem_Copy(tmp, cw_meta_cpe, sizeof(cw_meta_cpe));
1127 {
1128 Stream_SetU16_BE(&tmp[44], (cc_uint16)(LocalPlayer_Instance.ReachDistance * 32));
1129 tmp[78] = Env.Weather;
1130
1131 col = Env.SkyCol; tmp[103] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[109] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[115] = PackedCol_B(col)((cc_uint8)(col >> 16));
1132 col = Env.CloudsCol; tmp[130] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[136] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[142] = PackedCol_B(col)((cc_uint8)(col >> 16));
1133 col = Env.FogCol; tmp[155] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[161] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[167] = PackedCol_B(col)((cc_uint8)(col >> 16));
1134 col = Env.ShadowCol; tmp[184] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[190] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[196] = PackedCol_B(col)((cc_uint8)(col >> 16));
1135 col = Env.SunCol; tmp[214] = PackedCol_R(col)((cc_uint8)(col >> 0)); tmp[220] = PackedCol_G(col)((cc_uint8)(col >> 8)); tmp[226] = PackedCol_B(col)((cc_uint8)(col >> 16));
1136
1137 tmp[260] = (BlockRaw)Env.SidesBlock;
1138 tmp[273] = (BlockRaw)Env.EdgeBlock;
1139 Stream_SetU16_BE(&tmp[286], Env.EdgeHeight);
1140 }
1141 len = Cw_WriteEndString(&tmp[301], &TexturePack_Url);
1142 if ((res = Stream_Write(stream, tmp, sizeof(cw_meta_cpe) + len))) return res;
1143
1144 if ((res = Stream_Write(stream, cw_meta_defs, sizeof(cw_meta_defs)))) return res;
1145 /* Write block definitions in reverse order so that software that only reads byte 'ID' */
1146 /* still loads correct first 256 block defs when saving a map with over 256 block defs */
1147 for (b = BLOCK_MAX_DEFINED; b >= 1; b--) {
1148 if (!Block_IsCustomDefined(b)) continue;
1149 if ((res = Cw_WriteBockDef(stream, b))) return res;
1150 }
1151 return Stream_Write(stream, cw_end, sizeof(cw_end));
1152}
1153
1154
1155/*########################################################################################################################*
1156*---------------------------------------------------Schematic export------------------------------------------------------*
1157*#########################################################################################################################*/
1158
1159static cc_uint8 sc_begin[78] = {
1160NBT_DICT, 0,9, 'S','c','h','e','m','a','t','i','c',
1161 NBT_STR, 0,9, 'M','a','t','e','r','i','a','l','s', 0,7, 'C','l','a','s','s','i','c',
1162 NBT_I16, 0,5, 'W','i','d','t','h', 0,0,
1163 NBT_I16, 0,6, 'H','e','i','g','h','t', 0,0,
1164 NBT_I16, 0,6, 'L','e','n','g','t','h', 0,0,
1165 NBT_I8S, 0,6, 'B','l','o','c','k','s', 0,0,0,0,
1166};
1167static cc_uint8 sc_data[11] = {
1168 NBT_I8S, 0,4, 'D','a','t','a', 0,0,0,0,
1169};
1170static cc_uint8 sc_end[37] = {
1171 NBT_LIST, 0,8, 'E','n','t','i','t','i','e','s', NBT_DICT, 0,0,0,0,
1172 NBT_LIST, 0,12, 'T','i','l','e','E','n','t','i','t','i','e','s', NBT_DICT, 0,0,0,0,
1173NBT_END,
1174};
1175
1176cc_result Schematic_Save(struct Stream* stream) {
1177 cc_uint8 tmp[256], chunk[8192] = { 0 };
1178 cc_result res;
1179 int i;
1180
1181 Mem_Copy(tmp, sc_begin, sizeof(sc_begin));
1182 {
1183 Stream_SetU16_BE(&tmp[41], World.Width);
1184 Stream_SetU16_BE(&tmp[52], World.Height);
1185 Stream_SetU16_BE(&tmp[63], World.Length);
1186 Stream_SetU32_BE(&tmp[74], World.Volume);
1187 }
1188 if ((res = Stream_Write(stream, tmp, sizeof(sc_begin)))) return res;
1189 if ((res = Stream_Write(stream, World.Blocks, World.Volume))) return res;
1190
1191 Mem_Copy(tmp, sc_data, sizeof(sc_data));
1192 {
1193 Stream_SetU32_BE(&tmp[7], World.Volume);
1194 }
1195 if ((res = Stream_Write(stream, tmp, sizeof(sc_data)))) return res;
1196
1197 for (i = 0; i < World.Volume; i += sizeof(chunk)) {
1198 int count = World.Volume - i; count = min(count, sizeof(chunk))((count) < (sizeof(chunk)) ? (count) : (sizeof(chunk)));
1199 if ((res = Stream_Write(stream, chunk, count))) return res;
1200 }
1201 return Stream_Write(stream, sc_end, sizeof(sc_end));
1202}