diff --git a/src/Animations.c b/src/Animations.c
deleted file mode 100644
index 19c64cd10..000000000
--- a/src/Animations.c
+++ /dev/null
@@ -1,376 +0,0 @@
-#include "Animations.h"
-#include "ExtMath.h"
-#include "TerrainAtlas.h"
-#include "Platform.h"
-#include "Event.h"
-#include "Funcs.h"
-#include "Graphics.h"
-#include "Chat.h"
-#include "World.h"
-#include "Options.h"
-#include "ErrorHandler.h"
-#include "Errors.h"
-#include "Stream.h"
-#include "Bitmap.h"
-#define LIQUID_ANIM_MAX 64
-
-/*########################################################################################################################*
-*-----------------------------------------------------Lava animation------------------------------------------------------*
-*#########################################################################################################################*/
-static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static RNGState L_rnd;
-static bool L_rndInitalised;
-
-static void LavaAnimation_Tick(BitmapCol* ptr, int size) {
- int mask = size - 1, shift = Math_Log2(size);
- float soupHeat, potHeat, col;
- int x, y, i = 0;
-
- if (!L_rndInitalised) {
- Random_InitFromCurrentTime(&L_rnd);
- L_rndInitalised = true;
- }
-
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++) {
- /* Calculate the colour at this coordinate in the heatmap */
-
- /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */
- /* [ANGLE] is integer x/y, so repeats every 16 intervals */
- static int8_t sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 };
- int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF];
-
- soupHeat =
- L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] +
- L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] +
-
- L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[(yy & mask) << shift | (xx & mask)] +
- L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] +
-
- L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] +
- L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] +
- L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)];
-
- potHeat =
- L_potHeat[i] + /* x , y */
- L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */
- L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */
- L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */
-
- L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f;
-
- L_potHeat[i] += L_flameHeat[i];
- if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f;
-
- L_flameHeat[i] -= 0.06f * 0.01f;
- if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f;
-
- /* Output the pixel */
- col = 2.0f * L_soupHeat[i];
- Math_Clamp(col, 0.0f, 1.0f);
-
- ptr->R = (uint8_t)(col * 100.0f + 155.0f);
- ptr->G = (uint8_t)(col * col * 255.0f);
- ptr->B = (uint8_t)(col * col * col * col * 128.0f);
- ptr->A = 255;
-
- ptr++; i++;
- }
- }
-}
-
-
-/*########################################################################################################################*
-*----------------------------------------------------Water animation------------------------------------------------------*
-*#########################################################################################################################*/
-static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
-static RNGState W_rnd;
-static bool W_rndInitalised;
-
-static void WaterAnimation_Tick(BitmapCol* ptr, int size) {
- int mask = size - 1, shift = Math_Log2(size);
- float soupHeat, col;
- int x, y, i = 0;
-
- if (!W_rndInitalised) {
- Random_InitFromCurrentTime(&W_rnd);
- W_rndInitalised = true;
- }
-
- for (y = 0; y < size; y++) {
- for (x = 0; x < size; x++) {
- /* Calculate the colour at this coordinate in the heatmap */
- soupHeat =
- W_soupHeat[y << shift | ((x - 1) & mask)] +
- W_soupHeat[y << shift | x ] +
- W_soupHeat[y << shift | ((x + 1) & mask)];
-
- W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f;
-
- W_potHeat[i] += W_flameHeat[i];
- if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f;
-
- W_flameHeat[i] -= 0.1f * 0.05f;
- if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f;
-
- /* Output the pixel */
- col = W_soupHeat[i];
- Math_Clamp(col, 0.0f, 1.0f);
- col = col * col;
-
- ptr->R = (uint8_t)(32.0f + col * 32.0f);
- ptr->G = (uint8_t)(50.0f + col * 64.0f);
- ptr->A = (uint8_t)(146.0f + col * 50.0f);
- ptr->B = 255;
-
- ptr++; i++;
- }
- }
-}
-
-
-/*########################################################################################################################*
-*-------------------------------------------------------Animations--------------------------------------------------------*
-*#########################################################################################################################*/
-struct AnimationData {
- TextureLoc TexLoc; /* Tile (not pixel) coordinates in terrain.png */
- uint16_t FrameX, FrameY; /* Top left pixel coordinates of start frame in animatons.png */
- uint16_t FrameSize; /* Size of each frame in pixel coordinates */
- uint16_t State; /* Current animation frame index */
- uint16_t StatesCount; /* Total number of animation frames */
- int16_t Tick, TickDelay;
-};
-
-static Bitmap anims_bmp;
-static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES];
-static int anims_count;
-static bool anims_validated, anims_useLavaAnim, anims_useWaterAnim;
-#define ANIM_MIN_ARGS 7
-
-static void Animations_ReadDescription(struct Stream* stream, const String* path) {
- String line; char lineBuffer[STRING_SIZE * 2];
- String parts[ANIM_MIN_ARGS];
- int count;
- struct AnimationData data = { 0 };
- uint8_t tileX, tileY;
-
- uint8_t buffer[2048];
- struct Stream buffered;
- ReturnCode res;
-
- String_InitArray(line, lineBuffer);
- /* ReadLine reads single byte at a time */
- Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer));
-
- for (;;) {
- res = Stream_ReadLine(&buffered, &line);
- if (res == ERR_END_OF_STREAM) break;
- if (res) { Chat_LogError2(res, "reading from", path); break; }
-
- if (!line.length || line.buffer[0] == '#') continue;
- count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS);
- if (count < ANIM_MIN_ARGS) {
- Chat_Add1("&cNot enough arguments for anim: %s", &line); continue;
- }
-
- if (!Convert_TryParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) {
- Chat_Add1("&cInvalid anim tile X coord: %s", &line); continue;
- }
- if (!Convert_TryParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) {
- Chat_Add1("&cInvalid anim tile Y coord: %s", &line); continue;
- }
- if (!Convert_TryParseUInt16(&parts[2], &data.FrameX)) {
- Chat_Add1("&cInvalid anim frame X coord: %s", &line); continue;
- }
- if (!Convert_TryParseUInt16(&parts[3], &data.FrameY)) {
- Chat_Add1("&cInvalid anim frame Y coord: %s", &line); continue;
- }
- if (!Convert_TryParseUInt16(&parts[4], &data.FrameSize)) {
- Chat_Add1("&cInvalid anim frame size: %s", &line); continue;
- }
- if (!Convert_TryParseUInt16(&parts[5], &data.StatesCount)) {
- Chat_Add1("&cInvalid anim states count: %s", &line); continue;
- }
- if (!Convert_TryParseInt16(&parts[6], &data.TickDelay)) {
- Chat_Add1("&cInvalid anim tick delay: %s", &line); continue;
- }
-
- if (anims_count == Array_Elems(anims_list)) {
- Chat_AddRaw("&cCannot show over 512 animations"); return;
- }
-
- data.TexLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW);
- anims_list[anims_count++] = data;
- }
-}
-
-#define ANIMS_FAST_SIZE 64
-static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) {
- int dstX = Atlas1D_Index(texLoc), srcX;
- int dstY = Atlas1D_RowId(texLoc) * Atlas_TileSize;
- GfxResourceID tex;
-
- uint8_t buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)];
- uint8_t* ptr = buffer;
- Bitmap frame;
-
- /* cannot allocate memory on the stack for very big animation.png frames */
- if (size > ANIMS_FAST_SIZE) {
- ptr = Mem_Alloc(size * size, 4, "anim frame");
- }
- Bitmap_Create(&frame, size, size, ptr);
-
- if (!data) {
- if (texLoc == 30) {
- LavaAnimation_Tick((BitmapCol*)frame.Scan0, size);
- } else if (texLoc == 14) {
- WaterAnimation_Tick((BitmapCol*)frame.Scan0, size);
- }
- } else {
- srcX = data->FrameX + data->State * size;
- Bitmap_CopyBlock(srcX, data->FrameY, 0, 0, &anims_bmp, &frame, size);
- }
-
- tex = Atlas1D_TexIds[dstX];
- if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx_Mipmaps); }
- if (size > ANIMS_FAST_SIZE) Mem_Free(ptr);
-}
-
-static void Animations_Apply(struct AnimationData* data) {
- TextureLoc loc;
- data->Tick--;
- if (data->Tick >= 0) return;
-
- data->State++;
- data->State %= data->StatesCount;
- data->Tick = data->TickDelay;
-
- loc = data->TexLoc;
- if (loc == 30 && anims_useLavaAnim) return;
- if (loc == 14 && anims_useWaterAnim) return;
- Animations_Draw(data, loc, data->FrameSize);
-}
-
-static bool Animations_IsDefaultZip(void) {
- String texPack; char texPackBuffer[STRING_SIZE];
- if (World_TextureUrl.length) return false;
-
- String_InitArray(texPack, texPackBuffer);
- Options_Get(OPT_DEFAULT_TEX_PACK, &texPack, "default.zip");
- return String_CaselessEqualsConst(&texPack, "default.zip");
-}
-
-static void Animations_Clear(void) {
- Mem_Free(anims_bmp.Scan0);
- anims_count = 0;
- anims_bmp.Scan0 = NULL;
- anims_validated = false;
-}
-
-static void Animations_Validate(void) {
- struct AnimationData data;
- int maxX, maxY, tileX, tileY;
- int i, j;
-
- anims_validated = true;
- for (i = 0; i < anims_count; i++) {
- data = anims_list[i];
-
- maxX = data.FrameX + data.FrameSize * data.StatesCount;
- maxY = data.FrameY + data.FrameSize;
- tileX = Atlas2D_TileX(data.TexLoc);
- tileY = Atlas2D_TileY(data.TexLoc);
-
- if (data.FrameSize > Atlas_TileSize || tileY >= Atlas_RowsCount) {
- Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY);
- } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) {
- Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY);
- } else {
- continue;
- }
-
- /* Remove this animation from the list */
- for (j = i; j < anims_count - 1; j++) {
- anims_list[j] = anims_list[j + 1];
- }
- i--; anims_count--;
- }
-}
-
-
-static void Animations_Tick(struct ScheduledTask* task) {
- int i, size;
-
- if (anims_useLavaAnim) {
- size = min(Atlas_TileSize, 64);
- Animations_Draw(NULL, 30, size);
- }
- if (anims_useWaterAnim) {
- size = min(Atlas_TileSize, 64);
- Animations_Draw(NULL, 14, size);
- }
-
- if (!anims_count) return;
- if (!anims_bmp.Scan0) {
- Chat_AddRaw("&cCurrent texture pack specifies it uses animations,");
- Chat_AddRaw("&cbut is missing animations.png");
- anims_count = 0; return;
- }
-
- /* deferred, because when reading animations.txt, might not have read animations.png yet */
- if (!anims_validated) Animations_Validate();
- for (i = 0; i < anims_count; i++) {
- Animations_Apply(&anims_list[i]);
- }
-}
-
-static void Animations_PackChanged(void* obj) {
- Animations_Clear();
- anims_useLavaAnim = Animations_IsDefaultZip();
- anims_useWaterAnim = anims_useLavaAnim;
-}
-
-static void Animations_FileChanged(void* obj, struct Stream* stream, const String* name) {
- ReturnCode res;
- if (String_CaselessEqualsConst(name, "animations.png")) {
- res = Png_Decode(&anims_bmp, stream);
- if (!res) return;
-
- Chat_LogError2(res, "decoding", name);
- Mem_Free(anims_bmp.Scan0);
- anims_bmp.Scan0 = NULL;
- } else if (String_CaselessEqualsConst(name, "animations.txt")) {
- Animations_ReadDescription(stream, name);
- } else if (String_CaselessEqualsConst(name, "uselavaanim")) {
- anims_useLavaAnim = true;
- } else if (String_CaselessEqualsConst(name, "usewateranim")) {
- anims_useWaterAnim = true;
- }
-}
-
-
-/*########################################################################################################################*
-*--------------------------------------------------Animations component---------------------------------------------------*
-*#########################################################################################################################*/
-static void Animations_Init(void) {
- ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick);
- Event_RegisterVoid(&TextureEvents_PackChanged, NULL, Animations_PackChanged);
- Event_RegisterEntry(&TextureEvents_FileChanged, NULL, Animations_FileChanged);
-}
-
-static void Animations_Free(void) {
- Animations_Clear();
- Event_UnregisterVoid(&TextureEvents_PackChanged, NULL, Animations_PackChanged);
- Event_UnregisterEntry(&TextureEvents_FileChanged, NULL, Animations_FileChanged);
-}
-
-struct IGameComponent Animations_Component = {
- Animations_Init, /* Init */
- Animations_Free /* Free */
-};
diff --git a/src/Animations.h b/src/Animations.h
deleted file mode 100644
index 3ddf6694d..000000000
--- a/src/Animations.h
+++ /dev/null
@@ -1,14 +0,0 @@
-#ifndef CC_ANIMS_H
-#define CC_ANIMS_H
-#include "Core.h"
-/* Texture animations, and water and lava liquid animations.
- Copyright 2014 - 2017 ClassicalSharp | Licensed under BSD-3
- Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt
- mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm
- Water animation originally written by cybertoon, big thanks!
-*/
-
-struct IGameComponent;
-struct ScheduledTask;
-extern struct IGameComponent Animations_Component;
-#endif
diff --git a/src/Block.c b/src/Block.c
index 508045fad..e09a7c4f8 100644
--- a/src/Block.c
+++ b/src/Block.c
@@ -1,13 +1,12 @@
#include "Block.h"
#include "Funcs.h"
#include "ExtMath.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Game.h"
#include "Entity.h"
#include "Inventory.h"
#include "Event.h"
#include "Platform.h"
-#include "Bitmap.h"
#include "GameStructs.h"
bool Block_IsLiquid[BLOCK_COUNT];
diff --git a/src/Builder.c b/src/Builder.c
index 6b0e50eb8..5f5886b1d 100644
--- a/src/Builder.c
+++ b/src/Builder.c
@@ -12,7 +12,7 @@
#include "BlockID.h"
#include "Block.h"
#include "PackedCol.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "VertexStructs.h"
int Builder_SidesLevel, Builder_EdgeLevel;
diff --git a/src/ClassiCube.vcxproj b/src/ClassiCube.vcxproj
index 707102fcb..bad7110e5 100644
--- a/src/ClassiCube.vcxproj
+++ b/src/ClassiCube.vcxproj
@@ -218,7 +218,6 @@
-
@@ -231,7 +230,6 @@
-
@@ -280,7 +278,6 @@
-
@@ -296,7 +293,6 @@
-
diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters
index 0917ef396..6d89e34ea 100644
--- a/src/ClassiCube.vcxproj.filters
+++ b/src/ClassiCube.vcxproj.filters
@@ -195,9 +195,6 @@
Header Files\MeshBuilder
-
- Header Files\TexturePack
-
Header Files\Math
@@ -216,9 +213,6 @@
Header Files\Entities
-
- Header Files\TexturePack
-
Header Files\2D
@@ -353,9 +347,6 @@
Source Files
-
- Source Files\TexturePack
-
Source Files\Entities
@@ -386,9 +377,6 @@
Source Files\Entities
-
- Source Files\TexturePack
-
Source Files\2D
diff --git a/src/Deflate.c b/src/Deflate.c
index e9cde793d..e3a2bbeb5 100644
--- a/src/Deflate.c
+++ b/src/Deflate.c
@@ -1056,3 +1056,156 @@ void ZLib_MakeStream(struct Stream* stream, struct ZLibState* state, struct Stre
stream->Write = ZLib_StreamWriteFirst;
stream->Close = ZLib_StreamClose;
}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------------ZipEntry---------------------------------------------------------*
+*#########################################################################################################################*/
+#define ZIP_MAXNAMELEN 512
+static ReturnCode Zip_ReadLocalFileHeader(struct ZipState* state, struct ZipEntry* entry) {
+ struct Stream* stream = state->Input;
+ uint8_t header[26];
+ uint32_t compressedSize, uncompressedSize;
+ int method, pathLen, extraLen;
+
+ String path; char pathBuffer[ZIP_MAXNAMELEN];
+ struct Stream portion, compStream;
+ struct InflateState inflate;
+ ReturnCode res;
+ if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
+
+ method = Stream_GetU16_LE(&header[4]);
+ compressedSize = Stream_GetU32_LE(&header[14]);
+ uncompressedSize = Stream_GetU32_LE(&header[18]);
+
+ /* Some .zip files don't set these in local file header */
+ if (!compressedSize) compressedSize = entry->CompressedSize;
+ if (!uncompressedSize) uncompressedSize = entry->UncompressedSize;
+
+ pathLen = Stream_GetU16_LE(&header[22]);
+ extraLen = Stream_GetU16_LE(&header[24]);
+ if (pathLen > ZIP_MAXNAMELEN) return ZIP_ERR_FILENAME_LEN;
+
+ path = String_Init(pathBuffer, pathLen, pathLen);
+ if ((res = Stream_Read(stream, pathBuffer, pathLen))) return res;
+
+ if (!state->SelectEntry(&path)) return 0;
+ /* local file may have extra data before actual data (e.g. ZIP64) */
+ if ((res = stream->Skip(stream, extraLen))) return res;
+
+ if (method == 0) {
+ Stream_ReadonlyPortion(&portion, stream, uncompressedSize);
+ state->ProcessEntry(&path, &portion, entry);
+ } else if (method == 8) {
+ Stream_ReadonlyPortion(&portion, stream, compressedSize);
+ Inflate_MakeStream(&compStream, &inflate, &portion);
+ state->ProcessEntry(&path, &compStream, entry);
+ } else {
+ Platform_Log1("Unsupported.zip entry compression method: %i", &method);
+ }
+ return 0;
+}
+
+static ReturnCode Zip_ReadCentralDirectory(struct ZipState* state, struct ZipEntry* entry) {
+ struct Stream* stream = state->Input;
+ uint8_t header[42];
+ int pathLen, extraLen, commentLen;
+ ReturnCode res;
+ if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
+
+ entry->Crc32 = Stream_GetU32_LE(&header[12]);
+ entry->CompressedSize = Stream_GetU32_LE(&header[16]);
+ entry->UncompressedSize = Stream_GetU32_LE(&header[20]);
+
+ pathLen = Stream_GetU16_LE(&header[24]);
+ extraLen = Stream_GetU16_LE(&header[26]);
+ commentLen = Stream_GetU16_LE(&header[28]);
+
+ entry->LocalHeaderOffset = Stream_GetU32_LE(&header[38]);
+ /* skip data following central directory entry header */
+ return stream->Skip(stream, pathLen + extraLen + commentLen);
+}
+
+static ReturnCode Zip_ReadEndOfCentralDirectory(struct ZipState* state, uint32_t* centralDirectoryOffset) {
+ struct Stream* stream = state->Input;
+ uint8_t header[18];
+
+ ReturnCode res;
+ if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
+
+ state->EntriesCount = Stream_GetU16_LE(&header[6]);
+ *centralDirectoryOffset = Stream_GetU32_LE(&header[12]);
+ return 0;
+}
+
+enum ZipSig {
+ ZIP_SIG_ENDOFCENTRALDIR = 0x06054b50,
+ ZIP_SIG_CENTRALDIR = 0x02014b50,
+ ZIP_SIG_LOCALFILEHEADER = 0x04034b50
+};
+
+static void Zip_DefaultProcessor(const String* path, struct Stream* data, struct ZipEntry* entry) { }
+static bool Zip_DefaultSelector(const String* path) { return true; }
+void Zip_Init(struct ZipState* state, struct Stream* input) {
+ state->Input = input;
+ state->EntriesCount = 0;
+ state->ProcessEntry = Zip_DefaultProcessor;
+ state->SelectEntry = Zip_DefaultSelector;
+}
+
+ReturnCode Zip_Extract(struct ZipState* state) {
+ struct Stream* stream = state->Input;
+ uint32_t stream_len, centralDirOffset;
+ uint32_t sig = 0;
+ int i, count;
+
+ ReturnCode res;
+ state->EntriesCount = 0;
+ if ((res = stream->Length(stream, &stream_len))) return res;
+
+ /* At -22 for nearly all zips, but try a bit further back in case of comment */
+ count = min(257, stream_len);
+ for (i = 22; i < count; i++) {
+ res = stream->Seek(stream, stream_len - i);
+ if (res) return ZIP_ERR_SEEK_END_OF_CENTRAL_DIR;
+
+ if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
+ if (sig == ZIP_SIG_ENDOFCENTRALDIR) break;
+ }
+
+ if (sig != ZIP_SIG_ENDOFCENTRALDIR) return ZIP_ERR_NO_END_OF_CENTRAL_DIR;
+ res = Zip_ReadEndOfCentralDirectory(state, ¢ralDirOffset);
+ if (res) return res;
+
+ res = stream->Seek(stream, centralDirOffset);
+ if (res) return ZIP_ERR_SEEK_CENTRAL_DIR;
+ if (state->EntriesCount > ZIP_MAX_ENTRIES) return ZIP_ERR_TOO_MANY_ENTRIES;
+
+ /* Read all the central directory entries */
+ for (count = 0; count < state->EntriesCount; count++) {
+ if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
+
+ if (sig == ZIP_SIG_CENTRALDIR) {
+ res = Zip_ReadCentralDirectory(state, &state->Entries[count]);
+ if (res) return res;
+ } else if (sig == ZIP_SIG_ENDOFCENTRALDIR) {
+ break;
+ } else {
+ return ZIP_ERR_INVALID_CENTRAL_DIR;
+ }
+ }
+
+ /* Now read the local file header entries */
+ for (i = 0; i < count; i++) {
+ struct ZipEntry* entry = &state->Entries[i];
+ res = stream->Seek(stream, entry->LocalHeaderOffset);
+ if (res) return ZIP_ERR_SEEK_LOCAL_DIR;
+
+ if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
+ if (sig != ZIP_SIG_LOCALFILEHEADER) return ZIP_ERR_INVALID_LOCAL_DIR;
+
+ res = Zip_ReadLocalFileHeader(state, entry);
+ if (res) return res;
+ }
+ return 0;
+}
diff --git a/src/Deflate.h b/src/Deflate.h
index 737998769..ec4c742a1 100644
--- a/src/Deflate.h
+++ b/src/Deflate.h
@@ -1,6 +1,6 @@
#ifndef CC_DEFLATE_H
#define CC_DEFLATE_H
-#include "Core.h"
+#include "String.h"
/* Decodes data compressed using DEFLATE in a streaming manner.
Partially based off information from
https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way
@@ -65,11 +65,14 @@ struct InflateState {
uint8_t Window[INFLATE_WINDOW_SIZE]; /* Holds circular buffer of recent output data, used for LZ77 */
};
-void Inflate_Init(struct InflateState* state, struct Stream* source);
+/* Initialises DEFLATE decompressor state to defaults. */
+CC_EXPORT void Inflate_Init(struct InflateState* state, struct Stream* source);
+/* Attempts to decompress all currently pending data. */
+/* NOTE: This is a low level call - usually you should use Inflate_MakeStream. */
void Inflate_Process(struct InflateState* state);
/* Deompresses input data read from another stream using DEFLATE. Read only stream. */
/* NOTE: This only uncompresses pure DEFLATE compressed data. */
-/* If the data starts with a GZIP or ZLIB header, use GZipHeader_Read or ZLibHeader_Read to skip it. */
+/* If data starts with a GZIP or ZLIB header, use GZipHeader_Read or ZLibHeader_Read to first skip it. */
CC_EXPORT void Inflate_MakeStream(struct Stream* stream, struct InflateState* state, struct Stream* underlying);
@@ -100,8 +103,35 @@ struct GZipState { struct DeflateState Base; uint32_t Crc32, Size; };
/* Compresses input data using GZIP, then writes compressed output to another stream. Write only stream. */
/* GZIP compression is GZIP header, followed by DEFLATE compressed data, followed by GZIP footer. */
CC_EXPORT void GZip_MakeStream(struct Stream* stream, struct GZipState* state, struct Stream* underlying);
+
struct ZLibState { struct DeflateState Base; uint32_t Adler32; };
/* Compresses input data using ZLIB, then writes compressed output to another stream. Write only stream. */
/* ZLIB compression is ZLIB header, followed by DEFLATE compressed data, followed by ZLIB footer. */
CC_EXPORT void ZLib_MakeStream(struct Stream* stream, struct ZLibState* state, struct Stream* underlying);
+
+/* Minimal data needed to describe an entry in a .zip archive. */
+struct ZipEntry { uint32_t CompressedSize, UncompressedSize, LocalHeaderOffset, Crc32; };
+#define ZIP_MAX_ENTRIES 2048
+
+/* Stores state for reading and processing entries in a .zip archive. */
+struct ZipState {
+ /* Source of the .zip archive data. Must be seekable. */
+ struct Stream* Input;
+ /* Callback function to process the data in a .zip archive entry. */
+ /* NOTE: data stream may not be seekable. (entry data might be compressed) */
+ void (*ProcessEntry)(const String* path, struct Stream* data, struct ZipEntry* entry);
+ /* Predicate used to select which entries in a .zip archive get proessed. */
+ /* NOTE: returning false entirely skips the entry. (avoids pointless seek to entry) */
+ bool (*SelectEntry)(const String* path);
+ /* Number of entries in the .zip archive. */
+ int EntriesCount;
+ /* Data for each entry in the .zip archive. */
+ struct ZipEntry Entries[ZIP_MAX_ENTRIES];
+};
+
+/* Initialises .zip archive reader state to defaults. */
+CC_EXPORT void Zip_Init(struct ZipState* state, struct Stream* input);
+/* Reads and processes the entries in a .zip archive. */
+/* NOTE: Must have been initialised with Zip_Init first. */
+CC_EXPORT ReturnCode Zip_Extract(struct ZipState* state);
#endif
diff --git a/src/Drawer.c b/src/Drawer.c
index 40fb5d55c..ae031a971 100644
--- a/src/Drawer.c
+++ b/src/Drawer.c
@@ -1,5 +1,5 @@
#include "Drawer.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Constants.h"
bool Drawer_Tinted;
diff --git a/src/EnvRenderer.c b/src/EnvRenderer.c
index b933c6a73..d80df144c 100644
--- a/src/EnvRenderer.c
+++ b/src/EnvRenderer.c
@@ -13,7 +13,7 @@
#include "Stream.h"
#include "Block.h"
#include "Event.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Platform.h"
#include "Camera.h"
#include "Particle.h"
diff --git a/src/Game.c b/src/Game.c
index 924a8a454..5d464b889 100644
--- a/src/Game.c
+++ b/src/Game.c
@@ -19,7 +19,6 @@
#include "Model.h"
#include "Particle.h"
#include "AsyncDownloader.h"
-#include "Animations.h"
#include "Inventory.h"
#include "InputHandler.h"
#include "ServerConnection.h"
@@ -33,7 +32,6 @@
#include "Menus.h"
#include "Audio.h"
#include "Stream.h"
-#include "TerrainAtlas.h"
int Game_Width, Game_Height;
double Game_Accumulator;
diff --git a/src/IsometricDrawer.c b/src/IsometricDrawer.c
index 0a503bf73..9f88d1afb 100644
--- a/src/IsometricDrawer.c
+++ b/src/IsometricDrawer.c
@@ -4,7 +4,7 @@
#include "PackedCol.h"
#include "ExtMath.h"
#include "Block.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Block.h"
static float iso_scale;
diff --git a/src/MapRenderer.c b/src/MapRenderer.c
index 87265a4a5..00016a113 100644
--- a/src/MapRenderer.c
+++ b/src/MapRenderer.c
@@ -10,7 +10,7 @@
#include "Game.h"
#include "Graphics.h"
#include "Platform.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Utils.h"
#include "World.h"
diff --git a/src/Menus.c b/src/Menus.c
index 0f5e68740..dbd353482 100644
--- a/src/Menus.c
+++ b/src/Menus.c
@@ -7,7 +7,6 @@
#include "Drawer2D.h"
#include "Graphics.h"
#include "Funcs.h"
-#include "TerrainAtlas.h"
#include "Model.h"
#include "MapGenerator.h"
#include "ServerConnection.h"
diff --git a/src/Model.c b/src/Model.c
index 75dd84f95..804956d28 100644
--- a/src/Model.c
+++ b/src/Model.c
@@ -7,7 +7,7 @@
#include "Camera.h"
#include "Event.h"
#include "ExtMath.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Drawer.h"
#include "Block.h"
#include "Stream.h"
diff --git a/src/PacketHandlers.c b/src/PacketHandlers.c
index d2e65b408..ab763145f 100644
--- a/src/PacketHandlers.c
+++ b/src/PacketHandlers.c
@@ -23,7 +23,6 @@
#include "TexturePack.h"
#include "Gui.h"
#include "Errors.h"
-#include "TerrainAtlas.h"
/* Classic state */
static uint8_t classic_tabList[ENTITIES_MAX_COUNT >> 3];
diff --git a/src/Particle.c b/src/Particle.c
index cda6442df..ddd1fe91b 100644
--- a/src/Particle.c
+++ b/src/Particle.c
@@ -4,7 +4,7 @@
#include "ExtMath.h"
#include "Lighting.h"
#include "Entity.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Graphics.h"
#include "Funcs.h"
#include "Game.h"
diff --git a/src/Screens.c b/src/Screens.c
index 8463d791b..9243e3ecb 100644
--- a/src/Screens.c
+++ b/src/Screens.c
@@ -7,7 +7,7 @@
#include "Drawer2D.h"
#include "Graphics.h"
#include "Funcs.h"
-#include "TerrainAtlas.h"
+#include "TexturePack.h"
#include "Model.h"
#include "MapGenerator.h"
#include "ServerConnection.h"
diff --git a/src/TerrainAtlas.c b/src/TerrainAtlas.c
deleted file mode 100644
index 3f591de84..000000000
--- a/src/TerrainAtlas.c
+++ /dev/null
@@ -1,113 +0,0 @@
-#include "TerrainAtlas.h"
-#include "Block.h"
-#include "ExtMath.h"
-#include "Funcs.h"
-#include "Graphics.h"
-#include "Platform.h"
-
-Bitmap Atlas_Bitmap;
-int Atlas_TileSize, Atlas_RowsCount;
-int Atlas1D_Count, Atlas1D_TilesPerAtlas;
-int Atlas1D_Mask, Atlas1D_Shift;
-float Atlas1D_InvTileSize;
-GfxResourceID Atlas1D_TexIds[ATLAS1D_MAX_ATLASES];
-
-TextureRec Atlas1D_TexRec(TextureLoc texLoc, int uCount, int* index) {
- TextureRec rec;
- int y = Atlas1D_RowId(texLoc);
- *index = Atlas1D_Index(texLoc);
-
- /* Adjust coords to be slightly inside - fixes issues with AMD/ATI cards */
- rec.U1 = 0.0f;
- rec.V1 = y * Atlas1D_InvTileSize;
- rec.U2 = (uCount - 1) + UV2_Scale;
- rec.V2 = rec.V1 + UV2_Scale * Atlas1D_InvTileSize;
- return rec;
-}
-
-static void Atlas_Convert2DTo1D(void) {
- int tileSize = Atlas_TileSize;
- int tilesPerAtlas = Atlas1D_TilesPerAtlas;
- int atlasesCount = Atlas1D_Count;
- Bitmap atlas1D;
- int atlasX, atlasY;
- int tile = 0, i, y;
-
- Platform_Log2("Loaded new atlas: %i bmps, %i per bmp", &atlasesCount, &tilesPerAtlas);
- Bitmap_Allocate(&atlas1D, tileSize, tilesPerAtlas * tileSize);
-
- for (i = 0; i < atlasesCount; i++) {
- for (y = 0; y < tilesPerAtlas; y++, tile++) {
- atlasX = Atlas2D_TileX(tile) * tileSize;
- atlasY = Atlas2D_TileY(tile) * tileSize;
-
- Bitmap_CopyBlock(atlasX, atlasY, 0, y * tileSize,
- &Atlas_Bitmap, &atlas1D, tileSize);
- }
- Atlas1D_TexIds[i] = Gfx_CreateTexture(&atlas1D, true, Gfx_Mipmaps);
- }
- Mem_Free(atlas1D.Scan0);
-}
-
-static void Atlas_Update1D(void) {
- int maxAtlasHeight, maxTilesPerAtlas, maxTiles;
-
- maxAtlasHeight = min(4096, Gfx_MaxTexHeight);
- maxTilesPerAtlas = maxAtlasHeight / Atlas_TileSize;
- maxTiles = Atlas_RowsCount * ATLAS2D_TILES_PER_ROW;
-
- Atlas1D_TilesPerAtlas = min(maxTilesPerAtlas, maxTiles);
- Atlas1D_Count = Math_CeilDiv(maxTiles, Atlas1D_TilesPerAtlas);
-
- Atlas1D_InvTileSize = 1.0f / Atlas1D_TilesPerAtlas;
- Atlas1D_Mask = Atlas1D_TilesPerAtlas - 1;
- Atlas1D_Shift = Math_Log2(Atlas1D_TilesPerAtlas);
-}
-
-void Atlas_Update(Bitmap* bmp) {
- Atlas_Bitmap = *bmp;
- Atlas_TileSize = bmp->Width / ATLAS2D_TILES_PER_ROW;
- Atlas_RowsCount = bmp->Height / Atlas_TileSize;
- Atlas_RowsCount = min(Atlas_RowsCount, ATLAS2D_MAX_ROWS_COUNT);
-
- Block_RecalculateAllSpriteBB();
- Atlas_Update1D();
- Atlas_Convert2DTo1D();
-}
-
-static GfxResourceID Atlas_LoadTile_Raw(TextureLoc texLoc, Bitmap* element) {
- int size = Atlas_TileSize;
- int x = Atlas2D_TileX(texLoc), y = Atlas2D_TileY(texLoc);
- if (y >= Atlas_RowsCount) return GFX_NULL;
-
- Bitmap_CopyBlock(x * size, y * size, 0, 0, &Atlas_Bitmap, element, size);
- return Gfx_CreateTexture(element, false, Gfx_Mipmaps);
-}
-
-GfxResourceID Atlas_LoadTile(TextureLoc texLoc) {
- int tileSize = Atlas_TileSize;
- Bitmap tile;
- GfxResourceID texId;
- uint8_t scan0[Bitmap_DataSize(64, 64)];
-
- /* Try to allocate bitmap on stack if possible */
- if (tileSize > 64) {
- Bitmap_Allocate(&tile, tileSize, tileSize);
- texId = Atlas_LoadTile_Raw(texLoc, &tile);
- Mem_Free(tile.Scan0);
- return texId;
- } else {
- Bitmap_Create(&tile, tileSize, tileSize, scan0);
- return Atlas_LoadTile_Raw(texLoc, &tile);
- }
-}
-
-void Atlas_Free(void) {
- int i;
- Mem_Free(Atlas_Bitmap.Scan0);
- Atlas_Bitmap.Scan0 = NULL;
-
- for (i = 0; i < Atlas1D_Count; i++) {
- Gfx_DeleteTexture(&Atlas1D_TexIds[i]);
- }
-}
diff --git a/src/TerrainAtlas.h b/src/TerrainAtlas.h
deleted file mode 100644
index 23bb5cf55..000000000
--- a/src/TerrainAtlas.h
+++ /dev/null
@@ -1,58 +0,0 @@
-#ifndef CC_TERRAINATLAS_H
-#define CC_TERRAINATLAS_H
-#include "Bitmap.h"
-/* Represents the 2D texture atlas of terrain.png, and converted into an array of 1D textures.
- Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
-*/
-
-/* Number of tiles in each row */
-#define ATLAS2D_TILES_PER_ROW 16
-#define ATLAS2D_MASK 15
-#define ATLAS2D_SHIFT 4
-/* Maximum supported number of rows in the atlas. */
-#ifdef EXTENDED_TEXTURES
-#define ATLAS2D_MAX_ROWS_COUNT 32
-#else
-#define ATLAS2D_MAX_ROWS_COUNT 16
-#endif
-/* Maximum possible number of 1D terrain atlases. (worst case, each 1D atlas only has 1 tile) */
-#define ATLAS1D_MAX_ATLASES (ATLAS2D_TILES_PER_ROW * ATLAS2D_MAX_ROWS_COUNT)
-
-/* Bitmap that contains the textures of all tiles. */
-/* Tiles are indexed left to right, top to bottom. */
-extern Bitmap Atlas_Bitmap;
-/* Size of each tile in pixels. (default 16x16) */
-extern int Atlas_TileSize;
-/* Number of rows in the atlas. (default 16, can be 32) */
-extern int Atlas_RowsCount;
-/* Number of 1D atlases the atlas was split into. */
-extern int Atlas1D_Count;
-/* Number of tiles in each 1D atlas. */
-extern int Atlas1D_TilesPerAtlas;
-/* Converts a tile id into 1D atlas index, and index within that atlas. */
-extern int Atlas1D_Mask, Atlas1D_Shift;
-/* Texture V coord that equals the size of one tile. (i.e. 1/Atlas1D_TilesPerAtlas) */
-/* NOTE: The texture U coord that equals the size of one tile is 1. */
-extern float Atlas1D_InvTileSize;
-/* Textures for each 1D atlas. Only Atlas1D_Count of these are valid. */
-extern GfxResourceID Atlas1D_TexIds[ATLAS1D_MAX_ATLASES];
-
-#define Atlas2D_TileX(texLoc) ((texLoc) & ATLAS2D_MASK) /* texLoc % ATLAS2D_TILES_PER_ROW */
-#define Atlas2D_TileY(texLoc) ((texLoc) >> ATLAS2D_SHIFT) /* texLoc / ATLAS2D_TILES_PER_ROW */
-/* Returns the index of the given tile id within a 1D atlas */
-#define Atlas1D_RowId(texLoc) ((texLoc) & Atlas1D_Mask) /* texLoc % Atlas1D_TilesPerAtlas */
-/* Returns the index of the 1D atlas within the array of 1D atlases that contains the given tile id */
-#define Atlas1D_Index(texLoc) ((texLoc) >> Atlas1D_Shift) /* texLoc / Atlas1D_TilesPerAtlas */
-
-/* Loads the given atlas and converts it into an array of 1D atlases. */
-/* NOTE: Use Game_ChangeTerrainAtlas to change atlas, because that raises TextureEvents_AtlasChanged */
-void Atlas_Update(Bitmap* bmp);
-/* Loads the given tile into a new separate texture. */
-GfxResourceID Atlas_LoadTile(TextureLoc texLoc);
-/* Frees the atlas and 1D atlas textures. */
-void Atlas_Free(void);
-/* Returns the UV rectangle of the given tile id in the 1D atlases. */
-/* That is, returns U1/U2/V1/V2 coords that make up the tile in a 1D atlas. */
-/* index is set to the index of the 1D atlas that the tile is in. */
-TextureRec Atlas1D_TexRec(TextureLoc texLoc, int uCount, int* index);
-#endif
diff --git a/src/TexturePack.c b/src/TexturePack.c
index c41fa76ad..23389ce2e 100644
--- a/src/TexturePack.c
+++ b/src/TexturePack.c
@@ -1,189 +1,499 @@
#include "TexturePack.h"
#include "Constants.h"
#include "Platform.h"
-#include "ErrorHandler.h"
#include "Stream.h"
-#include "Bitmap.h"
#include "World.h"
#include "Graphics.h"
#include "Event.h"
#include "Game.h"
#include "AsyncDownloader.h"
-#include "ErrorHandler.h"
#include "Platform.h"
#include "Deflate.h"
#include "Stream.h"
#include "Funcs.h"
#include "Errors.h"
#include "Chat.h"
+#include "ExtMath.h"
+#include "Block.h" /* NOTE: Just for block_recalculateallspritebb */
+#include "Chat.h"
+#include "Options.h"
+
+#define LIQUID_ANIM_MAX 64
+/* Based off the incredible work from https://dl.dropboxusercontent.com/u/12694594/lava.txt
+ mirrored at https://github.com/UnknownShadow200/ClassicalSharp/wiki/Minecraft-Classic-lava-animation-algorithm
+ Water animation originally written by cybertoon, big thanks!
+*/
/*########################################################################################################################*
-*--------------------------------------------------------ZipEntry---------------------------------------------------------*
+*-----------------------------------------------------Lava animation------------------------------------------------------*
*#########################################################################################################################*/
-#define ZIP_MAXNAMELEN 512
-static ReturnCode Zip_ReadLocalFileHeader(struct ZipState* state, struct ZipEntry* entry) {
- struct Stream* stream = state->Input;
- uint8_t contents[26];
- uint32_t compressedSize, uncompressedSize;
- int method, pathLen, extraLen;
+static float L_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float L_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float L_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static RNGState L_rnd;
+static bool L_rndInitalised;
- String path; char pathBuffer[ZIP_MAXNAMELEN];
- struct Stream portion, compStream;
- struct InflateState inflate;
- ReturnCode res;
- if ((res = Stream_Read(stream, contents, sizeof(contents)))) return res;
+static void LavaAnimation_Tick(BitmapCol* ptr, int size) {
+ int mask = size - 1, shift = Math_Log2(size);
+ float soupHeat, potHeat, col;
+ int x, y, i = 0;
- /* contents[0] (2) version needed */
- /* contents[2] (2) flags */
- method = Stream_GetU16_LE(&contents[4]);
- /* contents[6] (4) last modified */
- /* contents[10] (4) CRC32 */
-
- compressedSize = Stream_GetU32_LE(&contents[14]);
- if (!compressedSize) compressedSize = entry->CompressedSize;
- uncompressedSize = Stream_GetU32_LE(&contents[18]);
- if (!uncompressedSize) uncompressedSize = entry->UncompressedSize;
-
- pathLen = Stream_GetU16_LE(&contents[22]);
- extraLen = Stream_GetU16_LE(&contents[24]);
- if (pathLen > ZIP_MAXNAMELEN) return ZIP_ERR_FILENAME_LEN;
-
- path = String_Init(pathBuffer, pathLen, pathLen);
- if ((res = Stream_Read(stream, pathBuffer, pathLen))) return res;
-
- if (!state->SelectEntry(&path)) return 0;
- if ((res = stream->Skip(stream, extraLen))) return res;
-
- if (method == 0) {
- Stream_ReadonlyPortion(&portion, stream, uncompressedSize);
- state->ProcessEntry(&path, &portion, entry);
- } else if (method == 8) {
- Stream_ReadonlyPortion(&portion, stream, compressedSize);
- Inflate_MakeStream(&compStream, &inflate, &portion);
- state->ProcessEntry(&path, &compStream, entry);
- } else {
- Platform_Log1("Unsupported.zip entry compression method: %i", &method);
+ if (!L_rndInitalised) {
+ Random_InitFromCurrentTime(&L_rnd);
+ L_rndInitalised = true;
}
- return 0;
-}
+
+ for (y = 0; y < size; y++) {
+ for (x = 0; x < size; x++) {
+ /* Calculate the colour at this coordinate in the heatmap */
-static ReturnCode Zip_ReadCentralDirectory(struct ZipState* state, struct ZipEntry* entry) {
- struct Stream* stream = state->Input;
- uint8_t contents[42];
- int pathLen, extraLen, commentLen;
- uint32_t extraDataLen;
+ /* Lookup table for (int)(1.2 * sin([ANGLE] * 22.5 * MATH_DEG2RAD)); */
+ /* [ANGLE] is integer x/y, so repeats every 16 intervals */
+ static int8_t sin_adj_table[16] = { 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, -1, -1, -1, 0, 0 };
+ int xx = x + sin_adj_table[y & 0xF], yy = y + sin_adj_table[x & 0xF];
- ReturnCode res;
- if ((res = Stream_Read(stream, contents, sizeof(contents)))) return res;
+ soupHeat =
+ L_soupHeat[((yy - 1) & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[((yy - 1) & mask) << shift | (xx & mask)] +
+ L_soupHeat[((yy - 1) & mask) << shift | ((xx + 1) & mask)] +
- /* contents[0] (2) OS */
- /* contents[2] (2) version needed*/
- /* contents[4] (2) flags */
- /* contents[6] (2) compresssion method*/
- /* contents[8] (4) last modified */
- entry->Crc32 = Stream_GetU32_LE(&contents[12]);
- entry->CompressedSize = Stream_GetU32_LE(&contents[16]);
- entry->UncompressedSize = Stream_GetU32_LE(&contents[20]);
+ L_soupHeat[(yy & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[(yy & mask) << shift | (xx & mask)] +
+ L_soupHeat[(yy & mask) << shift | ((xx + 1) & mask)] +
- pathLen = Stream_GetU16_LE(&contents[24]);
- extraLen = Stream_GetU16_LE(&contents[26]);
- commentLen = Stream_GetU16_LE(&contents[28]);
- /* contents[30] (2) disk number */
- /* contents[32] (2) internal attributes */
- /* contents[34] (4) external attributes */
- entry->LocalHeaderOffset = Stream_GetU32_LE(&contents[38]);
+ L_soupHeat[((yy + 1) & mask) << shift | ((xx - 1) & mask)] +
+ L_soupHeat[((yy + 1) & mask) << shift | (xx & mask)] +
+ L_soupHeat[((yy + 1) & mask) << shift | ((xx + 1) & mask)];
- extraDataLen = pathLen + extraLen + commentLen;
- return stream->Skip(stream, extraDataLen);
-}
+ potHeat =
+ L_potHeat[i] + /* x , y */
+ L_potHeat[y << shift | ((x + 1) & mask)] + /* x + 1, y */
+ L_potHeat[((y + 1) & mask) << shift | x] + /* x , y + 1 */
+ L_potHeat[((y + 1) & mask) << shift | ((x + 1) & mask)];/* x + 1, y + 1 */
-static ReturnCode Zip_ReadEndOfCentralDirectory(struct ZipState* state, uint32_t* centralDirectoryOffset) {
- struct Stream* stream = state->Input;
- uint8_t contents[18];
+ L_soupHeat[i] = soupHeat * 0.1f + potHeat * 0.2f;
- ReturnCode res;
- if ((res = Stream_Read(stream, contents, sizeof(contents)))) return res;
+ L_potHeat[i] += L_flameHeat[i];
+ if (L_potHeat[i] < 0.0f) L_potHeat[i] = 0.0f;
- /* contents[0] (2) disk number */
- /* contents[2] (2) disk number start */
- /* contents[4] (2) disk entries */
- state->EntriesCount = Stream_GetU16_LE(&contents[6]);
- /* contents[8] (4) central directory size */
- *centralDirectoryOffset = Stream_GetU32_LE(&contents[12]);
- /* contents[16] (2) comment length */
- return 0;
-}
+ L_flameHeat[i] -= 0.06f * 0.01f;
+ if (Random_Float(&L_rnd) <= 0.005f) L_flameHeat[i] = 1.5f * 0.01f;
-enum ZipSig {
- ZIP_SIG_ENDOFCENTRALDIR = 0x06054b50,
- ZIP_SIG_CENTRALDIR = 0x02014b50,
- ZIP_SIG_LOCALFILEHEADER = 0x04034b50
-};
+ /* Output the pixel */
+ col = 2.0f * L_soupHeat[i];
+ Math_Clamp(col, 0.0f, 1.0f);
-static void Zip_DefaultProcessor(const String* path, struct Stream* data, struct ZipEntry* entry) { }
-static bool Zip_DefaultSelector(const String* path) { return true; }
-void Zip_Init(struct ZipState* state, struct Stream* input) {
- state->Input = input;
- state->EntriesCount = 0;
- state->ProcessEntry = Zip_DefaultProcessor;
- state->SelectEntry = Zip_DefaultSelector;
-}
+ ptr->R = (uint8_t)(col * 100.0f + 155.0f);
+ ptr->G = (uint8_t)(col * col * 255.0f);
+ ptr->B = (uint8_t)(col * col * col * col * 128.0f);
+ ptr->A = 255;
-ReturnCode Zip_Extract(struct ZipState* state) {
- struct Stream* stream = state->Input;
- uint32_t stream_len, centralDirOffset;
- uint32_t sig = 0;
- int i, count;
-
- ReturnCode res;
- state->EntriesCount = 0;
- if ((res = stream->Length(stream, &stream_len))) return res;
-
- /* At -22 for nearly all zips, but try a bit further back in case of comment */
- count = min(257, stream_len);
- for (i = 22; i < count; i++) {
- res = stream->Seek(stream, stream_len - i);
- if (res) return ZIP_ERR_SEEK_END_OF_CENTRAL_DIR;
-
- if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
- if (sig == ZIP_SIG_ENDOFCENTRALDIR) break;
- }
-
- if (sig != ZIP_SIG_ENDOFCENTRALDIR) return ZIP_ERR_NO_END_OF_CENTRAL_DIR;
- res = Zip_ReadEndOfCentralDirectory(state, ¢ralDirOffset);
- if (res) return res;
-
- res = stream->Seek(stream, centralDirOffset);
- if (res) return ZIP_ERR_SEEK_CENTRAL_DIR;
- if (state->EntriesCount > ZIP_MAX_ENTRIES) return ZIP_ERR_TOO_MANY_ENTRIES;
-
- /* Read all the central directory entries */
- for (count = 0; count < state->EntriesCount; count++) {
- if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
-
- if (sig == ZIP_SIG_CENTRALDIR) {
- res = Zip_ReadCentralDirectory(state, &state->Entries[count]);
- if (res) return res;
- } else if (sig == ZIP_SIG_ENDOFCENTRALDIR) {
- break;
- } else {
- return ZIP_ERR_INVALID_CENTRAL_DIR;
+ ptr++; i++;
}
}
+}
- /* Now read the local file header entries */
- for (i = 0; i < count; i++) {
- struct ZipEntry* entry = &state->Entries[i];
- res = stream->Seek(stream, entry->LocalHeaderOffset);
- if (res) return ZIP_ERR_SEEK_LOCAL_DIR;
- if ((res = Stream_ReadU32_LE(stream, &sig))) return res;
- if (sig != ZIP_SIG_LOCALFILEHEADER) return ZIP_ERR_INVALID_LOCAL_DIR;
+/*########################################################################################################################*
+*----------------------------------------------------Water animation------------------------------------------------------*
+*#########################################################################################################################*/
+static float W_soupHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float W_potHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static float W_flameHeat[LIQUID_ANIM_MAX * LIQUID_ANIM_MAX];
+static RNGState W_rnd;
+static bool W_rndInitalised;
- res = Zip_ReadLocalFileHeader(state, entry);
- if (res) return res;
+static void WaterAnimation_Tick(BitmapCol* ptr, int size) {
+ int mask = size - 1, shift = Math_Log2(size);
+ float soupHeat, col;
+ int x, y, i = 0;
+
+ if (!W_rndInitalised) {
+ Random_InitFromCurrentTime(&W_rnd);
+ W_rndInitalised = true;
+ }
+
+ for (y = 0; y < size; y++) {
+ for (x = 0; x < size; x++) {
+ /* Calculate the colour at this coordinate in the heatmap */
+ soupHeat =
+ W_soupHeat[y << shift | ((x - 1) & mask)] +
+ W_soupHeat[y << shift | x ] +
+ W_soupHeat[y << shift | ((x + 1) & mask)];
+
+ W_soupHeat[i] = soupHeat / 3.3f + W_potHeat[i] * 0.8f;
+
+ W_potHeat[i] += W_flameHeat[i];
+ if (W_potHeat[i] < 0.0f) W_potHeat[i] = 0.0f;
+
+ W_flameHeat[i] -= 0.1f * 0.05f;
+ if (Random_Float(&W_rnd) <= 0.05f) W_flameHeat[i] = 0.5f * 0.05f;
+
+ /* Output the pixel */
+ col = W_soupHeat[i];
+ Math_Clamp(col, 0.0f, 1.0f);
+ col = col * col;
+
+ ptr->R = (uint8_t)(32.0f + col * 32.0f);
+ ptr->G = (uint8_t)(50.0f + col * 64.0f);
+ ptr->A = (uint8_t)(146.0f + col * 50.0f);
+ ptr->B = 255;
+
+ ptr++; i++;
+ }
+ }
+}
+
+
+/*########################################################################################################################*
+*-------------------------------------------------------Animations--------------------------------------------------------*
+*#########################################################################################################################*/
+struct AnimationData {
+ TextureLoc TexLoc; /* Tile (not pixel) coordinates in terrain.png */
+ uint16_t FrameX, FrameY; /* Top left pixel coordinates of start frame in animatons.png */
+ uint16_t FrameSize; /* Size of each frame in pixel coordinates */
+ uint16_t State; /* Current animation frame index */
+ uint16_t StatesCount; /* Total number of animation frames */
+ int16_t Tick, TickDelay;
+};
+
+static Bitmap anims_bmp;
+static struct AnimationData anims_list[ATLAS1D_MAX_ATLASES];
+static int anims_count;
+static bool anims_validated, anims_useLavaAnim, anims_useWaterAnim;
+#define ANIM_MIN_ARGS 7
+
+static void Animations_ReadDescription(struct Stream* stream, const String* path) {
+ String line; char lineBuffer[STRING_SIZE * 2];
+ String parts[ANIM_MIN_ARGS];
+ int count;
+ struct AnimationData data = { 0 };
+ uint8_t tileX, tileY;
+
+ uint8_t buffer[2048];
+ struct Stream buffered;
+ ReturnCode res;
+
+ String_InitArray(line, lineBuffer);
+ /* ReadLine reads single byte at a time */
+ Stream_ReadonlyBuffered(&buffered, stream, buffer, sizeof(buffer));
+
+ for (;;) {
+ res = Stream_ReadLine(&buffered, &line);
+ if (res == ERR_END_OF_STREAM) break;
+ if (res) { Chat_LogError2(res, "reading from", path); break; }
+
+ if (!line.length || line.buffer[0] == '#') continue;
+ count = String_UNSAFE_Split(&line, ' ', parts, ANIM_MIN_ARGS);
+ if (count < ANIM_MIN_ARGS) {
+ Chat_Add1("&cNot enough arguments for anim: %s", &line); continue;
+ }
+
+ if (!Convert_TryParseUInt8(&parts[0], &tileX) || tileX >= ATLAS2D_TILES_PER_ROW) {
+ Chat_Add1("&cInvalid anim tile X coord: %s", &line); continue;
+ }
+ if (!Convert_TryParseUInt8(&parts[1], &tileY) || tileY >= ATLAS2D_MAX_ROWS_COUNT) {
+ Chat_Add1("&cInvalid anim tile Y coord: %s", &line); continue;
+ }
+ if (!Convert_TryParseUInt16(&parts[2], &data.FrameX)) {
+ Chat_Add1("&cInvalid anim frame X coord: %s", &line); continue;
+ }
+ if (!Convert_TryParseUInt16(&parts[3], &data.FrameY)) {
+ Chat_Add1("&cInvalid anim frame Y coord: %s", &line); continue;
+ }
+ if (!Convert_TryParseUInt16(&parts[4], &data.FrameSize)) {
+ Chat_Add1("&cInvalid anim frame size: %s", &line); continue;
+ }
+ if (!Convert_TryParseUInt16(&parts[5], &data.StatesCount)) {
+ Chat_Add1("&cInvalid anim states count: %s", &line); continue;
+ }
+ if (!Convert_TryParseInt16(&parts[6], &data.TickDelay)) {
+ Chat_Add1("&cInvalid anim tick delay: %s", &line); continue;
+ }
+
+ if (anims_count == Array_Elems(anims_list)) {
+ Chat_AddRaw("&cCannot show over 512 animations"); return;
+ }
+
+ data.TexLoc = tileX + (tileY * ATLAS2D_TILES_PER_ROW);
+ anims_list[anims_count++] = data;
+ }
+}
+
+#define ANIMS_FAST_SIZE 64
+static void Animations_Draw(struct AnimationData* data, TextureLoc texLoc, int size) {
+ int dstX = Atlas1D_Index(texLoc), srcX;
+ int dstY = Atlas1D_RowId(texLoc) * Atlas_TileSize;
+ GfxResourceID tex;
+
+ uint8_t buffer[Bitmap_DataSize(ANIMS_FAST_SIZE, ANIMS_FAST_SIZE)];
+ uint8_t* ptr = buffer;
+ Bitmap frame;
+
+ /* cannot allocate memory on the stack for very big animation.png frames */
+ if (size > ANIMS_FAST_SIZE) {
+ ptr = Mem_Alloc(size * size, 4, "anim frame");
+ }
+ Bitmap_Create(&frame, size, size, ptr);
+
+ if (!data) {
+ if (texLoc == 30) {
+ LavaAnimation_Tick((BitmapCol*)frame.Scan0, size);
+ } else if (texLoc == 14) {
+ WaterAnimation_Tick((BitmapCol*)frame.Scan0, size);
+ }
+ } else {
+ srcX = data->FrameX + data->State * size;
+ Bitmap_CopyBlock(srcX, data->FrameY, 0, 0, &anims_bmp, &frame, size);
+ }
+
+ tex = Atlas1D_TexIds[dstX];
+ if (tex) { Gfx_UpdateTexturePart(tex, 0, dstY, &frame, Gfx_Mipmaps); }
+ if (size > ANIMS_FAST_SIZE) Mem_Free(ptr);
+}
+
+static void Animations_Apply(struct AnimationData* data) {
+ TextureLoc loc;
+ data->Tick--;
+ if (data->Tick >= 0) return;
+
+ data->State++;
+ data->State %= data->StatesCount;
+ data->Tick = data->TickDelay;
+
+ loc = data->TexLoc;
+ if (loc == 30 && anims_useLavaAnim) return;
+ if (loc == 14 && anims_useWaterAnim) return;
+ Animations_Draw(data, loc, data->FrameSize);
+}
+
+static bool Animations_IsDefaultZip(void) {
+ String texPack; char texPackBuffer[STRING_SIZE];
+ if (World_TextureUrl.length) return false;
+
+ String_InitArray(texPack, texPackBuffer);
+ Options_Get(OPT_DEFAULT_TEX_PACK, &texPack, "default.zip");
+ return String_CaselessEqualsConst(&texPack, "default.zip");
+}
+
+static void Animations_Clear(void) {
+ Mem_Free(anims_bmp.Scan0);
+ anims_count = 0;
+ anims_bmp.Scan0 = NULL;
+ anims_validated = false;
+}
+
+static void Animations_Validate(void) {
+ struct AnimationData data;
+ int maxX, maxY, tileX, tileY;
+ int i, j;
+
+ anims_validated = true;
+ for (i = 0; i < anims_count; i++) {
+ data = anims_list[i];
+
+ maxX = data.FrameX + data.FrameSize * data.StatesCount;
+ maxY = data.FrameY + data.FrameSize;
+ tileX = Atlas2D_TileX(data.TexLoc);
+ tileY = Atlas2D_TileY(data.TexLoc);
+
+ if (data.FrameSize > Atlas_TileSize || tileY >= Atlas_RowsCount) {
+ Chat_Add2("&cAnimation frames for tile (%i, %i) are bigger than the size of a tile in terrain.png", &tileX, &tileY);
+ } else if (maxX > anims_bmp.Width || maxY > anims_bmp.Height) {
+ Chat_Add2("&cSome of the animation frames for tile (%i, %i) are at coordinates outside animations.png", &tileX, &tileY);
+ } else {
+ continue;
+ }
+
+ /* Remove this animation from the list */
+ for (j = i; j < anims_count - 1; j++) {
+ anims_list[j] = anims_list[j + 1];
+ }
+ i--; anims_count--;
+ }
+}
+
+
+static void Animations_Tick(struct ScheduledTask* task) {
+ int i, size;
+
+ if (anims_useLavaAnim) {
+ size = min(Atlas_TileSize, 64);
+ Animations_Draw(NULL, 30, size);
+ }
+ if (anims_useWaterAnim) {
+ size = min(Atlas_TileSize, 64);
+ Animations_Draw(NULL, 14, size);
+ }
+
+ if (!anims_count) return;
+ if (!anims_bmp.Scan0) {
+ Chat_AddRaw("&cCurrent texture pack specifies it uses animations,");
+ Chat_AddRaw("&cbut is missing animations.png");
+ anims_count = 0; return;
+ }
+
+ /* deferred, because when reading animations.txt, might not have read animations.png yet */
+ if (!anims_validated) Animations_Validate();
+ for (i = 0; i < anims_count; i++) {
+ Animations_Apply(&anims_list[i]);
+ }
+}
+
+static void Animations_PackChanged(void* obj) {
+ Animations_Clear();
+ anims_useLavaAnim = Animations_IsDefaultZip();
+ anims_useWaterAnim = anims_useLavaAnim;
+}
+
+static void Animations_FileChanged(void* obj, struct Stream* stream, const String* name) {
+ ReturnCode res;
+ if (String_CaselessEqualsConst(name, "animations.png")) {
+ res = Png_Decode(&anims_bmp, stream);
+ if (!res) return;
+
+ Chat_LogError2(res, "decoding", name);
+ Mem_Free(anims_bmp.Scan0);
+ anims_bmp.Scan0 = NULL;
+ } else if (String_CaselessEqualsConst(name, "animations.txt")) {
+ Animations_ReadDescription(stream, name);
+ } else if (String_CaselessEqualsConst(name, "uselavaanim")) {
+ anims_useLavaAnim = true;
+ } else if (String_CaselessEqualsConst(name, "usewateranim")) {
+ anims_useWaterAnim = true;
+ }
+}
+
+
+/*########################################################################################################################*
+*--------------------------------------------------Animations component---------------------------------------------------*
+*#########################################################################################################################*/
+static void Animations_Init(void) {
+ ScheduledTask_Add(GAME_DEF_TICKS, Animations_Tick);
+ Event_RegisterVoid(&TextureEvents_PackChanged, NULL, Animations_PackChanged);
+ Event_RegisterEntry(&TextureEvents_FileChanged, NULL, Animations_FileChanged);
+}
+
+static void Animations_Free(void) {
+ Animations_Clear();
+ Event_UnregisterVoid(&TextureEvents_PackChanged, NULL, Animations_PackChanged);
+ Event_UnregisterEntry(&TextureEvents_FileChanged, NULL, Animations_FileChanged);
+}
+
+struct IGameComponent Animations_Component = {
+ Animations_Init, /* Init */
+ Animations_Free /* Free */
+};
+
+
+/*########################################################################################################################*
+*------------------------------------------------------TerrainAtlas-------------------------------------------------------*
+*#########################################################################################################################*/
+Bitmap Atlas_Bitmap;
+int Atlas_TileSize, Atlas_RowsCount;
+int Atlas1D_Count, Atlas1D_TilesPerAtlas;
+int Atlas1D_Mask, Atlas1D_Shift;
+float Atlas1D_InvTileSize;
+GfxResourceID Atlas1D_TexIds[ATLAS1D_MAX_ATLASES];
+
+TextureRec Atlas1D_TexRec(TextureLoc texLoc, int uCount, int* index) {
+ TextureRec rec;
+ int y = Atlas1D_RowId(texLoc);
+ *index = Atlas1D_Index(texLoc);
+
+ /* Adjust coords to be slightly inside - fixes issues with AMD/ATI cards */
+ rec.U1 = 0.0f;
+ rec.V1 = y * Atlas1D_InvTileSize;
+ rec.U2 = (uCount - 1) + UV2_Scale;
+ rec.V2 = rec.V1 + UV2_Scale * Atlas1D_InvTileSize;
+ return rec;
+}
+
+static void Atlas_Convert2DTo1D(void) {
+ int tileSize = Atlas_TileSize;
+ int tilesPerAtlas = Atlas1D_TilesPerAtlas;
+ int atlasesCount = Atlas1D_Count;
+ Bitmap atlas1D;
+ int atlasX, atlasY;
+ int tile = 0, i, y;
+
+ Platform_Log2("Loaded new atlas: %i bmps, %i per bmp", &atlasesCount, &tilesPerAtlas);
+ Bitmap_Allocate(&atlas1D, tileSize, tilesPerAtlas * tileSize);
+
+ for (i = 0; i < atlasesCount; i++) {
+ for (y = 0; y < tilesPerAtlas; y++, tile++) {
+ atlasX = Atlas2D_TileX(tile) * tileSize;
+ atlasY = Atlas2D_TileY(tile) * tileSize;
+
+ Bitmap_CopyBlock(atlasX, atlasY, 0, y * tileSize,
+ &Atlas_Bitmap, &atlas1D, tileSize);
+ }
+ Atlas1D_TexIds[i] = Gfx_CreateTexture(&atlas1D, true, Gfx_Mipmaps);
+ }
+ Mem_Free(atlas1D.Scan0);
+}
+
+static void Atlas_Update1D(void) {
+ int maxAtlasHeight, maxTilesPerAtlas, maxTiles;
+
+ maxAtlasHeight = min(4096, Gfx_MaxTexHeight);
+ maxTilesPerAtlas = maxAtlasHeight / Atlas_TileSize;
+ maxTiles = Atlas_RowsCount * ATLAS2D_TILES_PER_ROW;
+
+ Atlas1D_TilesPerAtlas = min(maxTilesPerAtlas, maxTiles);
+ Atlas1D_Count = Math_CeilDiv(maxTiles, Atlas1D_TilesPerAtlas);
+
+ Atlas1D_InvTileSize = 1.0f / Atlas1D_TilesPerAtlas;
+ Atlas1D_Mask = Atlas1D_TilesPerAtlas - 1;
+ Atlas1D_Shift = Math_Log2(Atlas1D_TilesPerAtlas);
+}
+
+void Atlas_Update(Bitmap* bmp) {
+ Atlas_Bitmap = *bmp;
+ Atlas_TileSize = bmp->Width / ATLAS2D_TILES_PER_ROW;
+ Atlas_RowsCount = bmp->Height / Atlas_TileSize;
+ Atlas_RowsCount = min(Atlas_RowsCount, ATLAS2D_MAX_ROWS_COUNT);
+
+ Block_RecalculateAllSpriteBB();
+ Atlas_Update1D();
+ Atlas_Convert2DTo1D();
+}
+
+static GfxResourceID Atlas_LoadTile_Raw(TextureLoc texLoc, Bitmap* element) {
+ int size = Atlas_TileSize;
+ int x = Atlas2D_TileX(texLoc), y = Atlas2D_TileY(texLoc);
+ if (y >= Atlas_RowsCount) return GFX_NULL;
+
+ Bitmap_CopyBlock(x * size, y * size, 0, 0, &Atlas_Bitmap, element, size);
+ return Gfx_CreateTexture(element, false, Gfx_Mipmaps);
+}
+
+GfxResourceID Atlas_LoadTile(TextureLoc texLoc) {
+ int tileSize = Atlas_TileSize;
+ Bitmap tile;
+ GfxResourceID texId;
+ uint8_t scan0[Bitmap_DataSize(64, 64)];
+
+ /* Try to allocate bitmap on stack if possible */
+ if (tileSize > 64) {
+ Bitmap_Allocate(&tile, tileSize, tileSize);
+ texId = Atlas_LoadTile_Raw(texLoc, &tile);
+ Mem_Free(tile.Scan0);
+ return texId;
+ } else {
+ Bitmap_Create(&tile, tileSize, tileSize, scan0);
+ return Atlas_LoadTile_Raw(texLoc, &tile);
+ }
+}
+
+void Atlas_Free(void) {
+ int i;
+ Mem_Free(Atlas_Bitmap.Scan0);
+ Atlas_Bitmap.Scan0 = NULL;
+
+ for (i = 0; i < Atlas1D_Count; i++) {
+ Gfx_DeleteTexture(&Atlas1D_TexIds[i]);
}
- return 0;
}
diff --git a/src/TexturePack.h b/src/TexturePack.h
index 4f4e3ec0d..475fea231 100644
--- a/src/TexturePack.h
+++ b/src/TexturePack.h
@@ -1,38 +1,69 @@
#ifndef CC_TEXPACKS_H
#define CC_TEXPACKS_H
-#include "Utils.h"
-/* Extracts entries from a .zip archive stream (mostly resources for .zip texture pack)
- Caches terrain atlases and texture packs to avoid making redundant downloads.
- Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3
-*/
+#include "String.h"
+#include "Bitmap.h"
+/* Contains everything relating to texture packs.
+ - Extracting the textures from a .zip archive
+ - Caching terrain atlases and texture packs to avoid redundant downloads
+ - Terrain atlas (including breaking it down into multiple 1D atlases)
+ - Texture animations (including default water/lava ones)
+ Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 */
+
struct Stream;
struct AsyncRequest;
+struct IGameComponent;
+extern struct IGameComponent Animations_Component;
-/* Minimal data needed to describe an entry in a .zip archive. */
-struct ZipEntry { uint32_t CompressedSize, UncompressedSize, LocalHeaderOffset, Crc32; };
+/* Number of tiles in each row */
+#define ATLAS2D_TILES_PER_ROW 16
+#define ATLAS2D_MASK 15
+#define ATLAS2D_SHIFT 4
+/* Maximum supported number of rows in the atlas. */
+#ifdef EXTENDED_TEXTURES
+#define ATLAS2D_MAX_ROWS_COUNT 32
+#else
+#define ATLAS2D_MAX_ROWS_COUNT 16
+#endif
+/* Maximum possible number of 1D terrain atlases. (worst case, each 1D atlas only has 1 tile) */
+#define ATLAS1D_MAX_ATLASES (ATLAS2D_TILES_PER_ROW * ATLAS2D_MAX_ROWS_COUNT)
-#define ZIP_MAX_ENTRIES 2048
-/* Stores state for reading and processing entries in a .zip archive. */
-struct ZipState {
- /* Source of the .zip archive data. Must be seekable. */
- struct Stream* Input;
- /* Callback function to process the data in a .zip archive entry. */
- /* Note that data stream may not be seekable. (entry data might be compressed) */
- void (*ProcessEntry)(const String* path, struct Stream* data, struct ZipEntry* entry);
- /* Predicate used to select which entries in a .zip archive get proessed.*/
- /* Return false to skip the entry. (this avoids seeking to the entry's data) */
- bool (*SelectEntry)(const String* path);
- /* Number of entries in the .zip archive. */
- int EntriesCount;
- /* Data for each entry in the .zip archive. */
- struct ZipEntry Entries[ZIP_MAX_ENTRIES];
-};
+/* Bitmap that contains the textures of all tiles. */
+/* Tiles are indexed left to right, top to bottom. */
+extern Bitmap Atlas_Bitmap;
+/* Size of each tile in pixels. (default 16x16) */
+extern int Atlas_TileSize;
+/* Number of rows in the atlas. (default 16, can be 32) */
+extern int Atlas_RowsCount;
+/* Number of 1D atlases the atlas was split into. */
+extern int Atlas1D_Count;
+/* Number of tiles in each 1D atlas. */
+extern int Atlas1D_TilesPerAtlas;
+/* Converts a tile id into 1D atlas index, and index within that atlas. */
+extern int Atlas1D_Mask, Atlas1D_Shift;
+/* Texture V coord that equals the size of one tile. (i.e. 1/Atlas1D_TilesPerAtlas) */
+/* NOTE: The texture U coord that equals the size of one tile is 1. */
+extern float Atlas1D_InvTileSize;
+/* Textures for each 1D atlas. Only Atlas1D_Count of these are valid. */
+extern GfxResourceID Atlas1D_TexIds[ATLAS1D_MAX_ATLASES];
-/* Initialises .zip archive reader state. */
-void Zip_Init(struct ZipState* state, struct Stream* input);
-/* Reads and processes the entries in a .zip archive. */
-/* Must have been initialised with Zip_Init first. */
-ReturnCode Zip_Extract(struct ZipState* state);
+#define Atlas2D_TileX(texLoc) ((texLoc) & ATLAS2D_MASK) /* texLoc % ATLAS2D_TILES_PER_ROW */
+#define Atlas2D_TileY(texLoc) ((texLoc) >> ATLAS2D_SHIFT) /* texLoc / ATLAS2D_TILES_PER_ROW */
+/* Returns the index of the given tile id within a 1D atlas */
+#define Atlas1D_RowId(texLoc) ((texLoc) & Atlas1D_Mask) /* texLoc % Atlas1D_TilesPerAtlas */
+/* Returns the index of the 1D atlas within the array of 1D atlases that contains the given tile id */
+#define Atlas1D_Index(texLoc) ((texLoc) >> Atlas1D_Shift) /* texLoc / Atlas1D_TilesPerAtlas */
+
+/* Loads the given atlas and converts it into an array of 1D atlases. */
+/* NOTE: Use Game_ChangeTerrainAtlas to change atlas, because that raises TextureEvents_AtlasChanged */
+void Atlas_Update(Bitmap* bmp);
+/* Loads the given tile into a new separate texture. */
+GfxResourceID Atlas_LoadTile(TextureLoc texLoc);
+/* Frees the atlas and 1D atlas textures. */
+void Atlas_Free(void);
+/* Returns the UV rectangle of the given tile id in the 1D atlases. */
+/* That is, returns U1/U2/V1/V2 coords that make up the tile in a 1D atlas. */
+/* index is set to the index of the 1D atlas that the tile is in. */
+TextureRec Atlas1D_TexRec(TextureLoc texLoc, int uCount, int* index);
/* Initialises cache state. (e.g. loading accepted/denied lists) */
void TextureCache_Init(void);