2022-05-19 01:51:26 -07:00
|
|
|
#include "audio_file.h"
|
|
|
|
|
2022-09-15 02:38:23 -07:00
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
#include "debug.h"
|
|
|
|
#include "memory_manager.h"
|
2022-05-28 02:34:49 -07:00
|
|
|
#include "platform_compat.h"
|
2022-05-19 01:51:26 -07:00
|
|
|
#include "sound.h"
|
2022-12-26 00:48:47 -08:00
|
|
|
#include "sound_decoder.h"
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-09-23 05:43:44 -07:00
|
|
|
namespace fallout {
|
|
|
|
|
2022-12-26 00:48:47 -08:00
|
|
|
typedef enum AudioFileFlags {
|
|
|
|
AUDIO_FILE_IN_USE = 0x01,
|
|
|
|
AUDIO_FILE_COMPRESSED = 0x02,
|
|
|
|
} AudioFileFlags;
|
|
|
|
|
|
|
|
typedef struct AudioFile {
|
|
|
|
int flags;
|
2023-02-13 00:02:17 -08:00
|
|
|
FILE* stream;
|
2022-12-26 00:48:47 -08:00
|
|
|
SoundDecoder* soundDecoder;
|
|
|
|
int fileSize;
|
|
|
|
int sampleRate;
|
|
|
|
int channels;
|
|
|
|
int position;
|
|
|
|
} AudioFile;
|
|
|
|
|
|
|
|
static bool defaultCompressionFunc(char* filePath);
|
2023-02-13 00:02:17 -08:00
|
|
|
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size);
|
2022-06-18 05:39:21 -07:00
|
|
|
|
2022-05-19 01:51:26 -07:00
|
|
|
// 0x5108C0
|
2022-12-26 00:48:47 -08:00
|
|
|
static AudioFileQueryCompressedFunc* queryCompressedFunc = defaultCompressionFunc;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x56CB10
|
2022-06-18 05:39:21 -07:00
|
|
|
static AudioFile* gAudioFileList;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x56CB14
|
2022-06-18 05:39:21 -07:00
|
|
|
static int gAudioFileListLength;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
// 0x41A850
|
2022-12-26 00:48:47 -08:00
|
|
|
static bool defaultCompressionFunc(char* filePath)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
char* pch = strrchr(filePath, '.');
|
|
|
|
if (pch != NULL) {
|
2022-12-26 00:48:47 -08:00
|
|
|
strcpy(pch + 1, "raw");
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41A870
|
2023-02-13 00:02:17 -08:00
|
|
|
static int audioFileSoundDecoderReadHandler(void* data, void* buffer, unsigned int size)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2023-02-13 00:02:17 -08:00
|
|
|
return fread(buffer, 1, size, reinterpret_cast<FILE*>(data));
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41A88C
|
2023-04-11 06:37:01 -07:00
|
|
|
int audioFileOpen(const char* fname, int* channels, int* sampleRate)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2022-05-28 02:34:49 -07:00
|
|
|
char path[COMPAT_MAX_PATH];
|
2022-05-19 01:51:26 -07:00
|
|
|
strcpy(path, fname);
|
|
|
|
|
|
|
|
int compression;
|
2022-12-26 00:48:47 -08:00
|
|
|
if (queryCompressedFunc(path)) {
|
2022-05-19 01:51:26 -07:00
|
|
|
compression = 2;
|
|
|
|
} else {
|
|
|
|
compression = 0;
|
|
|
|
}
|
|
|
|
|
2023-04-11 06:37:01 -07:00
|
|
|
FILE* stream = compat_fopen(path, "rb");
|
2022-05-19 01:51:26 -07:00
|
|
|
if (stream == NULL) {
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int index;
|
|
|
|
for (index = 0; index < gAudioFileListLength; index++) {
|
|
|
|
if ((gAudioFileList[index].flags & AUDIO_FILE_IN_USE) == 0) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (index == gAudioFileListLength) {
|
|
|
|
if (gAudioFileList != NULL) {
|
2022-05-21 08:22:03 -07:00
|
|
|
gAudioFileList = (AudioFile*)internal_realloc_safe(gAudioFileList, sizeof(*gAudioFileList) * (gAudioFileListLength + 1), __FILE__, __LINE__); // "..\int\audiof.c", 207
|
2022-05-19 01:51:26 -07:00
|
|
|
} else {
|
2022-05-21 08:22:03 -07:00
|
|
|
gAudioFileList = (AudioFile*)internal_malloc_safe(sizeof(*gAudioFileList), __FILE__, __LINE__); // "..\int\audiof.c", 209
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
gAudioFileListLength++;
|
|
|
|
}
|
|
|
|
|
|
|
|
AudioFile* audioFile = &(gAudioFileList[index]);
|
|
|
|
audioFile->flags = AUDIO_FILE_IN_USE;
|
2023-02-13 00:02:17 -08:00
|
|
|
audioFile->stream = stream;
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
if (compression == 2) {
|
|
|
|
audioFile->flags |= AUDIO_FILE_COMPRESSED;
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
2022-05-19 01:51:26 -07:00
|
|
|
audioFile->fileSize *= 2;
|
2023-04-11 06:37:01 -07:00
|
|
|
|
|
|
|
*channels = audioFile->channels;
|
|
|
|
*sampleRate = audioFile->sampleRate;
|
2022-05-19 01:51:26 -07:00
|
|
|
} else {
|
2022-08-31 21:37:00 -07:00
|
|
|
audioFile->fileSize = getFileSize(stream);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
audioFile->position = 0;
|
|
|
|
|
|
|
|
return index + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AAA0
|
2022-12-26 00:48:47 -08:00
|
|
|
int audioFileClose(int handle)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2022-12-26 00:48:47 -08:00
|
|
|
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
2023-02-13 00:02:17 -08:00
|
|
|
fclose(audioFile->stream);
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
|
|
|
soundDecoderFree(audioFile->soundDecoder);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reset audio file (which also resets it's use flag).
|
|
|
|
memset(audioFile, 0, sizeof(*audioFile));
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AB08
|
2022-12-26 00:48:47 -08:00
|
|
|
int audioFileRead(int handle, void* buffer, unsigned int size)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
|
2022-12-26 00:48:47 -08:00
|
|
|
AudioFile* ptr = &(gAudioFileList[handle - 1]);
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
int bytesRead;
|
|
|
|
if ((ptr->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
|
|
|
bytesRead = soundDecoderDecode(ptr->soundDecoder, buffer, size);
|
|
|
|
} else {
|
2023-02-13 00:02:17 -08:00
|
|
|
bytesRead = fread(buffer, 1, size, ptr->stream);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
ptr->position += bytesRead;
|
|
|
|
|
|
|
|
return bytesRead;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AB74
|
2022-12-26 00:48:47 -08:00
|
|
|
long audioFileSeek(int handle, long offset, int origin)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
void* buf;
|
|
|
|
int remaining;
|
|
|
|
int a4;
|
|
|
|
|
2022-12-26 00:48:47 -08:00
|
|
|
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
2022-05-19 01:51:26 -07:00
|
|
|
|
|
|
|
switch (origin) {
|
|
|
|
case SEEK_SET:
|
|
|
|
a4 = offset;
|
|
|
|
break;
|
|
|
|
case SEEK_CUR:
|
|
|
|
a4 = audioFile->fileSize + offset;
|
|
|
|
break;
|
|
|
|
case SEEK_END:
|
|
|
|
a4 = audioFile->position + offset;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
assert(false && "Should be unreachable");
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((audioFile->flags & AUDIO_FILE_COMPRESSED) != 0) {
|
|
|
|
if (a4 <= audioFile->position) {
|
|
|
|
soundDecoderFree(audioFile->soundDecoder);
|
|
|
|
|
2023-02-13 00:02:17 -08:00
|
|
|
fseek(audioFile->stream, 0, 0);
|
2022-05-19 01:51:26 -07:00
|
|
|
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFile->soundDecoder = soundDecoderInit(audioFileSoundDecoderReadHandler, audioFile->stream, &(audioFile->channels), &(audioFile->sampleRate), &(audioFile->fileSize));
|
2022-05-19 01:51:26 -07:00
|
|
|
audioFile->fileSize *= 2;
|
|
|
|
audioFile->position = 0;
|
|
|
|
|
|
|
|
if (a4) {
|
|
|
|
buf = internal_malloc_safe(4096, __FILE__, __LINE__); // "..\int\audiof.c", 364
|
|
|
|
while (a4 > 4096) {
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFileRead(handle, buf, 4096);
|
2022-05-19 01:51:26 -07:00
|
|
|
a4 -= 4096;
|
|
|
|
}
|
|
|
|
if (a4 != 0) {
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFileRead(handle, buf, a4);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
internal_free_safe(buf, __FILE__, __LINE__); // "..\int\audiof.c", 370
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
buf = internal_malloc_safe(0x400, __FILE__, __LINE__); // "..\int\audiof.c", 316
|
|
|
|
remaining = audioFile->position - a4;
|
|
|
|
while (remaining > 1024) {
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFileRead(handle, buf, 1024);
|
2022-05-19 01:51:26 -07:00
|
|
|
remaining -= 1024;
|
|
|
|
}
|
|
|
|
if (remaining != 0) {
|
2022-12-26 00:48:47 -08:00
|
|
|
audioFileRead(handle, buf, remaining);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
// TODO: Obiously leaks memory.
|
|
|
|
}
|
|
|
|
return audioFile->position;
|
|
|
|
}
|
|
|
|
|
2023-02-13 00:02:17 -08:00
|
|
|
return fseek(audioFile->stream, offset, origin);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AD20
|
2022-12-26 00:48:47 -08:00
|
|
|
long audioFileGetSize(int handle)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2022-12-26 00:48:47 -08:00
|
|
|
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
2022-05-19 01:51:26 -07:00
|
|
|
return audioFile->fileSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AD3C
|
2022-12-26 00:48:47 -08:00
|
|
|
long audioFileTell(int handle)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2022-12-26 00:48:47 -08:00
|
|
|
AudioFile* audioFile = &(gAudioFileList[handle - 1]);
|
2022-05-19 01:51:26 -07:00
|
|
|
return audioFile->position;
|
|
|
|
}
|
|
|
|
|
|
|
|
// AudiofWrite
|
|
|
|
// 0x41AD58
|
2022-12-26 00:48:47 -08:00
|
|
|
int audioFileWrite(int handle, const void* buffer, unsigned int size)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
|
|
|
debugPrint("AudiofWrite shouldn't be ever called\n");
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41AD68
|
2022-12-26 00:48:47 -08:00
|
|
|
int audioFileInit(AudioFileQueryCompressedFunc* func)
|
2022-05-19 01:51:26 -07:00
|
|
|
{
|
2022-12-26 00:48:47 -08:00
|
|
|
queryCompressedFunc = func;
|
2022-05-19 01:51:26 -07:00
|
|
|
gAudioFileList = NULL;
|
|
|
|
gAudioFileListLength = 0;
|
|
|
|
|
|
|
|
return soundSetDefaultFileIO(audioFileOpen, audioFileClose, audioFileRead, audioFileWrite, audioFileSeek, audioFileTell, audioFileGetSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x41ADAC
|
|
|
|
void audioFileExit()
|
|
|
|
{
|
|
|
|
if (gAudioFileList != NULL) {
|
|
|
|
internal_free_safe(gAudioFileList, __FILE__, __LINE__); // "..\int\audiof.c", 405
|
|
|
|
}
|
|
|
|
|
|
|
|
gAudioFileListLength = 0;
|
|
|
|
gAudioFileList = NULL;
|
|
|
|
}
|
2022-09-23 05:43:44 -07:00
|
|
|
|
|
|
|
} // namespace fallout
|