fallout2-ce/src/sound_effects_list.cc

473 lines
12 KiB
C++
Raw Normal View History

2022-05-19 01:51:26 -07:00
#include "sound_effects_list.h"
2022-09-15 02:38:23 -07:00
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
2022-05-19 01:51:26 -07:00
#include "db.h"
#include "debug.h"
#include "memory.h"
#include "platform_compat.h"
2022-07-05 01:00:55 -07:00
#include "pointer_registry.h"
2022-05-19 01:51:26 -07:00
#include "sound_decoder.h"
2022-06-18 22:07:49 -07:00
typedef struct SoundEffectsListEntry {
char* name;
int dataSize;
int fileSize;
int tag;
} SoundEffectsListEntry;
static int soundEffectsListTagToIndex(int tag, int* indexPtr);
static void soundEffectsListClear();
static int soundEffectsListPopulateFileNames();
static int soundEffectsListCopyFileNames(char** fileNameList);
static int soundEffectsListPopulateFileSizes();
static int soundEffectsListSort();
static int soundEffectsListCompareByName(const void* a1, const void* a2);
static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size);
2022-05-19 01:51:26 -07:00
// 0x51C8F8
2022-06-18 22:07:49 -07:00
static bool gSoundEffectsListInitialized = false;
2022-05-19 01:51:26 -07:00
// 0x51C8FC
2022-06-18 22:07:49 -07:00
static int gSoundEffectsListDebugLevel = INT_MAX;
2022-05-19 01:51:26 -07:00
// sfxl_effect_path
// 0x51C900
2022-06-18 22:07:49 -07:00
static char* gSoundEffectsListPath = NULL;
2022-05-19 01:51:26 -07:00
// sfxl_effect_path_len
// 0x51C904
2022-06-18 22:07:49 -07:00
static int gSoundEffectsListPathLength = 0;
2022-05-19 01:51:26 -07:00
// sndlist.lst
//
// sfxl_list
// 0x51C908
2022-06-18 22:07:49 -07:00
static SoundEffectsListEntry* gSoundEffectsListEntries = NULL;
2022-05-19 01:51:26 -07:00
// The length of [gSoundEffectsListEntries] array.
//
// 0x51C90C
2022-06-18 22:07:49 -07:00
static int gSoundEffectsListEntriesLength = 0;
2022-05-19 01:51:26 -07:00
// 0x667F94
2022-06-18 22:07:49 -07:00
static int _sfxl_compression;
2022-05-19 01:51:26 -07:00
// sfxl_tag_is_legal
// 0x4A98E0
bool soundEffectsListIsValidTag(int a1)
{
return soundEffectsListTagToIndex(a1, NULL) == SFXL_OK;
}
// sfxl_init
// 0x4A98F4
int soundEffectsListInit(const char* soundEffectsPath, int a2, int debugLevel)
{
2022-05-28 02:34:49 -07:00
char path[COMPAT_MAX_PATH];
2022-05-19 01:51:26 -07:00
// TODO: What for?
// memcpy(path, byte_4A97E0, 0xFF);
gSoundEffectsListDebugLevel = debugLevel;
_sfxl_compression = a2;
gSoundEffectsListEntriesLength = 0;
gSoundEffectsListPath = internal_strdup(soundEffectsPath);
if (gSoundEffectsListPath == NULL) {
return SFXL_ERR;
}
gSoundEffectsListPathLength = strlen(gSoundEffectsListPath);
if (gSoundEffectsListPathLength == 0 || soundEffectsPath[gSoundEffectsListPathLength - 1] == '\\') {
sprintf(path, "%sSNDLIST.LST", soundEffectsPath);
} else {
sprintf(path, "%s\\SNDLIST.LST", soundEffectsPath);
}
File* stream = fileOpen(path, "rt");
if (stream != NULL) {
fileReadString(path, 255, stream);
gSoundEffectsListEntriesLength = atoi(path);
2022-05-21 08:22:03 -07:00
gSoundEffectsListEntries = (SoundEffectsListEntry*)internal_malloc(sizeof(*gSoundEffectsListEntries) * gSoundEffectsListEntriesLength);
2022-05-19 01:51:26 -07:00
for (int index = 0; index < gSoundEffectsListEntriesLength; index++) {
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
fileReadString(path, 255, stream);
// Remove trailing newline.
*(path + strlen(path) - 1) = '\0';
entry->name = internal_strdup(path);
fileReadString(path, 255, stream);
entry->dataSize = atoi(path);
fileReadString(path, 255, stream);
entry->fileSize = atoi(path);
fileReadString(path, 255, stream);
entry->tag = atoi(path);
}
fileClose(stream);
debugPrint("Reading SNDLIST.LST Sound FX Count: %d", gSoundEffectsListEntriesLength);
} else {
int err;
err = soundEffectsListPopulateFileNames();
if (err != SFXL_OK) {
internal_free(gSoundEffectsListPath);
return err;
}
err = soundEffectsListPopulateFileSizes();
if (err != SFXL_OK) {
soundEffectsListClear();
internal_free(gSoundEffectsListPath);
return err;
}
// NOTE: For unknown reason tag generation functionality is missing.
// You won't be able to produce the same SNDLIST.LST as the game have.
// All tags will be 0 (see [soundEffectsListPopulateFileNames]).
//
// On the other hand, tags read from the SNDLIST.LST are not used in
// the game. Instead tag is automatically determined from entry's
// index (see [soundEffectsListGetTag]).
// NOTE: Uninline.
soundEffectsListSort();
File* stream = fileOpen(path, "wt");
if (stream != NULL) {
filePrintFormatted(stream, "%d\n", gSoundEffectsListEntriesLength);
for (int index = 0; index < gSoundEffectsListEntriesLength; index++) {
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
filePrintFormatted(stream, "%s\n", entry->name);
filePrintFormatted(stream, "%d\n", entry->dataSize);
filePrintFormatted(stream, "%d\n", entry->fileSize);
filePrintFormatted(stream, "%d\n", entry->tag);
}
fileClose(stream);
} else {
debugPrint("SFXLIST: Can't open file for write %s\n", path);
}
}
gSoundEffectsListInitialized = true;
return SFXL_OK;
}
// 0x4A9C04
void soundEffectsListExit()
{
if (gSoundEffectsListInitialized) {
soundEffectsListClear();
internal_free(gSoundEffectsListPath);
gSoundEffectsListInitialized = false;
}
}
// sfxl_name_to_tag
// 0x4A9C28
int soundEffectsListGetTag(char* name, int* tagPtr)
{
if (compat_strnicmp(gSoundEffectsListPath, name, gSoundEffectsListPathLength) != 0) {
2022-05-19 01:51:26 -07:00
return SFXL_ERR;
}
SoundEffectsListEntry dummy;
dummy.name = name + gSoundEffectsListPathLength;
2022-05-21 08:22:03 -07:00
SoundEffectsListEntry* entry = (SoundEffectsListEntry*)bsearch(&dummy, gSoundEffectsListEntries, gSoundEffectsListEntriesLength, sizeof(*gSoundEffectsListEntries), soundEffectsListCompareByName);
2022-05-19 01:51:26 -07:00
if (entry == NULL) {
return SFXL_ERR;
}
int index = entry - gSoundEffectsListEntries;
if (index < 0 || index >= gSoundEffectsListEntriesLength) {
return SFXL_ERR;
}
*tagPtr = 2 * index + 2;
return SFXL_OK;
}
// sfxl_name
// 0x4A9CD8
int soundEffectsListGetFilePath(int tag, char** pathPtr)
{
int index;
int err = soundEffectsListTagToIndex(tag, &index);
if (err != SFXL_OK) {
return err;
}
char* name = gSoundEffectsListEntries[index].name;
2022-05-21 08:22:03 -07:00
char* path = (char*)internal_malloc(strlen(gSoundEffectsListPath) + strlen(name) + 1);
2022-05-19 01:51:26 -07:00
if (path == NULL) {
return SFXL_ERR;
}
strcpy(path, gSoundEffectsListPath);
strcat(path, name);
*pathPtr = path;
return SFXL_OK;
}
// 0x4A9D90
int soundEffectsListGetDataSize(int tag, int* sizePtr)
{
int index;
int rc = soundEffectsListTagToIndex(tag, &index);
if (rc != SFXL_OK) {
return rc;
}
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
*sizePtr = entry->dataSize;
return SFXL_OK;
}
// 0x4A9DBC
int soundEffectsListGetFileSize(int tag, int* sizePtr)
{
int index;
int err = soundEffectsListTagToIndex(tag, &index);
if (err != SFXL_OK) {
return err;
}
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
*sizePtr = entry->fileSize;
return SFXL_OK;
}
// sfxl_tag_to_index
// 0x4A9DE8
2022-06-18 22:07:49 -07:00
static int soundEffectsListTagToIndex(int tag, int* indexPtr)
2022-05-19 01:51:26 -07:00
{
if (tag <= 0) {
return SFXL_ERR_TAG_INVALID;
}
if ((tag & 1) != 0) {
return SFXL_ERR_TAG_INVALID;
}
int index = (tag / 2) - 1;
if (index >= gSoundEffectsListEntriesLength) {
return SFXL_ERR_TAG_INVALID;
}
if (indexPtr != NULL) {
*indexPtr = index;
}
return SFXL_OK;
}
// 0x4A9E44
2022-06-18 22:07:49 -07:00
static void soundEffectsListClear()
2022-05-19 01:51:26 -07:00
{
if (gSoundEffectsListEntriesLength < 0) {
return;
}
if (gSoundEffectsListEntries == NULL) {
return;
}
for (int index = 0; index < gSoundEffectsListEntriesLength; index++) {
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
if (entry->name != NULL) {
internal_free(entry->name);
}
}
internal_free(gSoundEffectsListEntries);
gSoundEffectsListEntries = NULL;
gSoundEffectsListEntriesLength = 0;
}
// sfxl_get_names
// 0x4A9EA0
2022-06-18 22:07:49 -07:00
static int soundEffectsListPopulateFileNames()
2022-05-19 01:51:26 -07:00
{
const char* extension;
switch (_sfxl_compression) {
case 0:
extension = "*.SND";
break;
case 1:
extension = "*.ACM";
break;
default:
return SFXL_ERR;
}
2022-05-21 08:22:03 -07:00
char* pattern = (char*)internal_malloc(strlen(gSoundEffectsListPath) + strlen(extension) + 1);
2022-05-19 01:51:26 -07:00
if (pattern == NULL) {
return SFXL_ERR;
}
strcpy(pattern, gSoundEffectsListPath);
strcat(pattern, extension);
char** fileNameList;
gSoundEffectsListEntriesLength = fileNameListInit(pattern, &fileNameList, 0, 0);
internal_free(pattern);
if (gSoundEffectsListEntriesLength > 10000) {
fileNameListFree(&fileNameList, 0);
return SFXL_ERR;
}
if (gSoundEffectsListEntriesLength <= 0) {
return SFXL_ERR;
}
2022-05-21 08:22:03 -07:00
gSoundEffectsListEntries = (SoundEffectsListEntry*)internal_malloc(sizeof(*gSoundEffectsListEntries) * gSoundEffectsListEntriesLength);
2022-05-19 01:51:26 -07:00
if (gSoundEffectsListEntries == NULL) {
fileNameListFree(&fileNameList, 0);
return SFXL_ERR;
}
memset(gSoundEffectsListEntries, 0, sizeof(*gSoundEffectsListEntries) * gSoundEffectsListEntriesLength);
int err = soundEffectsListCopyFileNames(fileNameList);
fileNameListFree(&fileNameList, 0);
if (err != SFXL_OK) {
soundEffectsListClear();
return err;
}
return SFXL_OK;
}
// sfxl_copy_names
// 0x4AA000
2022-06-18 22:07:49 -07:00
static int soundEffectsListCopyFileNames(char** fileNameList)
2022-05-19 01:51:26 -07:00
{
for (int index = 0; index < gSoundEffectsListEntriesLength; index++) {
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
entry->name = internal_strdup(*fileNameList++);
if (entry->name == NULL) {
soundEffectsListClear();
return SFXL_ERR;
}
}
return SFXL_OK;
}
// 0x4AA050
2022-06-18 22:07:49 -07:00
static int soundEffectsListPopulateFileSizes()
2022-05-19 01:51:26 -07:00
{
2022-05-21 08:22:03 -07:00
char* path = (char*)internal_malloc(gSoundEffectsListPathLength + 13);
2022-05-19 01:51:26 -07:00
if (path == NULL) {
return SFXL_ERR;
}
strcpy(path, gSoundEffectsListPath);
char* fileName = path + gSoundEffectsListPathLength;
for (int index = 0; index < gSoundEffectsListEntriesLength; index++) {
SoundEffectsListEntry* entry = &(gSoundEffectsListEntries[index]);
strcpy(fileName, entry->name);
int fileSize;
if (dbGetFileSize(path, &fileSize) != 0) {
internal_free(path);
return SFXL_ERR;
}
if (fileSize <= 0) {
internal_free(path);
return SFXL_ERR;
}
entry->fileSize = fileSize;
switch (_sfxl_compression) {
case 0:
entry->dataSize = fileSize;
break;
case 1:
if (1) {
File* stream = fileOpen(path, "rb");
if (stream == NULL) {
internal_free(path);
return 1;
}
2022-07-05 01:00:55 -07:00
int fileHandle = ptrToInt((void*)stream);
2022-05-19 01:51:26 -07:00
int v1;
int v2;
int v3;
2022-07-05 01:00:55 -07:00
SoundDecoder* soundDecoder = soundDecoderInit(_sfxl_ad_reader, fileHandle, &v1, &v2, &v3);
2022-05-19 01:51:26 -07:00
entry->dataSize = 2 * v3;
soundDecoderFree(soundDecoder);
fileClose(stream);
2022-07-05 01:00:55 -07:00
intToPtr(fileHandle, true);
2022-05-19 01:51:26 -07:00
}
break;
default:
internal_free(path);
return SFXL_ERR;
}
}
internal_free(path);
return SFXL_OK;
}
// NOTE: Inlined.
//
// 0x4AA200
2022-06-18 22:07:49 -07:00
static int soundEffectsListSort()
2022-05-19 01:51:26 -07:00
{
if (gSoundEffectsListEntriesLength != 1) {
qsort(gSoundEffectsListEntries, gSoundEffectsListEntriesLength, sizeof(*gSoundEffectsListEntries), soundEffectsListCompareByName);
}
return 0;
}
// 0x4AA228
2022-06-18 22:07:49 -07:00
static int soundEffectsListCompareByName(const void* a1, const void* a2)
2022-05-19 01:51:26 -07:00
{
SoundEffectsListEntry* v1 = (SoundEffectsListEntry*)a1;
SoundEffectsListEntry* v2 = (SoundEffectsListEntry*)a2;
return compat_stricmp(v1->name, v2->name);
2022-05-19 01:51:26 -07:00
}
// read via xfile
2022-06-18 22:07:49 -07:00
static int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size)
2022-05-19 01:51:26 -07:00
{
2022-07-05 01:00:55 -07:00
return fileRead(buf, 1, size, (File*)intToPtr(fileHandle));
2022-05-19 01:51:26 -07:00
}