diff --git a/src/Audio.c b/src/Audio.c index 3c3a042d8..435f37579 100644 --- a/src/Audio.c +++ b/src/Audio.c @@ -12,11 +12,15 @@ #include "Stream.h" #include "Utils.h" #include "Options.h" +#include "Deflate.h" #ifdef CC_BUILD_ANDROID /* TODO: Refactor maybe to not rely on checking WinInfo.Handle != NULL */ #include "Window.h" #endif + int Audio_SoundsVolume, Audio_MusicVolume; +const cc_string Sounds_ZipPathMC = String_FromConst("audio/default.zip"); +const cc_string Sounds_ZipPathCC = String_FromConst("audio/classicube.zip"); static const cc_string audio_dir = String_FromConst("audio"); struct Sound { @@ -91,7 +95,12 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { if ((res = Audio_AllocChunks(size, &snd->data, 1))) return res; snd->size = size; - return Stream_Read(stream, (cc_uint8*)snd->data, size); + res = Stream_Read(stream, (cc_uint8*)snd->data, size); + + #ifdef CC_BUILD_BIGENDIAN + Utils_SwapEndian16((cc_int16*)snd->data, size / 2); + #endif + return res; } /* Skip over unhandled data */ @@ -99,30 +108,18 @@ static cc_result Sound_ReadWaveData(struct Stream* stream, struct Sound* snd) { } } -static cc_result Sound_ReadWave(const cc_string* path, struct Sound* snd) { - struct Stream stream; - cc_result res; - - res = Stream_OpenFile(&stream, path); - if (res) return res; - res = Sound_ReadWaveData(&stream, snd); - - /* No point logging error for closing readonly file */ - (void)stream.Close(&stream); - return res; -} - -static struct SoundGroup* Soundboard_Find(struct Soundboard* board, const cc_string* name) { +static struct SoundGroup* Soundboard_FindGroup(struct Soundboard* board, const cc_string* name) { struct SoundGroup* groups = board->groups; int i; - for (i = 0; i < SOUND_COUNT; i++) { + for (i = 0; i < SOUND_COUNT; i++) + { if (String_CaselessEqualsConst(name, Sound_Names[i])) return &groups[i]; } return NULL; } -static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file) { +static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName, const cc_string* file, struct Stream* stream) { struct SoundGroup* group; struct Sound* snd; cc_string name = *file; @@ -139,7 +136,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName name = String_UNSAFE_SubstringAt(&name, boardName->length); name = String_UNSAFE_Substring(&name, 0, name.length - 1); - group = Soundboard_Find(board, &name); + group = Soundboard_FindGroup(board, &name); if (!group) { Chat_Add1("&cUnknown sound group '%s'", &name); return; } @@ -148,7 +145,7 @@ static void Soundboard_Load(struct Soundboard* board, const cc_string* boardName } snd = &group->sounds[group->count]; - res = Sound_ReadWave(file, snd); + res = Sound_ReadWaveData(stream, snd); if (res) { Logger_SysWarn2(res, "decoding", file); @@ -219,12 +216,29 @@ static void Audio_PlayBlockSound(void* obj, IVec3 coords, BlockID old, BlockID n } } -static void Sounds_LoadFile(const cc_string* path, void* obj) { +static cc_bool SelectZipEntry(const cc_string* path) { return true; } +static cc_result ProcessZipEntry(const cc_string* path, struct Stream* stream, struct ZipEntry* source) { static const cc_string dig = String_FromConst("dig_"); static const cc_string step = String_FromConst("step_"); - Soundboard_Load(&digBoard, &dig, path); - Soundboard_Load(&stepBoard, &step, path); + Soundboard_Load(&digBoard, &dig, path, stream); + Soundboard_Load(&stepBoard, &step, path, stream); + return 0; +} + +static cc_result Sounds_ExtractZip(const cc_string* path) { + struct Stream stream; + cc_result res; + + res = Stream_OpenFile(&stream, path); + if (res) { Logger_SysWarn2(res, "opening", path); return res; } + + res = Zip_Extract(&stream, SelectZipEntry, ProcessZipEntry); + if (res) Logger_SysWarn2(res, "extracting", path); + + /* No point logging error for closing readonly file */ + (void)stream.Close(&stream); + return res; } /* TODO this is a pretty terrible solution */ @@ -269,6 +283,7 @@ static void InitWebSounds(void) { static cc_bool sounds_loaded; static void Sounds_Start(void) { + cc_result res; if (!AudioBackend_Init()) { AudioBackend_Free(); Audio_SoundsVolume = 0; @@ -280,7 +295,9 @@ static void Sounds_Start(void) { #ifdef CC_BUILD_WEBAUDIO InitWebSounds(); #else - Directory_Enum(&audio_dir, NULL, Sounds_LoadFile); + res = Sounds_ExtractZip(&Sounds_ZipPathMC); + if (res == ReturnCode_FileNotFound) + Sounds_ExtractZip(&Sounds_ZipPathCC); #endif } @@ -335,7 +352,7 @@ static cc_result Music_Buffer(cc_int16* data, int maxSamples, struct VorbisState static cc_result Music_PlayOgg(struct Stream* source) { struct OggState ogg; - struct VorbisState vorbis = { 0 }; + struct VorbisState vorbis; int channels, sampleRate, volume; int chunkSize, samplesPerSecond; @@ -344,6 +361,7 @@ static cc_result Music_PlayOgg(struct Stream* source) { cc_result res; Ogg_Init(&ogg, source); + Vorbis_Init(&vorbis); vorbis.source = &ogg; if ((res = Vorbis_DecodeHeaders(&vorbis))) goto cleanup; diff --git a/src/Audio.h b/src/Audio.h index 416a42b36..e054d1c2d 100644 --- a/src/Audio.h +++ b/src/Audio.h @@ -34,6 +34,8 @@ extern int Audio_SoundsVolume; /* Volume music is played at, from 0-100. */ /* NOTE: Use Audio_SetMusic, don't change this directly. */ extern int Audio_MusicVolume; +extern const cc_string Sounds_ZipPathMC; +extern const cc_string Sounds_ZipPathCC; void Audio_SetMusic(int volume); void Audio_SetSounds(int volume); diff --git a/src/ClassiCube.vcxproj.filters b/src/ClassiCube.vcxproj.filters index 86b40ac23..aef0be6f9 100644 --- a/src/ClassiCube.vcxproj.filters +++ b/src/ClassiCube.vcxproj.filters @@ -620,9 +620,6 @@ Source Files\Platform - - Source Files\Window - Source Files\Window @@ -713,6 +710,9 @@ Source Files\Graphics + + Source Files\Audio + diff --git a/src/LScreens.c b/src/LScreens.c index 539df3b5f..a4a5e25b5 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -1012,9 +1012,11 @@ static void CheckResourcesScreen_Next(void* w) { } static void CheckResourcesScreen_AddWidgets(struct CheckResourcesScreen* s) { + const char* line1_msg = Resources_MissingRequired ? "Some required resources weren't found" + : "Some optional resources weren't found"; s->lblStatus.small = true; - LLabel_Add(s, &s->lblLine1, "Some required resources weren't found", cres_lblLine1); + LLabel_Add(s, &s->lblLine1, line1_msg, cres_lblLine1); LLabel_Add(s, &s->lblLine2, "Okay to download?", cres_lblLine2); LLabel_Add(s, &s->lblStatus, "", cres_lblStatus); @@ -1030,7 +1032,7 @@ static void CheckResourcesScreen_Activated(struct LScreen* s_) { float size; CheckResourcesScreen_AddWidgets(s); - size = Resources_Size / 1024.0f; + size = Resources_MissingSize / 1024.0f; String_InitArray(str, buffer); String_Format1(&str, "&eDownload size: %f2 megabytes", &size); LLabel_SetText(&s->lblStatus, &str); @@ -1121,7 +1123,7 @@ static void FetchResourcesScreen_UpdateStatus(struct FetchResourcesScreen* s, in String_InitArray(str, strBuffer); count = Fetcher_Downloaded + 1; - String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_Count); + String_Format3(&str, "&eFetching %c.. (%i/%i)", name, &count, &Resources_MissingCount); if (String_Equals(&str, &s->lblStatus.text)) return; LLabel_SetText(&s->lblStatus, &str); diff --git a/src/Launcher.c b/src/Launcher.c index 8293dc74e..79d80cf83 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -260,7 +260,7 @@ void Launcher_Run(void) { #ifdef CC_BUILD_RESOURCES Resources_CheckExistence(); - if (Resources_Count) { + if (Resources_MissingCount) { CheckResourcesScreen_SetActive(); } else { MainScreen_SetActive(); diff --git a/src/Resources.c b/src/Resources.c index a81710053..167fc7aba 100644 --- a/src/Resources.c +++ b/src/Resources.c @@ -14,6 +14,7 @@ #include "LWeb.h" #include "Http.h" #include "Game.h" +#include "Audio.h" /* Represents a set of assets/resources */ /* E.g. music set, sounds set, textures set */ @@ -28,9 +29,12 @@ struct AssetSet { const char* (*GetRequestName)(int reqID); /* Checks if any assets have been downloaded, and processes them if so */ void (*CheckStatus)(void); + /* Resets state and frees and allocated memory */ + void (*ResetState)(void); }; -int Resources_Count, Resources_Size; +int Resources_MissingCount, Resources_MissingSize; +cc_bool Resources_MissingRequired; union ResourceValue { cc_uint8* data; @@ -44,6 +48,10 @@ struct ResourceZipEntry { union ResourceValue value; cc_uint32 offset, crc32; }; +#define RESOURCE_TYPE_DATA 1 +#define RESOURCE_TYPE_PNG 2 +#define RESOURCE_TYPE_CONST 3 +#define RESOURCE_TYPE_SOUND 4 static CC_NOINLINE cc_bool Fetcher_Get(int reqID, struct HttpRequest* item); CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* name); @@ -51,223 +59,44 @@ static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* /*########################################################################################################################* -*---------------------------------------------------------Music assets----------------------------------------------------* +*------------------------------------------------------Utility functions -------------------------------------------------* *#########################################################################################################################*/ -static struct MusicAsset { - const char* name; - const char* hash; - short size; - cc_bool downloaded; - int reqID; -} musicAssets[] = { - { "calm1.ogg", "50a59a4f56e4046701b758ddbb1c1587efa4cadf", 2472 }, - { "calm2.ogg", "74da65c99aa578486efa7b69983d3533e14c0d6e", 1931 }, - { "calm3.ogg", "14ae57a6bce3d4254daa8be2b098c2d99743cc3f", 2181 }, - { "hal1.ogg", "df1ff11b79757432c5c3f279e5ecde7b63ceda64", 1926 }, - { "hal2.ogg", "ceaaaa1d57dfdfbb0bd4da5ea39628b42897a687", 1714 }, - { "hal3.ogg", "dd85fb564e96ee2dbd4754f711ae9deb08a169f9", 1879 }, - { "hal4.ogg", "5e7d63e75c6e042f452bc5e151276911ef92fed8", 2499 } -}; - -static void MusicAssets_CheckExistence(void) { - cc_string path; char pathBuffer[FILENAME_SIZE]; - int i; - String_InitArray(path, pathBuffer); - - for (i = 0; i < Array_Elems(musicAssets); i++) - { - path.length = 0; - String_Format1(&path, "audio/%c", musicAssets[i].name); - - musicAssets[i].downloaded = File_Exists(&path); - } -} - -static void MusicAssets_CountMissing(void) { - int i; - for (i = 0; i < Array_Elems(musicAssets); i++) - { - if (musicAssets[i].downloaded) continue; - - Resources_Size += musicAssets[i].size; - Resources_Count++; - } -} - - -/*########################################################################################################################* -*-----------------------------------------------------Music asset fetching -----------------------------------------------* -*#########################################################################################################################*/ -CC_NOINLINE static int MusicAsset_Download(const char* hash) { - cc_string url; char urlBuffer[URL_MAX_SIZE]; - - String_InitArray(url, urlBuffer); - String_Format3(&url, "https://resources.download.minecraft.net/%r%r/%c", - &hash[0], &hash[1], hash); - return Http_AsyncGetData(&url, 0); -} - -static void MusicAssets_DownloadAssets(void) { - int i; - for (i = 0; i < Array_Elems(musicAssets); i++) - { - if (musicAssets[i].downloaded) continue; - musicAssets[i].reqID = MusicAsset_Download(musicAssets[i].hash); - } -} - -static const char* MusicAssets_GetRequestName(int reqID) { - int i; - for (i = 0; i < Array_Elems(musicAssets); i++) - { - if (reqID == musicAssets[i].reqID) return musicAssets[i].name; - } - return NULL; -} - - -/*########################################################################################################################* -*----------------------------------------------------Music asset processing ----------------------------------------------* -*#########################################################################################################################*/ -static void MusicAsset_Save(const char* name, struct HttpRequest* req) { - cc_string path; char pathBuffer[STRING_SIZE]; +static void ZipFile_InspectEntries(const cc_string* path, Zip_SelectEntry selector) { + struct Stream stream; cc_result res; - String_InitArray(path, pathBuffer); - String_Format1(&path, "audio/%c", name); + res = Stream_OpenFile(&stream, path); + if (res == ReturnCode_FileNotFound) return; + if (res) { Logger_SysWarn2(res, "opening", path); return; } - res = Stream_WriteAllTo(&path, req->data, req->size); - if (res) Logger_SysWarn(res, "saving music file"); + res = Zip_Extract(&stream, selector, NULL); + if (res) Logger_SysWarn2(res, "inspecting", path); + + /* No point logging error for closing readonly file */ + (void)stream.Close(&stream); } -static void MusicAsset_Check(struct MusicAsset* music) { - struct HttpRequest item; - if (!Fetcher_Get(music->reqID, &item)) return; +static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source) { + cc_uint32 size = source->UncompressedSize; + e->value.data = Mem_TryAlloc(size, 1); + e->size = size; - music->downloaded = true; - MusicAsset_Save(music->name, &item); - HttpRequest_Free(&item); -} - -static void MusicAssets_CheckStatus(void) { - int i; - for (i = 0; i < Array_Elems(musicAssets); i++) - { - if (musicAssets[i].downloaded) continue; - MusicAsset_Check(&musicAssets[i]); - } -} - -static const struct AssetSet mccMusicAssetSet = { - MusicAssets_CheckExistence, - MusicAssets_CountMissing, - MusicAssets_DownloadAssets, - MusicAssets_GetRequestName, - MusicAssets_CheckStatus -}; - - -/*########################################################################################################################* -*---------------------------------------------------------Sound assets----------------------------------------------------* -*#########################################################################################################################*/ -static struct SoundAsset { - const char* name; - const char* hash; - int reqID; -} soundAssets[] = { - { "dig_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "dig_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "dig_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, - { "dig_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, - { "dig_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, - { "dig_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, - { "dig_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, - { "dig_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, - { "dig_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "dig_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "dig_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, - { "dig_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, - { "dig_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, - { "dig_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4", "dc66978374a46ab2b87db6472804185824868095" }, - { "dig_glass1", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2", "87c47bda3645c68f18a49e83cbf06e5302d087ff" }, - { "dig_glass3", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" }, - - { "step_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "step_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "step_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, - { "step_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, - { "step_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, - { "step_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, - { "step_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, - { "step_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, - { "step_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "step_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "step_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, - { "step_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, - { "step_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, - { "step_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4", "dc66978374a46ab2b87db6472804185824868095" } -}; -static cc_bool allSoundsExist; - -static void SoundAssets_CheckExistence(void) { - cc_string path; char pathBuffer[FILENAME_SIZE]; - int i; - String_InitArray(path, pathBuffer); - - for (i = 0; i < Array_Elems(soundAssets); i++) - { - path.length = 0; - String_Format1(&path, "audio/%c.wav", soundAssets[i].name); - - if (File_Exists(&path)) continue; - allSoundsExist = false; - return; - } - allSoundsExist = true; -} - -static void SoundAssets_CountMissing(void) { - if (allSoundsExist) return; - - Resources_Count += Array_Elems(soundAssets); - Resources_Size += 417; -} - -/*########################################################################################################################* -*-----------------------------------------------------Sound asset fetching -----------------------------------------------* -*#########################################################################################################################*/ -#define SoundAsset_Download(hash) MusicAsset_Download(hash) - -static void SoundAssets_DownloadAssets(void) { - int i; - for (i = 0; i < Array_Elems(soundAssets); i++) - { - if (allSoundsExist) continue; - soundAssets[i].reqID = SoundAsset_Download(soundAssets[i].hash); - } -} - -static const char* SoundAssets_GetRequestName(int reqID) { - int i; - for (i = 0; i < Array_Elems(soundAssets); i++) - { - if (reqID == soundAssets[i].reqID) return soundAssets[i].name; - } - return NULL; + if (!e->value.data) return ERR_OUT_OF_MEMORY; + return Stream_Read(data, e->value.data, size); } /*########################################################################################################################* -*----------------------------------------------------Sound asset processing ----------------------------------------------* +*-----------------------------------------------------Sound asset writing ------------------------------------------------* *#########################################################################################################################*/ #define WAV_FourCC(a, b, c, d) (((cc_uint32)a << 24) | ((cc_uint32)b << 16) | ((cc_uint32)c << 8) | (cc_uint32)d) #define WAV_HDR_SIZE 44 /* Fixes up the .WAV header after having written all samples */ -static void SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx, cc_uint32 len) { +static cc_result SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx, cc_uint32 offset, cc_uint32 len) { cc_uint8 header[WAV_HDR_SIZE]; - cc_result res = s->Seek(s, 0); - if (res) { Logger_SysWarn(res, "seeking to .wav start"); return; } + cc_result res = s->Seek(s, offset); + if (res) return res; Stream_SetU32_BE(header + 0, WAV_FourCC('R','I','F','F')); Stream_SetU32_LE(header + 4, len - 8); @@ -284,186 +113,80 @@ static void SoundPatcher_FixupHeader(struct Stream* s, struct VorbisState* ctx, Stream_SetU32_BE(header + 36, WAV_FourCC('d','a','t','a')); Stream_SetU32_LE(header + 40, len - WAV_HDR_SIZE); - res = Stream_Write(s, header, WAV_HDR_SIZE); - if (res) Logger_SysWarn(res, "fixing .wav header"); + return Stream_Write(s, header, WAV_HDR_SIZE); } /* Decodes all samples, then produces a .WAV file from them */ -static void SoundPatcher_WriteWav(struct Stream* s, struct VorbisState* ctx) { +static cc_result SoundPatcher_WriteWav(struct Stream* s, struct VorbisState* ctx) { cc_int16* samples; + cc_uint32 begOffset; cc_uint32 len = WAV_HDR_SIZE; cc_result res; int count; - /* ctx is all 0, so reuse here for empty header */ - res = Stream_Write(s, (const cc_uint8*)ctx, WAV_HDR_SIZE); - if (res) { Logger_SysWarn(res, "writing .wav header"); return; } + if ((res = s->Position(s, &begOffset))) return res; - res = Vorbis_DecodeHeaders(ctx); - if (res) { Logger_SysWarn(res, "decoding .ogg header"); return; } + /* reuse context here for a temp garbage header */ + if ((res = Stream_Write(s, (const cc_uint8*)ctx, WAV_HDR_SIZE))) return res; + if ((res = Vorbis_DecodeHeaders(ctx))) return res; samples = (cc_int16*)Mem_TryAlloc(ctx->blockSizes[1] * ctx->channels, 2); - if (!samples) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating .ogg samples"); return; } + if (!samples) return ERR_OUT_OF_MEMORY; for (;;) { res = Vorbis_DecodeFrame(ctx); if (res == ERR_END_OF_STREAM) { /* reached end of samples, so done */ - SoundPatcher_FixupHeader(s, ctx, len); break; + res = SoundPatcher_FixupHeader(s, ctx, begOffset, len); + break; } - if (res) { Logger_SysWarn(res, "decoding .ogg"); break; } + if (res) break; count = Vorbis_OutputFrame(ctx, samples); len += count * 2; - /* TODO: Do we need to account for big endian */ - res = Stream_Write(s, samples, count * 2); - if (res) { Logger_SysWarn(res, "writing samples"); break; } + +#ifdef CC_BUILD_BIGENDIAN + Utils_SwapEndian16(samples, count); +#endif + res = Stream_Write(s, (cc_uint8*)samples, count * 2); + if (res) break; } + Mem_Free(samples); + if (!res) res = s->Seek(s, begOffset + len); + return res; } /* Converts an OGG sound to a WAV sound for faster decoding later */ -static void SoundPatcher_Save(const char* name, struct HttpRequest* req) { - cc_string path; char pathBuffer[STRING_SIZE]; - struct OggState ogg; - struct Stream src, dst; - struct VorbisState* ctx; - cc_result res; - - ctx = (struct VorbisState*)Mem_TryAllocCleared(1, sizeof(struct VorbisState)); - if (!ctx) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating memory"); return; } - - Stream_ReadonlyMemory(&src, req->data, req->size); - String_InitArray(path, pathBuffer); - String_Format1(&path, "audio/%c.wav", name); - - res = Stream_CreateFile(&dst, &path); - if (res) { Logger_SysWarn(res, "creating .wav file"); return; } - - Ogg_Init(&ogg, &src); - ctx->source = &ogg; - SoundPatcher_WriteWav(&dst, ctx); - - res = dst.Close(&dst); - if (res) Logger_SysWarn(res, "closing .wav file"); - - Vorbis_Free(ctx); - Mem_Free(ctx); -} - - -static void SoundAsset_Check(const struct SoundAsset* sound) { - struct HttpRequest item; - if (!Fetcher_Get(sound->reqID, &item)) return; - - SoundPatcher_Save(sound->name, &item); - HttpRequest_Free(&item); -} - -static void SoundAssets_CheckStatus(void) { - int i; - for (i = 0; i < Array_Elems(soundAssets); i++) - { - SoundAsset_Check(&soundAssets[i]); - } -} - -static const struct AssetSet mccSoundAssetSet = { - SoundAssets_CheckExistence, - SoundAssets_CountMissing, - SoundAssets_DownloadAssets, - SoundAssets_GetRequestName, - SoundAssets_CheckStatus -}; - - -/*########################################################################################################################* -*------------------------------------------------------CC texture assets--------------------------------------------------* -*#########################################################################################################################*/ -static const cc_string ccTexPack = String_FromConst("texpacks/classicube.zip"); -static cc_bool ccTexturesExist, ccTexturesDownloaded; -static int ccTexturesReqID; - -static void CCTextures_CheckExistence(void) { - ccTexturesExist = File_Exists(&ccTexPack); -} - -static void CCTextures_CountMissing(void) { - if (ccTexturesExist) return; - - Resources_Count++; - Resources_Size += 83; -} - - -/*########################################################################################################################* -*--------------------------------------------------CC texture assets fetching --------------------------------------------* -*#########################################################################################################################*/ -static void CCTextures_DownloadAssets(void) { - static cc_string url = String_FromConst(RESOURCE_SERVER "/default.zip"); - if (ccTexturesExist) return; - - ccTexturesReqID = Http_AsyncGetData(&url, 0); -} - -static const char* CCTextures_GetRequestName(int reqID) { - return reqID == ccTexturesReqID ? "ClassiCube textures" : NULL; -} - - -/*########################################################################################################################* -*-------------------------------------------------CC texture assets processing -------------------------------------------* -*#########################################################################################################################*/ -#ifdef CC_BUILD_MOBILE -/* Android needs the touch.png */ -/* TODO: Unify both android and desktop platforms to both just extract from default.zip */ -static cc_bool CCTextures_SelectEntry(const cc_string* path) { - return String_CaselessEqualsConst(path, "touch.png"); -} -static cc_result CCTextures_ProcessEntry(const cc_string* path, struct Stream* data, struct ZipEntry* source) { - struct ResourceZipEntry* e = ZipEntries_Find(path); - return ZipEntry_ExtractData(e, data, source); -} - -static cc_result CCTextures_ExtractZip(struct HttpRequest* req) { +static cc_result SoundPatcher_Save(struct Stream* s, struct ResourceZipEntry* e) { + struct OggState* ogg = NULL; + struct VorbisState* ctx = NULL; struct Stream src; - Stream_WriteAllTo(&ccTexPack, req->data, req->size); - Stream_ReadonlyMemory(&src, req->data, req->size); - - return Zip_Extract(&src, - CCTextures_SelectEntry, CCTextures_ProcessEntry); -} -#else -static cc_result CCTextures_ExtractZip(struct HttpRequest* req) { - return Stream_WriteAllTo(&ccTexPack, req->data, req->size); -} -#endif - -static void CCTextures_CheckStatus(void) { - struct HttpRequest item; cc_result res; - if (ccTexturesDownloaded) return; - if (!Fetcher_Get(ccTexturesReqID, &item)) return; + ogg = (struct OggState*)Mem_TryAlloc(1, sizeof(struct OggState)); + if (!ogg) { res = ERR_OUT_OF_MEMORY; goto cleanup; } - ccTexturesDownloaded = true; - res = CCTextures_ExtractZip(&item); - if (res) Logger_SysWarn(res, "saving ClassiCube textures"); + ctx = (struct VorbisState*)Mem_TryAlloc(1, sizeof(struct VorbisState)); + if (!ctx) { res = ERR_OUT_OF_MEMORY; goto cleanup; } - HttpRequest_Free(&item); + Stream_ReadonlyMemory(&src, e->value.data, e->size); + + Ogg_Init(ogg, &src); + Vorbis_Init(ctx); + ctx->source = ogg; + res = SoundPatcher_WriteWav(s, ctx); + +cleanup: + if (ctx) Vorbis_Free(ctx); + Mem_Free(ctx); + Mem_Free(ogg); + return res; } -static const struct AssetSet ccTexsAssetSet = { - CCTextures_CheckExistence, - CCTextures_CountMissing, - CCTextures_DownloadAssets, - CCTextures_GetRequestName, - CCTextures_CheckStatus -}; - /*########################################################################################################################* -*---------------------------------------------------------Zip writer------------------------------------------------------* +*------------------------------------------------------Zip entry writer---------------------------------------------------* *#########################################################################################################################*/ static void GetCurrentZipDate(int* modTime, int* modDate) { struct DateTime now; @@ -597,14 +320,450 @@ static cc_result ZipWriter_WritePng(struct Stream* dst, struct ResourceZipEntry* return ZipWriter_FixupLocalFile(dst, e); } +static cc_result ZipWriter_WriteWav(struct Stream* dst, struct ResourceZipEntry* e) { + cc_result res; + + if ((res = ZipWriter_LocalFile(dst, e))) return res; + if ((res = SoundPatcher_Save(dst, e))) return res; + return ZipWriter_FixupLocalFile(dst, e); +} + + +/*########################################################################################################################* +*------------------------------------------------------Zip file writer----------------------------------------------------* +*#########################################################################################################################*/ +static cc_result ZipFile_WriteEntries(struct Stream* s, struct ResourceZipEntry* entries, int numEntries) { + struct ResourceZipEntry* e; + cc_uint32 beg, end; + int i; + cc_result res; + + for (i = 0; i < numEntries; i++) + { + e = &entries[i]; + + if (e->type == RESOURCE_TYPE_PNG) { + if ((res = ZipWriter_WritePng(s, e))) return res; + } else if (e->type == RESOURCE_TYPE_SOUND) { + if ((res = ZipWriter_WriteWav(s, e))) return res; + } else { + if ((res = ZipWriter_WriteData(s, e))) return res; + } + } + + if ((res = s->Position(s, &beg))) return res; + for (i = 0; i < numEntries; i++) + { + if ((res = ZipWriter_CentralDir(s, &entries[i]))) return res; + } + + if ((res = s->Position(s, &end))) return res; + return ZipWriter_EndOfCentralDir(s, numEntries, beg, end); +} + +static void ZipFile_Create(const cc_string* path, struct ResourceZipEntry* entries, int numEntries) { + struct Stream s; + cc_result res; + + res = Stream_CreateFile(&s, path); + if (res) { + Logger_SysWarn2(res, "creating", path); return; + } + + res = ZipFile_WriteEntries(&s, entries, numEntries); + if (res) Logger_SysWarn2(res, "making", path); + + res = s.Close(&s); + if (res) Logger_SysWarn2(res, "closing", path); +} + + +/*########################################################################################################################* +*---------------------------------------------------------Music assets----------------------------------------------------* +*#########################################################################################################################*/ +static struct MusicAsset { + const char* name; + const char* hash; + short size; + cc_bool downloaded; + int reqID; +} musicAssets[] = { + { "calm1.ogg", "50a59a4f56e4046701b758ddbb1c1587efa4cadf", 2472 }, + { "calm2.ogg", "74da65c99aa578486efa7b69983d3533e14c0d6e", 1931 }, + { "calm3.ogg", "14ae57a6bce3d4254daa8be2b098c2d99743cc3f", 2181 }, + { "hal1.ogg", "df1ff11b79757432c5c3f279e5ecde7b63ceda64", 1926 }, + { "hal2.ogg", "ceaaaa1d57dfdfbb0bd4da5ea39628b42897a687", 1714 }, + { "hal3.ogg", "dd85fb564e96ee2dbd4754f711ae9deb08a169f9", 1879 }, + { "hal4.ogg", "5e7d63e75c6e042f452bc5e151276911ef92fed8", 2499 } +}; + +static void MusicAssets_CheckExistence(void) { + cc_string path; char pathBuffer[FILENAME_SIZE]; + int i; + String_InitArray(path, pathBuffer); + + for (i = 0; i < Array_Elems(musicAssets); i++) + { + path.length = 0; + String_Format1(&path, "audio/%c", musicAssets[i].name); + + musicAssets[i].downloaded = File_Exists(&path); + } +} + +static void MusicAssets_CountMissing(void) { + int i; + for (i = 0; i < Array_Elems(musicAssets); i++) + { + if (musicAssets[i].downloaded) continue; + + Resources_MissingSize += musicAssets[i].size; + Resources_MissingCount++; + } +} + + +/*########################################################################################################################* +*-----------------------------------------------------Music asset fetching -----------------------------------------------* +*#########################################################################################################################*/ +CC_NOINLINE static int MusicAsset_Download(const char* hash) { + cc_string url; char urlBuffer[URL_MAX_SIZE]; + + String_InitArray(url, urlBuffer); + String_Format3(&url, "https://resources.download.minecraft.net/%r%r/%c", + &hash[0], &hash[1], hash); + return Http_AsyncGetData(&url, 0); +} + +static void MusicAssets_DownloadAssets(void) { + int i; + for (i = 0; i < Array_Elems(musicAssets); i++) + { + if (musicAssets[i].downloaded) continue; + musicAssets[i].reqID = MusicAsset_Download(musicAssets[i].hash); + } +} + +static const char* MusicAssets_GetRequestName(int reqID) { + int i; + for (i = 0; i < Array_Elems(musicAssets); i++) + { + if (reqID == musicAssets[i].reqID) return musicAssets[i].name; + } + return NULL; +} + + +/*########################################################################################################################* +*----------------------------------------------------Music asset processing ----------------------------------------------* +*#########################################################################################################################*/ +static void MusicAsset_Save(const char* name, struct HttpRequest* req) { + cc_string path; char pathBuffer[STRING_SIZE]; + cc_result res; + + String_InitArray(path, pathBuffer); + String_Format1(&path, "audio/%c", name); + + res = Stream_WriteAllTo(&path, req->data, req->size); + if (res) Logger_SysWarn(res, "saving music file"); +} + +static void MusicAsset_Check(struct MusicAsset* music) { + struct HttpRequest item; + if (!Fetcher_Get(music->reqID, &item)) return; + + music->downloaded = true; + MusicAsset_Save(music->name, &item); + HttpRequest_Free(&item); +} + +static void MusicAssets_CheckStatus(void) { + int i; + for (i = 0; i < Array_Elems(musicAssets); i++) + { + if (musicAssets[i].downloaded) continue; + MusicAsset_Check(&musicAssets[i]); + } +} + +static void MusicAssets_ResetState(void) { +} + +static const struct AssetSet mccMusicAssetSet = { + MusicAssets_CheckExistence, + MusicAssets_CountMissing, + MusicAssets_DownloadAssets, + MusicAssets_GetRequestName, + MusicAssets_CheckStatus, + MusicAssets_ResetState +}; + + +/*########################################################################################################################* +*---------------------------------------------------------Sound assets----------------------------------------------------* +*#########################################################################################################################*/ +static struct SoundAsset { + const char* filename; + const char* hash; + int reqID, size; + void* data; +} soundAssets[] = { + { "dig_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, + { "dig_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, + { "dig_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, + { "dig_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, + { "dig_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, + { "dig_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, + { "dig_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" }, + { "dig_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" }, + { "dig_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, + { "dig_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, + { "dig_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" }, + { "dig_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, + { "dig_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" }, + { "dig_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" }, + { "dig_glass1.wav", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2.wav", "87c47bda3645c68f18a49e83cbf06e5302d087ff" }, + { "dig_glass3.wav", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" }, + + { "step_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, + { "step_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, + { "step_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, + { "step_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, + { "step_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, + { "step_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, + { "step_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" }, + { "step_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" }, + { "step_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, + { "step_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, + { "step_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" }, + { "step_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, + { "step_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" }, + { "step_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" } +}; +static cc_bool allSoundsExist; + +static void SoundAssets_ResetState(void) { + int i; + allSoundsExist = false; + + for (i = 0; i < Array_Elems(soundAssets); i++) + { + Mem_Free(soundAssets[i].data); + soundAssets[i].data = NULL; + soundAssets[i].size = 0; + } +} + + +/*########################################################################################################################* +*-----------------------------------------------------Sound asset checking -----------------------------------------------* +*#########################################################################################################################*/ +static int soundEntriesFound; + +static struct SoundAsset* SoundAssest_Find(const cc_string* name) { + struct SoundAsset* a; + int i; + + for (i = 0; i < Array_Elems(soundAssets); i++) + { + a = &soundAssets[i]; + if (String_CaselessEqualsConst(name, a->filename)) return a; + } + return NULL; +} + +static cc_bool SoundAssets_CheckEntry(const cc_string* path) { + cc_string name = *path; + Utils_UNSAFE_GetFilename(&name); + + if (SoundAssest_Find(&name)) soundEntriesFound++; + return false; +} + +static void SoundAssets_CheckExistence(void) { + soundEntriesFound = 0; + ZipFile_InspectEntries(&Sounds_ZipPathMC, SoundAssets_CheckEntry); + + /* >= in case somehow have say "gui.png", "GUI.png" */ + allSoundsExist = soundEntriesFound >= Array_Elems(soundAssets); +} + +static void SoundAssets_CountMissing(void) { + if (allSoundsExist) return; + + Resources_MissingCount += Array_Elems(soundAssets); + Resources_MissingSize += 417; +} + + +/*########################################################################################################################* +*----------------------------------------------------Sound asset generation ----------------------------------------------* +*#########################################################################################################################*/ +static void SoundAsset_CreateZip(void) { + struct ResourceZipEntry entries[Array_Elems(soundAssets)]; + int i; + + for (i = 0; i < Array_Elems(soundAssets); i++) + { + entries[i].filename = soundAssets[i].filename; + entries[i].type = RESOURCE_TYPE_SOUND; + + entries[i].value.data = soundAssets[i].data; + entries[i].size = soundAssets[i].size; + } + + ZipFile_Create(&Sounds_ZipPathMC, entries, Array_Elems(soundAssets)); + SoundAssets_ResetState(); +} + + +/*########################################################################################################################* +*-----------------------------------------------------Sound asset fetching -----------------------------------------------* +*#########################################################################################################################*/ +#define SoundAsset_Download(hash) MusicAsset_Download(hash) + +static void SoundAssets_DownloadAssets(void) { + int i; + for (i = 0; i < Array_Elems(soundAssets); i++) + { + if (allSoundsExist) continue; + soundAssets[i].reqID = SoundAsset_Download(soundAssets[i].hash); + } +} + +static const char* SoundAssets_GetRequestName(int reqID) { + int i; + for (i = 0; i < Array_Elems(soundAssets); i++) + { + if (reqID == soundAssets[i].reqID) return soundAssets[i].filename; + } + return NULL; +} + +static void SoundAsset_Check(struct SoundAsset* sound, int i) { + struct HttpRequest item; + if (!Fetcher_Get(sound->reqID, &item)) return; + + sound->data = item.data; + sound->size = item.size; + item.data = NULL; + HttpRequest_Free(&item); + + if (i == Array_Elems(soundAssets) - 1) + SoundAsset_CreateZip(); +} + +static void SoundAssets_CheckStatus(void) { + int i; + for (i = 0; i < Array_Elems(soundAssets); i++) + { + SoundAsset_Check(&soundAssets[i], i); + } +} + +static const struct AssetSet mccSoundAssetSet = { + SoundAssets_CheckExistence, + SoundAssets_CountMissing, + SoundAssets_DownloadAssets, + SoundAssets_GetRequestName, + SoundAssets_CheckStatus, + SoundAssets_ResetState +}; + + +/*########################################################################################################################* +*------------------------------------------------------CC texture assets--------------------------------------------------* +*#########################################################################################################################*/ +static const cc_string ccTexPack = String_FromConst("texpacks/classicube.zip"); +static cc_bool ccTexturesExist, ccTexturesDownloaded; +static int ccTexturesReqID; + +static void CCTextures_CheckExistence(void) { + ccTexturesExist = File_Exists(&ccTexPack); +} + +static void CCTextures_CountMissing(void) { + if (ccTexturesExist) return; + + Resources_MissingCount++; + Resources_MissingSize += 83; + Resources_MissingRequired = true; +} + + +/*########################################################################################################################* +*--------------------------------------------------CC texture assets fetching --------------------------------------------* +*#########################################################################################################################*/ +static void CCTextures_DownloadAssets(void) { + static cc_string url = String_FromConst(RESOURCE_SERVER "/default.zip"); + if (ccTexturesExist) return; + + ccTexturesReqID = Http_AsyncGetData(&url, 0); +} + +static const char* CCTextures_GetRequestName(int reqID) { + return reqID == ccTexturesReqID ? "ClassiCube textures" : NULL; +} + + +/*########################################################################################################################* +*-------------------------------------------------CC texture assets processing -------------------------------------------* +*#########################################################################################################################*/ +/* Android needs the touch.png */ +/* TODO: Unify both android and desktop platforms to both just extract from default.zip */ +static cc_bool CCTextures_SelectEntry(const cc_string* path) { + return String_CaselessEqualsConst(path, "touch.png"); +} + +static cc_result CCTextures_ProcessEntry(const cc_string* path, struct Stream* data, struct ZipEntry* source) { + struct ResourceZipEntry* e = ZipEntries_Find(path); + if (!e) return 0; /* TODO exteact on PC too */ + + return ZipEntry_ExtractData(e, data, source); +} + +static cc_result CCTextures_ExtractZip(struct HttpRequest* req) { + struct Stream src; + cc_result res; + + Stream_ReadonlyMemory(&src, req->data, req->size); + if ((res = Zip_Extract(&src, CCTextures_SelectEntry, CCTextures_ProcessEntry))) return res; + + return Stream_WriteAllTo(&ccTexPack, req->data, req->size); +} + +static void CCTextures_CheckStatus(void) { + struct HttpRequest item; + cc_result res; + + if (ccTexturesDownloaded) return; + if (!Fetcher_Get(ccTexturesReqID, &item)) return; + + ccTexturesDownloaded = true; + res = CCTextures_ExtractZip(&item); + if (res) Logger_SysWarn(res, "saving ClassiCube textures"); + + HttpRequest_Free(&item); +} + +static void CCTextures_ResetState(void) { + ccTexturesExist = false; + ccTexturesDownloaded = false; +} + +static const struct AssetSet ccTexsAssetSet = { + CCTextures_CheckExistence, + CCTextures_CountMissing, + CCTextures_DownloadAssets, + CCTextures_GetRequestName, + CCTextures_CheckStatus, + CCTextures_ResetState +}; + /*########################################################################################################################* *----------------------------------------------------default.zip resources------------------------------------------------* *#########################################################################################################################*/ -#define RESOURCE_TYPE_DATA 1 -#define RESOURCE_TYPE_PNG 2 -#define RESOURCE_TYPE_CONST 3 - #define ANIMS_TXT \ "# This file defines the animations used in a texture pack for ClassiCube.\r\n" \ "# Each line is in the format : \r\n" \ @@ -656,15 +815,6 @@ CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* nam return NULL; } -static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source) { - cc_uint32 size = source->UncompressedSize; - e->value.data = Mem_TryAlloc(size, 1); - e->size = size; - - if (!e->value.data) return ERR_OUT_OF_MEMORY; - return Stream_Read(data, e->value.data, size); -} - static cc_result ClassicPatcher_ExtractFiles(struct HttpRequest* req); static cc_result ModernPatcher_ExtractFiles(struct HttpRequest* req); @@ -689,6 +839,19 @@ static struct ZipfileSource { }; static int numDefaultZipSources, numDefaultZipProcessed; +static void MCCTextures_ResetState(void) { + int i; + for (i = 0; i < Array_Elems(defaultZipEntries); i++) + { + if (defaultZipEntries[i].type == RESOURCE_TYPE_CONST) continue; + + /* can reuse value.data for value.bmp case too */ + Mem_Free(defaultZipEntries[i].value.data); + defaultZipEntries[i].value.data = NULL; + defaultZipEntries[i].size = 0; + } +} + /*########################################################################################################################* *------------------------------------------------default.zip entry generators---------------------------------------------* @@ -883,52 +1046,6 @@ static cc_result Classic0023Patcher_OldGold(struct HttpRequest* req) { } -/*########################################################################################################################* -*------------------------------------------------------default.zip writer-------------------------------------------------* -*#########################################################################################################################*/ -static cc_result DefaultZip_WriteEntries(struct Stream* s) { - struct ResourceZipEntry* e; - cc_uint32 beg, end; - int i; - cc_result res; - - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - e = &defaultZipEntries[i]; - - if (e->type == RESOURCE_TYPE_PNG) { - if ((res = ZipWriter_WritePng(s, e))) return res; - } else { - if ((res = ZipWriter_WriteData(s, e))) return res; - } - } - - if ((res = s->Position(s, &beg))) return res; - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - if ((res = ZipWriter_CentralDir(s, &defaultZipEntries[i]))) return res; - } - - if ((res = s->Position(s, &end))) return res; - return ZipWriter_EndOfCentralDir(s, Array_Elems(defaultZipEntries), beg, end); -} - -static void DefaultZip_Create(void) { - cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); - struct Stream s; - cc_result res; - - res = Stream_CreateFile(&s, &path); - if (res) { - Logger_SysWarn2(res, "creating", &path); return; - } - - res = DefaultZip_WriteEntries(&s); - if (res) Logger_SysWarn2(res, "making", &path); - - res = s.Close(&s); - if (res) Logger_SysWarn2(res, "closing", &path); -} - - /*########################################################################################################################* *-----------------------------------------------Minecraft Classic texture assets------------------------------------------* *#########################################################################################################################*/ @@ -944,19 +1061,10 @@ static cc_bool DefaultZip_SelectEntry(const cc_string* path) { } static void MCCTextures_CheckExistence(void) { - cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); - struct Stream stream; - cc_result res; + cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); + zipEntriesFound = 0; - res = Stream_OpenFile(&stream, &path); - if (res == ReturnCode_FileNotFound) return; - if (res) { Logger_SysWarn2(res, "opening", &path); return; } - - res = Zip_Extract(&stream, DefaultZip_SelectEntry, NULL); - if (res) Logger_SysWarn2(res, "inspecting", &path); - - /* No point logging error for closing readonly file */ - (void)stream.Close(&stream); + ZipFile_InspectEntries(&path, DefaultZip_SelectEntry); /* >= in case somehow have say "gui.png", "GUI.png" */ allZipEntriesExist = zipEntriesFound >= Array_Elems(defaultZipEntries); @@ -973,8 +1081,8 @@ static void MCCTextures_CountMissing(void) { if (Game_Version.Version > VERSION_0023) numDefaultZipSources--; for (i = 0; i < numDefaultZipSources; i++) { - Resources_Count++; - Resources_Size += defaultZipSources[i].size; + Resources_MissingCount++; + Resources_MissingSize += defaultZipSources[i].size; } } @@ -992,6 +1100,7 @@ static void MCCTextures_DownloadAssets(void) { { url = String_FromReadonly(defaultZipSources[i].url); defaultZipSources[i].reqID = Http_AsyncGetData(&url, 0); + defaultZipSources[i].downloaded = false; } } @@ -1008,6 +1117,12 @@ static const char* MCCTextures_GetRequestName(int reqID) { /*########################################################################################################################* *------------------------------------------Minecraft Classic texture assets processing -----------------------------------* *#########################################################################################################################*/ +static void MCCTextures_CreateDefaultZip(void) { + cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); + ZipFile_Create(&path, defaultZipEntries, Array_Elems(defaultZipEntries)); + MCCTextures_ResetState(); +} + static void MCCTextures_CheckSource(struct ZipfileSource* source) { struct HttpRequest item; cc_result res; @@ -1023,7 +1138,7 @@ static void MCCTextures_CheckSource(struct ZipfileSource* source) { HttpRequest_Free(&item); if (++numDefaultZipProcessed < numDefaultZipSources) return; - DefaultZip_Create(); + MCCTextures_CreateDefaultZip(); } static void MCCTextures_CheckStatus(void) { @@ -1040,7 +1155,8 @@ static const struct AssetSet mccTexsAssetSet = { MCCTextures_CountMissing, MCCTextures_DownloadAssets, MCCTextures_GetRequestName, - MCCTextures_CheckStatus + MCCTextures_CheckStatus, + MCCTextures_ResetState }; @@ -1058,10 +1174,20 @@ static const struct AssetSet* const asset_sets[] = { &mccSoundAssetSet }; +static void ResetState() { + int i; + Resources_MissingCount = 0; + Resources_MissingSize = 0; + + for (i = 0; i < Array_Elems(asset_sets); i++) + { + asset_sets[i]->ResetState(); + } +} + void Resources_CheckExistence(void) { int i; - Resources_Count = 0; - Resources_Size = 0; + ResetState(); for (i = 0; i < Array_Elems(asset_sets); i++) { @@ -1100,18 +1226,9 @@ void Fetcher_Run(void) { } static void Fetcher_Finish(void) { - int i; Fetcher_Completed = true; Fetcher_Working = false; - - for (i = 0; i < Array_Elems(defaultZipEntries); i++) { - if (defaultZipEntries[i].type == RESOURCE_TYPE_CONST) continue; - - /* can reuse value.data for value.bmp case too */ - Mem_Free(defaultZipEntries[i].value.data); - defaultZipEntries[i].value.data = NULL; - defaultZipEntries[i].size = 0; - } + ResetState(); } static void Fetcher_Fail(struct HttpRequest* item) { @@ -1146,7 +1263,7 @@ void Fetcher_Update(void) { asset_sets[i]->CheckStatus(); } - if (Fetcher_Downloaded != Resources_Count) return; + if (Fetcher_Downloaded != Resources_MissingCount) return; Fetcher_Finish(); } #endif diff --git a/src/Resources.h b/src/Resources.h index 427da4221..4e629e55d 100644 --- a/src/Resources.h +++ b/src/Resources.h @@ -8,9 +8,11 @@ struct HttpRequest; typedef void (*FetcherErrorCallback)(struct HttpRequest* req); /* Number of resources that need to be downloaded */ -extern int Resources_Count; +extern int Resources_MissingCount; /* Total size of resources that need to be downloaded */ -extern int Resources_Size; +extern int Resources_MissingSize; +/* Whether required resources need to be downloaded */ +extern cc_bool Resources_MissingRequired; /* Checks existence of all assets */ void Resources_CheckExistence(void); diff --git a/src/Utils.c b/src/Utils.c index 9de401d21..6ec7a57b7 100644 --- a/src/Utils.c +++ b/src/Utils.c @@ -138,6 +138,19 @@ void Utils_Resize(void** buffer, int* capacity, cc_uint32 elemSize, int defCapac } } +void Utils_SwapEndian16(cc_int16* values, int numValues) { + cc_uint8* data = (cc_uint8*)values; + int i; + + for (i = 0; i < numValues * 2; i += 2) + { + cc_uint8 tmp = data[i + 0]; + data[i + 0] = data[i + 1]; + data[i + 1] = tmp; + } +} + + static const char base64_table[64] = { 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P', 'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f', diff --git a/src/Utils.h b/src/Utils.h index 7d8f245ad..78995c0ef 100644 --- a/src/Utils.h +++ b/src/Utils.h @@ -45,6 +45,7 @@ cc_uint32 Utils_CRC32(const cc_uint8* data, cc_uint32 length); /* NOTE: This cannot be just indexed by byte value - see Utils_CRC32 implementation. */ extern const cc_uint32 Utils_Crc32Table[256]; CC_NOINLINE void Utils_Resize(void** buffer, int* capacity, cc_uint32 elemSize, int defCapacity, int expandElems); +void Utils_SwapEndian16(cc_int16* values, int numValues); /* Converts blocks of 3 bytes into 4 ASCII characters. (pads if needed) */ /* Returns the number of ASCII characters written. */ diff --git a/src/Vorbis.c b/src/Vorbis.c index 2c75d7f72..b0a2e74d5 100644 --- a/src/Vorbis.c +++ b/src/Vorbis.c @@ -1229,6 +1229,10 @@ static void Vorbis_CalcWindow(struct VorbisWindow* window, int blockSize) { } } +void Vorbis_Init(struct VorbisState* ctx) { + Mem_Set(ctx, 0, sizeof(*ctx) - sizeof(ctx->imdct)); +} + void Vorbis_Free(struct VorbisState* ctx) { int i; for (i = 0; i < ctx->numCodebooks; i++) diff --git a/src/Vorbis.h b/src/Vorbis.h index 56353aa90..b37437ad7 100644 --- a/src/Vorbis.h +++ b/src/Vorbis.h @@ -56,7 +56,9 @@ struct VorbisState { struct imdct_state imdct[2]; }; -/* Frees all dynamic memory allocated to decode the given vorbis audio. */ +/* Initialises the given context to defaults */ +void Vorbis_Init(struct VorbisState* ctx); +/* Frees all memory dynamically allocated by the given context */ void Vorbis_Free(struct VorbisState* ctx); /* Reads and decodes the initial vorbis headers and setup data. */ cc_result Vorbis_DecodeHeaders(struct VorbisState* ctx);