diff --git a/ClassicalSharp/TexturePack/TextureCache.cs b/ClassicalSharp/TexturePack/TextureCache.cs index ad7c85304..820ef0def 100644 --- a/ClassicalSharp/TexturePack/TextureCache.cs +++ b/ClassicalSharp/TexturePack/TextureCache.cs @@ -130,7 +130,7 @@ namespace ClassicalSharp.Textures { tags.Add(entry); } - public static string MakePath(string url) { return PathIO.Combine(folder, CRC32(url)); } + static string MakePath(string url) { return PathIO.Combine(folder, CRC32(url)); } static string CRC32(string url) { byte[] data = Encoding.UTF8.GetBytes(url); diff --git a/ClassicalSharp/TexturePack/TexturePack.cs b/ClassicalSharp/TexturePack/TexturePack.cs index 043c1f5c3..bcf8099b9 100644 --- a/ClassicalSharp/TexturePack/TexturePack.cs +++ b/ClassicalSharp/TexturePack/TexturePack.cs @@ -45,7 +45,7 @@ namespace ClassicalSharp.Textures { } } - public static void ExtractZip(Stream stream, Game game) { + static void ExtractZip(Stream stream, Game game) { TexturePack.game = game; game.Events.RaiseTexturePackChanged(); if (game.Graphics.LostContext) return; diff --git a/src/Client/Chat.c b/src/Client/Chat.c index f53ea4ea1..4909bfc7f 100644 --- a/src/Client/Chat.c +++ b/src/Client/Chat.c @@ -16,7 +16,7 @@ #define CHAT_LOGTIMES_DEF_ELEMS 256 #define CHAT_LOGTIMES_EXPAND_ELEMS 512 Int64 Chat_DefaultLogTimes[CHAT_LOGTIMES_DEF_ELEMS]; -Int64* Chat_LogTimes = &Chat_DefaultLogTimes; +Int64* Chat_LogTimes = Chat_DefaultLogTimes; UInt32 Chat_LogTimesCount = CHAT_LOGTIMES_DEF_ELEMS, Chat_LogTimesUsed; void Chat_GetLogTime(UInt32 index, Int64* timeMs) { diff --git a/src/Client/Client.vcxproj b/src/Client/Client.vcxproj index 097dbd0ec..abb5e53cb 100644 --- a/src/Client/Client.vcxproj +++ b/src/Client/Client.vcxproj @@ -229,6 +229,7 @@ + @@ -250,7 +251,6 @@ - @@ -320,7 +320,6 @@ - diff --git a/src/Client/Client.vcxproj.filters b/src/Client/Client.vcxproj.filters index f3d8a78f5..25ae35c53 100644 --- a/src/Client/Client.vcxproj.filters +++ b/src/Client/Client.vcxproj.filters @@ -300,9 +300,6 @@ Header Files\Map - - Header Files\TexturePack - Header Files\2D @@ -366,6 +363,9 @@ Header Files\2D + + Header Files\TexturePack + @@ -503,9 +503,6 @@ Source Files\Map - - Source Files\TexturePack - Source Files\2D diff --git a/src/Client/Menus.c b/src/Client/Menus.c index ceb053274..1fac436d2 100644 --- a/src/Client/Menus.c +++ b/src/Client/Menus.c @@ -24,6 +24,7 @@ #include "ErrorHandler.h" #include "BlockPhysics.h" #include "MapRenderer.h" +#include "TexturePack.h" #define LIST_SCREEN_ITEMS 5 #define LIST_SCREEN_BUTTONS (LIST_SCREEN_ITEMS + 3) diff --git a/src/Client/Program.c b/src/Client/Program.c index cfa99d793..2266312d6 100644 --- a/src/Client/Program.c +++ b/src/Client/Program.c @@ -5,7 +5,7 @@ #include "GraphicsAPI.h" #include "Deflate.h" #include "Formats.h" -#include "ZipArchive.h" +#include "TexturePack.h" #include "Bitmap.h" #include diff --git a/src/Client/Screens.c b/src/Client/Screens.c index 22c18d51f..37fe1b17e 100644 --- a/src/Client/Screens.c +++ b/src/Client/Screens.c @@ -1378,7 +1378,7 @@ void HUDScreen_AppendInput(Screen* hud, STRING_PURE String* text) { Widget* HUDScreen_GetHotbar(Screen* hud) { HUDScreen* screen = (HUDScreen*)hud; - return &screen->Hotbar; + return (Widget*)(&screen->Hotbar); } diff --git a/src/Client/ServerConnection.c b/src/Client/ServerConnection.c index 29670c327..aea686341 100644 --- a/src/Client/ServerConnection.c +++ b/src/Client/ServerConnection.c @@ -14,6 +14,7 @@ #include "MapGenerator.h" #include "World.h" #include "Camera.h" +#include "TexturePack.h" UInt8 ServerConnection_ServerNameBuffer[String_BufferSize(STRING_SIZE)]; String ServerConnection_ServerName = String_FromEmptyArray(ServerConnection_ServerNameBuffer); diff --git a/src/Client/TexturePack.c b/src/Client/TexturePack.c index a3af68524..7dd38acc7 100644 --- a/src/Client/TexturePack.c +++ b/src/Client/TexturePack.c @@ -1,11 +1,179 @@ -#include "Typedefs.h" -#include "String.h" +#include "TexturePack.h" #include "Constants.h" #include "Platform.h" #include "ErrorHandler.h" #include "Stream.h" #include "Bitmap.h" +#include "World.h" +#include "GraphicsAPI.h" +#include "Event.h" +#include "Game.h" +#include "AsyncDownloader.h" +#include "ErrorHandler.h" +#include "Platform.h" +#include "Deflate.h" +#include "Stream.h" +/*########################################################################################################################* +*--------------------------------------------------------ZipEntry---------------------------------------------------------* +*#########################################################################################################################*/ +String Zip_ReadFixedString(Stream* stream, UInt8* buffer, UInt16 length) { + String fileName; + fileName.buffer = buffer; + fileName.length = length; + fileName.capacity = length; + + Stream_Read(stream, buffer, length); + buffer[length] = NULL; /* Ensure null terminated */ + return fileName; +} + +void Zip_ReadLocalFileHeader(ZipState* state, ZipEntry* entry) { + Stream* stream = state->Input; + UInt16 versionNeeded = Stream_ReadUInt16_LE(stream); + UInt16 flags = Stream_ReadUInt16_LE(stream); + UInt16 compressionMethod = Stream_ReadUInt16_LE(stream); + Stream_ReadUInt32_LE(stream); /* last modified */ + Stream_ReadUInt32_LE(stream); /* CRC32 */ + + Int32 compressedSize = Stream_ReadInt32_LE(stream); + if (compressedSize == 0) compressedSize = entry->CompressedDataSize; + Int32 uncompressedSize = Stream_ReadInt32_LE(stream); + if (uncompressedSize == 0) uncompressedSize = entry->UncompressedDataSize; + + UInt16 fileNameLen = Stream_ReadUInt16_LE(stream); + UInt16 extraFieldLen = Stream_ReadUInt16_LE(stream); + UInt8 filenameBuffer[String_BufferSize(UInt16_MaxValue)]; + String filename = Zip_ReadFixedString(stream, filenameBuffer, fileNameLen); + if (!state->SelectEntry(&filename)) return; + + ReturnCode code = Stream_Skip(stream, extraFieldLen); + ErrorHandler_CheckOrFail(code, "Zip - skipping local header extra"); + if (versionNeeded > 20) { + String warnMsg = String_FromConst("May not be able to properly extract a .zip enty with a version later than 2.0"); + Platform_Log(&warnMsg); + } + + Stream portion, compStream; + if (compressionMethod == 0) { + Stream_ReadonlyPortion(&portion, stream, uncompressedSize); + state->ProcessEntry(&filename, &portion, entry); + } else if (compressionMethod == 8) { + DeflateState deflate; + Stream_ReadonlyPortion(&portion, stream, compressedSize); + Deflate_MakeStream(&compStream, &deflate, &portion); + state->ProcessEntry(&filename, &compStream, entry); + } else { + String warnMsg = String_FromConst("Unsupported .zip entry compression method"); + Platform_Log(&warnMsg); + } +} + +void Zip_ReadCentralDirectory(ZipState* state, ZipEntry* entry) { + Stream* stream = state->Input; + Stream_ReadUInt16_LE(stream); /* OS */ + UInt16 versionNeeded = Stream_ReadUInt16_LE(stream); + UInt16 flags = Stream_ReadUInt16_LE(stream); + UInt16 compressionMethod = Stream_ReadUInt16_LE(stream); + Stream_ReadUInt32_LE(stream); /* last modified */ + entry->Crc32 = Stream_ReadUInt32_LE(stream); + entry->CompressedDataSize = Stream_ReadInt32_LE(stream); + entry->UncompressedDataSize = Stream_ReadInt32_LE(stream); + + UInt16 fileNameLen = Stream_ReadUInt16_LE(stream); + UInt16 extraFieldLen = Stream_ReadUInt16_LE(stream); + UInt16 fileCommentLen = Stream_ReadUInt16_LE(stream); + UInt16 diskNum = Stream_ReadUInt16_LE(stream); + UInt16 internalAttributes = Stream_ReadUInt16_LE(stream); + UInt32 externalAttributes = Stream_ReadUInt32_LE(stream); + entry->LocalHeaderOffset = Stream_ReadInt32_LE(stream); + + UInt32 extraDataLen = fileNameLen + extraFieldLen + fileCommentLen; + ReturnCode code = Stream_Skip(stream, extraDataLen); + ErrorHandler_CheckOrFail(code, "Zip - skipping central header extra"); +} + +void Zip_ReadEndOfCentralDirectory(ZipState* state, Int32* centralDirectoryOffset) { + Stream* stream = state->Input; + UInt16 diskNum = Stream_ReadUInt16_LE(stream); + UInt16 diskNumStart = Stream_ReadUInt16_LE(stream); + UInt16 diskEntries = Stream_ReadUInt16_LE(stream); + state->EntriesCount = Stream_ReadUInt16_LE(stream); + Int32 centralDirectorySize = Stream_ReadInt32_LE(stream); + *centralDirectoryOffset = Stream_ReadInt32_LE(stream); + UInt16 commentLength = Stream_ReadUInt16_LE(stream); +} + +#define ZIP_ENDOFCENTRALDIR 0x06054b50UL +#define ZIP_CENTRALDIR 0x02014b50UL +#define ZIP_LOCALFILEHEADER 0x04034b50UL + +void Zip_DefaultProcessor(STRING_TRANSIENT String* path, Stream* data, ZipEntry* entry) { } +bool Zip_DefaultSelector(STRING_TRANSIENT String* path) { return true; } +void Zip_Init(ZipState* state, Stream* input) { + state->Input = input; + state->EntriesCount = 0; + state->ProcessEntry = Zip_DefaultProcessor; + state->SelectEntry = Zip_DefaultSelector; +} + +void Zip_Extract(ZipState* state) { + state->EntriesCount = 0; + Stream* stream = state->Input; + ReturnCode result = stream->Seek(stream, -22, STREAM_SEEKFROM_END); + ErrorHandler_CheckOrFail(result, "ZIP - Seek to end of central directory"); + + UInt32 sig = Stream_ReadUInt32_LE(stream); + if (sig != ZIP_ENDOFCENTRALDIR) { + ErrorHandler_Fail("ZIP - Comment in .zip file not supported"); + return; + } + + Int32 centralDirectoryOffset; + Zip_ReadEndOfCentralDirectory(state, ¢ralDirectoryOffset); + result = stream->Seek(stream, centralDirectoryOffset, STREAM_SEEKFROM_BEGIN); + ErrorHandler_CheckOrFail(result, "ZIP - Seek to central directory"); + if (state->EntriesCount > ZIP_MAX_ENTRIES) { + ErrorHandler_Fail("ZIP - Max of 2048 entries supported"); + } + + /* Read all the central directory entries */ + Int32 count = 0; + while (count < state->EntriesCount) { + sig = Stream_ReadUInt32_LE(stream); + if (sig == ZIP_CENTRALDIR) { + Zip_ReadCentralDirectory(state, &state->Entries[count]); + count++; + } else if (sig == ZIP_ENDOFCENTRALDIR) { + break; + } else { + String sigMsg = String_FromConst("ZIP - Unsupported signature found, aborting"); + ErrorHandler_Log(&sigMsg); + return; + } + } + + /* Now read the local file header entries */ + Int32 i; + for (i = 0; i < count; i++) { + ZipEntry* entry = &state->Entries[i]; + result = stream->Seek(stream, entry->LocalHeaderOffset, STREAM_SEEKFROM_BEGIN); + ErrorHandler_CheckOrFail(result, "ZIP - Seek to local file header"); + + sig = Stream_ReadUInt32_LE(stream); + if (sig != ZIP_LOCALFILEHEADER) { + String sigMsg = String_FromConst("ZIP - Invalid entry found, skipping"); + ErrorHandler_Log(&sigMsg); + continue; + } + Zip_ReadLocalFileHeader(state, entry); + } +} + + +/*########################################################################################################################* +*--------------------------------------------------------EntryList--------------------------------------------------------* +*#########################################################################################################################*/ typedef struct EntryList_ { UInt8 FolderBuffer[String_BufferSize(STRING_SIZE)]; UInt8 FileBuffer[String_BufferSize(STRING_SIZE)]; @@ -90,6 +258,10 @@ void EntryList_Make(EntryList* list, STRING_PURE const UInt8* folder, STRING_PUR EntryList_Load(list); } + +/*########################################################################################################################* +*------------------------------------------------------TextureCache-------------------------------------------------------* +*#########################################################################################################################*/ #define TEXCACHE_FOLDER "texturecache" /* Because I didn't store milliseconds in original C# client */ #define TEXCACHE_TICKS_PER_MS 10000LL @@ -239,4 +411,125 @@ void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModifie String data = String_InitAndClearArray(dataBuffer); String_AppendInt64(&data, ticks); TextureCache_AddToTags(url, &data, &cache_lastModified); +} + + +/*########################################################################################################################* +*-------------------------------------------------------TexturePack-------------------------------------------------------* +*#########################################################################################################################*/ +void TexturePack_ProcessZipEntry(STRING_TRANSIENT String* path, Stream* stream, ZipEntry* entry) { + /* Ignore directories: convert x/name to name and x\name to name. */ + String_MakeLowercase(path); + String name = *path; + Int32 i; + + i = String_LastIndexOf(&name, '\\'); + if (i >= 0) { name = String_UNSAFE_SubstringAt(&name, i + 1); } + i = String_LastIndexOf(&name, '/'); + if (i >= 0) { name = String_UNSAFE_SubstringAt(&name, i + 1); } + + String_Set(&stream->Name, &name); + Event_RaiseStream(&TextureEvents_FileChanged, stream); +} + +void TexturePack_ExtractZip(Stream* stream) { + Event_RaiseVoid(&TextureEvents_PackChanged); + if (Gfx_LostContext) return; + + ZipState state; + Zip_Init(&state, stream); + state.ProcessEntry = TexturePack_ProcessZipEntry; + Zip_Extract(&state); +} + +void TexturePack_ExtractZip_File(STRING_PURE String* filename) { + UInt8 pathBuffer[String_BufferSize(FILENAME_SIZE)]; + String path = String_InitAndClearArray(pathBuffer); + String_Format2(&path, "texpacks%b%s", TEXCACHE_FOLDER, &Platform_DirectorySeparator, filename); + + void* file; + ReturnCode result = Platform_FileOpen(&file, &path); + ErrorHandler_CheckOrFail(result, "TexturePack_Extract - opening file"); + + Stream stream; + Stream_FromFile(&stream, &file, &path); + TexturePack_ExtractZip(&stream); + + result = stream.Close(&stream); + ErrorHandler_CheckOrFail(result, "TexturePack_Extract - closing file"); +} + +void TexturePack_ExtractTerrainPng(Stream* stream) { + Bitmap bmp; Bitmap_DecodePng(&bmp, stream); + Event_RaiseVoid(&TextureEvents_PackChanged); + + if (Game_ChangeTerrainAtlas(&bmp)) return; + Platform_MemFree(&bmp.Scan0); +} + +void TexturePack_ExtractDefault(void) { + UInt8 texPackBuffer[String_BufferSize(STRING_SIZE)]; + String texPack = String_InitAndClearArray(texPackBuffer); + Game_GetDefaultTexturePack(&texPack); + + TexturePack_ExtractZip_File(&texPack); + String_Clear(&World_TextureUrl); +} + +void TexturePack_ExtractCurrent(STRING_PURE String* url) { + if (url->length == 0) { TexturePack_ExtractDefault(); return; } + + Stream stream; + if (!TextureCache_GetStream(url, &stream)) { + /* e.g. 404 errors */ + if (World_TextureUrl.length > 0) TexturePack_ExtractDefault(); + } else { + String zip = String_FromConst(".zip"); + if (String_Equals(url, &World_TextureUrl)) { + } else if (String_ContainsString(url, &zip)) { + String_Set(&World_TextureUrl, url); + TexturePack_ExtractZip(&stream); + } else { + String_Set(&World_TextureUrl, url); + TexturePack_ExtractTerrainPng(&stream); + } + + ReturnCode result = stream.Close(&stream); + ErrorHandler_CheckOrFail(result, "TexturePack_ExtractCurrent - slow stream"); + } +} + +void TexturePack_ExtractTerrainPng_Req(AsyncRequest* item) { + if (item->ResultBitmap.Scan0 == NULL) return; + String url = String_FromRawArray(item->URL); + String_Set(&World_TextureUrl, &url); + Bitmap bmp = item->ResultBitmap; + + String etag = String_FromRawArray(item->Etag); + TextureCache_AddImage(&url, &bmp); + TextureCache_AddETag(&url, &etag); + TextureCache_AddLastModified(&url, &item->LastModified); + + Event_RaiseVoid(&TextureEvents_PackChanged); + if (!Game_ChangeTerrainAtlas(&bmp)) { + Platform_MemFree(&bmp.Scan0); + } +} + +void TexturePack_ExtractTexturePack_Req(AsyncRequest* item) { + if (item->ResultData.Ptr == NULL) return; + String url = String_FromRawArray(item->URL); + String_Set(&World_TextureUrl, &url); + void* data = item->ResultData.Ptr; + UInt32 len = item->ResultData.Size; + + String etag = String_FromRawArray(item->Etag); + TextureCache_AddData(&url, data, len); + TextureCache_AddETag(&url, &etag); + TextureCache_AddLastModified(&url, &item->LastModified); + + String id = String_FromRawArray(item->ID); + Stream stream; Stream_ReadonlyMemory(&stream, data, len, &id); + TexturePack_ExtractZip(&stream); + stream.Close(&stream); } \ No newline at end of file diff --git a/src/Client/TexturePack.h b/src/Client/TexturePack.h new file mode 100644 index 000000000..32143b330 --- /dev/null +++ b/src/Client/TexturePack.h @@ -0,0 +1,49 @@ +#ifndef CC_TEXPACKS_H +#define CC_TEXPACKS_H +#include "String.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 +*/ +typedef struct Stream_ Stream; +typedef struct DateTime_ DateTime; +typedef struct Bitmap_ Bitmap; +typedef struct AsyncRequest_ AsyncRequest; + +typedef struct ZipEntry_ { + Int32 CompressedDataSize, UncompressedDataSize, LocalHeaderOffset; UInt32 Crc32; +} ZipEntry; + +#define ZIP_MAX_ENTRIES 2048 +typedef struct ZipState_ { + Stream* Input; + void (*ProcessEntry)(STRING_TRANSIENT String* path, Stream* data, ZipEntry* entry); + bool (*SelectEntry)(STRING_PURE String* path); + Int32 EntriesCount; + ZipEntry Entries[ZIP_MAX_ENTRIES]; +} ZipState; + +void Zip_Init(ZipState* state, Stream* input); +void Zip_Extract(ZipState* state); + +void TextureCache_Init(void); +bool TextureCache_HasAccepted(STRING_PURE String* url); +bool TextureCache_HasDenied(STRING_PURE String* url); +void TextureCache_Accept(STRING_PURE String* url); +void TextureCache_Deny(STRING_PURE String* url); + +bool TextureCache_HasUrl(STRING_PURE String* url); +bool TextureCache_GetStream(STRING_PURE String* url, Stream* stream); +void TextureCache_GetLastModified(STRING_PURE String* url, DateTime* time); +void TextureCache_GetETag(STRING_PURE String* url, STRING_PURE String* etag); +void TextureCache_AddImage(STRING_PURE String* url, Bitmap* bmp); +void TextureCache_AddData(STRING_PURE String* url, UInt8* data, UInt32 length); +void TextureCache_AddETag(STRING_PURE String* url, STRING_PURE String* etag); +void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModified); + +void TexturePack_ExtractZip_File(STRING_PURE String* filename); +void TexturePack_ExtractDefault(void); +void TexturePack_ExtractCurrent(STRING_PURE String* url); +void TexturePack_ExtractTerrainPng_Req(AsyncRequest* item); +void TexturePack_ExtractTexturePack_Req(AsyncRequest* item); +#endif \ No newline at end of file diff --git a/src/Client/ZipArchive.c b/src/Client/ZipArchive.c deleted file mode 100644 index 46acb9a60..000000000 --- a/src/Client/ZipArchive.c +++ /dev/null @@ -1,159 +0,0 @@ -#include "ZipArchive.h" -#include "ErrorHandler.h" -#include "Platform.h" -#include "Deflate.h" -#include "Stream.h" - -String Zip_ReadFixedString(Stream* stream, UInt8* buffer, UInt16 length) { - String fileName; - fileName.buffer = buffer; - fileName.length = length; - fileName.capacity = length; - - Stream_Read(stream, buffer, length); - buffer[length] = NULL; /* Ensure null terminated */ - return fileName; -} - -void Zip_ReadLocalFileHeader(ZipState* state, ZipEntry* entry) { - Stream* stream = state->Input; - UInt16 versionNeeded = Stream_ReadUInt16_LE(stream); - UInt16 flags = Stream_ReadUInt16_LE(stream); - UInt16 compressionMethod = Stream_ReadUInt16_LE(stream); - Stream_ReadUInt32_LE(stream); /* last modified */ - Stream_ReadUInt32_LE(stream); /* CRC32 */ - - Int32 compressedSize = Stream_ReadInt32_LE(stream); - if (compressedSize == 0) compressedSize = entry->CompressedDataSize; - Int32 uncompressedSize = Stream_ReadInt32_LE(stream); - if (uncompressedSize == 0) uncompressedSize = entry->UncompressedDataSize; - - UInt16 fileNameLen = Stream_ReadUInt16_LE(stream); - UInt16 extraFieldLen = Stream_ReadUInt16_LE(stream); - UInt8 filenameBuffer[String_BufferSize(UInt16_MaxValue)]; - String filename = Zip_ReadFixedString(stream, filenameBuffer, fileNameLen); - if (!state->SelectEntry(&filename)) return; - - ReturnCode code = Stream_Skip(stream, extraFieldLen); - ErrorHandler_CheckOrFail(code, "Zip - skipping local header extra"); - if (versionNeeded > 20) { - String warnMsg = String_FromConst("May not be able to properly extract a .zip enty with a version later than 2.0"); - Platform_Log(&warnMsg); - } - - Stream portion, compStream; - if (compressionMethod == 0) { - Stream_ReadonlyPortion(&portion, stream, uncompressedSize); - state->ProcessEntry(&filename, &portion, entry); - } else if (compressionMethod == 8) { - DeflateState deflate; - Stream_ReadonlyPortion(&portion, stream, compressedSize); - Deflate_MakeStream(&compStream, &deflate, &portion); - state->ProcessEntry(&filename, &compStream, entry); - } else { - String warnMsg = String_FromConst("Unsupported .zip entry compression method"); - Platform_Log(&warnMsg); - } -} - -void Zip_ReadCentralDirectory(ZipState* state, ZipEntry* entry) { - Stream* stream = state->Input; - Stream_ReadUInt16_LE(stream); /* OS */ - UInt16 versionNeeded = Stream_ReadUInt16_LE(stream); - UInt16 flags = Stream_ReadUInt16_LE(stream); - UInt16 compressionMethod = Stream_ReadUInt16_LE(stream); - Stream_ReadUInt32_LE(stream); /* last modified */ - entry->Crc32 = Stream_ReadUInt32_LE(stream); - entry->CompressedDataSize = Stream_ReadInt32_LE(stream); - entry->UncompressedDataSize = Stream_ReadInt32_LE(stream); - - UInt16 fileNameLen = Stream_ReadUInt16_LE(stream); - UInt16 extraFieldLen = Stream_ReadUInt16_LE(stream); - UInt16 fileCommentLen = Stream_ReadUInt16_LE(stream); - UInt16 diskNum = Stream_ReadUInt16_LE(stream); - UInt16 internalAttributes = Stream_ReadUInt16_LE(stream); - UInt32 externalAttributes = Stream_ReadUInt32_LE(stream); - entry->LocalHeaderOffset = Stream_ReadInt32_LE(stream); - - UInt32 extraDataLen = fileNameLen + extraFieldLen + fileCommentLen; - ReturnCode code = Stream_Skip(stream, extraDataLen); - ErrorHandler_CheckOrFail(code, "Zip - skipping central header extra"); -} - -void Zip_ReadEndOfCentralDirectory(ZipState* state, Int32* centralDirectoryOffset) { - Stream* stream = state->Input; - UInt16 diskNum = Stream_ReadUInt16_LE(stream); - UInt16 diskNumStart = Stream_ReadUInt16_LE(stream); - UInt16 diskEntries = Stream_ReadUInt16_LE(stream); - state->EntriesCount = Stream_ReadUInt16_LE(stream); - Int32 centralDirectorySize = Stream_ReadInt32_LE(stream); - *centralDirectoryOffset = Stream_ReadInt32_LE(stream); - UInt16 commentLength = Stream_ReadUInt16_LE(stream); -} - - -#define ZIP_ENDOFCENTRALDIR 0x06054b50UL -#define ZIP_CENTRALDIR 0x02014b50UL -#define ZIP_LOCALFILEHEADER 0x04034b50UL - -void Zip_DefaultProcessor(STRING_TRANSIENT String* path, Stream* data, ZipEntry* entry) { } -bool Zip_DefaultSelector(STRING_TRANSIENT String* path) { return true; } -void Zip_Init(ZipState* state, Stream* input) { - state->Input = input; - state->EntriesCount = 0; - state->ProcessEntry = Zip_DefaultProcessor; - state->SelectEntry = Zip_DefaultSelector; -} - -void Zip_Extract(ZipState* state) { - state->EntriesCount = 0; - Stream* stream = state->Input; - ReturnCode result = stream->Seek(stream, -22, STREAM_SEEKFROM_END); - ErrorHandler_CheckOrFail(result, "ZIP - Seek to end of central directory"); - - UInt32 sig = Stream_ReadUInt32_LE(stream); - if (sig != ZIP_ENDOFCENTRALDIR) { - ErrorHandler_Fail("ZIP - Comment in .zip file not supported"); - return; - } - - Int32 centralDirectoryOffset; - Zip_ReadEndOfCentralDirectory(state, ¢ralDirectoryOffset); - result = stream->Seek(stream, centralDirectoryOffset, STREAM_SEEKFROM_BEGIN); - ErrorHandler_CheckOrFail(result, "ZIP - Seek to central directory"); - if (state->EntriesCount > ZIP_MAX_ENTRIES) { - ErrorHandler_Fail("ZIP - Max of 2048 entries supported"); - } - - /* Read all the central directory entries */ - Int32 count = 0; - while (count < state->EntriesCount) { - sig = Stream_ReadUInt32_LE(stream); - if (sig == ZIP_CENTRALDIR) { - Zip_ReadCentralDirectory(state, &state->Entries[count]); - count++; - } else if (sig == ZIP_ENDOFCENTRALDIR) { - break; - } else { - String sigMsg = String_FromConst("ZIP - Unsupported signature found, aborting"); - ErrorHandler_Log(&sigMsg); - return; - } - } - - /* Now read the local file header entries */ - Int32 i; - for (i = 0; i < count; i++) { - ZipEntry* entry = &state->Entries[i]; - result = stream->Seek(stream, entry->LocalHeaderOffset, STREAM_SEEKFROM_BEGIN); - ErrorHandler_CheckOrFail(result, "ZIP - Seek to local file header"); - - sig = Stream_ReadUInt32_LE(stream); - if (sig != ZIP_LOCALFILEHEADER) { - String sigMsg = String_FromConst("ZIP - Invalid entry found, skipping"); - ErrorHandler_Log(&sigMsg); - continue; - } - Zip_ReadLocalFileHeader(state, entry); - } -} \ No newline at end of file diff --git a/src/Client/ZipArchive.h b/src/Client/ZipArchive.h deleted file mode 100644 index c8d3d59f0..000000000 --- a/src/Client/ZipArchive.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef CC_ZIPARCHIVE_H -#define CC_ZIPARCHIVE_H -#include "String.h" -/* Extracts entries from a .zip archive stream. - Copyright 2014-2017 ClassicalSharp | Licensed under BSD-3 -*/ -typedef struct Stream_ Stream; - -typedef struct ZipEntry_ { - Int32 CompressedDataSize, UncompressedDataSize, LocalHeaderOffset; - UInt32 Crc32; -} ZipEntry; - -#define ZIP_MAX_ENTRIES 2048 -typedef struct ZipState_ { - Stream* Input; - void (*ProcessEntry)(STRING_TRANSIENT String* path, Stream* data, ZipEntry* entry); - bool (*SelectEntry)(STRING_TRANSIENT String* path); - Int32 EntriesCount; - ZipEntry Entries[ZIP_MAX_ENTRIES]; -} ZipState; - -void Zip_Init(ZipState* state, Stream* input); -void Zip_Extract(ZipState* state); -#endif \ No newline at end of file