From 127aa283e94edb9f8041e8f0497f61241dbf479f Mon Sep 17 00:00:00 2001 From: Jack Andersen Date: Mon, 30 May 2016 12:46:43 -1000 Subject: [PATCH] Support for loading songs directly out of game containers --- driver/main.cpp | 67 +++--- include/amuse/ContainerRegistry.hpp | 11 + lib/ContainerRegistry.cpp | 326 ++++++++++++++++++++++++++++ 3 files changed, 374 insertions(+), 30 deletions(-) diff --git a/driver/main.cpp b/driver/main.cpp index 7625e71..5d7e19c 100644 --- a/driver/main.cpp +++ b/driver/main.cpp @@ -71,7 +71,7 @@ struct AppCallback : boo::IApplicationCallback int8_t m_octave = 4; int8_t m_velocity = 64; std::shared_ptr m_seq; - std::unique_ptr m_arrData; + amuse::ContainerRegistry::SongData* m_arrData = nullptr; /* SFX playback selection */ int m_sfxId = -1; @@ -116,7 +116,7 @@ struct AppCallback : boo::IApplicationCallback m_seq->setVolume(m_volume); if (m_arrData) - m_seq->playSong(m_arrData.get(), false); + m_seq->playSong(m_arrData->m_data.get(), false); UpdateSongDisplay(); } @@ -604,6 +604,40 @@ struct AppCallback : boo::IApplicationCallback allSFXGroups[it->first] = std::make_pair(&grp, &it->second); } + /* Attempt loading song */ + std::vector> songs; + if (m_argc > 2) + { + songs = amuse::ContainerRegistry::LoadSongs(m_argv[2]); + + /* Get song selection from user */ + if (songs.size() > 1) + { + /* Ask user to specify which song */ + printf("Multiple Songs discovered:\n"); + int idx = 0; + for (const auto& pair : songs) + { + printf(" %d %s (Group %d, Setup %d)\n", idx++, + pair.first.c_str(), pair.second.m_groupId, pair.second.m_setupId); + } + + int userSel = 0; + printf("Enter Song Number: "); + if (scanf("%d", &userSel) <= 0) + Log.report(logvisor::Fatal, "unable to parse prompt"); + + if (userSel < songs.size()) + { + m_arrData = &songs[userSel].second; + m_groupId = m_arrData->m_groupId; + m_setupId = m_arrData->m_setupId; + } + else + Log.report(logvisor::Fatal, "unable to find Song %d", userSel); + } + } + /* Get group selection from user */ if (m_groupId != -1) { @@ -618,7 +652,7 @@ struct AppCallback : boo::IApplicationCallback { /* Ask user to specify which group in project */ printf("Multiple Audio Groups discovered:\n"); - for (const auto& pair :allSFXGroups) + for (const auto& pair : allSFXGroups) { printf(" %d %s (SFXGroup) %" PRISize " sfx-entries\n", pair.first, pair.second.first->first.c_str(), @@ -691,33 +725,6 @@ struct AppCallback : boo::IApplicationCallback if (!selData) Log.report(logvisor::Fatal, "unable to select audio group data"); - - /* Attempt loading song */ - if (m_argc > 2) - { - std::experimental::optional r; - r.emplace(m_argv[2], 32 * 1024, false); - if (!r->hasError()) - { - uint32_t version = r->readUint32Big(); - if (version == 0x18) - { - /* Raw SON data */ - r->seek(0, athena::SeekOrigin::Begin); - m_arrData = r->readUBytes(r->length()); - } - else if (version == 0x2) - { - /* Retro CSNG data */ - m_setupId = r->readUint32Big(); - m_groupId = r->readUint32Big(); - r->readUint32Big(); - uint32_t sonLength = r->readUint32Big(); - m_arrData = r->readUBytes(sonLength); - } - } - } - /* Build voice engine */ std::unique_ptr voxEngine = boo::NewAudioVoiceEngine(); amuse::BooBackendVoiceAllocator booBackend(*voxEngine); diff --git a/include/amuse/ContainerRegistry.hpp b/include/amuse/ContainerRegistry.hpp index ca6b4fa..ca670f8 100644 --- a/include/amuse/ContainerRegistry.hpp +++ b/include/amuse/ContainerRegistry.hpp @@ -4,6 +4,7 @@ #include "AudioGroupData.hpp" #include #include +#include namespace amuse { @@ -24,9 +25,19 @@ public: RogueSquadron2, RogueSquadron3 }; + struct SongData + { + std::unique_ptr m_data; + size_t m_size; + int16_t m_groupId; + int16_t m_setupId; + SongData(std::unique_ptr&& data, size_t size, int16_t groupId, int16_t setupId) + : m_data(std::move(data)), m_size(size), m_groupId(groupId), m_setupId(setupId) {} + }; static const char* TypeToName(Type tp); static Type DetectContainerType(const char* path); static std::vector> LoadContainer(const char* path); + static std::vector> LoadSongs(const char* path); }; } diff --git a/lib/ContainerRegistry.cpp b/lib/ContainerRegistry.cpp index 8b32782..933f1cd 100644 --- a/lib/ContainerRegistry.cpp +++ b/lib/ContainerRegistry.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #if _WIN32 @@ -100,6 +101,21 @@ static bool IsChunkExtension(const char* path, const char*& dotOut) return false; } +static bool IsSongExtension(const char* path, const char*& dotOut) +{ + const char* ext = strrchr(path, '.'); + if (ext) + { + if (!CompareCaseInsensitive(ext, ".son") || + !CompareCaseInsensitive(ext, ".sng")) + { + dotOut = ext; + return true; + } + } + return false; +} + static bool ValidateMP1(FILE* fp) { FileLength(fp); @@ -244,6 +260,147 @@ static std::vector> LoadMP1(FILE return ret; } +static bool ValidateMP1Songs(FILE* fp) +{ + FileLength(fp); + + uint32_t magic; + fread(&magic, 1, 4, fp); + magic = SBig(magic); + + if (magic == 0x00030005) + { + FSeek(fp, 8, SEEK_SET); + + uint32_t nameCount; + fread(&nameCount, 1, 4, fp); + nameCount = SBig(nameCount); + for (uint32_t i=0 ; i> LoadMP1Songs(FILE* fp) +{ + std::vector> ret; + FileLength(fp); + + uint32_t magic; + fread(&magic, 1, 4, fp); + magic = SBig(magic); + + if (magic == 0x00030005) + { + FSeek(fp, 8, SEEK_SET); + + uint32_t nameCount; + fread(&nameCount, 1, 4, fp); + nameCount = SBig(nameCount); + + std::unordered_map names; + names.reserve(nameCount); + for (uint32_t i=0 ; i song(new uint8_t[sonLength]); + fread(song.get(), 1, sonLength, fp); + + auto search = names.find(id); + if (search != names.end()) + ret.emplace_back(std::move(search->second), + ContainerRegistry::SongData(std::move(song), sonLength, groupId, midiSetup)); + else + { + char name[128]; + snprintf(name, 128, "%08X", id); + ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonLength, groupId, midiSetup)); + } + + + FSeek(fp, origPos, SEEK_SET); + } + } + } + + return ret; +} + static bool ValidateMP2(FILE* fp) { FileLength(fp); @@ -962,6 +1119,13 @@ struct RS23GroupHead uint32_t sampOff; uint32_t sampLen; + uint32_t unkOff; + uint32_t unkLen; + + uint32_t sonCount; + uint32_t sonIdxBeginOff; + uint32_t sonIdxEndOff; + void swapBig() { projOff = SBig(projOff); @@ -972,6 +1136,27 @@ struct RS23GroupHead sdirLen = SBig(sdirLen); sampOff = SBig(sampOff); sampLen = SBig(sampLen); + unkOff = SBig(unkOff); + unkLen = SBig(unkLen); + sonCount = SBig(sonCount); + sonIdxBeginOff = SBig(sonIdxBeginOff); + sonIdxEndOff = SBig(sonIdxEndOff); + } +}; + +struct RS23SONHead +{ + uint32_t offset; + uint32_t length; + uint16_t groupId; + uint16_t setupId; + + void swapBig() + { + offset = SBig(offset); + length = SBig(length); + groupId = SBig(groupId); + setupId = SBig(setupId); } }; @@ -1066,6 +1251,68 @@ static std::vector> LoadRS2(FILE return ret; } +static std::vector> LoadRS2Songs(FILE* fp) +{ + std::vector> ret; + size_t endPos = FileLength(fp); + + uint64_t fstOff; + fread(&fstOff, 1, 8, fp); + fstOff = SBig(fstOff); + uint64_t fstSz; + fread(&fstSz, 1, 8, fp); + fstSz = SBig(fstSz); + + if (fstOff + fstSz > endPos) + return ret; + + FSeek(fp, int64_t(fstOff), SEEK_SET); + for (size_t i=0 ; i audData(new uint8_t[entry.decompSz]); + fread(audData.get(), 1, entry.decompSz, fp); + + uint32_t indexOff = SBig(*reinterpret_cast(audData.get() + 4)); + uint32_t groupCount = SBig(*reinterpret_cast(audData.get() + indexOff)); + const uint32_t* groupOffs = reinterpret_cast(audData.get() + indexOff + 4); + + for (uint32_t j=0 ; j(groupData); + head.swapBig(); + + if (!head.sonCount) + continue; + + const RS23SONHead* sonData = reinterpret_cast(audData.get() + head.sonIdxBeginOff); + for (int s=0 ; s song(new uint8_t[sonHead.length]); + memcpy(song.get(), audData.get() + sonHead.offset, sonHead.length); + ret.emplace_back(name, ContainerRegistry::SongData(std::move(song), sonHead.length, + sonHead.groupId, sonHead.setupId)); + } + } + + break; + } + } + + return ret; +} + struct RS3FSTEntry { uint64_t offset; @@ -1467,4 +1714,83 @@ ContainerRegistry::LoadContainer(const char* path) return {}; } +std::vector> +ContainerRegistry::LoadSongs(const char* path) +{ + FILE* fp; + + /* See if provided file is a raw song */ + const char* dot = nullptr; + if (IsSongExtension(path, dot)) + { + fp = fopen(path, "rb"); + size_t fLen = FileLength(fp); + if (!fLen) + { + fclose(fp); + return {}; + } + std::unique_ptr song(new uint8_t[fLen]); + fread(song.get(), 1, fLen, fp); + fclose(fp); + + std::vector> ret; + ret.emplace_back("Song", SongData(std::move(song), fLen, -1, -1)); + return ret; + } + + /* Now attempt archive-file case */ + fp = fopen(path, "rb"); + if (fp) + { + if (ValidateMP1Songs(fp)) + { + auto ret = LoadMP1Songs(fp); + fclose(fp); + return ret; + } + +#if 0 + if (ValidateRS1PCSongs(fp)) + { + auto ret = LoadRS1PCSongs(fp); + fclose(fp); + return ret; + } + + if (ValidateRS1N64Songs(fp)) + { + auto ret = LoadRS1N64Songs(fp); + fclose(fp); + return ret; + } + + if (ValidateBFNPCSongs(fp)) + { + auto ret = LoadBFNPCSongs(fp); + fclose(fp); + return ret; + } + + if (ValidateBFNN64Songs(fp)) + { + auto ret = LoadBFNN64Songs(fp); + fclose(fp); + return ret; + } +#endif + + if (ValidateRS2(fp)) + { + auto ret = LoadRS2Songs(fp); + fclose(fp); + return ret; + } + + fclose(fp); + } + + return {}; +} + }