fallout2-ce/src/sound_effects_cache.cc

530 lines
12 KiB
C++

#include "sound_effects_cache.h"
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cache.h"
#include "db.h"
#include "memory.h"
#include "settings.h"
#include "sound_decoder.h"
#include "sound_effects_list.h"
namespace fallout {
#define SOUND_EFFECTS_CACHE_MIN_SIZE (0x40000)
typedef struct SoundEffect {
// NOTE: This field is only 1 byte, likely unsigned char. It always uses
// cmp for checking implying it's not bitwise flags. Therefore it's better
// to express it as boolean.
bool used;
CacheEntry* cacheHandle;
int tag;
int dataSize;
int fileSize;
// TODO: Make size_t.
int position;
int dataPosition;
unsigned char* data;
} SoundEffect;
static int soundEffectsCacheGetFileSizeImpl(int tag, int* sizePtr);
static int soundEffectsCacheReadDataImpl(int tag, int* sizePtr, unsigned char* data);
static void soundEffectsCacheFreeImpl(void* ptr);
static int soundEffectsCacheCreateHandles();
static void soundEffectsCacheFreeHandles();
static int soundEffectsCreate(int* handlePtr, int id, void* data, CacheEntry* cacheHandle);
static bool soundEffectsIsValidHandle(int a1);
static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned int size);
static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size);
// 0x50DE04
static const char* off_50DE04 = "";
// sfxc_initialized
// 0x51C8F0
static bool gSoundEffectsCacheInitialized = false;
// 0x51C8F4
static int _sfxc_cmpr = 1;
// sfxc_pcache
// 0x51C8EC
static Cache* gSoundEffectsCache = NULL;
// sfxc_dlevel
// 0x51C8DC
static int gSoundEffectsCacheDebugLevel = INT_MAX;
// 0x51C8E0
static char* gSoundEffectsCacheEffectsPath = NULL;
// 0x51C8E4
static SoundEffect* gSoundEffects = NULL;
// 0x51C8E8
static int _sfxc_files_open = 0;
// sfxc_init
// 0x4A8FC0
int soundEffectsCacheInit(int cacheSize, const char* effectsPath)
{
gSoundEffectsCacheDebugLevel = settings.sound.debug_sfxc;
if (cacheSize <= SOUND_EFFECTS_CACHE_MIN_SIZE) {
return -1;
}
if (effectsPath == NULL) {
effectsPath = off_50DE04;
}
gSoundEffectsCacheEffectsPath = internal_strdup(effectsPath);
if (gSoundEffectsCacheEffectsPath == NULL) {
return -1;
}
if (soundEffectsListInit(gSoundEffectsCacheEffectsPath, _sfxc_cmpr, gSoundEffectsCacheDebugLevel) != SFXL_OK) {
internal_free(gSoundEffectsCacheEffectsPath);
return -1;
}
if (soundEffectsCacheCreateHandles() != 0) {
soundEffectsListExit();
internal_free(gSoundEffectsCacheEffectsPath);
return -1;
}
gSoundEffectsCache = (Cache*)internal_malloc(sizeof(*gSoundEffectsCache));
if (gSoundEffectsCache == NULL) {
soundEffectsCacheFreeHandles();
soundEffectsListExit();
internal_free(gSoundEffectsCacheEffectsPath);
return -1;
}
if (!cacheInit(gSoundEffectsCache, soundEffectsCacheGetFileSizeImpl, soundEffectsCacheReadDataImpl, soundEffectsCacheFreeImpl, cacheSize)) {
internal_free(gSoundEffectsCache);
soundEffectsCacheFreeHandles();
soundEffectsListExit();
internal_free(gSoundEffectsCacheEffectsPath);
return -1;
}
gSoundEffectsCacheInitialized = true;
return 0;
}
// 0x4A90FC
void soundEffectsCacheExit()
{
if (gSoundEffectsCacheInitialized) {
cacheFree(gSoundEffectsCache);
internal_free(gSoundEffectsCache);
gSoundEffectsCache = NULL;
soundEffectsCacheFreeHandles();
soundEffectsListExit();
internal_free(gSoundEffectsCacheEffectsPath);
gSoundEffectsCacheInitialized = false;
}
}
// 0x4A9140
int soundEffectsCacheInitialized()
{
return gSoundEffectsCacheInitialized;
}
// 0x4A9148
void soundEffectsCacheFlush()
{
if (gSoundEffectsCacheInitialized) {
cacheFlush(gSoundEffectsCache);
}
}
// sfxc_cached_open
// 0x4A915C
int soundEffectsCacheFileOpen(const char* fname, int* channels, int* sampleRate)
{
if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) {
return -1;
}
char* copy = internal_strdup(fname);
if (copy == NULL) {
return -1;
}
int tag;
int err = soundEffectsListGetTag(copy, &tag);
internal_free(copy);
if (err != SFXL_OK) {
return -1;
}
void* data;
CacheEntry* cacheHandle;
if (!cacheLock(gSoundEffectsCache, tag, &data, &cacheHandle)) {
return -1;
}
int handle;
if (soundEffectsCreate(&handle, tag, data, cacheHandle) != 0) {
cacheUnlock(gSoundEffectsCache, cacheHandle);
return -1;
}
return handle;
}
// sfxc_cached_close
// 0x4A9220
int soundEffectsCacheFileClose(int handle)
{
if (!soundEffectsIsValidHandle(handle)) {
return -1;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
if (!cacheUnlock(gSoundEffectsCache, soundEffect->cacheHandle)) {
return -1;
}
// FIXME: This is check is redundant and implemented incorrectly. There is
// an overflow when handle == SOUND_EFFECTS_MAX_COUNT, but thanks to
// [soundEffectsIsValidHandle] handle will always be less than
// [SOUND_EFFECTS_MAX_COUNT].
if (handle <= SOUND_EFFECTS_MAX_COUNT) {
soundEffect->used = false;
}
return 0;
}
// 0x4A9274
int soundEffectsCacheFileRead(int handle, void* buf, unsigned int size)
{
if (!soundEffectsIsValidHandle(handle)) {
return -1;
}
if (size == 0) {
return 0;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
if (soundEffect->dataSize - soundEffect->position <= 0) {
return 0;
}
size_t bytesToRead;
// NOTE: Original code uses signed comparison.
if ((int)size < (soundEffect->dataSize - soundEffect->position)) {
bytesToRead = size;
} else {
bytesToRead = soundEffect->dataSize - soundEffect->position;
}
switch (_sfxc_cmpr) {
case 0:
memcpy(buf, soundEffect->data + soundEffect->position, bytesToRead);
break;
case 1:
if (soundEffectsCacheFileReadCompressed(handle, buf, bytesToRead) != 0) {
return -1;
}
break;
default:
return -1;
}
soundEffect->position += bytesToRead;
return bytesToRead;
}
// 0x4A9350
int soundEffectsCacheFileWrite(int handle, const void* buf, unsigned int size)
{
return -1;
}
// 0x4A9358
long soundEffectsCacheFileSeek(int handle, long offset, int origin)
{
if (!soundEffectsIsValidHandle(handle)) {
return -1;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
int position;
switch (origin) {
case SEEK_SET:
position = 0;
break;
case SEEK_CUR:
position = soundEffect->position;
break;
case SEEK_END:
position = soundEffect->dataSize;
break;
default:
assert(false && "Should be unreachable");
}
long normalizedOffset = abs(offset);
if (offset >= 0) {
long remainingSize = soundEffect->dataSize - soundEffect->position;
if (normalizedOffset > remainingSize) {
normalizedOffset = remainingSize;
}
offset = position + normalizedOffset;
} else {
if (normalizedOffset > position) {
return -1;
}
offset = position - normalizedOffset;
}
soundEffect->position = offset;
return offset;
}
// 0x4A93F4
long soundEffectsCacheFileTell(int handle)
{
if (!soundEffectsIsValidHandle(handle)) {
return -1;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
return soundEffect->position;
}
// 0x4A9418
long soundEffectsCacheFileLength(int handle)
{
if (!soundEffectsIsValidHandle(handle)) {
return 0;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
return soundEffect->dataSize;
}
// sfxc_cached_file_size
// 0x4A9434
static int soundEffectsCacheGetFileSizeImpl(int tag, int* sizePtr)
{
int size;
if (soundEffectsListGetFileSize(tag, &size) == -1) {
return -1;
}
*sizePtr = size;
return 0;
}
// 0x4A945C
static int soundEffectsCacheReadDataImpl(int tag, int* sizePtr, unsigned char* data)
{
if (!soundEffectsListIsValidTag(tag)) {
return -1;
}
int size;
soundEffectsListGetFileSize(tag, &size);
char* name;
soundEffectsListGetFilePath(tag, &name);
if (dbGetFileContents(name, data)) {
internal_free(name);
return -1;
}
internal_free(name);
*sizePtr = size;
return 0;
}
// 0x4A94CC
static void soundEffectsCacheFreeImpl(void* ptr)
{
internal_free(ptr);
}
// 0x4A94D4
static int soundEffectsCacheCreateHandles()
{
gSoundEffects = (SoundEffect*)internal_malloc(sizeof(*gSoundEffects) * SOUND_EFFECTS_MAX_COUNT);
if (gSoundEffects == NULL) {
return -1;
}
for (int index = 0; index < SOUND_EFFECTS_MAX_COUNT; index++) {
SoundEffect* soundEffect = &(gSoundEffects[index]);
soundEffect->used = false;
}
_sfxc_files_open = 0;
return 0;
}
// 0x4A9518
static void soundEffectsCacheFreeHandles()
{
if (_sfxc_files_open) {
for (int index = 0; index < SOUND_EFFECTS_MAX_COUNT; index++) {
SoundEffect* soundEffect = &(gSoundEffects[index]);
if (!soundEffect->used) {
soundEffectsCacheFileClose(index);
}
}
}
internal_free(gSoundEffects);
}
// 0x4A9550
static int soundEffectsCreate(int* handlePtr, int tag, void* data, CacheEntry* cacheHandle)
{
if (_sfxc_files_open >= SOUND_EFFECTS_MAX_COUNT) {
return -1;
}
SoundEffect* soundEffect;
int index;
for (index = 0; index < SOUND_EFFECTS_MAX_COUNT; index++) {
soundEffect = &(gSoundEffects[index]);
if (!soundEffect->used) {
break;
}
}
if (index == SOUND_EFFECTS_MAX_COUNT) {
return -1;
}
soundEffect->used = true;
soundEffect->cacheHandle = cacheHandle;
soundEffect->tag = tag;
soundEffectsListGetDataSize(tag, &(soundEffect->dataSize));
soundEffectsListGetFileSize(tag, &(soundEffect->fileSize));
soundEffect->position = 0;
soundEffect->dataPosition = 0;
soundEffect->data = (unsigned char*)data;
*handlePtr = index;
return 0;
}
// 0x4A961C
static bool soundEffectsIsValidHandle(int handle)
{
if (handle >= SOUND_EFFECTS_MAX_COUNT) {
return false;
}
SoundEffect* soundEffect = &gSoundEffects[handle];
if (!soundEffect->used) {
return false;
}
if (soundEffect->dataSize < soundEffect->position) {
return false;
}
return soundEffectsListIsValidTag(soundEffect->tag);
}
// 0x4A967C
static int soundEffectsCacheFileReadCompressed(int handle, void* buf, unsigned int size)
{
if (!soundEffectsIsValidHandle(handle)) {
return -1;
}
SoundEffect* soundEffect = &(gSoundEffects[handle]);
soundEffect->dataPosition = 0;
int channels;
int sampleRate;
int sampleCount;
SoundDecoder* soundDecoder = soundDecoderInit(soundEffectsCacheSoundDecoderReadHandler, &handle, &channels, &sampleRate, &sampleCount);
if (soundEffect->position != 0) {
void* temp = internal_malloc(soundEffect->position);
if (temp == NULL) {
soundDecoderFree(soundDecoder);
return -1;
}
size_t bytesRead = soundDecoderDecode(soundDecoder, temp, soundEffect->position);
internal_free(temp);
if (bytesRead != soundEffect->position) {
soundDecoderFree(soundDecoder);
return -1;
}
}
size_t bytesRead = soundDecoderDecode(soundDecoder, buf, size);
soundDecoderFree(soundDecoder);
if (bytesRead != size) {
return -1;
}
return 0;
}
// 0x4A9774
static int soundEffectsCacheSoundDecoderReadHandler(void* data, void* buf, unsigned int size)
{
if (size == 0) {
return 0;
}
int handle = *reinterpret_cast<int*>(data);
SoundEffect* soundEffect = &(gSoundEffects[handle]);
unsigned int bytesToRead = soundEffect->fileSize - soundEffect->dataPosition;
if (size <= bytesToRead) {
bytesToRead = size;
}
memcpy(buf, soundEffect->data + soundEffect->dataPosition, bytesToRead);
soundEffect->dataPosition += bytesToRead;
return bytesToRead;
}
} // namespace fallout