Port TexturePack to C

This commit is contained in:
UnknownShadow200 2018-04-20 13:56:31 +10:00
parent 8a25708f6f
commit 7c674c4e82
13 changed files with 355 additions and 199 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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) {

View File

@ -229,6 +229,7 @@
<ClInclude Include="Stream.h" />
<ClInclude Include="GameStructs.h" />
<ClInclude Include="TerrainAtlas.h" />
<ClInclude Include="TexturePack.h" />
<ClInclude Include="TreeGen.h" />
<ClInclude Include="Utils.h" />
<ClInclude Include="WeatherRenderer.h" />
@ -250,7 +251,6 @@
<ClInclude Include="Window.h" />
<ClInclude Include="WordWrap.h" />
<ClInclude Include="World.h" />
<ClInclude Include="ZipArchive.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="2DStructs.c" />
@ -320,7 +320,6 @@
<ClCompile Include="WinWindow.c" />
<ClCompile Include="WordWrap.c" />
<ClCompile Include="World.c" />
<ClCompile Include="ZipArchive.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

View File

@ -300,9 +300,6 @@
<ClInclude Include="Formats.h">
<Filter>Header Files\Map</Filter>
</ClInclude>
<ClInclude Include="ZipArchive.h">
<Filter>Header Files\TexturePack</Filter>
</ClInclude>
<ClInclude Include="Gui.h">
<Filter>Header Files\2D</Filter>
</ClInclude>
@ -366,6 +363,9 @@
<ClInclude Include="Menus.h">
<Filter>Header Files\2D</Filter>
</ClInclude>
<ClInclude Include="TexturePack.h">
<Filter>Header Files\TexturePack</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Noise.c">
@ -503,9 +503,6 @@
<ClCompile Include="Formats.c">
<Filter>Source Files\Map</Filter>
</ClCompile>
<ClCompile Include="ZipArchive.c">
<Filter>Source Files\TexturePack</Filter>
</ClCompile>
<ClCompile Include="Gui.c">
<Filter>Source Files\2D</Filter>
</ClCompile>

View File

@ -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)

View File

@ -5,7 +5,7 @@
#include "GraphicsAPI.h"
#include "Deflate.h"
#include "Formats.h"
#include "ZipArchive.h"
#include "TexturePack.h"
#include "Bitmap.h"
#include <Windows.h>

View File

@ -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);
}

View File

@ -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);

View File

@ -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, &centralDirectoryOffset);
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
@ -240,3 +412,124 @@ void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModifie
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);
}

49
src/Client/TexturePack.h Normal file
View File

@ -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

View File

@ -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, &centralDirectoryOffset);
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);
}
}

View File

@ -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