2022-05-19 01:51:26 -07:00
|
|
|
#include "sound_effects_list.h"
|
|
|
|
|
|
|
|
#include "db.h"
|
|
|
|
#include "debug.h"
|
|
|
|
#include "memory.h"
|
2022-05-28 01:57:32 -07:00
|
|
|
#include "platform_compat.h"
|
2022-05-19 01:51:26 -07:00
|
|
|
#include "sound_decoder.h"
|
|
|
|
|
|
|
|
#include <limits.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
// 0x51C8F8
|
|
|
|
bool gSoundEffectsListInitialized = false;
|
|
|
|
|
|
|
|
// 0x51C8FC
|
|
|
|
int gSoundEffectsListDebugLevel = INT_MAX;
|
|
|
|
|
|
|
|
// sfxl_effect_path
|
|
|
|
// 0x51C900
|
|
|
|
char* gSoundEffectsListPath = NULL;
|
|
|
|
|
|
|
|
// sfxl_effect_path_len
|
|
|
|
// 0x51C904
|
|
|
|
int gSoundEffectsListPathLength = 0;
|
|
|
|
|
|
|
|
// sndlist.lst
|
|
|
|
//
|
|
|
|
// sfxl_list
|
|
|
|
// 0x51C908
|
|
|
|
SoundEffectsListEntry* gSoundEffectsListEntries = NULL;
|
|
|
|
|
|
|
|
// The length of [gSoundEffectsListEntries] array.
|
|
|
|
//
|
|
|
|
// 0x51C90C
|
|
|
|
int gSoundEffectsListEntriesLength = 0;
|
|
|
|
|
|
|
|
// 0x667F94
|
|
|
|
int _sfxl_compression;
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
char path[FILENAME_MAX];
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
2022-05-28 01:57:32 -07:00
|
|
|
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
|
|
|
|
int soundEffectsListTagToIndex(int tag, int* indexPtr)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
void soundEffectsListClear()
|
|
|
|
{
|
|
|
|
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
|
|
|
|
int soundEffectsListPopulateFileNames()
|
|
|
|
{
|
|
|
|
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
|
|
|
|
int soundEffectsListCopyFileNames(char** fileNameList)
|
|
|
|
{
|
|
|
|
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
|
|
|
|
int soundEffectsListPopulateFileSizes()
|
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
int v1;
|
|
|
|
int v2;
|
|
|
|
int v3;
|
|
|
|
SoundDecoder* soundDecoder = soundDecoderInit(_sfxl_ad_reader, (int)stream, &v1, &v2, &v3);
|
|
|
|
entry->dataSize = 2 * v3;
|
|
|
|
soundDecoderFree(soundDecoder);
|
|
|
|
fileClose(stream);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
internal_free(path);
|
|
|
|
return SFXL_ERR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
internal_free(path);
|
|
|
|
|
|
|
|
return SFXL_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// NOTE: Inlined.
|
|
|
|
//
|
|
|
|
// 0x4AA200
|
|
|
|
int soundEffectsListSort()
|
|
|
|
{
|
|
|
|
if (gSoundEffectsListEntriesLength != 1) {
|
|
|
|
qsort(gSoundEffectsListEntries, gSoundEffectsListEntriesLength, sizeof(*gSoundEffectsListEntries), soundEffectsListCompareByName);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 0x4AA228
|
|
|
|
int soundEffectsListCompareByName(const void* a1, const void* a2)
|
|
|
|
{
|
|
|
|
SoundEffectsListEntry* v1 = (SoundEffectsListEntry*)a1;
|
|
|
|
SoundEffectsListEntry* v2 = (SoundEffectsListEntry*)a2;
|
|
|
|
|
2022-05-28 01:57:32 -07:00
|
|
|
return compat_stricmp(v1->name, v2->name);
|
2022-05-19 01:51:26 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// read via xfile
|
|
|
|
int _sfxl_ad_reader(int fileHandle, void* buf, unsigned int size)
|
|
|
|
{
|
|
|
|
return fileRead(buf, 1, size, (File*)fileHandle);
|
|
|
|
}
|