mirror of https://github.com/AxioDL/amuse.git
Support for loading songs directly out of game containers
This commit is contained in:
parent
13a55764a0
commit
127aa283e9
|
@ -71,7 +71,7 @@ struct AppCallback : boo::IApplicationCallback
|
|||
int8_t m_octave = 4;
|
||||
int8_t m_velocity = 64;
|
||||
std::shared_ptr<amuse::Sequencer> m_seq;
|
||||
std::unique_ptr<uint8_t[]> 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<std::pair<std::string, amuse::ContainerRegistry::SongData>> 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)
|
||||
{
|
||||
|
@ -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<athena::io::FileReader> 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<boo::IAudioVoiceEngine> voxEngine = boo::NewAudioVoiceEngine();
|
||||
amuse::BooBackendVoiceAllocator booBackend(*voxEngine);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "AudioGroupData.hpp"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace amuse
|
||||
{
|
||||
|
@ -24,9 +25,19 @@ public:
|
|||
RogueSquadron2,
|
||||
RogueSquadron3
|
||||
};
|
||||
struct SongData
|
||||
{
|
||||
std::unique_ptr<uint8_t[]> m_data;
|
||||
size_t m_size;
|
||||
int16_t m_groupId;
|
||||
int16_t m_setupId;
|
||||
SongData(std::unique_ptr<uint8_t[]>&& 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<std::pair<std::string, IntrusiveAudioGroupData>> LoadContainer(const char* path);
|
||||
static std::vector<std::pair<std::string, SongData>> LoadSongs(const char* path);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include <string.h>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <zlib.h>
|
||||
|
||||
#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<std::pair<std::string, IntrusiveAudioGroupData>> 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<nameCount ; ++i)
|
||||
{
|
||||
FSeek(fp, 8, SEEK_CUR);
|
||||
uint32_t nameLen;
|
||||
fread(&nameLen, 1, 4, fp);
|
||||
nameLen = SBig(nameLen);
|
||||
FSeek(fp, nameLen, SEEK_CUR);
|
||||
}
|
||||
|
||||
uint32_t resCount;
|
||||
fread(&resCount, 1, 4, fp);
|
||||
resCount = SBig(resCount);
|
||||
for (uint32_t i=0 ; i<resCount ; ++i)
|
||||
{
|
||||
FSeek(fp, 4, SEEK_CUR);
|
||||
uint32_t type;
|
||||
fread(&type, 1, 4, fp);
|
||||
type = SBig(type);
|
||||
FSeek(fp, 8, SEEK_CUR);
|
||||
uint32_t offset;
|
||||
fread(&offset, 1, 4, fp);
|
||||
offset = SBig(offset);
|
||||
|
||||
if (type == 0x43534E47)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<std::string, ContainerRegistry::SongData>> LoadMP1Songs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<std::string, ContainerRegistry::SongData>> 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<uint32_t, std::string> names;
|
||||
names.reserve(nameCount);
|
||||
for (uint32_t i=0 ; i<nameCount ; ++i)
|
||||
{
|
||||
FSeek(fp, 4, SEEK_CUR);
|
||||
uint32_t id;
|
||||
fread(&id, 1, 4, fp);
|
||||
id = SBig(id);
|
||||
uint32_t nameLen;
|
||||
fread(&nameLen, 1, 4, fp);
|
||||
nameLen = SBig(nameLen);
|
||||
std::string str(nameLen, '\0');
|
||||
fread(&str[0], 1, nameLen, fp);
|
||||
names[id] = std::move(str);
|
||||
}
|
||||
|
||||
uint32_t resCount;
|
||||
fread(&resCount, 1, 4, fp);
|
||||
resCount = SBig(resCount);
|
||||
ret.reserve(resCount);
|
||||
for (uint32_t i=0 ; i<resCount ; ++i)
|
||||
{
|
||||
FSeek(fp, 4, SEEK_CUR);
|
||||
uint32_t type;
|
||||
fread(&type, 1, 4, fp);
|
||||
type = SBig(type);
|
||||
uint32_t id;
|
||||
fread(&id, 1, 4, fp);
|
||||
id = SBig(id);
|
||||
uint32_t size;
|
||||
fread(&size, 1, 4, fp);
|
||||
size = SBig(size);
|
||||
uint32_t offset;
|
||||
fread(&offset, 1, 4, fp);
|
||||
offset = SBig(offset);
|
||||
|
||||
if (type == 0x43534E47)
|
||||
{
|
||||
int64_t origPos = FTell(fp);
|
||||
FSeek(fp, offset + 4, SEEK_SET);
|
||||
|
||||
uint32_t midiSetup;
|
||||
fread(&midiSetup, 1, 4, fp);
|
||||
midiSetup = SBig(midiSetup);
|
||||
|
||||
uint32_t groupId;
|
||||
fread(&groupId, 1, 4, fp);
|
||||
groupId = SBig(groupId);
|
||||
|
||||
FSeek(fp, 4, SEEK_CUR);
|
||||
|
||||
uint32_t sonLength;
|
||||
fread(&sonLength, 1, 4, fp);
|
||||
sonLength = SBig(sonLength);
|
||||
|
||||
std::unique_ptr<uint8_t[]> 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<std::pair<std::string, IntrusiveAudioGroupData>> LoadRS2(FILE
|
|||
return ret;
|
||||
}
|
||||
|
||||
static std::vector<std::pair<std::string, ContainerRegistry::SongData>> LoadRS2Songs(FILE* fp)
|
||||
{
|
||||
std::vector<std::pair<std::string, ContainerRegistry::SongData>> 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<fstSz/64 ; ++i)
|
||||
{
|
||||
RS2FSTEntry entry;
|
||||
fread(&entry, 1, 64, fp);
|
||||
entry.swapBig();
|
||||
if (!strncmp("data", entry.name, 32))
|
||||
{
|
||||
FSeek(fp, int64_t(entry.offset), SEEK_SET);
|
||||
std::unique_ptr<uint8_t[]> audData(new uint8_t[entry.decompSz]);
|
||||
fread(audData.get(), 1, entry.decompSz, fp);
|
||||
|
||||
uint32_t indexOff = SBig(*reinterpret_cast<uint32_t*>(audData.get() + 4));
|
||||
uint32_t groupCount = SBig(*reinterpret_cast<uint32_t*>(audData.get() + indexOff));
|
||||
const uint32_t* groupOffs = reinterpret_cast<const uint32_t*>(audData.get() + indexOff + 4);
|
||||
|
||||
for (uint32_t j=0 ; j<groupCount ; ++j)
|
||||
{
|
||||
const uint8_t* groupData = audData.get() + SBig(groupOffs[j]);
|
||||
RS23GroupHead head = *reinterpret_cast<const RS23GroupHead*>(groupData);
|
||||
head.swapBig();
|
||||
|
||||
if (!head.sonCount)
|
||||
continue;
|
||||
|
||||
const RS23SONHead* sonData = reinterpret_cast<const RS23SONHead*>(audData.get() + head.sonIdxBeginOff);
|
||||
for (int s=0 ; s<head.sonCount ; ++s)
|
||||
{
|
||||
RS23SONHead sonHead = sonData[s];
|
||||
sonHead.swapBig();
|
||||
|
||||
char name[128];
|
||||
snprintf(name, 128, "GroupFile%u-%u", j, s);
|
||||
std::unique_ptr<uint8_t[]> 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<std::pair<std::string, ContainerRegistry::SongData>>
|
||||
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<uint8_t[]> song(new uint8_t[fLen]);
|
||||
fread(song.get(), 1, fLen, fp);
|
||||
fclose(fp);
|
||||
|
||||
std::vector<std::pair<std::string, SongData>> 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 {};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue