diff --git a/src/Bitmap.c b/src/Bitmap.c index f0058f769..b9ca41ae3 100644 --- a/src/Bitmap.c +++ b/src/Bitmap.c @@ -507,27 +507,6 @@ ReturnCode Png_Decode(Bitmap* bmp, struct Stream* stream) { /*########################################################################################################################* *------------------------------------------------------PNG encoder--------------------------------------------------------* *#########################################################################################################################*/ -static ReturnCode Bitmap_Crc32StreamWrite(struct Stream* stream, const uint8_t* data, uint32_t count, uint32_t* modified) { - struct Stream* source; - uint32_t i, crc32 = stream->Meta.CRC32.CRC32; - - /* TODO: Optimise this calculation */ - for (i = 0; i < count; i++) { - crc32 = Utils_Crc32Table[(crc32 ^ data[i]) & 0xFF] ^ (crc32 >> 8); - } - stream->Meta.CRC32.CRC32 = crc32; - - source = stream->Meta.CRC32.Source; - return source->Write(source, data, count, modified); -} - -static void Bitmap_Crc32Stream(struct Stream* stream, struct Stream* underlying) { - Stream_Init(stream); - stream->Meta.CRC32.Source = underlying; - stream->Meta.CRC32.CRC32 = 0xFFFFFFFFUL; - stream->Write = Bitmap_Crc32StreamWrite; -} - static void Png_Filter(uint8_t filter, uint8_t* cur, uint8_t* prior, uint8_t* best, int lineLen) { /* 3 bytes per pixel constant */ uint8_t a, b, c; @@ -616,6 +595,7 @@ static void Png_EncodeRow(const BitmapCol* src, uint8_t* cur, uint8_t* prior, ui if (bestFilter != PNG_FILTER_PAETH) { Png_Filter(bestFilter, cur, prior, dst, lineLen); } + best[0] = bestFilter; } @@ -633,7 +613,7 @@ ReturnCode Png_Encode(Bitmap* bmp, struct Stream* stream, Png_RowSelector select if (!selectRow) selectRow = Png_SelectRow; if ((res = Stream_Write(stream, png_sig, PNG_SIG_SIZE))) return res; - Bitmap_Crc32Stream(&chunk, stream); + Stream_WriteonlyCrc32(&chunk, stream); /* Write header chunk */ Stream_SetU32_BE(&tmp[0], PNG_IHDR_SIZE); diff --git a/src/Deflate.c b/src/Deflate.c index 24edc0d91..cae284911 100644 --- a/src/Deflate.c +++ b/src/Deflate.c @@ -1095,13 +1095,14 @@ static ReturnCode Zip_ReadLocalFileHeader(struct ZipState* state, struct ZipEntr if (method == 0) { Stream_ReadonlyPortion(&portion, stream, uncompressedSize); - state->ProcessEntry(&path, &portion, entry); + return state->ProcessEntry(&path, &portion, state->Obj); } else if (method == 8) { Stream_ReadonlyPortion(&portion, stream, compressedSize); Inflate_MakeStream(&compStream, &inflate, &portion); - state->ProcessEntry(&path, &compStream, entry); + return state->ProcessEntry(&path, &compStream, state->Obj); } else { Platform_Log1("Unsupported.zip entry compression method: %i", &method); + /* TODO: Should this be an error */ } return 0; } @@ -1113,7 +1114,6 @@ static ReturnCode Zip_ReadCentralDirectory(struct ZipState* state, struct ZipEnt ReturnCode res; if ((res = Stream_Read(stream, header, sizeof(header)))) return res; - entry->Crc32 = Stream_GetU32_LE(&header[12]); entry->CompressedSize = Stream_GetU32_LE(&header[16]); entry->UncompressedSize = Stream_GetU32_LE(&header[20]); @@ -1144,10 +1144,11 @@ enum ZipSig { ZIP_SIG_LOCALFILEHEADER = 0x04034b50 }; -static void Zip_DefaultProcessor(const String* path, struct Stream* data, struct ZipEntry* entry) { } +static ReturnCode Zip_DefaultProcessor(const String* path, struct Stream* data, void* obj) { return 0; } static bool Zip_DefaultSelector(const String* path) { return true; } void Zip_Init(struct ZipState* state, struct Stream* input) { state->Input = input; + state->Obj = NULL; state->EntriesCount = 0; state->ProcessEntry = Zip_DefaultProcessor; state->SelectEntry = Zip_DefaultSelector; diff --git a/src/Deflate.h b/src/Deflate.h index a38cfed50..7455703c7 100644 --- a/src/Deflate.h +++ b/src/Deflate.h @@ -110,7 +110,7 @@ struct ZLibState { struct DeflateState Base; uint32_t Adler32; }; CC_API void ZLib_MakeStream(struct Stream* stream, struct ZLibState* state, struct Stream* underlying); /* Minimal data needed to describe an entry in a .zip archive. */ -struct ZipEntry { uint32_t CompressedSize, UncompressedSize, LocalHeaderOffset, Crc32; }; +struct ZipEntry { uint32_t CompressedSize, UncompressedSize, LocalHeaderOffset; }; #define ZIP_MAX_ENTRIES 2048 /* Stores state for reading and processing entries in a .zip archive. */ @@ -118,11 +118,15 @@ struct ZipState { /* Source of the .zip archive data. Must be seekable. */ struct Stream* Input; /* Callback function to process the data in a .zip archive entry. */ - /* NOTE: data stream may not be seekable. (entry data might be compressed) */ - void (*ProcessEntry)(const String* path, struct Stream* data, struct ZipEntry* entry); + /* obj is user specified in state.Obj variable */ + /* Return non-zero to indicate an error and stop further processing. */ + /* NOTE: data stream MAY NOT be seekable. (i.e. entry data might be compressed) */ + ReturnCode (*ProcessEntry)(const String* path, struct Stream* data, void* obj); /* Predicate used to select which entries in a .zip archive get proessed. */ /* NOTE: returning false entirely skips the entry. (avoids pointless seek to entry) */ bool (*SelectEntry)(const String* path); + /* Generic object/pointer passed to ProcessEntry callback. */ + void* Obj; /* Number of entries in the .zip archive. */ int EntriesCount; /* Data for each entry in the .zip archive. */ diff --git a/src/Launcher.c b/src/Launcher.c index 6c62bc0ec..869c0ca09 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -148,6 +148,7 @@ static void Launcher_MouseUp(void* obj, int btn) { } static void Launcher_MouseMove(void* obj, int deltaX, int deltaY) { + if (!Launcher_Screen) return; Launcher_Screen->MouseMove(Launcher_Screen, deltaX, deltaY); } @@ -376,30 +377,31 @@ static void Launcher_LoadTextures(Bitmap* bmp) { 0, 0, TILESIZE, TILESIZE); } -static void Launcher_ProcessZipEntry(const String* path, struct Stream* data, struct ZipEntry* entry) { +static ReturnCode Launcher_ProcessZipEntry(const String* path, struct Stream* data, void* obj) { Bitmap bmp; ReturnCode res; if (String_CaselessEqualsConst(path, "default.png")) { - if (fontBmp.Scan0) return; + if (fontBmp.Scan0) return 0; res = Png_Decode(&fontBmp, data); if (res) { - Launcher_ShowError(res, "decoding default.png"); return; + Launcher_ShowError(res, "decoding default.png"); return res; } else { Drawer2D_SetFontBitmap(&fontBmp); useBitmappedFont = !Options_GetBool(OPT_USE_CHAT_FONT, false); } } else if (String_CaselessEqualsConst(path, "terrain.png")) { - if (terrainBmp.Scan0) return; + if (terrainBmp.Scan0) return 0; res = Png_Decode(&bmp, data); if (res) { - Launcher_ShowError(res, "decoding terrain.png"); return; + Launcher_ShowError(res, "decoding terrain.png"); return res; } else { Launcher_LoadTextures(&bmp); } } + return 0; } static void Launcher_ExtractTexturePack(const String* path) { diff --git a/src/Resources.c b/src/Resources.c index 32a5c2320..e2fcb0b75 100644 --- a/src/Resources.c +++ b/src/Resources.c @@ -13,19 +13,19 @@ /*########################################################################################################################* *---------------------------------------------------------List/Checker----------------------------------------------------* *#########################################################################################################################*/ -bool SoundsExist; +bool Textures_AllExist, Sounds_AllExist; int Resources_Count, Resources_Size; +static int texturesFound; -static void Resources_CheckFiles(void) { - int i, flags; - - flags = Resources_GetFetchFlags(); - for (i = 0; i < Array_Elems(Resources_Files); i++) { - if (!(flags & Resources_Files[i].Flag)) continue; +CC_NOINLINE static struct ResourceTexture* Resources_FindTex(const String* name) { + struct ResourceTexture* tex; + int i; - Resources_Size += Resources_Files[i].Size; - Resources_Count++; + for (i = 0; i < Array_Elems(Resources_Textures); i++) { + tex = &Resources_Textures[i]; + if (String_CaselessEqualsConst(name, tex->Filename)) return tex; } + return NULL; } static void Resources_CheckMusic(void) { @@ -37,8 +37,8 @@ static void Resources_CheckMusic(void) { path.length = 0; String_Format1(&path, "audio/%c", Resources_Music[i].Name); - Resources_Music[i].Exists = File_Exists(&path); - if (Resources_Music[i].Exists) continue; + Resources_Music[i].Downloaded = File_Exists(&path); + if (Resources_Music[i].Downloaded) continue; Resources_Size += Resources_Music[i].Size; Resources_Count++; @@ -55,32 +55,24 @@ static void Resources_CheckSounds(void) { String_Format1(&path, "audio/%c.wav", Resources_Sounds[i].Name); if (File_Exists(&path)) continue; - SoundsExist = false; + Sounds_AllExist = false; Resources_Count += Array_Elems(Resources_Sounds); Resources_Size += 417; return; } - SoundsExist = true; + Sounds_AllExist = true; } static bool Resources_SelectZipEntry(const String* path) { - String name; - int i; - - name = *path; + String name = *path; Utils_UNSAFE_GetFilename(&name); - for (i = 0; i < Array_Elems(Resources_Textures); i++) { - if (Resources_Textures[i].Exists) continue; - if (!String_CaselessEqualsConst(&name, Resources_Textures[i].Filename)) continue; - Resources_Textures[i].Exists = true; - break; - } + if (Resources_FindTex(&name)) texturesFound++; return false; } -static void Resources_CheckDefaultZip(void) { +static void Resources_CheckTextures(void) { const static String path = String_FromConst("texpacks/default.zip"); struct Stream stream; struct ZipState state; @@ -95,46 +87,41 @@ static void Resources_CheckDefaultZip(void) { res = Zip_Extract(&state); stream.Close(&stream); - if (res) { Launcher_ShowError(res, "inspecting default.zip"); } -} + if (res) Launcher_ShowError(res, "inspecting default.zip"); -int Resources_GetFetchFlags(void) { - int flags = 0, i; - for (i = 0; i < Array_Elems(Resources_Textures); i++) { - if (Resources_Textures[i].Exists) continue; - - flags |= Resources_Textures[i].Flags; - } - return flags; + /* if somehow have say "gui.png", "GUI.png" */ + Textures_AllExist = texturesFound >= Array_Elems(Resources_Textures); } void Resources_CheckExistence(void) { - Resources_CheckDefaultZip(); - Resources_CheckFiles(); + int i; + Resources_CheckTextures(); Resources_CheckMusic(); Resources_CheckSounds(); + + if (Textures_AllExist) return; + for (i = 0; i < Array_Elems(Resources_Files); i++) { + Resources_Count++; + Resources_Size += Resources_Files[i].Size; + } } struct ResourceFile Resources_Files[4] = { - { "classic jar", "http://launcher.mojang.com/mc/game/c0.30_01c/client/54622801f5ef1bcc1549a842c5b04cb5d5583005/client.jar", 291, FLAG_CLASSIC }, - { "1.6.2 jar", "http://launcher.mojang.com/mc/game/1.6.2/client/b6cb68afde1d9cf4a20cbf27fa90d0828bf440a4/client.jar", 4621, FLAG_MODERN }, - { "gui.png patch", "http://static.classicube.net/terrain-patch2.png", 7, FLAG_GUI }, - { "terrain.png patch", "http://static.classicube.net/gui.png", 21, FLAG_TERRAIN } + { "classic jar", "http://launcher.mojang.com/mc/game/c0.30_01c/client/54622801f5ef1bcc1549a842c5b04cb5d5583005/client.jar", 291 }, + { "1.6.2 jar", "http://launcher.mojang.com/mc/game/1.6.2/client/b6cb68afde1d9cf4a20cbf27fa90d0828bf440a4/client.jar", 4621 }, + { "gui.png patch", "http://static.classicube.net/terrain-patch2.png", 7 }, + { "terrain.png patch", "http://static.classicube.net/gui.png", 21 } }; -struct ResourceTexture Resources_Textures[19] = { +struct ResourceTexture Resources_Textures[20] = { /* classic jar files */ - { "char.png", FLAG_CLASSIC }, { "clouds.png", FLAG_CLASSIC }, - { "default.png", FLAG_CLASSIC }, { "particles.png", FLAG_CLASSIC }, - { "rain.png", FLAG_CLASSIC }, { "gui_classic.png", FLAG_CLASSIC }, - { "icons.png", FLAG_CLASSIC }, { "terrain.png", FLAG_CLASSIC | FLAG_TERRAIN | FLAG_MODERN }, - { "creeper.png", FLAG_CLASSIC }, { "pig.png", FLAG_CLASSIC }, - { "sheep.png", FLAG_CLASSIC }, { "sheep_fur.png", FLAG_CLASSIC }, - { "skeleton.png", FLAG_CLASSIC }, { "spider.png", FLAG_CLASSIC }, - { "zombie.png", FLAG_CLASSIC }, /* "arrows.png", "sign.png" */ + { "char.png" }, { "clouds.png" }, { "default.png" }, { "particles.png" }, + { "rain.png" }, { "gui_classic.png" }, { "icons.png" }, { "terrain.png" }, + { "creeper.png" }, { "pig.png" }, { "sheep.png" }, { "sheep_fur.png" }, + { "skeleton.png" }, { "spider.png" }, { "zombie.png" }, /* "arrows.png", "sign.png" */ /* other files */ - { "snow.png", FLAG_MODERN }, { "chicken.png", FLAG_MODERN }, - { "animations.png", FLAG_MODERN }, { "gui.png", FLAG_GUI } + { "snow.png" }, { "chicken.png" }, { "gui.png" }, + { "animations.png" }, { "animations.txt" } }; struct ResourceSound Resources_Sounds[59] = { @@ -182,9 +169,262 @@ struct ResourceMusic Resources_Music[7] = { }; +/*########################################################################################################################* +*---------------------------------------------------------Zip writer------------------------------------------------------* +*#########################################################################################################################*/ +static ReturnCode ZipPatcher_LocalFile(struct Stream* s, struct ResourceTexture* entry) { + String name = String_FromReadonly(entry->Filename); + uint8_t header[30 + STRING_SIZE]; + ReturnCode res; + if ((res = s->Position(s, &entry->Offset))) return res; + + Stream_SetU32_LE(&header[0], 0x04034b50); /* signature */ + Stream_SetU16_LE(&header[4], 20); /* version needed */ + Stream_SetU16_LE(&header[6], 0); /* bitflags */ + Stream_SetU16_LE(&header[8] , 0); /* compression method */ + Stream_SetU16_LE(&header[10], 0); /* last modified */ + Stream_SetU16_LE(&header[12], 0); /* last modified */ + + Stream_SetU32_LE(&header[14], 0); /* CRC32 */ + Stream_SetU32_LE(&header[18], 0); /* Compressed size */ + Stream_SetU32_LE(&header[22], 0); /* Uncompressed size */ + + Stream_SetU16_LE(&header[26], name.length); /* name length */ + Stream_SetU16_LE(&header[28], 0); /* extra field length */ + + Mem_Copy(&header[30], name.buffer, name.length); + return Stream_Write(s, header, 30 + name.length); +} + +static ReturnCode ZipPatcher_CentralDir(struct Stream* s, struct ResourceTexture* res) { + String name = String_FromReadonly(res->Filename); + uint8_t header[46 + STRING_SIZE]; + struct DateTime now; + int modTime, modDate; + + DateTime_CurrentLocal(&now); + modTime = (now.Second / 2) | (now.Minute << 5) | (now.Hour << 11); + modDate = (now.Day) | (now.Month << 5) | ((now.Year - 1980) << 9); + + Stream_SetU32_LE(&header[0], 0x02014b50); /* signature */ + Stream_SetU16_LE(&header[4], 20); /* version */ + Stream_SetU16_LE(&header[6], 20); /* version needed */ + Stream_SetU16_LE(&header[8], 0); /* bitflags */ + Stream_SetU16_LE(&header[10], 0); /* compression method */ + Stream_SetU16_LE(&header[12], modTime); /* last modified */ + Stream_SetU16_LE(&header[14], modDate); /* last modified */ + + Stream_SetU32_LE(&header[16], res->Crc32); /* CRC32 */ + Stream_SetU32_LE(&header[20], res->Size); /* compressed size */ + Stream_SetU32_LE(&header[24], res->Size); /* uncompressed size */ + + Stream_SetU16_LE(&header[28], name.length); /* name length */ + Stream_SetU16_LE(&header[30], 0); /* extra field length */ + Stream_SetU16_LE(&header[32], 0); /* file comment length */ + Stream_SetU16_LE(&header[34], 0); /* disk number */ + Stream_SetU16_LE(&header[36], 0); /* internal attributes */ + Stream_SetU32_LE(&header[38], 0); /* external attributes */ + Stream_SetU32_LE(&header[42], res->Offset); /* local header offset */ + + Mem_Copy(&header[46], name.buffer, name.length); + return Stream_Write(s, header, 46 + name.length); +} + +static ReturnCode ZipPatcher_EndOfCentralDir(struct Stream* s, uint32_t centralDirBeg, uint32_t centralDirEnd) { + uint8_t header[22]; + + Stream_SetU32_LE(&header[0], 0x06054b50); /* signature */ + Stream_SetU16_LE(&header[4], 0); /* disk number */ + Stream_SetU16_LE(&header[6], 0); /* disk number of start */ + Stream_SetU16_LE(&header[8], Array_Elems(Resources_Textures)); /* disk entries */ + Stream_SetU16_LE(&header[10], Array_Elems(Resources_Textures)); /* total entries */ + Stream_SetU32_LE(&header[12], centralDirEnd - centralDirBeg); /* central dir size */ + Stream_SetU32_LE(&header[16], centralDirBeg); /* central dir start */ + Stream_SetU16_LE(&header[20], 0); /* comment length */ + return Stream_Write(s, header, 22); +} + +static ReturnCode ZipPatcher_WriteData(struct Stream* s, struct ResourceTexture* tex, const uint8_t* data, uint32_t len) { + ReturnCode res; + tex->Size = len; + tex->Crc32 = Utils_CRC32(data, len); + + res = ZipPatcher_LocalFile(s, tex); + if (res) return res; + return Stream_Write(s, data, len); +} + +static ReturnCode ZipPatcher_WriteStream(struct Stream* s, struct ResourceTexture* tex, struct Stream* src) { + uint8_t tmp[2048]; + uint32_t read; + struct Stream crc32; + ReturnCode res; + + res = ZipPatcher_LocalFile(s, tex); + if (res) return res; + Stream_WriteonlyCrc32(&crc32, s); + + for (tex->Size = 0; ; tex->Size += read) { + res = src->Read(src, tmp, sizeof(tmp), &read); + if (res) return res; + if (!read) break; + + res = Stream_Write(&crc32, tmp, read); + if (res) return res; + } + + tex->Crc32 = crc32.Meta.CRC32.CRC32 ^ 0xFFFFFFFFUL; + return 0; +} + + /*########################################################################################################################* *-------------------------------------------------------Texture patcher---------------------------------------------------* *#########################################################################################################################*/ +#define ANIMS_TXT_CONTENTS \ +"# This file defines the animations used in a texture pack for ClassicalSharp and other supporting applications.\r\n" \ +"# Each line is in the format : \r\n" \ +"# - TileX and TileY are the coordinates of the tile in terrain.png that will be replaced by the animation frames.\r\n" \ +"# Essentially, TileX and TileY are the remainder and quotient of an ID in F10 menu divided by 16\r\n" \ +"# For instance, obsidian texture(37) has TileX of 5, and TileY of 2\r\n" \ +"# - FrameX and FrameY are the pixel coordinates of the first animation frame in animations.png.\r\n" \ +"# - Frame Size is the size in pixels of an animation frame.\r\n" \ +"# - Frames count is the number of used frames. The first frame is located at\r\n" \ +"# (FrameX, FrameY), the second one at (FrameX + FrameSize, FrameY) and so on.\r\n" \ +"# - Tick delay is the number of ticks a frame doesn't change. For instance, delay of 0\r\n" \ +"# means that the tile would be replaced every tick, while delay of 2 means\r\n" \ +"# 'replace with frame 1, don't change frame, don't change frame, replace with frame 2'.\r\n" \ +"# NOTE: If a file called 'uselavaanim' is in the texture pack, ClassicalSharp 0.99.2 onwards uses its built - in dynamic generation for the lava texture animation.\r\n" \ +"# NOTE : If a file called 'usewateranim' is in the texture pack, ClassicalSharp 0.99.5 onwards uses its built - in dynamic generation for the water texture animation.\r\n" \ +"\r\n" \ +"# fire\r\n" \ +"6 2 0 0 16 32 0" + +static bool TexPatcher_ClassicSelect(const String* path ) { + String name = *path; + Utils_UNSAFE_GetFilename(&name); + return Resources_FindTex(&name) != NULL; +} + +static ReturnCode TexPatcher_ClassicProcess(const String* path, struct Stream* data, void* obj) { + static const String guiClassicPng = String_FromConst("gui_classic.png"); + struct Stream* s = obj; + struct ResourceTexture* entry; + + String name = *path; + Utils_UNSAFE_GetFilename(&name); + if (String_CaselessEqualsConst(&name, "gui.png")) name = guiClassicPng; + + entry = Resources_FindTex(&name); + return ZipPatcher_WriteStream(s, entry, data); +} + +static ReturnCode TexPatcher_ClassicFiles(struct Stream* s) { + struct ZipState zip; + struct Stream src; + ReturnCode res; + + Stream_ReadonlyMemory(&src, Resources_Files[0].Data, Resources_Files[0].Len); + Zip_Init(&zip, &src); + + zip.Obj = s; + zip.SelectEntry = TexPatcher_ClassicSelect; + zip.ProcessEntry = TexPatcher_ClassicProcess; + return Zip_Extract(&zip); +} + +static bool TexPatcher_ModernSelect(const String* path) { + return + String_CaselessEqualsConst(path, "assets/minecraft/textures/environment/snow.png") || + String_CaselessEqualsConst(path, "assets/minecraft/textures/entity/chicken.png"); +} + +static ReturnCode TexPatcher_ModernProcess(const String* path, struct Stream* data, void* obj) { + struct Stream* s = obj; + struct ResourceTexture* entry; + + String name = *path; + Utils_UNSAFE_GetFilename(&name); + + entry = Resources_FindTex(&name); + return ZipPatcher_WriteStream(s, entry, data); +} + +static ReturnCode TexPatcher_ModernFiles(struct Stream* s) { + struct ZipState zip; + struct Stream src; + ReturnCode res; + + Stream_ReadonlyMemory(&src, Resources_Files[1].Data, Resources_Files[1].Len); + Zip_Init(&zip, &src); + + zip.Obj = s; + zip.SelectEntry = TexPatcher_ModernSelect; + zip.ProcessEntry = TexPatcher_ModernProcess; + return Zip_Extract(&zip); +} + +static ReturnCode TexPatcher_NewFiles(struct Stream* s) { + static const String guiPng = String_FromConst("gui.png"); + static const String animsTxt = String_FromConst("animations.txt"); + struct ResourceTexture* entry; + ReturnCode res; + + /* make our own animations.txt */ + entry = Resources_FindTex(&animsTxt); + res = ZipPatcher_WriteData(s, entry, ANIMS_TXT_CONTENTS, sizeof(ANIMS_TXT_CONTENTS) - 1); + if (res) return res; + + /* make ClassiCube gui.png */ + entry = Resources_FindTex(&guiPng); + res = ZipPatcher_WriteData(s, entry, Resources_Files[3].Data, Resources_Files[3].Len); + return res; +} + +static ReturnCode TexPatcher_WriteEntries(struct Stream* s) { + static const String guiPng = String_FromConst("gui.png"); + static const String animsTxt = String_FromConst("animations.txt"); + struct ResourceTexture* entry; + + uint32_t beg, end; + int i; + ReturnCode res; + + if ((res = TexPatcher_ClassicFiles(s))) return res; + if ((res = TexPatcher_ModernFiles(s))) return res; + if ((res = TexPatcher_NewFiles(s))) return res; + + if ((res = s->Position(s, &beg))) return res; + for (i = 0; i < Array_Elems(Resources_Textures); i++) { + if ((res = ZipPatcher_CentralDir(s, &Resources_Textures[i]))) return res; + } + + if ((res = s->Position(s, &end))) return res; + return ZipPatcher_EndOfCentralDir(s, beg, end); +} + +static void TexPatcher_MakeDefaultZip(void) { + const static String path = String_FromConst("texpacks/default.zip"); + struct Stream s; + int i; + ReturnCode res; + + res = Stream_CreateFile(&s, &path); + if (res) { + Launcher_ShowError(res, "creating default.zip"); + } else { + res = TexPatcher_WriteEntries(&s); + if (res) Launcher_ShowError(res, "making default.zip"); + + res = s.Close(&s); + if (res) Launcher_ShowError(res, "closing default.zip"); + } + + for (i = 0; i < Array_Elems(Resources_Files); i++) { + Mem_Free(Resources_Files[i].Data); + Resources_Files[i].Data = NULL; + } +} /*########################################################################################################################* @@ -192,7 +432,7 @@ struct ResourceMusic Resources_Music[7] = { *#########################################################################################################################*/ #define WAV_FourCC(a, b, c, d) (((uint32_t)a << 24) | ((uint32_t)b << 16) | ((uint32_t)c << 8) | (uint32_t)d) -static void Patcher_FixupWaveHeader(struct Stream* s, struct VorbisState* ctx) { +static void SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx) { uint8_t header[44]; uint32_t length; ReturnCode res; @@ -202,9 +442,9 @@ static void Patcher_FixupWaveHeader(struct Stream* s, struct VorbisState* ctx) { res = s->Seek(s, 0); if (res) { Launcher_ShowError(res, "seeking to .wav start"); return; } - Stream_SetU32_BE(&header[0], WAV_FourCC('R','I','F','F')); - Stream_SetU32_LE(&header[4], length - 8); - Stream_SetU32_BE(&header[8], WAV_FourCC('W','A','V','E')); + Stream_SetU32_BE(&header[0], WAV_FourCC('R','I','F','F')); + Stream_SetU32_LE(&header[4], length - 8); + Stream_SetU32_BE(&header[8], WAV_FourCC('W','A','V','E')); Stream_SetU32_BE(&header[12], WAV_FourCC('f','m','t',' ')); Stream_SetU32_LE(&header[16], 16); /* fmt chunk size */ Stream_SetU16_LE(&header[20], 1); /* PCM audio format */ @@ -221,7 +461,7 @@ static void Patcher_FixupWaveHeader(struct Stream* s, struct VorbisState* ctx) { if (res) Launcher_ShowError(res, "fixing .wav header"); } -static void Patcher_DecodeSound(struct Stream* s, struct VorbisState* ctx) { +static void SoundPatcher_DecodeAudio(struct Stream* s, struct VorbisState* ctx) { int16_t* samples; int count; ReturnCode res; @@ -247,7 +487,7 @@ static void Patcher_DecodeSound(struct Stream* s, struct VorbisState* ctx) { Mem_Free(samples); } -static void Patcher_SaveSound(struct ResourceSound* sound, struct AsyncRequest* req) { +static void SoundPatcher_Save(struct ResourceSound* sound, struct AsyncRequest* req) { String path; char pathBuffer[STRING_SIZE]; uint8_t buffer[OGG_BUFFER_SIZE]; struct Stream src, ogg, dst; @@ -264,14 +504,14 @@ static void Patcher_SaveSound(struct ResourceSound* sound, struct AsyncRequest* Ogg_MakeStream(&ogg, buffer, &src); ctx.Source = &ogg; - Patcher_DecodeSound(&dst, &ctx); - Patcher_FixupWaveHeader(&dst, &ctx); + SoundPatcher_DecodeAudio(&dst, &ctx); + SoundPatcher_FixupHeader(&dst, &ctx); res = dst.Close(&dst); if (res) Launcher_ShowError(res, "closing .wav file"); } -static void Patcher_SaveMusic(struct ResourceMusic* music, struct AsyncRequest* req) { +static void MusicPatcher_Save(struct ResourceMusic* music, struct AsyncRequest* req) { String path; char pathBuffer[STRING_SIZE]; ReturnCode res; @@ -302,7 +542,7 @@ CC_NOINLINE static void Fetcher_DownloadAudio(const char* name, const char* hash void Fetcher_Run(void) { String id, url; - int i, flags; + int i; if (Fetcher_Working) return; Fetcher_Error = 0; @@ -311,10 +551,9 @@ void Fetcher_Run(void) { Fetcher_Working = true; Fetcher_Completed = false; - flags = Resources_GetFetchFlags(); for (i = 0; i < Array_Elems(Resources_Files); i++) { - if (!(flags & Resources_Files[i].Flag)) continue; + if (Textures_AllExist) continue; id = String_FromReadonly(Resources_Files[i].Name); url = String_FromReadonly(Resources_Files[i].Url); @@ -322,11 +561,11 @@ void Fetcher_Run(void) { } for (i = 0; i < Array_Elems(Resources_Music); i++) { - if (Resources_Music[i].Exists) continue; + if (Resources_Music[i].Downloaded) continue; Fetcher_DownloadAudio(Resources_Music[i].Name, Resources_Music[i].Hash); } for (i = 0; i < Array_Elems(Resources_Sounds); i++) { - if (SoundsExist) continue; + if (Sounds_AllExist) continue; Fetcher_DownloadAudio(Resources_Sounds[i].Name, Resources_Sounds[i].Hash); } } @@ -357,13 +596,24 @@ CC_NOINLINE static bool Fetcher_Get(const String* id, struct AsyncRequest* req) return true; } +static void Fetcher_CheckFile(struct ResourceFile* file) { + String id = String_FromReadonly(file->Name); + struct AsyncRequest req; + if (!Fetcher_Get(&id, &req)) return; + + file->Downloaded = true; + file->Data = req.Data; + file->Len = req.Size; + /* don't free request */ +} + static void Fetcher_CheckMusic(struct ResourceMusic* music) { String id = String_FromReadonly(music->Name); struct AsyncRequest req; if (!Fetcher_Get(&id, &req)) return; - music->Exists = true; - Patcher_SaveMusic(music, &req); + music->Downloaded = true; + MusicPatcher_Save(music, &req); ASyncRequest_Free(&req); } @@ -372,16 +622,23 @@ static void Fetcher_CheckSound(struct ResourceSound* sound) { struct AsyncRequest req; if (!Fetcher_Get(&id, &req)) return; - Patcher_SaveSound(sound, &req); + SoundPatcher_Save(sound, &req); ASyncRequest_Free(&req); } /* TODO: Implement this.. */ +/* TODO: How expensive is it to constantly do 'Get' over and make all these strings */ void Fetcher_Update(void) { int i; + for (i = 0; i < Array_Elems(Resources_Files); i++) { + if (Resources_Files[i].Downloaded) continue; + Fetcher_CheckFile(&Resources_Files[i]); + } + //if (Resources_Files[3].Data) TexPatcher_MakeDefaultZip(); + for (i = 0; i < Array_Elems(Resources_Music); i++) { - if (Resources_Music[i].Exists) continue; + if (Resources_Music[i].Downloaded) continue; Fetcher_CheckMusic(&Resources_Music[i]); } @@ -391,4 +648,4 @@ void Fetcher_Update(void) { if (Fetcher_Downloaded != Resources_Count) return; Fetcher_Finish(); -} \ No newline at end of file +} diff --git a/src/Resources.h b/src/Resources.h index 8f988e913..9620baeef 100644 --- a/src/Resources.h +++ b/src/Resources.h @@ -14,15 +14,16 @@ extern struct ResourceFile { const char* Name; const char* Url; uint16_t Size; - uint8_t Flag; bool Downloaded; + /* downloaded archive */ + uint8_t* Data; uint32_t Len; } Resources_Files[4]; extern struct ResourceTexture { const char* Filename; - uint8_t Flags; - bool Exists; -} Resources_Textures[19]; + /* zip data */ + uint32_t Size, Offset, Crc32; +} Resources_Textures[20]; extern struct ResourceSound { const char* Name; @@ -33,16 +34,17 @@ extern struct ResourceMusic { const char* Name; const char* Hash; uint16_t Size; - bool Exists; + bool Downloaded; } Resources_Music[7]; -extern bool SoundsExist; +/* Whether all textures exist. */ +extern bool Textures_AllExist; +/* Whether all sounds exist. */ +extern bool Sounds_AllExist; /* Number of resources that need to be downloaded. */ extern int Resources_Count; /* Total size of resources that need to be downloaded. */ extern int Resources_Size; -/* Returns flags of files that need to be fetched. */ -int Resources_GetFetchFlags(void); /* Checks existence of all assets. */ void Resources_CheckExistence(void); @@ -57,8 +59,10 @@ extern int Fetcher_StatusCode; /* Error (if any) that occurs when downloaded resources. */ extern ReturnCode Fetcher_Error; -/* Starts asynchronous download of required resources. */ +/* Starts asynchronous download of missing resources. */ void Fetcher_Run(void); +/* Checks if any resources have finished downloading. */ +/* If any have, performs required patching and saving. */ void Fetcher_Update(void); #endif diff --git a/src/Stream.c b/src/Stream.c index 560721d6d..cf4fad0e6 100644 --- a/src/Stream.c +++ b/src/Stream.c @@ -3,6 +3,7 @@ #include "Funcs.h" #include "ErrorHandler.h" #include "Errors.h" +#include "Utils.h" /*########################################################################################################################* *---------------------------------------------------------Stream----------------------------------------------------------* @@ -362,6 +363,32 @@ void Stream_ReadonlyBuffered(struct Stream* s, struct Stream* source, void* data } +/*########################################################################################################################* +*-----------------------------------------------------CRC32Stream---------------------------------------------------------* +*#########################################################################################################################*/ +static ReturnCode Stream_Crc32Write(struct Stream* stream, const uint8_t* data, uint32_t count, uint32_t* modified) { + struct Stream* source; + uint32_t i, crc32 = stream->Meta.CRC32.CRC32; + + /* TODO: Optimise this calculation */ + for (i = 0; i < count; i++) { + crc32 = Utils_Crc32Table[(crc32 ^ data[i]) & 0xFF] ^ (crc32 >> 8); + } + stream->Meta.CRC32.CRC32 = crc32; + + source = stream->Meta.CRC32.Source; + return source->Write(source, data, count, modified); +} + +void Stream_WriteonlyCrc32(struct Stream* s, struct Stream* source) { + Stream_Init(s); + s->Write = Stream_Crc32Write; + + s->Meta.CRC32.Source = source; + s->Meta.CRC32.CRC32 = 0xFFFFFFFFUL; +} + + /*########################################################################################################################* *-------------------------------------------------Read/Write primitives---------------------------------------------------* *#########################################################################################################################*/ diff --git a/src/Stream.h b/src/Stream.h index 719b5f053..614592eef 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -67,6 +67,10 @@ CC_API void Stream_WriteonlyMemory(struct Stream* s, void* data, uint32_t len); /* Wraps another Stream, reading through an intermediary buffer. (Useful for files, since each read call is expensive) */ CC_API void Stream_ReadonlyBuffered(struct Stream* s, struct Stream* source, void* data, uint32_t size); +/* Wraps another Stream, calculating a running CRC32 as data is written. */ +/* To get the final CRC32, xor it with 0xFFFFFFFFUL */ +void Stream_WriteonlyCrc32(struct Stream* s, struct Stream* source); + /* Reads a little-endian 16 bit unsigned integer from memory. */ uint16_t Stream_GetU16_LE(const uint8_t* data); /* Reads a big-endian 16 bit unsigned integer from memory. */ diff --git a/src/TexturePack.c b/src/TexturePack.c index 8df69bda3..10d0d1d75 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -621,9 +621,11 @@ void TextureCache_SetLastModified(const String* url, const TimeMS* lastModified) /*########################################################################################################################* *-------------------------------------------------------TexturePack-------------------------------------------------------* *#########################################################################################################################*/ -static void TexturePack_ProcessZipEntry(const String* path, struct Stream* stream, struct ZipEntry* entry) { - String name = *path; Utils_UNSAFE_GetFilename(&name); +static ReturnCode TexturePack_ProcessZipEntry(const String* path, struct Stream* stream, void* obj) { + String name = *path; + Utils_UNSAFE_GetFilename(&name); Event_RaiseEntry(&TextureEvents_FileChanged, stream, &name); + return 0; } static ReturnCode TexturePack_ExtractZip(struct Stream* stream) { diff --git a/src/Window.c b/src/Window.c index 36075c8a9..e3e822fdc 100644 --- a/src/Window.c +++ b/src/Window.c @@ -2115,6 +2115,8 @@ static void Window_ConnectEvents(void) { target = GetApplicationEventTarget(); /* TODO: Use EventTargetRef target = GetWindowEventTarget(windowRef); instead?? */ + /* need WindowEventTargetRef, otherwise message boxes don't work */ + /* but if use WindowEventTargetRef, can't click quit/move buttons anymore */ res = InstallEventHandler(target, NewEventHandlerUPP(Window_EventHandler), Array_Elems(eventTypes), eventTypes, NULL, NULL); if (res) ErrorHandler_Fail2(res, "Connecting events");