diff --git a/src/LScreens.c b/src/LScreens.c index e92cfcb53..2379ed6fd 100644 --- a/src/LScreens.c +++ b/src/LScreens.c @@ -1018,6 +1018,18 @@ static void ResourcesScreen_Draw(struct LScreen* s) { LScreen_Draw(s); } +static void ResourcesScreen_SetStatus(const String* str) { + BitmapCol boxCol = BITMAPCOL_CONST(120, 85, 151, 255); + struct LLabel* w = &ResourcesScreen_Instance.LblStatus; + + Drawer2D_Clear(&Launcher_Framebuffer, boxCol, + w->X, w->Y, w->Width, w->Height); + LLabel_SetText(w, str); + w->YOffset = -10; + LWidget_CalcPosition(w); + LWidget_Draw(w); +} + static void ResourcesScreen_UpdateStatus(struct AsyncRequest* req) { String str; char strBuffer[STRING_SIZE]; String id; @@ -1030,14 +1042,8 @@ static void ResourcesScreen_UpdateStatus(struct AsyncRequest* req) { count = Fetcher_Downloaded + 1; String_Format3(&str, "&eFetching %s.. (%i/%i)", &id, &count, &Resources_Count); - /* Don't redraw status if can avoid it */ if (String_Equals(&str, &w->Text)) return; - - Drawer2D_Clear(&Launcher_Framebuffer, boxCol, - w->X, w->Y, w->Width, w->Height); - LLabel_SetText(w, &str); - LWidget_CalcPosition(w); - LWidget_Draw(w); + ResourcesScreen_SetStatus(&str); } static void ResourcesScreen_UpdateProgress(struct ResourcesScreen* s) { @@ -1055,15 +1061,29 @@ static void ResourcesScreen_UpdateProgress(struct ResourcesScreen* s) { s->SdrProgress.VTABLE->Draw(&s->SdrProgress); } +static void ResourcesScreen_Error(struct ResourcesScreen* s) { + String str; char buffer[STRING_SIZE]; + String_InitArray(str, buffer); + + if (Fetcher_Error) { + String_Format1(&str, "&cError %h when downloading resources", &Fetcher_Error); + } else { + String_Format1(&str, "&c%i error when downloading resources", &Fetcher_StatusCode); + } + ResourcesScreen_SetStatus(&str); +} + static void ResourcesScreen_Tick(struct LScreen* s_) { struct ResourcesScreen* s = (struct ResourcesScreen*)s_; if (!Fetcher_Working) return; - ResourcesScreen_UpdateProgress(s); + ResourcesScreen_UpdateProgress(s); Fetcher_Update(); if (!Fetcher_Completed) return; - /* TODO: Log error here */ - //if (!FetchResourcesTask.Base.Success) return; + + if (Fetcher_Error || Fetcher_StatusCode) { + ResourcesScreen_Error(s); return; + } Launcher_TryLoadTexturePack(); ResourcesScreen_Next(NULL, 0, 0); diff --git a/src/LWeb.c b/src/LWeb.c index abf41b252..4e1587562 100644 --- a/src/LWeb.c +++ b/src/LWeb.c @@ -456,7 +456,7 @@ static void FetchUpdateTask_Handle(uint8_t* data, uint32_t len) { const static String path = String_FromConst("ClassiCube.update"); ReturnCode res; - res = Stream_WriteTo(&path, data, len); + res = Stream_WriteAllTo(&path, data, len); if (res) { Launcher_ShowError(res, "saving update"); return; } res = File_SetModifiedTime(&path, FetchUpdateTask.Timestamp); diff --git a/src/Launcher.c b/src/Launcher.c index 30157f48f..6c62bc0ec 100644 --- a/src/Launcher.c +++ b/src/Launcher.c @@ -348,7 +348,6 @@ void Launcher_SaveSkin(void) { /*########################################################################################################################* *----------------------------------------------------------Background-----------------------------------------------------* *#########################################################################################################################*/ -static FontDesc logoFont; static bool useBitmappedFont; static Bitmap terrainBmp, fontBmp; #define TILESIZE 48 @@ -599,7 +598,7 @@ static void Launcher_ApplyUpdate(void) { ReturnCode res; /* Can't use WriteLine, want \n as actual newline not code page 437 */ - res = Stream_WriteTo(&scriptPath, UPDATE_SCRIPT, sizeof(UPDATE_SCRIPT) - 1); + res = Stream_WriteAllTo(&scriptPath, UPDATE_SCRIPT, sizeof(UPDATE_SCRIPT) - 1); if (res) { Launcher_ShowError(res, "saving update script"); return; } res = Platform_MarkExecutable(&scriptPath); diff --git a/src/Resources.c b/src/Resources.c index 56216a840..32a5c2320 100644 --- a/src/Resources.c +++ b/src/Resources.c @@ -7,13 +7,14 @@ #include "Platform.h" #include "Launcher.h" #include "Utils.h" - +#include "Vorbis.h" +#include "Errors.h" /*########################################################################################################################* *---------------------------------------------------------List/Checker----------------------------------------------------* *#########################################################################################################################*/ -bool DigSoundsExist, StepSoundsExist; -int Resources_Count, Resources_Downloaded, Resources_Size; +bool SoundsExist; +int Resources_Count, Resources_Size; static void Resources_CheckFiles(void) { int i, flags; @@ -30,8 +31,8 @@ static void Resources_CheckFiles(void) { static void Resources_CheckMusic(void) { String path; char pathBuffer[FILENAME_SIZE]; int i; - String_InitArray(path, pathBuffer); + for (i = 0; i < Array_Elems(Resources_Music); i++) { path.length = 0; String_Format1(&path, "audio/%c", Resources_Music[i].Name); @@ -44,32 +45,23 @@ static void Resources_CheckMusic(void) { } } -CC_NOINLINE static bool Resources_CheckExist(const char* prefix, struct ResourceSound* sounds, int count) { - String path; char pathBuffer[FILENAME_SIZE]; - int i = 0; - - String_InitArray(path, pathBuffer); - for (i = 0; i < count; i++) { - path.length = 0; - String_Format2(&path, "audio/%c_%c.wav", prefix, sounds[i].Name); - - if (!File_Exists(&path)) return false; - } - return true; -} - static void Resources_CheckSounds(void) { - DigSoundsExist = Resources_CheckExist("dig", Resources_Dig, Array_Elems(Resources_Dig)); - if (!DigSoundsExist) { - Resources_Count += Array_Elems(Resources_Dig); - Resources_Size += 173; - } + String path; char pathBuffer[FILENAME_SIZE]; + int i; + String_InitArray(path, pathBuffer); - StepSoundsExist = Resources_CheckExist("step", Resources_Step, Array_Elems(Resources_Step)); - if (!StepSoundsExist) { - Resources_Count += Array_Elems(Resources_Step); - Resources_Size += 244; + for (i = 0; i < Array_Elems(Resources_Sounds); i++) { + path.length = 0; + String_Format1(&path, "audio/%c.wav", Resources_Sounds[i].Name); + + if (File_Exists(&path)) continue; + SoundsExist = false; + + Resources_Count += Array_Elems(Resources_Sounds); + Resources_Size += 417; + return; } + SoundsExist = true; } static bool Resources_SelectZipEntry(const String* path) { @@ -145,67 +137,166 @@ struct ResourceTexture Resources_Textures[19] = { { "animations.png", FLAG_MODERN }, { "gui.png", FLAG_GUI } }; -struct ResourceSound Resources_Dig[31] = { - { "cloth1", "5f/5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "cloth2", "56/56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "cloth3", "9c/9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "cloth4", "55/55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "grass1", "41/41cbf5dd08e951ad65883854e74d2e034929f572" }, { "grass2", "86/86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, - { "grass3", "f7/f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "grass4", "c7/c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, - { "gravel1", "e8/e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "gravel2", "c3/c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, - { "gravel3", "48/48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "gravel4", "7b/7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, - { "sand1", "9e/9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "sand2", "0f/0fa4234797f336ada4e3735e013e44d1099afe57" }, - { "sand3", "c7/c75589cc0087069f387de127dd1499580498738e" }, { "sand4", "37/37afa06f97d58767a1cd1382386db878be1532dd" }, - { "snow1", "e9/e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "snow2", "58/5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "snow3", "a4/a4bc069321a96236fde04a3820664cc23b2ea619" }, { "snow4", "e2/e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "stone1", "4e/4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "stone2", "9c/9c92f697142ae320584bf64c0d54381d59703528" }, - { "stone3", "8f/8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "stone4", "36/363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, - { "wood1", "9b/9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "wood2", "98/98102533e6085617a2962157b4f3658f59aea018" }, - { "wood3", "45/45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "wood4", "dc/dc66978374a46ab2b87db6472804185824868095" }, - { "glass1", "72/7274a2231ed4544a37e599b7b014e589e5377094" }, { "glass2", "87/87c47bda3645c68f18a49e83cbf06e5302d087ff" }, - { "glass3", "ad/ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" } -}; +struct ResourceSound Resources_Sounds[59] = { + { "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" }, -struct ResourceSound Resources_Step[28] = { - { "cloth1", "5f/5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "cloth2", "56/56c1d0ac0de2265018b2c41cb571cc6631101484" }, - { "cloth3", "9c/9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "cloth4", "55/55da1856e77cfd31a7e8c3d358e1f856c5583198" }, - { "grass1", "22/227ab99bf7c6cf0b2002e0f7957d0ff7e5cb0c96" }, { "grass2", "5c/5c971029d9284676dce1dda2c9d202f8c47163b2" }, - { "grass3", "76/76de0a736928eac5003691d73bdc2eda92116198" }, { "grass4", "bc/bc28801224a0aa77fdc93bb7c6c94beacdf77331" }, - { "gravel1", "1d/1d761cb3bcb45498719e4fba0751e1630e134f1a" }, { "gravel2", "ac/ac7a7c8d106e26abc775b1b46150c083825d8ddc" }, - { "gravel3", "c1/c109b985a7e6d5d3828c92e00aefa49deca0eb8c" }, { "gravel4", "a4/a47adece748059294c5f563c0fcac02fa0e4bab4" }, - { "sand1", "98/9813c8185197f4a4296649f27a9d738c4a6dfc78" }, { "sand2", "bd/bd1750c016f6bab40934eff0b0fb64c41c86e44b" }, - { "sand3", "ab/ab07279288fa49215bada5c17627e6a54ad0437c" }, { "sand4", "a4/a474236fb0c75bd65a6010e87dbc000d345fc185" }, - { "snow1", "e9/e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "snow2", "58/5887d10234c4f244ec5468080412f3e6ef9522f3" }, - { "snow3", "a4/a4bc069321a96236fde04a3820664cc23b2ea619" }, { "snow4", "e2/e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, - { "stone1", "4a/4a2e3795ffd4d3aab0834b7e41903af3a8f7d197" }, { "stone2", "22/22a383d9c22342305a4f16fec0bb479a885f8da2" }, - { "stone3", "a5/a533e7ae975e62592de868e0d0572778614bd587" }, { "stone4", "d5/d5218034051a13322d7b5efc0b5a9af739615f04" }, - { "wood1", "af/afb01196e2179e3b15b48f3373cea4c155d56b84" }, { "wood2", "1e/1e82a43c30cf8fcbe05d0bc2760ecba5d2320314" }, - { "wood3", "27/27722125968ac60c0f191a961b17e406f1351c6e" }, { "wood4", "29/29586f60bfe6f521dbc748919d4f0dc5b28beefd" } + { "step_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, + { "step_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, + { "step_grass1", "227ab99bf7c6cf0b2002e0f7957d0ff7e5cb0c96" }, { "step_grass2", "5c971029d9284676dce1dda2c9d202f8c47163b2" }, + { "step_grass3", "76de0a736928eac5003691d73bdc2eda92116198" }, { "step_grass4", "bc28801224a0aa77fdc93bb7c6c94beacdf77331" }, + { "step_gravel1", "1d761cb3bcb45498719e4fba0751e1630e134f1a" }, { "step_gravel2", "ac7a7c8d106e26abc775b1b46150c083825d8ddc" }, + { "step_gravel3", "c109b985a7e6d5d3828c92e00aefa49deca0eb8c" }, { "step_gravel4", "a47adece748059294c5f563c0fcac02fa0e4bab4" }, + { "step_sand1", "9813c8185197f4a4296649f27a9d738c4a6dfc78" }, { "step_sand2", "bd1750c016f6bab40934eff0b0fb64c41c86e44b" }, + { "step_sand3", "ab07279288fa49215bada5c17627e6a54ad0437c" }, { "step_sand4", "a474236fb0c75bd65a6010e87dbc000d345fc185" }, + { "step_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, + { "step_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, + { "step_stone1", "4a2e3795ffd4d3aab0834b7e41903af3a8f7d197" }, { "step_stone2", "22a383d9c22342305a4f16fec0bb479a885f8da2" }, + { "step_stone3", "a533e7ae975e62592de868e0d0572778614bd587" }, { "step_stone4", "d5218034051a13322d7b5efc0b5a9af739615f04" }, + { "step_wood1", "afb01196e2179e3b15b48f3373cea4c155d56b84" }, { "step_wood2", "1e82a43c30cf8fcbe05d0bc2760ecba5d2320314" }, + { "step_wood3", "27722125968ac60c0f191a961b17e406f1351c6e" }, { "step_wood4", "29586f60bfe6f521dbc748919d4f0dc5b28beefd" } }; struct ResourceMusic Resources_Music[7] = { - { "calm1.ogg", "50/50a59a4f56e4046701b758ddbb1c1587efa4cadf", 2472 }, - { "calm2.ogg", "74/74da65c99aa578486efa7b69983d3533e14c0d6e", 1931 }, - { "calm3.ogg", "14/14ae57a6bce3d4254daa8be2b098c2d99743cc3f", 2181 }, - { "hal1.ogg", "df/df1ff11b79757432c5c3f279e5ecde7b63ceda64", 1926 }, - { "hal2.ogg", "ce/ceaaaa1d57dfdfbb0bd4da5ea39628b42897a687", 1714 }, - { "hal3.ogg", "dd/dd85fb564e96ee2dbd4754f711ae9deb08a169f9", 1879 }, - { "hal4.ogg", "5e/5e7d63e75c6e042f452bc5e151276911ef92fed8", 2499 } + { "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 } }; +/*########################################################################################################################* +*-------------------------------------------------------Texture patcher---------------------------------------------------* +*#########################################################################################################################*/ + + +/*########################################################################################################################* +*--------------------------------------------------------Audio patcher----------------------------------------------------* +*#########################################################################################################################*/ +#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) { + uint8_t header[44]; + uint32_t length; + ReturnCode res; + + res = s->Length(s, &length); + if (res) { Launcher_ShowError(res, "getting .wav length"); return; } + 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[12], WAV_FourCC('f','m','t',' ')); + Stream_SetU32_LE(&header[16], 16); /* fmt chunk size */ + Stream_SetU16_LE(&header[20], 1); /* PCM audio format */ + Stream_SetU16_LE(&header[22], ctx->Channels); + Stream_SetU32_LE(&header[24], ctx->SampleRate); + + Stream_SetU32_LE(&header[28], ctx->SampleRate * ctx->Channels * 2); /* byte rate */ + Stream_SetU16_LE(&header[32], ctx->Channels * 2); /* block align */ + Stream_SetU16_LE(&header[34], 16); /* bits per sample */ + Stream_SetU32_BE(&header[36], WAV_FourCC('d','a','t','a')); + Stream_SetU32_LE(&header[40], length - sizeof(header)); + + res = Stream_Write(s, header, sizeof(header)); + if (res) Launcher_ShowError(res, "fixing .wav header"); +} + +static void Patcher_DecodeSound(struct Stream* s, struct VorbisState* ctx) { + int16_t* samples; + int count; + ReturnCode res; + + /* ctx is all 0, so reuse it here for header */ + res = Stream_Write(s, ctx, 44); + if (res) { Launcher_ShowError(res, "writing .wav header"); return; } + + res = Vorbis_DecodeHeaders(ctx); + if (res) { Launcher_ShowError(res, "decoding .ogg header"); return; } + samples = Mem_Alloc(ctx->BlockSizes[1] * ctx->Channels, 2, ".ogg samples"); + + for (;;) { + res = Vorbis_DecodeFrame(ctx); + if (res == ERR_END_OF_STREAM) break; + if (res) { Launcher_ShowError(res, "decoding .ogg"); break; } + + count = Vorbis_OutputFrame(ctx, samples); + /* TODO: Do we need to account for big endian */ + res = Stream_Write(s, samples, count * 2); + if (res) { Launcher_ShowError(res, "writing samples"); break; } + } + Mem_Free(samples); +} + +static void Patcher_SaveSound(struct ResourceSound* sound, struct AsyncRequest* req) { + String path; char pathBuffer[STRING_SIZE]; + uint8_t buffer[OGG_BUFFER_SIZE]; + struct Stream src, ogg, dst; + struct VorbisState ctx = { 0 }; + ReturnCode res; + + Stream_ReadonlyMemory(&src, req->Data, req->Size); + String_InitArray(path, pathBuffer); + String_Format1(&path, "audio/%c.wav", sound->Name); + + res = Stream_CreateFile(&dst, &path); + if (res) { Launcher_ShowError(res, "creating .wav file"); return; } + + Ogg_MakeStream(&ogg, buffer, &src); + ctx.Source = &ogg; + + Patcher_DecodeSound(&dst, &ctx); + Patcher_FixupWaveHeader(&dst, &ctx); + + res = dst.Close(&dst); + if (res) Launcher_ShowError(res, "closing .wav file"); +} + +static void Patcher_SaveMusic(struct ResourceMusic* music, struct AsyncRequest* req) { + String path; char pathBuffer[STRING_SIZE]; + ReturnCode res; + + String_InitArray(path, pathBuffer); + String_Format1(&path, "audio/%c", music->Name); + + res = Stream_WriteAllTo(&path, req->Data, req->Size); + if (res) Launcher_ShowError(res, "saving music file"); +} + + /*########################################################################################################################* *-----------------------------------------------------------Fetcher-------------------------------------------------------* *#########################################################################################################################*/ bool Fetcher_Working, Fetcher_Completed; -int Fetcher_Downloaded; -CC_NOINLINE static void Fetcher_DownloadAudio(const char* fmt, const char* name, const char* hash) { - String id; char idBuffer[STRING_SIZE]; - String url; char urlBuffer[STRING_SIZE * 2]; +int Fetcher_StatusCode, Fetcher_Downloaded; +ReturnCode Fetcher_Error; - String_InitArray(id, idBuffer); - String_Format1(&id, fmt, name); +CC_NOINLINE static void Fetcher_DownloadAudio(const char* name, const char* hash) { + String url; char urlBuffer[STRING_SIZE * 2]; + String id = String_FromReadonly(name); String_InitArray(url, urlBuffer); - String_Format1(&url, "http://resources.download.minecraft.net/%c", hash); + String_Format3(&url, "http://resources.download.minecraft.net/%r%r/%c", + &hash[0], &hash[1], hash); AsyncDownloader_GetData(&url, false, &id); } @@ -214,8 +305,12 @@ void Fetcher_Run(void) { int i, flags; if (Fetcher_Working) return; - Fetcher_Working = true; - Fetcher_Completed = false; + Fetcher_Error = 0; + Fetcher_StatusCode = 0; + Fetcher_Downloaded = 0; + + Fetcher_Working = true; + Fetcher_Completed = false; flags = Resources_GetFetchFlags(); for (i = 0; i < Array_Elems(Resources_Files); i++) { @@ -228,19 +323,72 @@ void Fetcher_Run(void) { for (i = 0; i < Array_Elems(Resources_Music); i++) { if (Resources_Music[i].Exists) continue; - Fetcher_DownloadAudio(Resources_Music[i].Name, NULL, Resources_Music[i].Hash); + Fetcher_DownloadAudio(Resources_Music[i].Name, Resources_Music[i].Hash); } - for (i = 0; i < Array_Elems(Resources_Dig); i++) { - if (DigSoundsExist) continue; - Fetcher_DownloadAudio("dig_%c", Resources_Dig[i].Name, Resources_Dig[i].Hash); + for (i = 0; i < Array_Elems(Resources_Sounds); i++) { + if (SoundsExist) continue; + Fetcher_DownloadAudio(Resources_Sounds[i].Name, Resources_Sounds[i].Hash); } - for (i = 0; i < Array_Elems(Resources_Step); i++) { - if (StepSoundsExist) continue; - Fetcher_DownloadAudio("step_%c", Resources_Dig[i].Name, Resources_Step[i].Hash); +} + +static void Fetcher_Finish(void) { + Fetcher_Completed = true; + Fetcher_Working = false; +} + +CC_NOINLINE static bool Fetcher_Get(const String* id, struct AsyncRequest* req) { + if (!AsyncDownloader_Get(id, req)) return false; + + if (req->Result) { + Fetcher_Error = req->Result; + Fetcher_Finish(); + return false; + } else if (req->StatusCode != 200) { + Fetcher_StatusCode = req->StatusCode; + Fetcher_Finish(); + return false; + } else if (!req->Data) { + Fetcher_Error = ReturnCode_InvalidArg; + Fetcher_Finish(); + return false; } + + Fetcher_Downloaded++; + return true; +} + +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); + ASyncRequest_Free(&req); +} + +static void Fetcher_CheckSound(struct ResourceSound* sound) { + String id = String_FromReadonly(sound->Name); + struct AsyncRequest req; + if (!Fetcher_Get(&id, &req)) return; + + Patcher_SaveSound(sound, &req); + ASyncRequest_Free(&req); } /* TODO: Implement this.. */ void Fetcher_Update(void) { + int i; + for (i = 0; i < Array_Elems(Resources_Music); i++) { + if (Resources_Music[i].Exists) continue; + Fetcher_CheckMusic(&Resources_Music[i]); + } + + for (i = 0; i < Array_Elems(Resources_Sounds); i++) { + Fetcher_CheckSound(&Resources_Sounds[i]); + } + + 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 02644e798..8f988e913 100644 --- a/src/Resources.h +++ b/src/Resources.h @@ -27,7 +27,7 @@ extern struct ResourceTexture { extern struct ResourceSound { const char* Name; const char* Hash; -} Resources_Dig[31], Resources_Step[28]; +} Resources_Sounds[59]; extern struct ResourceMusic { const char* Name; @@ -36,7 +36,7 @@ extern struct ResourceMusic { bool Exists; } Resources_Music[7]; -extern bool DigSoundsExist, StepSoundsExist; +extern bool SoundsExist; /* Number of resources that need to be downloaded. */ extern int Resources_Count; /* Total size of resources that need to be downloaded. */ @@ -52,6 +52,11 @@ extern bool Fetcher_Working; extern bool Fetcher_Completed; /* Number of resources that have been downloaded so far. */ extern int Fetcher_Downloaded; +/* HTTP error (if any) that occurs when downloaded resources. */ +extern int Fetcher_StatusCode; +/* Error (if any) that occurs when downloaded resources. */ +extern ReturnCode Fetcher_Error; + /* Starts asynchronous download of required resources. */ void Fetcher_Run(void); void Fetcher_Update(void); diff --git a/src/Stream.c b/src/Stream.c index 8eb089200..560721d6d 100644 --- a/src/Stream.c +++ b/src/Stream.c @@ -121,7 +121,7 @@ ReturnCode Stream_CreateFile(struct Stream* s, const String* path) { return res; } -ReturnCode Stream_WriteTo(const String* path, const uint8_t* data, uint32_t length) { +ReturnCode Stream_WriteAllTo(const String* path, const uint8_t* data, uint32_t length) { struct Stream stream; ReturnCode res, closeRes; @@ -385,6 +385,10 @@ uint32_t Stream_GetU32_BE(const uint8_t* data) { ((uint32_t)data[2] << 8) | (uint32_t)data[3]); } +void Stream_SetU16_LE(uint8_t* data, uint16_t value) { + data[0] = (uint8_t)(value ); data[1] = (uint8_t)(value >> 8 ); +} + void Stream_SetU16_BE(uint8_t* data, uint16_t value) { data[0] = (uint8_t)(value >> 8 ); data[1] = (uint8_t)(value ); } diff --git a/src/Stream.h b/src/Stream.h index 877e05376..719b5f053 100644 --- a/src/Stream.h +++ b/src/Stream.h @@ -54,7 +54,7 @@ CC_API ReturnCode Stream_OpenFile(struct Stream* s, const String* path); /* Wrapper for File_Create() then Stream_FromFile() */ CC_API ReturnCode Stream_CreateFile(struct Stream* s, const String* path); /* Creates or overwrites a file, setting the contents to the given data. */ -ReturnCode Stream_WriteTo(const String* path, const uint8_t* data, uint32_t length); +ReturnCode Stream_WriteAllTo(const String* path, const uint8_t* data, uint32_t length); /* Wraps a file, allowing reading from/writing to/seeking in the file. */ CC_API void Stream_FromFile(struct Stream* s, FileHandle file); @@ -76,6 +76,8 @@ uint32_t Stream_GetU32_LE(const uint8_t* data); /* Reads a big-endian 32 bit unsigned integer from memory. */ uint32_t Stream_GetU32_BE(const uint8_t* data); +/* Writes a little-endian 16 bit unsigned integer to memory. */ +void Stream_SetU16_LE(uint8_t* data, uint16_t value); /* Writes a big-endian 16 bit unsigned integer to memory. */ void Stream_SetU16_BE(uint8_t* data, uint16_t value); /* Writes a little-endian 32 bit unsigned integer to memory. */ diff --git a/src/TexturePack.c b/src/TexturePack.c index e4d024e88..8df69bda3 100644 --- a/src/TexturePack.c +++ b/src/TexturePack.c @@ -589,7 +589,7 @@ void TextureCache_Set(const String* url, uint8_t* data, uint32_t length) { TextureCache_MakePath(&path, url); if (!Utils_EnsureDirectory("texturecache")) return; - res = Stream_WriteTo(&path, data, length); + res = Stream_WriteAllTo(&path, data, length); if (res) { Chat_LogError2(res, "caching", url); } }