fix terrain.png in C client being cached discarding alpha channel

This commit is contained in:
UnknownShadow200 2018-07-20 15:13:07 +10:00
parent 0086f047f8
commit d60472be45
11 changed files with 75 additions and 156 deletions

View File

@ -6,17 +6,7 @@
#include "GameStructs.h" #include "GameStructs.h"
void ASyncRequest_Free(struct AsyncRequest* request) { void ASyncRequest_Free(struct AsyncRequest* request) {
switch (request->RequestType) { Platform_MemFree(&request->ResultData);
case REQUEST_TYPE_IMAGE:
Platform_MemFree(&request->ResultBitmap.Scan0);
break;
case REQUEST_TYPE_DATA:
Platform_MemFree(&request->ResultData.Ptr);
break;
case REQUEST_TYPE_STRING:
Platform_MemFree(&request->ResultString.buffer);
break;
}
} }
#define ASYNCREQUESTLIST_DEFELEMS 10 #define ASYNCREQUESTLIST_DEFELEMS 10
@ -127,37 +117,25 @@ void AsyncDownloader_GetSkin(STRING_PURE String* id, STRING_PURE String* skinNam
String_AppendConst(&url, ".png"); String_AppendConst(&url, ".png");
} }
AsyncDownloader_Add(&url, false, id, REQUEST_TYPE_IMAGE, NULL, NULL, NULL); AsyncDownloader_Add(&url, false, id, REQUEST_TYPE_DATA, NULL, NULL, NULL);
} }
void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id) { void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, NULL); AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, NULL);
} }
void AsyncDownloader_GetImage(STRING_PURE String* url, bool priority, STRING_PURE String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_IMAGE, NULL, NULL, NULL);
}
void AsyncDownloader_GetString(STRING_PURE String* url, bool priority, STRING_PURE String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_STRING, NULL, NULL, NULL);
}
void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id) { void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_CONTENT_LENGTH, NULL, NULL, NULL); AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_CONTENT_LENGTH, NULL, NULL, NULL);
} }
void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents) { void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_STRING, NULL, NULL, contents); AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, NULL, NULL, contents);
} }
void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag) { void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, lastModified, etag, NULL); AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_DATA, lastModified, etag, NULL);
} }
void AsyncDownloader_GetImageEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag) {
AsyncDownloader_Add(url, priority, id, REQUEST_TYPE_IMAGE, lastModified, etag, NULL);
}
void AsyncDownloader_PurgeOldEntriesTask(struct ScheduledTask* task) { void AsyncDownloader_PurgeOldEntriesTask(struct ScheduledTask* task) {
Platform_MutexLock(async_processedMutex); Platform_MutexLock(async_processedMutex);
{ {
@ -247,27 +225,8 @@ static void AsyncDownloader_ProcessRequest(struct AsyncRequest* request) {
UInt64 addr = (UInt64)data; UInt64 addr = (UInt64)data;
Platform_Log2("OK I got the DATA! %i bytes at %x", &size, &addr); Platform_Log2("OK I got the DATA! %i bytes at %x", &size, &addr);
request->ResultData = data;
struct Stream memStream; request->ResultSize = size;
switch (request->RequestType) {
case REQUEST_TYPE_DATA:
request->ResultData.Ptr = data;
request->ResultData.Size = size;
break;
case REQUEST_TYPE_IMAGE:
Stream_ReadonlyMemory(&memStream, data, size, &url);
Bitmap_DecodePng(&request->ResultBitmap, &memStream);
break;
case REQUEST_TYPE_STRING:
request->ResultString = String_Init(data, size, size);
break;
case REQUEST_TYPE_CONTENT_LENGTH:
request->ResultContentLength = size;
break;
}
} }
static void AsyncDownloader_CompleteResult(struct AsyncRequest* request) { static void AsyncDownloader_CompleteResult(struct AsyncRequest* request) {

View File

@ -9,10 +9,7 @@
struct IGameComponent; struct IGameComponent;
struct ScheduledTask; struct ScheduledTask;
enum REQUEST_TYPE { enum REQUEST_TYPE { REQUEST_TYPE_DATA, REQUEST_TYPE_CONTENT_LENGTH };
REQUEST_TYPE_DATA, REQUEST_TYPE_IMAGE,
REQUEST_TYPE_STRING, REQUEST_TYPE_CONTENT_LENGTH,
};
#define ASYNC_PROGRESS_NOTHING -3 #define ASYNC_PROGRESS_NOTHING -3
#define ASYNC_PROGRESS_MAKING_REQUEST -2 #define ASYNC_PROGRESS_MAKING_REQUEST -2
#define ASYNC_PROGRESS_FETCHING_DATA -1 #define ASYNC_PROGRESS_FETCHING_DATA -1
@ -25,12 +22,8 @@ struct AsyncRequest {
DateTime TimeDownloaded; DateTime TimeDownloaded;
UInt16 StatusCode; UInt16 StatusCode;
union { void* ResultData;
struct { void* Ptr; UInt32 Size; } ResultData; UInt32 ResultSize;
struct Bitmap ResultBitmap;
String ResultString;
UInt32 ResultContentLength;
};
DateTime LastModified; /* Time item cached at (if at all) */ DateTime LastModified; /* Time item cached at (if at all) */
UInt8 Etag[String_BufferSize(STRING_SIZE)]; /* ETag of cached item (if any) */ UInt8 Etag[String_BufferSize(STRING_SIZE)]; /* ETag of cached item (if any) */
@ -42,13 +35,10 @@ void ASyncRequest_Free(struct AsyncRequest* request);
void AsyncDownloader_MakeComponent(struct IGameComponent* comp); void AsyncDownloader_MakeComponent(struct IGameComponent* comp);
void AsyncDownloader_GetSkin(STRING_PURE String* id, STRING_PURE String* skinName); void AsyncDownloader_GetSkin(STRING_PURE String* id, STRING_PURE String* skinName);
void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id); void AsyncDownloader_GetData(STRING_PURE String* url, bool priority, STRING_PURE String* id);
void AsyncDownloader_GetImage(STRING_PURE String* url, bool priority, STRING_PURE String* id);
void AsyncDownloader_GetString(STRING_PURE String* url, bool priority, STRING_PURE String* id);
void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id); void AsyncDownloader_GetContentLength(STRING_PURE String* url, bool priority, STRING_PURE String* id);
/* TODO: Implement post */ /* TODO: Implement post */
//void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents); //void AsyncDownloader_PostString(STRING_PURE String* url, bool priority, STRING_PURE String* id, STRING_PURE String* contents);
void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag); void AsyncDownloader_GetDataEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag);
void AsyncDownloader_GetImageEx(STRING_PURE String* url, bool priority, STRING_PURE String* id, DateTime* lastModified, STRING_PURE String* etag);
bool AsyncDownloader_Get(STRING_PURE String* id, struct AsyncRequest* item); bool AsyncDownloader_Get(STRING_PURE String* id, struct AsyncRequest* item);
bool AsyncDownloader_GetCurrent(struct AsyncRequest* request, Int32* progress); bool AsyncDownloader_GetCurrent(struct AsyncRequest* request, Int32* progress);

View File

@ -63,13 +63,20 @@ enum PNG_FILTER {
typedef void(*Png_RowExpander)(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst); typedef void(*Png_RowExpander)(UInt8 bpp, Int32 width, UInt32* palette, UInt8* src, UInt32* dst);
UInt8 png_sig[PNG_SIG_SIZE] = { 137, 80, 78, 71, 13, 10, 26, 10 }; UInt8 png_sig[PNG_SIG_SIZE] = { 137, 80, 78, 71, 13, 10, 26, 10 };
bool Bitmap_DetectPng(UInt8* data, UInt32 len) {
if (len < PNG_SIG_SIZE) return false;
Int32 i;
for (i = 0; i < PNG_SIG_SIZE; i++) {
if (data[i] != png_sig[i]) return false;
}
return true;
}
static void Png_CheckHeader(struct Stream* stream) { static void Png_CheckHeader(struct Stream* stream) {
UInt8 header[PNG_SIG_SIZE]; UInt8 header[PNG_SIG_SIZE];
Stream_Read(stream, header, PNG_SIG_SIZE); Stream_Read(stream, header, PNG_SIG_SIZE);
Int32 i; if (!Bitmap_DetectPng(header, PNG_SIG_SIZE)) ErrorHandler_Fail("Invalid PNG header");
for (i = 0; i < PNG_SIG_SIZE; i++) {
if (header[i] != png_sig[i]) ErrorHandler_Fail("Invalid PNG header");
}
} }
static void Png_Reconstruct(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt32 lineLen) { static void Png_Reconstruct(UInt8 type, UInt8 bytesPerPixel, UInt8* line, UInt8* prior, UInt32 lineLen) {

View File

@ -25,6 +25,7 @@ void Bitmap_Allocate(struct Bitmap* bmp, Int32 width, Int32 height);
/* Allocates a power-of-2 sized bitmap larger or equal to to the given size, and clears it to 0. You are responsible for freeing its memory! */ /* Allocates a power-of-2 sized bitmap larger or equal to to the given size, and clears it to 0. You are responsible for freeing its memory! */
void Bitmap_AllocateClearedPow2(struct Bitmap* bmp, Int32 width, Int32 height); void Bitmap_AllocateClearedPow2(struct Bitmap* bmp, Int32 width, Int32 height);
bool Bitmap_DetectPng(UInt8* data, UInt32 len);
/* /*
Partially based off information from Partially based off information from
https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way https://handmade.network/forums/wip/t/2363-implementing_a_basic_png_reader_the_handmade_way

View File

@ -697,8 +697,12 @@ static void Player_CheckSkin(struct Player* player) {
struct AsyncRequest item; struct AsyncRequest item;
if (!AsyncDownloader_Get(&skin, &item)) return; if (!AsyncDownloader_Get(&skin, &item)) return;
struct Bitmap bmp = item.ResultBitmap; if (item.ResultData == NULL) { Player_SetSkinAll(player, true); return; }
if (bmp.Scan0 == NULL) { Player_SetSkinAll(player, true); return; }
String url = String_FromRawArray(item.URL);
struct Stream mem; struct Bitmap bmp;
Stream_ReadonlyMemory(&mem, item.ResultData, item.ResultSize, &url);
Bitmap_DecodePng(&bmp, &mem);
Gfx_DeleteTexture(&entity->TextureId); Gfx_DeleteTexture(&entity->TextureId);
Player_SetSkinAll(player, true); Player_SetSkinAll(player, true);

View File

@ -3324,7 +3324,7 @@ static void TexPackOverlay_Render(struct GuiElem* elem, Real64 delta) {
struct AsyncRequest item; struct AsyncRequest item;
if (!AsyncDownloader_Get(&identifier, &item)) return; if (!AsyncDownloader_Get(&identifier, &item)) return;
screen->ContentLength = item.ResultContentLength; screen->ContentLength = item.ResultSize;
if (screen->ContentLength == 0) return; if (screen->ContentLength == 0) return;
screen->ContextLost(elem); screen->ContextLost(elem);

View File

@ -200,7 +200,7 @@ static void WoM_CheckMotd(void) {
applied in the new world if the async 'get env request' didn't complete before the old world was unloaded */ applied in the new world if the async 'get env request' didn't complete before the old world was unloaded */
wom_counter++; wom_counter++;
WoM_UpdateIdentifier(); WoM_UpdateIdentifier();
AsyncDownloader_GetString(&url, true, &wom_identifier); AsyncDownloader_GetData(&url, true, &wom_identifier);
wom_sendId = true; wom_sendId = true;
} }
@ -288,11 +288,13 @@ static void WoM_Reset(void) {
static void WoM_Tick(void) { static void WoM_Tick(void) {
struct AsyncRequest item; struct AsyncRequest item;
bool success = AsyncDownloader_Get(&wom_identifier, &item); if (!AsyncDownloader_Get(&wom_identifier, &item)) return;
if (success && item.ResultString.length > 0) {
Wom_ParseConfig(&item.ResultString); if (item.ResultData) {
ASyncRequest_Free(&item); String str = String_Init(item.ResultData, item.ResultSize, item.ResultSize);
Wom_ParseConfig(&str);
} }
ASyncRequest_Free(&item);
} }

View File

@ -60,47 +60,27 @@ void ServerConnection_DownloadTexturePack(STRING_PURE String* url) {
TextureCache_GetLastModified(url, &lastModified); TextureCache_GetLastModified(url, &lastModified);
TextureCache_GetETag(url, &etag); TextureCache_GetETag(url, &etag);
} }
TexturePack_ExtractCurrent(url); TexturePack_ExtractCurrent(url);
String texPack = String_FromConst("texturePack");
String zip = String_FromConst(".zip"); AsyncDownloader_GetDataEx(url, true, &texPack, &lastModified, &etag);
if (String_ContainsString(url, &zip)) {
String texPack = String_FromConst("texturePack");
AsyncDownloader_GetDataEx(url, true, &texPack, &lastModified, &etag);
} else {
String terrain = String_FromConst("terrain");
AsyncDownloader_GetImageEx(url, true, &terrain, &lastModified, &etag);
}
}
void ServerConnection_LogResourceFail(struct AsyncRequest* item) {
Int32 status = item->StatusCode;
if (status == 0 || status == 304) return;
UChar msgBuffer[String_BufferSize(STRING_SIZE)];
String msg = String_InitAndClearArray(msgBuffer);
String_Format1(&msg, "&c%i error when trying to download texture pack", &status);
Chat_Add(&msg);
} }
void ServerConnection_CheckAsyncResources(void) { void ServerConnection_CheckAsyncResources(void) {
struct AsyncRequest item; struct AsyncRequest item;
String terrain = String_FromConst("terrain");
String texPack = String_FromConst("texturePack"); String texPack = String_FromConst("texturePack");
if (!AsyncDownloader_Get(&texPack, &item)) return;
if (AsyncDownloader_Get(&terrain, &item)) { if (item.ResultData) {
if (item.ResultBitmap.Scan0 != NULL) { TexturePack_Extract_Req(&item);
TexturePack_ExtractTerrainPng_Req(&item); } else {
} else { Int32 status = item.StatusCode;
ServerConnection_LogResourceFail(&item); if (status == 0 || status == 304) return;
}
}
if (AsyncDownloader_Get(&texPack, &item)) { UChar msgBuffer[String_BufferSize(STRING_SIZE)];
if (item.ResultData.Ptr != NULL) { String msg = String_InitAndClearArray(msgBuffer);
TexturePack_ExtractTexturePack_Req(&item); String_Format1(&msg, "&c%i error when trying to download texture pack", &status);
} else { Chat_Add(&msg);
ServerConnection_LogResourceFail(&item);
}
} }
} }

View File

@ -63,7 +63,7 @@ ReturnCode Platform_SocketConnect(SocketPtr socket, STRING_PURE String* ip, Int3
ReturnCode Platform_SocketRead(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) { ReturnCode Platform_SocketRead(SocketPtr socket, UInt8* buffer, UInt32 count, UInt32* modified) {
Int32 recvCount = recv(socket, buffer, count, 0); Int32 recvCount = recv(socket, buffer, count, 0);
if (recvCount == -1) { *modified = recvCount; return 0; } if (recvCount != -1) { *modified = recvCount; return 0; }
*modified = 0; return Socket__Error(); *modified = 0; return Socket__Error();
} }

View File

@ -354,39 +354,24 @@ void TextureCache_GetETag(STRING_PURE String* url, STRING_PURE String* etag) {
TexturePack_GetFromTags(url, etag, &cache_eTags); TexturePack_GetFromTags(url, etag, &cache_eTags);
} }
static void* TextureCache_CreateFile(STRING_PURE String* path) {
String folder = String_FromConst(TEXCACHE_FOLDER);
if (!Platform_DirectoryExists(&folder)) {
ReturnCode dirResult = Platform_DirectoryCreate(&folder);
ErrorHandler_CheckOrFail(dirResult, "TextureCache_CreateFile - create directory");
}
void* file;
ReturnCode result = Platform_FileCreate(&file, path);
/* TODO: Should we just log failure to save? */
ErrorHandler_CheckOrFail(result, "TextureCache_CreateFile - open file");
return file;
}
void TextureCache_AddImage(STRING_PURE String* url, struct Bitmap* bmp) {
String path; TexCache_InitAndMakePath(url);
void* file = TextureCache_CreateFile(&path);
struct Stream stream; Stream_FromFile(&stream, file, &path);
{
Bitmap_EncodePng(bmp, &stream);
}
ReturnCode result = stream.Close(&stream);
ErrorHandler_CheckOrFail(result, "TextureCache_AddImage - close file");
}
void TextureCache_AddData(STRING_PURE String* url, UInt8* data, UInt32 length) { void TextureCache_AddData(STRING_PURE String* url, UInt8* data, UInt32 length) {
String path; TexCache_InitAndMakePath(url); String path; TexCache_InitAndMakePath(url);
void* file = TextureCache_CreateFile(&path); ReturnCode result;
String folder = String_FromConst(TEXCACHE_FOLDER);
if (!Platform_DirectoryExists(&folder)) {
result = Platform_DirectoryCreate(&folder);
ErrorHandler_CheckOrFail(result, "TextureCache_AddData - create directory");
}
void* file; result = Platform_FileCreate(&file, &path);
/* TODO: Should we just log failure to save? */
ErrorHandler_CheckOrFail(result, "TextureCache_AddData - open file");
struct Stream stream; Stream_FromFile(&stream, file, &path); struct Stream stream; Stream_FromFile(&stream, file, &path);
{ {
Stream_Write(&stream, data, length); Stream_Write(&stream, data, length);
} }
ReturnCode result = stream.Close(&stream); result = stream.Close(&stream);
ErrorHandler_CheckOrFail(result, "TextureCache_AddData - close file"); ErrorHandler_CheckOrFail(result, "TextureCache_AddData - close file");
} }
@ -495,29 +480,15 @@ void TexturePack_ExtractCurrent(STRING_PURE String* url) {
} }
ReturnCode result = stream.Close(&stream); ReturnCode result = stream.Close(&stream);
ErrorHandler_CheckOrFail(result, "TexturePack_ExtractCurrent - slow stream"); ErrorHandler_CheckOrFail(result, "TexturePack_ExtractCurrent - close stream");
} }
} }
void TexturePack_ExtractTerrainPng_Req(struct AsyncRequest* item) { void TexturePack_Extract_Req(struct AsyncRequest* item) {
String url = String_FromRawArray(item->URL); String url = String_FromRawArray(item->URL);
String_Set(&World_TextureUrl, &url); String_Set(&World_TextureUrl, &url);
struct Bitmap bmp = item->ResultBitmap; void* data = item->ResultData;
UInt32 len = item->ResultSize;
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)) ASyncRequest_Free(item);
}
void TexturePack_ExtractTexturePack_Req(struct AsyncRequest* item) {
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); String etag = String_FromRawArray(item->Etag);
TextureCache_AddData(&url, data, len); TextureCache_AddData(&url, data, len);
@ -525,7 +496,14 @@ void TexturePack_ExtractTexturePack_Req(struct AsyncRequest* item) {
TextureCache_AddLastModified(&url, &item->LastModified); TextureCache_AddLastModified(&url, &item->LastModified);
String id = String_FromRawArray(item->ID); String id = String_FromRawArray(item->ID);
struct Stream stream; Stream_ReadonlyMemory(&stream, data, len, &id); struct Stream mem; Stream_ReadonlyMemory(&mem, data, len, &id);
TexturePack_ExtractZip(&stream);
if (Bitmap_DetectPng(data, len)) {
struct Bitmap bmp; Bitmap_DecodePng(&bmp, &mem);
Event_RaiseVoid(&TextureEvents_PackChanged);
if (!Game_ChangeTerrainAtlas(&bmp)) Platform_MemFree(&bmp.Scan0);
} else {
TexturePack_ExtractZip(&mem);
}
ASyncRequest_Free(item); ASyncRequest_Free(item);
} }

View File

@ -35,7 +35,6 @@ bool TextureCache_HasUrl(STRING_PURE String* url);
bool TextureCache_GetStream(STRING_PURE String* url, struct Stream* stream); bool TextureCache_GetStream(STRING_PURE String* url, struct Stream* stream);
void TextureCache_GetLastModified(STRING_PURE String* url, DateTime* time); void TextureCache_GetLastModified(STRING_PURE String* url, DateTime* time);
void TextureCache_GetETag(STRING_PURE String* url, STRING_PURE String* etag); void TextureCache_GetETag(STRING_PURE String* url, STRING_PURE String* etag);
void TextureCache_AddImage(STRING_PURE String* url, struct Bitmap* bmp);
void TextureCache_AddData(STRING_PURE String* url, UInt8* data, UInt32 length); void TextureCache_AddData(STRING_PURE String* url, UInt8* data, UInt32 length);
void TextureCache_AddETag(STRING_PURE String* url, STRING_PURE String* etag); void TextureCache_AddETag(STRING_PURE String* url, STRING_PURE String* etag);
void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModified); void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModified);
@ -43,6 +42,5 @@ void TextureCache_AddLastModified(STRING_PURE String* url, DateTime* lastModifie
void TexturePack_ExtractZip_File(STRING_PURE String* filename); void TexturePack_ExtractZip_File(STRING_PURE String* filename);
void TexturePack_ExtractDefault(void); void TexturePack_ExtractDefault(void);
void TexturePack_ExtractCurrent(STRING_PURE String* url); void TexturePack_ExtractCurrent(STRING_PURE String* url);
void TexturePack_ExtractTerrainPng_Req(struct AsyncRequest* item); void TexturePack_Extract_Req(struct AsyncRequest* item);
void TexturePack_ExtractTexturePack_Req(struct AsyncRequest* item);
#endif #endif