mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-19 04:26:52 -04:00
More work on audio zip generation
This commit is contained in:
parent
f600f84cbf
commit
4ecffcedb0
@ -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 {
|
||||||
|
@ -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);
|
||||||
|
@ -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">
|
||||||
|
450
src/Resources.c
450
src/Resources.c
@ -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,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user