mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-18 12:05:14 -04:00
Port TexturePack to C
This commit is contained in:
parent
8a25708f6f
commit
7c674c4e82
@ -130,7 +130,7 @@ namespace ClassicalSharp.Textures {
|
|||||||
tags.Add(entry);
|
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) {
|
static string CRC32(string url) {
|
||||||
byte[] data = Encoding.UTF8.GetBytes(url);
|
byte[] data = Encoding.UTF8.GetBytes(url);
|
||||||
|
@ -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;
|
TexturePack.game = game;
|
||||||
game.Events.RaiseTexturePackChanged();
|
game.Events.RaiseTexturePackChanged();
|
||||||
if (game.Graphics.LostContext) return;
|
if (game.Graphics.LostContext) return;
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
#define CHAT_LOGTIMES_DEF_ELEMS 256
|
#define CHAT_LOGTIMES_DEF_ELEMS 256
|
||||||
#define CHAT_LOGTIMES_EXPAND_ELEMS 512
|
#define CHAT_LOGTIMES_EXPAND_ELEMS 512
|
||||||
Int64 Chat_DefaultLogTimes[CHAT_LOGTIMES_DEF_ELEMS];
|
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;
|
UInt32 Chat_LogTimesCount = CHAT_LOGTIMES_DEF_ELEMS, Chat_LogTimesUsed;
|
||||||
|
|
||||||
void Chat_GetLogTime(UInt32 index, Int64* timeMs) {
|
void Chat_GetLogTime(UInt32 index, Int64* timeMs) {
|
||||||
|
@ -229,6 +229,7 @@
|
|||||||
<ClInclude Include="Stream.h" />
|
<ClInclude Include="Stream.h" />
|
||||||
<ClInclude Include="GameStructs.h" />
|
<ClInclude Include="GameStructs.h" />
|
||||||
<ClInclude Include="TerrainAtlas.h" />
|
<ClInclude Include="TerrainAtlas.h" />
|
||||||
|
<ClInclude Include="TexturePack.h" />
|
||||||
<ClInclude Include="TreeGen.h" />
|
<ClInclude Include="TreeGen.h" />
|
||||||
<ClInclude Include="Utils.h" />
|
<ClInclude Include="Utils.h" />
|
||||||
<ClInclude Include="WeatherRenderer.h" />
|
<ClInclude Include="WeatherRenderer.h" />
|
||||||
@ -250,7 +251,6 @@
|
|||||||
<ClInclude Include="Window.h" />
|
<ClInclude Include="Window.h" />
|
||||||
<ClInclude Include="WordWrap.h" />
|
<ClInclude Include="WordWrap.h" />
|
||||||
<ClInclude Include="World.h" />
|
<ClInclude Include="World.h" />
|
||||||
<ClInclude Include="ZipArchive.h" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="2DStructs.c" />
|
<ClCompile Include="2DStructs.c" />
|
||||||
@ -320,7 +320,6 @@
|
|||||||
<ClCompile Include="WinWindow.c" />
|
<ClCompile Include="WinWindow.c" />
|
||||||
<ClCompile Include="WordWrap.c" />
|
<ClCompile Include="WordWrap.c" />
|
||||||
<ClCompile Include="World.c" />
|
<ClCompile Include="World.c" />
|
||||||
<ClCompile Include="ZipArchive.c" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||||
<ImportGroup Label="ExtensionTargets">
|
<ImportGroup Label="ExtensionTargets">
|
||||||
|
@ -300,9 +300,6 @@
|
|||||||
<ClInclude Include="Formats.h">
|
<ClInclude Include="Formats.h">
|
||||||
<Filter>Header Files\Map</Filter>
|
<Filter>Header Files\Map</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="ZipArchive.h">
|
|
||||||
<Filter>Header Files\TexturePack</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="Gui.h">
|
<ClInclude Include="Gui.h">
|
||||||
<Filter>Header Files\2D</Filter>
|
<Filter>Header Files\2D</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -366,6 +363,9 @@
|
|||||||
<ClInclude Include="Menus.h">
|
<ClInclude Include="Menus.h">
|
||||||
<Filter>Header Files\2D</Filter>
|
<Filter>Header Files\2D</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="TexturePack.h">
|
||||||
|
<Filter>Header Files\TexturePack</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="Noise.c">
|
<ClCompile Include="Noise.c">
|
||||||
@ -503,9 +503,6 @@
|
|||||||
<ClCompile Include="Formats.c">
|
<ClCompile Include="Formats.c">
|
||||||
<Filter>Source Files\Map</Filter>
|
<Filter>Source Files\Map</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
<ClCompile Include="ZipArchive.c">
|
|
||||||
<Filter>Source Files\TexturePack</Filter>
|
|
||||||
</ClCompile>
|
|
||||||
<ClCompile Include="Gui.c">
|
<ClCompile Include="Gui.c">
|
||||||
<Filter>Source Files\2D</Filter>
|
<Filter>Source Files\2D</Filter>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
#include "ErrorHandler.h"
|
#include "ErrorHandler.h"
|
||||||
#include "BlockPhysics.h"
|
#include "BlockPhysics.h"
|
||||||
#include "MapRenderer.h"
|
#include "MapRenderer.h"
|
||||||
|
#include "TexturePack.h"
|
||||||
|
|
||||||
#define LIST_SCREEN_ITEMS 5
|
#define LIST_SCREEN_ITEMS 5
|
||||||
#define LIST_SCREEN_BUTTONS (LIST_SCREEN_ITEMS + 3)
|
#define LIST_SCREEN_BUTTONS (LIST_SCREEN_ITEMS + 3)
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
#include "GraphicsAPI.h"
|
#include "GraphicsAPI.h"
|
||||||
#include "Deflate.h"
|
#include "Deflate.h"
|
||||||
#include "Formats.h"
|
#include "Formats.h"
|
||||||
#include "ZipArchive.h"
|
#include "TexturePack.h"
|
||||||
#include "Bitmap.h"
|
#include "Bitmap.h"
|
||||||
#include <Windows.h>
|
#include <Windows.h>
|
||||||
|
|
||||||
|
@ -1378,7 +1378,7 @@ void HUDScreen_AppendInput(Screen* hud, STRING_PURE String* text) {
|
|||||||
|
|
||||||
Widget* HUDScreen_GetHotbar(Screen* hud) {
|
Widget* HUDScreen_GetHotbar(Screen* hud) {
|
||||||
HUDScreen* screen = (HUDScreen*)hud;
|
HUDScreen* screen = (HUDScreen*)hud;
|
||||||
return &screen->Hotbar;
|
return (Widget*)(&screen->Hotbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
#include "MapGenerator.h"
|
#include "MapGenerator.h"
|
||||||
#include "World.h"
|
#include "World.h"
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
|
#include "TexturePack.h"
|
||||||
|
|
||||||
UInt8 ServerConnection_ServerNameBuffer[String_BufferSize(STRING_SIZE)];
|
UInt8 ServerConnection_ServerNameBuffer[String_BufferSize(STRING_SIZE)];
|
||||||
String ServerConnection_ServerName = String_FromEmptyArray(ServerConnection_ServerNameBuffer);
|
String ServerConnection_ServerName = String_FromEmptyArray(ServerConnection_ServerNameBuffer);
|
||||||
|
@ -1,11 +1,179 @@
|
|||||||
#include "Typedefs.h"
|
#include "TexturePack.h"
|
||||||
#include "String.h"
|
|
||||||
#include "Constants.h"
|
#include "Constants.h"
|
||||||
#include "Platform.h"
|
#include "Platform.h"
|
||||||
#include "ErrorHandler.h"
|
#include "ErrorHandler.h"
|
||||||
#include "Stream.h"
|
#include "Stream.h"
|
||||||
#include "Bitmap.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_ {
|
typedef struct EntryList_ {
|
||||||
UInt8 FolderBuffer[String_BufferSize(STRING_SIZE)];
|
UInt8 FolderBuffer[String_BufferSize(STRING_SIZE)];
|
||||||
UInt8 FileBuffer[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);
|
EntryList_Load(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*########################################################################################################################*
|
||||||
|
*------------------------------------------------------TextureCache-------------------------------------------------------*
|
||||||
|
*#########################################################################################################################*/
|
||||||
#define TEXCACHE_FOLDER "texturecache"
|
#define TEXCACHE_FOLDER "texturecache"
|
||||||
/* Because I didn't store milliseconds in original C# client */
|
/* Because I didn't store milliseconds in original C# client */
|
||||||
#define TEXCACHE_TICKS_PER_MS 10000LL
|
#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 data = String_InitAndClearArray(dataBuffer);
|
||||||
String_AppendInt64(&data, ticks);
|
String_AppendInt64(&data, ticks);
|
||||||
TextureCache_AddToTags(url, &data, &cache_lastModified);
|
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
49
src/Client/TexturePack.h
Normal 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
|
@ -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);
|
|
||||||
}
|
|
||||||
}
|
|
@ -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
|
|
Loading…
x
Reference in New Issue
Block a user