More work on audio zip generation

This commit is contained in:
UnknownShadow200 2024-04-03 18:37:52 +11:00
parent f600f84cbf
commit 4ecffcedb0
4 changed files with 257 additions and 201 deletions

View File

@ -17,6 +17,7 @@
#include "Window.h" #include "Window.h"
#endif #endif
int Audio_SoundsVolume, Audio_MusicVolume; int Audio_SoundsVolume, Audio_MusicVolume;
const cc_string Sounds_DefaultZipPath = String_FromConst("audio/default.zip");
static const cc_string audio_dir = String_FromConst("audio"); static const cc_string audio_dir = String_FromConst("audio");
struct Sound { struct Sound {

View File

@ -34,6 +34,7 @@ extern int Audio_SoundsVolume;
/* Volume music is played at, from 0-100. */ /* Volume music is played at, from 0-100. */
/* NOTE: Use Audio_SetMusic, don't change this directly. */ /* NOTE: Use Audio_SetMusic, don't change this directly. */
extern int Audio_MusicVolume; extern int Audio_MusicVolume;
extern const cc_string Sounds_DefaultZipPath;
void Audio_SetMusic(int volume); void Audio_SetMusic(int volume);
void Audio_SetSounds(int volume); void Audio_SetSounds(int volume);

View File

@ -620,9 +620,6 @@
<ClCompile Include="Platform_Dreamcast.c"> <ClCompile Include="Platform_Dreamcast.c">
<Filter>Source Files\Platform</Filter> <Filter>Source Files\Platform</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="Window_Carbon.c">
<Filter>Source Files\Window</Filter>
</ClCompile>
<ClCompile Include="Window_Android.c"> <ClCompile Include="Window_Android.c">
<Filter>Source Files\Window</Filter> <Filter>Source Files\Window</Filter>
</ClCompile> </ClCompile>
@ -713,6 +710,9 @@
<ClCompile Include="Graphics_Xbox360.c"> <ClCompile Include="Graphics_Xbox360.c">
<Filter>Source Files\Graphics</Filter> <Filter>Source Files\Graphics</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="AudioBackend.c">
<Filter>Source Files\Audio</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ResourceCompile Include="..\misc\windows\CCicon.rc"> <ResourceCompile Include="..\misc\windows\CCicon.rc">

View File

@ -14,6 +14,7 @@
#include "LWeb.h" #include "LWeb.h"
#include "Http.h" #include "Http.h"
#include "Game.h" #include "Game.h"
#include "Audio.h"
/* Represents a set of assets/resources */ /* Represents a set of assets/resources */
/* E.g. music set, sounds set, textures set */ /* E.g. music set, sounds set, textures set */
@ -56,6 +57,130 @@ CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* nam
static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source); static cc_result ZipEntry_ExtractData(struct ResourceZipEntry* e, struct Stream* data, struct ZipEntry* source);
/*########################################################################################################################*
*------------------------------------------------=-----Utility functions ------------------------=------------------------*
*#########################################################################################################################*/
static void ZipFile_InspectEntries(const cc_string* path, Zip_SelectEntry selector) {
struct Stream stream;
cc_result res;
res = Stream_OpenFile(&stream, path);
if (res == ReturnCode_FileNotFound) return;
if (res) { Logger_SysWarn2(res, "opening", path); return; }
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 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);
}
/*########################################################################################################################*
*-----------------------------------------------------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 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, offset);
if (res) return res;
Stream_SetU32_BE(header + 0, WAV_FourCC('R','I','F','F'));
Stream_SetU32_LE(header + 4, len - 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, len - WAV_HDR_SIZE);
return Stream_Write(s, header, WAV_HDR_SIZE);
}
/* Decodes all samples, then produces a .WAV file from them */
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;
if ((res = s->Position(s, &begOffset))) return res;
/* 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) return ERR_OUT_OF_MEMORY;
for (;;) {
res = Vorbis_DecodeFrame(ctx);
if (res == ERR_END_OF_STREAM) {
/* reached end of samples, so done */
res = SoundPatcher_FixupHeader(s, ctx, begOffset, len);
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) 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 cc_result SoundPatcher_Save(struct Stream* s, struct ResourceZipEntry* e) {
struct OggState* ogg = NULL;
struct VorbisState* ctx = NULL;
struct Stream src;
cc_result res;
ogg = (struct OggState*)Mem_TryAlloc(1, sizeof(struct OggState));
if (!ogg) { res = ERR_OUT_OF_MEMORY; goto cleanup; }
ctx = (struct VorbisState*)Mem_TryAlloc(1, sizeof(struct VorbisState));
if (!ctx) { res = ERR_OUT_OF_MEMORY; goto cleanup; }
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;
}
/*########################################################################################################################* /*########################################################################################################################*
*------------------------------------------------------Zip entry writer---------------------------------------------------* *------------------------------------------------------Zip entry writer---------------------------------------------------*
*#########################################################################################################################*/ *#########################################################################################################################*/
@ -191,6 +316,14 @@ static cc_result ZipWriter_WritePng(struct Stream* dst, struct ResourceZipEntry*
return ZipWriter_FixupLocalFile(dst, e); 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----------------------------------------------------* *------------------------------------------------------Zip file writer----------------------------------------------------*
@ -207,9 +340,11 @@ static cc_result ZipFile_WriteEntries(struct Stream* s, struct ResourceZipEntry*
if (e->type == RESOURCE_TYPE_PNG) { if (e->type == RESOURCE_TYPE_PNG) {
if ((res = ZipWriter_WritePng(s, e))) return res; if ((res = ZipWriter_WritePng(s, e))) return res;
} else if (e->type == RESOURCE_TYPE_SOUND) {
if ((res = ZipWriter_WriteWav(s, e))) return res;
} else { } else {
if ((res = ZipWriter_WriteData(s, e))) return res; if ((res = ZipWriter_WriteData(s, e))) return res;
} }
} }
if ((res = s->Position(s, &beg))) return res; if ((res = s->Position(s, &beg))) return res;
@ -222,7 +357,7 @@ static cc_result ZipFile_WriteEntries(struct Stream* s, struct ResourceZipEntry*
return ZipWriter_EndOfCentralDir(s, numEntries, beg, end); return ZipWriter_EndOfCentralDir(s, numEntries, beg, end);
} }
static void ZipFile_Create(cc_string* path, struct ResourceZipEntry* entries, int numEntries) { static void ZipFile_Create(const cc_string* path, struct ResourceZipEntry* entries, int numEntries) {
struct Stream s; struct Stream s;
cc_result res; cc_result res;
@ -364,60 +499,89 @@ static const struct AssetSet mccMusicAssetSet = {
*---------------------------------------------------------Sound assets----------------------------------------------------* *---------------------------------------------------------Sound assets----------------------------------------------------*
*#########################################################################################################################*/ *#########################################################################################################################*/
static struct SoundAsset { static struct SoundAsset {
const char* name; const char* filename;
const char* hash; const char* hash;
int reqID, size; int reqID, size;
void* data; void* data;
} soundAssets[] = { } soundAssets[] = {
{ "dig_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, { "dig_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "dig_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" },
{ "dig_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, { "dig_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "dig_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" },
{ "dig_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, { "dig_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "dig_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" },
{ "dig_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, { "dig_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "dig_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" },
{ "dig_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, { "dig_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "dig_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" },
{ "dig_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, { "dig_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "dig_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" },
{ "dig_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, { "dig_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "dig_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" },
{ "dig_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, { "dig_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "dig_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" },
{ "dig_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, { "dig_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "dig_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" },
{ "dig_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, { "dig_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "dig_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" },
{ "dig_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, { "dig_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "dig_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" },
{ "dig_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, { "dig_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "dig_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" },
{ "dig_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, { "dig_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "dig_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" },
{ "dig_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4", "dc66978374a46ab2b87db6472804185824868095" }, { "dig_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "dig_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" },
{ "dig_glass1", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2", "87c47bda3645c68f18a49e83cbf06e5302d087ff" }, { "dig_glass1.wav", "7274a2231ed4544a37e599b7b014e589e5377094" }, { "dig_glass2.wav", "87c47bda3645c68f18a49e83cbf06e5302d087ff" },
{ "dig_glass3", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" }, { "dig_glass3.wav", "ad7d770b7fff3b64121f75bd60cecfc4866d1cd6" },
{ "step_cloth1", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2", "56c1d0ac0de2265018b2c41cb571cc6631101484" }, { "step_cloth1.wav", "5fd568d724ba7d53911b6cccf5636f859d2662e8" }, { "step_cloth2.wav", "56c1d0ac0de2265018b2c41cb571cc6631101484" },
{ "step_cloth3", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4", "55da1856e77cfd31a7e8c3d358e1f856c5583198" }, { "step_cloth3.wav", "9c63f2a3681832dc32d206f6830360bfe94b5bfc" }, { "step_cloth4.wav", "55da1856e77cfd31a7e8c3d358e1f856c5583198" },
{ "step_grass1", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" }, { "step_grass1.wav", "41cbf5dd08e951ad65883854e74d2e034929f572" }, { "step_grass2.wav", "86cb1bb0c45625b18e00a64098cd425a38f6d3f2" },
{ "step_grass3", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" }, { "step_grass3.wav", "f7d7e5c7089c9b45fa5d1b31542eb455fad995db" }, { "step_grass4.wav", "c7b1005d4926f6a2e2387a41ab1fb48a72f18e98" },
{ "step_gravel1", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" }, { "step_gravel1.wav", "e8b89f316f3e9989a87f6e6ff12db9abe0f8b09f" }, { "step_gravel2.wav", "c3b3797d04cb9640e1d3a72d5e96edb410388fa3" },
{ "step_gravel3", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" }, { "step_gravel3.wav", "48f7e1bb098abd36b9760cca27b9d4391a23de26" }, { "step_gravel4.wav", "7bf3553a4fe41a0078f4988a13d6e1ed8663ef4c" },
{ "step_sand1", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2", "0fa4234797f336ada4e3735e013e44d1099afe57" }, { "step_sand1.wav", "9e59c3650c6c3fc0a475f1b753b2fcfef430bf81" }, { "step_sand2.wav", "0fa4234797f336ada4e3735e013e44d1099afe57" },
{ "step_sand3", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4", "37afa06f97d58767a1cd1382386db878be1532dd" }, { "step_sand3.wav", "c75589cc0087069f387de127dd1499580498738e" }, { "step_sand4.wav", "37afa06f97d58767a1cd1382386db878be1532dd" },
{ "step_snow1", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2", "5887d10234c4f244ec5468080412f3e6ef9522f3" }, { "step_snow1.wav", "e9bab7d3d15541f0aaa93fad31ad37fd07e03a6c" }, { "step_snow2.wav", "5887d10234c4f244ec5468080412f3e6ef9522f3" },
{ "step_snow3", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" }, { "step_snow3.wav", "a4bc069321a96236fde04a3820664cc23b2ea619" }, { "step_snow4.wav", "e26fa3036cdab4c2264ceb19e1cd197a2a510227" },
{ "step_stone1", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2", "9c92f697142ae320584bf64c0d54381d59703528" }, { "step_stone1.wav", "4e094ed8dfa98656d8fec52a7d20c5ee6098b6ad" }, { "step_stone2.wav", "9c92f697142ae320584bf64c0d54381d59703528" },
{ "step_stone3", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" }, { "step_stone3.wav", "8f23c02475d388b23e5faa680eafe6b991d7a9d4" }, { "step_stone4.wav", "363545a76277e5e47538b2dd3a0d6aa4f7a87d34" },
{ "step_wood1", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2", "98102533e6085617a2962157b4f3658f59aea018" }, { "step_wood1.wav", "9bc2a84d0aa98113fc52609976fae8fc88ea6333" }, { "step_wood2.wav", "98102533e6085617a2962157b4f3658f59aea018" },
{ "step_wood3", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4", "dc66978374a46ab2b87db6472804185824868095" } { "step_wood3.wav", "45b2aef7b5049e81b39b58f8d631563fadcc778b" }, { "step_wood4.wav", "dc66978374a46ab2b87db6472804185824868095" }
}; };
static cc_bool allSoundsExist; static cc_bool allSoundsExist;
static void SoundAssets_CheckExistence(void) { static void SoundAssets_ResetState(void) {
cc_string path; char pathBuffer[FILENAME_SIZE]; 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; int i;
String_InitArray(path, pathBuffer);
for (i = 0; i < Array_Elems(soundAssets); i++) for (i = 0; i < Array_Elems(soundAssets); i++)
{ {
path.length = 0; a = &soundAssets[i];
String_Format1(&path, "audio/%c.wav", soundAssets[i].name); if (String_CaselessEqualsConst(name, a->filename)) return a;
if (File_Exists(&path)) continue;
allSoundsExist = false;
return;
} }
allSoundsExist = true; 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_DefaultZipPath, SoundAssets_CheckEntry);
/* >= in case somehow have say "gui.png", "GUI.png" */
allSoundsExist = soundEntriesFound >= Array_Elems(soundAssets);
} }
static void SoundAssets_CountMissing(void) { static void SoundAssets_CountMissing(void) {
@ -427,6 +591,28 @@ static void SoundAssets_CountMissing(void) {
Resources_Size += 417; Resources_Size += 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_DefaultZipPath, entries, Array_Elems(soundAssets));
SoundAssets_ResetState();
}
/*########################################################################################################################* /*########################################################################################################################*
*-----------------------------------------------------Sound asset fetching -----------------------------------------------* *-----------------------------------------------------Sound asset fetching -----------------------------------------------*
*#########################################################################################################################*/ *#########################################################################################################################*/
@ -445,141 +631,29 @@ static const char* SoundAssets_GetRequestName(int reqID) {
int i; int i;
for (i = 0; i < Array_Elems(soundAssets); i++) for (i = 0; i < Array_Elems(soundAssets); i++)
{ {
if (reqID == soundAssets[i].reqID) return soundAssets[i].name; if (reqID == soundAssets[i].reqID) return soundAssets[i].filename;
} }
return NULL; return NULL;
} }
static void SoundAsset_Check(struct SoundAsset* sound, int i) {
/*########################################################################################################################*
*----------------------------------------------------Sound asset processing ----------------------------------------------*
*#########################################################################################################################*/
#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) {
cc_uint8 header[WAV_HDR_SIZE];
cc_result res = s->Seek(s, 0);
if (res) { Logger_SysWarn(res, "seeking to .wav start"); return; }
Stream_SetU32_BE(header + 0, WAV_FourCC('R','I','F','F'));
Stream_SetU32_LE(header + 4, len - 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, len - WAV_HDR_SIZE);
res = Stream_Write(s, header, WAV_HDR_SIZE);
if (res) Logger_SysWarn(res, "fixing .wav header");
}
/* Decodes all samples, then produces a .WAV file from them */
static void SoundPatcher_WriteWav(struct Stream* s, struct VorbisState* ctx) {
cc_int16* samples;
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; }
res = Vorbis_DecodeHeaders(ctx);
if (res) { Logger_SysWarn(res, "decoding .ogg header"); return; }
samples = (cc_int16*)Mem_TryAlloc(ctx->blockSizes[1] * ctx->channels, 2);
if (!samples) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating .ogg samples"); return; }
for (;;) {
res = Vorbis_DecodeFrame(ctx);
if (res == ERR_END_OF_STREAM) {
/* reached end of samples, so done */
SoundPatcher_FixupHeader(s, ctx, len); break;
}
if (res) { Logger_SysWarn(res, "decoding .ogg"); 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; }
}
Mem_Free(samples);
}
/* 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 = NULL;
struct VorbisState* ctx = NULL;
struct Stream src, dst;
cc_result res;
ogg = (struct OggState*)Mem_TryAlloc(1, sizeof(struct OggState));
if (!ogg) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating memory"); goto cleanup; }
ctx = (struct VorbisState*)Mem_TryAlloc(1, sizeof(struct VorbisState));
if (!ctx) { Logger_SysWarn(ERR_OUT_OF_MEMORY, "allocating memory"); goto cleanup; }
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"); goto cleanup; }
Ogg_Init(ogg, &src);
Vorbis_Init(ctx);
ctx->source = ogg;
SoundPatcher_WriteWav(&dst, ctx);
res = dst.Close(&dst);
if (res) Logger_SysWarn(res, "closing .wav file");
cleanup:
if (ctx) Vorbis_Free(ctx);
Mem_Free(ctx);
Mem_Free(ogg);
}
static void SoundAsset_Check(struct SoundAsset* sound) {
struct HttpRequest item; struct HttpRequest item;
if (!Fetcher_Get(sound->reqID, &item)) return; if (!Fetcher_Get(sound->reqID, &item)) return;
SoundPatcher_Save(sound->name, &item);
sound->data = item.data; sound->data = item.data;
sound->size = item.size; sound->size = item.size;
item.data = NULL; item.data = NULL;
HttpRequest_Free(&item); HttpRequest_Free(&item);
if (i == Array_Elems(soundAssets) - 1)
SoundAsset_CreateZip();
} }
static void SoundAssets_CheckStatus(void) { static void SoundAssets_CheckStatus(void) {
int i; int i;
for (i = 0; i < Array_Elems(soundAssets); i++) for (i = 0; i < Array_Elems(soundAssets); i++)
{ {
SoundAsset_Check(&soundAssets[i]); SoundAsset_Check(&soundAssets[i], i);
}
}
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;
} }
} }
@ -738,15 +812,6 @@ CC_NOINLINE static struct ResourceZipEntry* ZipEntries_Find(const cc_string* nam
return NULL; 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 ClassicPatcher_ExtractFiles(struct HttpRequest* req);
static cc_result ModernPatcher_ExtractFiles(struct HttpRequest* req); static cc_result ModernPatcher_ExtractFiles(struct HttpRequest* req);
@ -771,6 +836,19 @@ static struct ZipfileSource {
}; };
static int numDefaultZipSources, numDefaultZipProcessed; 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---------------------------------------------* *------------------------------------------------default.zip entry generators---------------------------------------------*
@ -980,19 +1058,10 @@ static cc_bool DefaultZip_SelectEntry(const cc_string* path) {
} }
static void MCCTextures_CheckExistence(void) { static void MCCTextures_CheckExistence(void) {
cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); cc_string path = String_FromReadonly(Game_Version.DefaultTexpack);
struct Stream stream; zipEntriesFound = 0;
cc_result res;
res = Stream_OpenFile(&stream, &path); ZipFile_InspectEntries(&path, DefaultZip_SelectEntry);
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);
/* >= in case somehow have say "gui.png", "GUI.png" */ /* >= in case somehow have say "gui.png", "GUI.png" */
allZipEntriesExist = zipEntriesFound >= Array_Elems(defaultZipEntries); allZipEntriesExist = zipEntriesFound >= Array_Elems(defaultZipEntries);
@ -1048,6 +1117,7 @@ static const char* MCCTextures_GetRequestName(int reqID) {
static void MCCTextures_CreateDefaultZip(void) { static void MCCTextures_CreateDefaultZip(void) {
cc_string path = String_FromReadonly(Game_Version.DefaultTexpack); cc_string path = String_FromReadonly(Game_Version.DefaultTexpack);
ZipFile_Create(&path, defaultZipEntries, Array_Elems(defaultZipEntries)); ZipFile_Create(&path, defaultZipEntries, Array_Elems(defaultZipEntries));
MCCTextures_ResetState();
} }
static void MCCTextures_CheckSource(struct ZipfileSource* source) { static void MCCTextures_CheckSource(struct ZipfileSource* source) {
@ -1077,22 +1147,6 @@ static void MCCTextures_CheckStatus(void) {
} }
} }
static void MCCTextures_ResetState(void) {
int i;
allZipEntriesExist = false;
zipEntriesFound = 0;
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;
}
}
static const struct AssetSet mccTexsAssetSet = { static const struct AssetSet mccTexsAssetSet = {
MCCTextures_CheckExistence, MCCTextures_CheckExistence,
MCCTextures_CountMissing, MCCTextures_CountMissing,