2016-05-09 07:22:58 +00:00
|
|
|
#include "amuse/AudioGroupProject.hpp"
|
2016-05-28 02:28:59 +00:00
|
|
|
#include "amuse/AudioGroupData.hpp"
|
2018-07-14 06:06:33 +00:00
|
|
|
#include "athena/MemoryReader.hpp"
|
2018-07-16 07:41:15 +00:00
|
|
|
#include "athena/FileWriter.hpp"
|
|
|
|
#include "athena/FileReader.hpp"
|
2016-05-09 07:22:58 +00:00
|
|
|
|
|
|
|
namespace amuse
|
|
|
|
{
|
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
static bool AtEnd32(athena::io::IStreamReader& r)
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
uint32_t v = r.readUint32Big();
|
|
|
|
r.seek(-4, athena::Current);
|
|
|
|
return v == 0xffffffff;
|
|
|
|
}
|
2016-05-10 05:32:31 +00:00
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
static bool AtEnd16(athena::io::IStreamReader& r)
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
uint16_t v = r.readUint16Big();
|
|
|
|
r.seek(-2, athena::Current);
|
|
|
|
return v == 0xffff;
|
|
|
|
}
|
2016-05-10 05:32:31 +00:00
|
|
|
|
2018-07-28 04:34:29 +00:00
|
|
|
template <athena::Endian DNAE>
|
|
|
|
static void ReadRangedObjectIds(NameDB* db, athena::io::IStreamReader& r, NameDB::Type tp)
|
|
|
|
{
|
|
|
|
uint16_t id;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(id), DNAE>({}, id, r);
|
|
|
|
if ((id & 0x8000) == 0x8000)
|
|
|
|
{
|
|
|
|
uint16_t endId;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(endId), DNAE>({}, endId, r);
|
|
|
|
for (uint16_t i = uint16_t(id & 0x7fff); i <= uint16_t(endId & 0x7fff); ++i)
|
|
|
|
{
|
|
|
|
ObjectId useId = i;
|
|
|
|
if (tp == NameDB::Type::Layer)
|
|
|
|
useId.id |= 0x8000;
|
|
|
|
db->registerPair(NameDB::generateName(useId, tp), useId);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
db->registerPair(NameDB::generateName(id, tp), id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
AudioGroupProject::AudioGroupProject(athena::io::IStreamReader& r, GCNDataTag)
|
2016-05-09 07:22:58 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
while (!AtEnd32(r))
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
GroupHeader<athena::Big> header;
|
|
|
|
header.read(r);
|
2016-05-11 04:48:08 +00:00
|
|
|
|
2018-07-28 04:34:29 +00:00
|
|
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
|
|
|
|
|
|
|
/* Sound Macros */
|
|
|
|
r.seek(header.soundMacroIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
|
|
|
|
|
|
|
/* Samples */
|
|
|
|
r.seek(header.samplIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
|
|
|
|
|
|
|
/* Tables */
|
|
|
|
r.seek(header.tableIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(TableId::CurNameDB, r, NameDB::Type::Table);
|
|
|
|
|
|
|
|
/* Keymaps */
|
|
|
|
r.seek(header.keymapIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
|
|
|
|
|
|
|
/* Layers */
|
|
|
|
r.seek(header.layerIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
|
|
|
|
2016-05-10 05:32:31 +00:00
|
|
|
if (header.type == GroupType::Song)
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = m_songGroups[header.groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SongGroupIndex>();
|
2016-05-10 05:32:31 +00:00
|
|
|
|
|
|
|
/* Normal pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.pageTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::PageEntryDNA<athena::Big> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_normPages[entry.programNo] = entry;
|
2016-05-10 05:32:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Drum pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.drumTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::PageEntryDNA<athena::Big> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_drumPages[entry.programNo] = entry;
|
2016-05-10 05:32:31 +00:00
|
|
|
}
|
2016-05-10 05:52:19 +00:00
|
|
|
|
|
|
|
/* MIDI setups */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < header.groupEndOff)
|
2016-05-10 05:52:19 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
uint16_t songId = r.readUint16Big();
|
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
2018-07-14 06:06:33 +00:00
|
|
|
for (int i = 0; i < 16 ; ++i)
|
|
|
|
setup[i].read(r);
|
2018-07-28 04:34:29 +00:00
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
2016-05-10 05:52:19 +00:00
|
|
|
}
|
2016-05-10 05:32:31 +00:00
|
|
|
}
|
|
|
|
else if (header.type == GroupType::SFX)
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = m_sfxGroups[header.groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SFXGroupIndex>();
|
2016-05-10 05:32:31 +00:00
|
|
|
|
|
|
|
/* SFX entries */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.pageTableOff, athena::Begin);
|
|
|
|
uint16_t count = r.readUint16Big();
|
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_sfxEntries.reserve(count);
|
2016-07-14 04:54:46 +00:00
|
|
|
for (int i = 0; i < count; ++i)
|
2016-05-10 05:32:31 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
2018-07-28 04:34:29 +00:00
|
|
|
SFXId::CurNameDB->registerPair(
|
|
|
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.groupEndOff, athena::Begin);
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
template <athena::Endian DNAE>
|
|
|
|
AudioGroupProject AudioGroupProject::_AudioGroupProject(athena::io::IStreamReader& r, bool absOffs)
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
AudioGroupProject ret;
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
while (!AtEnd32(r))
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
atInt64 groupBegin = r.position();
|
|
|
|
atInt64 subDataOff = absOffs ? 0 : groupBegin + 8;
|
|
|
|
GroupHeader<DNAE> header;
|
|
|
|
header.read(r);
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2018-07-28 04:34:29 +00:00
|
|
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
|
|
|
|
|
|
|
/* Sound Macros */
|
|
|
|
r.seek(subDataOff + header.soundMacroIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
|
|
|
|
|
|
|
/* Samples */
|
|
|
|
r.seek(subDataOff + header.samplIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
|
|
|
|
|
|
|
/* Tables */
|
|
|
|
r.seek(subDataOff + header.tableIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(TableId::CurNameDB, r, NameDB::Type::Table);
|
|
|
|
|
|
|
|
/* Keymaps */
|
|
|
|
r.seek(subDataOff + header.keymapIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
|
|
|
|
|
|
|
/* Layers */
|
|
|
|
r.seek(subDataOff + header.layerIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
|
|
|
|
2016-05-28 02:28:59 +00:00
|
|
|
if (header.type == GroupType::Song)
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = ret.m_songGroups[header.groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SongGroupIndex>();
|
2016-05-28 02:28:59 +00:00
|
|
|
|
2016-07-18 22:38:28 +00:00
|
|
|
if (absOffs)
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
2016-07-18 22:38:28 +00:00
|
|
|
/* Normal pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.pageTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::PageEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_normPages[entry.programNo] = entry;
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Drum pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.drumTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::PageEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_drumPages[entry.programNo] = entry;
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* MIDI setups */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < header.groupEndOff)
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-15 06:10:50 +00:00
|
|
|
uint16_t songId;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(songId), DNAE>({}, songId, r);
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
2018-07-14 06:06:33 +00:00
|
|
|
for (int i = 0; i < 16 ; ++i)
|
|
|
|
setup[i].read(r);
|
2018-07-28 04:34:29 +00:00
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
2016-07-18 22:38:28 +00:00
|
|
|
else
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
2016-07-18 22:38:28 +00:00
|
|
|
/* Normal pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(subDataOff + header.pageTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_normPages[entry.programNo] = entry;
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Drum pages */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(subDataOff + header.drumTableOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SongGroupIndex::MusyX1PageEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_drumPages[entry.programNo] = entry;
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* MIDI setups */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(subDataOff + header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < groupBegin + header.groupEndOff)
|
2016-07-18 22:38:28 +00:00
|
|
|
{
|
2018-07-15 06:10:50 +00:00
|
|
|
uint16_t songId;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(songId), DNAE>({}, songId, r);
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
2018-07-14 06:06:33 +00:00
|
|
|
for (int i = 0; i < 16 ; ++i)
|
|
|
|
{
|
|
|
|
SongGroupIndex::MusyX1MIDISetup ent;
|
|
|
|
ent.read(r);
|
|
|
|
setup[i] = ent;
|
|
|
|
}
|
2018-07-28 04:34:29 +00:00
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(songId, NameDB::Type::Song), songId);
|
2016-07-18 22:38:28 +00:00
|
|
|
}
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (header.type == GroupType::SFX)
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = ret.m_sfxGroups[header.groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SFXGroupIndex>();
|
2016-05-28 02:28:59 +00:00
|
|
|
|
|
|
|
/* SFX entries */
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(subDataOff + header.pageTableOff, athena::Begin);
|
2018-07-15 06:10:50 +00:00
|
|
|
uint16_t count;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_sfxEntries.reserve(count);
|
2016-07-14 04:54:46 +00:00
|
|
|
for (int i = 0; i < count; ++i)
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
SFXGroupIndex::SFXEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
|
|
|
r.seek(2, athena::Current);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_sfxEntries[entry.sfxId.id] = entry;
|
2018-07-28 04:34:29 +00:00
|
|
|
SFXId::CurNameDB->registerPair(
|
|
|
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-29 18:22:20 +00:00
|
|
|
if (absOffs)
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(header.groupEndOff, athena::Begin);
|
2016-05-29 18:22:20 +00:00
|
|
|
else
|
2018-07-14 06:06:33 +00:00
|
|
|
r.seek(groupBegin + header.groupEndOff, athena::Begin);
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
2018-07-14 06:06:33 +00:00
|
|
|
return ret;
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(const AudioGroupData& data)
|
|
|
|
{
|
2018-07-14 06:06:33 +00:00
|
|
|
athena::io::MemoryReader r(data.getProj(), data.getProjSize());
|
2016-05-29 04:31:58 +00:00
|
|
|
switch (data.getDataFormat())
|
2016-05-28 02:28:59 +00:00
|
|
|
{
|
|
|
|
case DataFormat::GCN:
|
2016-05-31 22:09:38 +00:00
|
|
|
default:
|
2018-07-14 06:06:33 +00:00
|
|
|
return AudioGroupProject(r, GCNDataTag{});
|
2016-05-28 02:28:59 +00:00
|
|
|
case DataFormat::N64:
|
2018-07-14 06:06:33 +00:00
|
|
|
return _AudioGroupProject<athena::Big>(r, data.getAbsoluteProjOffsets());
|
2016-05-28 02:28:59 +00:00
|
|
|
case DataFormat::PC:
|
2018-07-14 06:06:33 +00:00
|
|
|
return _AudioGroupProject<athena::Little>(r, data.getAbsoluteProjOffsets());
|
2016-05-28 02:28:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 04:48:38 +00:00
|
|
|
std::string ParseStringSlashId(const std::string& str, uint16_t& idOut)
|
|
|
|
{
|
|
|
|
size_t slashPos = str.find('/');
|
|
|
|
if (slashPos == std::string::npos)
|
|
|
|
return {};
|
|
|
|
idOut = uint16_t(strtoul(str.data() + slashPos + 1, nullptr, 0));
|
|
|
|
return {str.begin(), str.begin() + slashPos};
|
|
|
|
}
|
|
|
|
|
2018-07-16 07:41:15 +00:00
|
|
|
AudioGroupProject AudioGroupProject::CreateAudioGroupProject(SystemStringView groupPath)
|
|
|
|
{
|
|
|
|
AudioGroupProject ret;
|
|
|
|
SystemString projPath(groupPath);
|
|
|
|
projPath += _S("/!project.yaml");
|
|
|
|
athena::io::FileReader fi(projPath);
|
|
|
|
|
|
|
|
if (!fi.hasError())
|
|
|
|
{
|
|
|
|
athena::io::YAMLDocReader r;
|
2018-07-18 07:39:26 +00:00
|
|
|
if (r.parse(&fi) && !r.readString("DNAType").compare("amuse::Project"))
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
if (auto __v = r.enterSubRecord("songGroups"))
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
ret.m_songGroups.reserve(r.getCurNode()->m_mapChildren.size());
|
|
|
|
for (const auto& grp : r.getCurNode()->m_mapChildren)
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
if (auto __r = r.enterSubRecord(grp.first.c_str()))
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
uint16_t groupId;
|
|
|
|
std::string groupName = ParseStringSlashId(grp.first, groupId);
|
|
|
|
if (groupName.empty() || groupId == 0xffff)
|
|
|
|
continue;
|
|
|
|
GroupId::CurNameDB->registerPair(groupName, groupId);
|
|
|
|
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = ret.m_songGroups[groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SongGroupIndex>();
|
2018-07-16 07:41:15 +00:00
|
|
|
if (auto __v2 = r.enterSubRecord("normPages"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_normPages.reserve(r.getCurNode()->m_mapChildren.size());
|
2018-07-16 07:41:15 +00:00
|
|
|
for (const auto& pg : r.getCurNode()->m_mapChildren)
|
|
|
|
if (auto __r2 = r.enterSubRecord(pg.first.c_str()))
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_normPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
|
2018-07-16 07:41:15 +00:00
|
|
|
}
|
|
|
|
if (auto __v2 = r.enterSubRecord("drumPages"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_drumPages.reserve(r.getCurNode()->m_mapChildren.size());
|
2018-07-16 07:41:15 +00:00
|
|
|
for (const auto& pg : r.getCurNode()->m_mapChildren)
|
|
|
|
if (auto __r2 = r.enterSubRecord(pg.first.c_str()))
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_drumPages[strtoul(pg.first.c_str(), nullptr, 0)].read(r);
|
2018-07-16 07:41:15 +00:00
|
|
|
}
|
|
|
|
if (auto __v2 = r.enterSubRecord("songs"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_midiSetups.reserve(r.getCurNode()->m_mapChildren.size());
|
2018-07-16 07:41:15 +00:00
|
|
|
for (const auto& song : r.getCurNode()->m_mapChildren)
|
|
|
|
{
|
|
|
|
size_t chanCount;
|
|
|
|
if (auto __v3 = r.enterSubVector(song.first.c_str(), chanCount))
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
uint16_t songId;
|
|
|
|
std::string songName = ParseStringSlashId(song.first, songId);
|
|
|
|
if (songName.empty() || songId == 0xffff)
|
|
|
|
continue;
|
|
|
|
SongId::CurNameDB->registerPair(songName, songId);
|
|
|
|
|
2018-07-26 03:41:48 +00:00
|
|
|
std::array<SongGroupIndex::MIDISetup, 16>& setup = idx->m_midiSetups[songId];
|
2018-07-16 07:41:15 +00:00
|
|
|
for (int i = 0; i < 16 && i < chanCount; ++i)
|
|
|
|
if (auto __r2 = r.enterSubRecord(nullptr))
|
|
|
|
setup[i].read(r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-17 04:48:38 +00:00
|
|
|
if (auto __v = r.enterSubRecord("sfxGroups"))
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
ret.m_sfxGroups.reserve(r.getCurNode()->m_mapChildren.size());
|
|
|
|
for (const auto& grp : r.getCurNode()->m_mapChildren)
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-18 07:39:26 +00:00
|
|
|
if (auto __r = r.enterSubRecord(grp.first.c_str()))
|
2018-07-16 07:41:15 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
uint16_t groupId;
|
|
|
|
std::string groupName = ParseStringSlashId(grp.first, groupId);
|
|
|
|
if (groupName.empty() || groupId == 0xffff)
|
|
|
|
continue;
|
|
|
|
GroupId::CurNameDB->registerPair(groupName, groupId);
|
|
|
|
|
2018-07-26 03:41:48 +00:00
|
|
|
auto& idx = ret.m_sfxGroups[groupId];
|
2018-07-29 03:37:06 +00:00
|
|
|
idx = MakeObj<SFXGroupIndex>();
|
2018-07-16 07:41:15 +00:00
|
|
|
for (const auto& sfx : r.getCurNode()->m_mapChildren)
|
|
|
|
if (auto __r2 = r.enterSubRecord(sfx.first.c_str()))
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
uint16_t sfxId;
|
|
|
|
std::string sfxName = ParseStringSlashId(sfx.first, sfxId);
|
|
|
|
if (sfxName.empty() || sfxId == 0xffff)
|
|
|
|
continue;
|
|
|
|
SFXId::CurNameDB->registerPair(sfxName, sfxId);
|
2018-07-26 03:41:48 +00:00
|
|
|
idx->m_sfxEntries[sfxId].read(r);
|
2018-07-16 07:41:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-07-15 06:10:50 +00:00
|
|
|
void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, GCNDataTag)
|
|
|
|
{
|
|
|
|
while (!AtEnd32(r))
|
|
|
|
{
|
|
|
|
GroupHeader<athena::Big> header;
|
|
|
|
header.read(r);
|
|
|
|
|
2018-07-17 04:48:38 +00:00
|
|
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
|
|
|
|
2018-07-15 06:10:50 +00:00
|
|
|
/* Sound Macros */
|
|
|
|
r.seek(header.soundMacroIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
|
|
|
|
|
|
|
/* Samples */
|
|
|
|
r.seek(header.samplIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
|
|
|
|
|
|
|
/* Tables */
|
|
|
|
r.seek(header.tableIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(TableId::CurNameDB, r, NameDB::Type::Table);
|
|
|
|
|
|
|
|
/* Keymaps */
|
|
|
|
r.seek(header.keymapIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
|
|
|
|
|
|
|
/* Layers */
|
|
|
|
r.seek(header.layerIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<athena::Big>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
|
|
|
|
|
|
|
if (header.type == GroupType::Song)
|
|
|
|
{
|
|
|
|
/* MIDI setups */
|
|
|
|
r.seek(header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < header.groupEndOff)
|
|
|
|
{
|
|
|
|
uint16_t id = r.readUint16Big();
|
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id);
|
|
|
|
r.seek(2 + 5 * 16, athena::Current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (header.type == GroupType::SFX)
|
|
|
|
{
|
|
|
|
/* SFX entries */
|
|
|
|
r.seek(header.pageTableOff, athena::Begin);
|
|
|
|
uint16_t count = r.readUint16Big();
|
|
|
|
r.seek(2, athena::Current);
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
SFXGroupIndex::SFXEntryDNA<athena::Big> entry;
|
|
|
|
entry.read(r);
|
|
|
|
SFXId::CurNameDB->registerPair(
|
|
|
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
r.seek(header.groupEndOff, athena::Begin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
template <athena::Endian DNAE>
|
|
|
|
void AudioGroupProject::BootstrapObjectIDs(athena::io::IStreamReader& r, bool absOffs)
|
|
|
|
{
|
|
|
|
while (!AtEnd32(r))
|
|
|
|
{
|
|
|
|
atInt64 groupBegin = r.position();
|
|
|
|
atInt64 subDataOff = absOffs ? 0 : groupBegin + 8;
|
|
|
|
GroupHeader<DNAE> header;
|
|
|
|
header.read(r);
|
|
|
|
|
2018-07-17 04:48:38 +00:00
|
|
|
GroupId::CurNameDB->registerPair(NameDB::generateName(header.groupId, NameDB::Type::Group), header.groupId);
|
|
|
|
|
2018-07-15 06:10:50 +00:00
|
|
|
/* Sound Macros */
|
|
|
|
r.seek(subDataOff + header.soundMacroIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(SoundMacroId::CurNameDB, r, NameDB::Type::SoundMacro);
|
|
|
|
|
|
|
|
/* Samples */
|
|
|
|
r.seek(subDataOff + header.samplIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(SampleId::CurNameDB, r, NameDB::Type::Sample);
|
|
|
|
|
|
|
|
/* Tables */
|
|
|
|
r.seek(subDataOff + header.tableIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(TableId::CurNameDB, r, NameDB::Type::Table);
|
|
|
|
|
|
|
|
/* Keymaps */
|
|
|
|
r.seek(subDataOff + header.keymapIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(KeymapId::CurNameDB, r, NameDB::Type::Keymap);
|
|
|
|
|
|
|
|
/* Layers */
|
|
|
|
r.seek(subDataOff + header.layerIdsOff, athena::Begin);
|
|
|
|
while (!AtEnd16(r))
|
|
|
|
ReadRangedObjectIds<DNAE>(LayersId::CurNameDB, r, NameDB::Type::Layer);
|
|
|
|
|
|
|
|
if (header.type == GroupType::Song)
|
|
|
|
{
|
|
|
|
/* MIDI setups */
|
|
|
|
if (absOffs)
|
|
|
|
{
|
|
|
|
r.seek(header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < header.groupEndOff)
|
|
|
|
{
|
|
|
|
uint16_t id;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(id), DNAE>({}, id, r);
|
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id);
|
|
|
|
r.seek(2 + 5 * 16, athena::Current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
r.seek(subDataOff + header.midiSetupsOff, athena::Begin);
|
|
|
|
while (r.position() < groupBegin + header.groupEndOff)
|
|
|
|
{
|
|
|
|
uint16_t id;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(id), DNAE>({}, id, r);
|
|
|
|
SongId::CurNameDB->registerPair(NameDB::generateName(id, NameDB::Type::Song), id);
|
|
|
|
r.seek(2 + 8 * 16, athena::Current);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (header.type == GroupType::SFX)
|
|
|
|
{
|
|
|
|
/* SFX entries */
|
|
|
|
r.seek(subDataOff + header.pageTableOff, athena::Begin);
|
|
|
|
uint16_t count;
|
|
|
|
athena::io::Read<athena::io::PropType::None>::Do<decltype(count), DNAE>({}, count, r);
|
|
|
|
r.seek(2, athena::Current);
|
|
|
|
for (int i = 0; i < count; ++i)
|
|
|
|
{
|
|
|
|
SFXGroupIndex::SFXEntryDNA<DNAE> entry;
|
|
|
|
entry.read(r);
|
|
|
|
r.seek(2, athena::Current);
|
|
|
|
SFXId::CurNameDB->registerPair(
|
|
|
|
NameDB::generateName(entry.sfxId.id, NameDB::Type::SFX), entry.sfxId.id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (absOffs)
|
|
|
|
r.seek(header.groupEndOff, athena::Begin);
|
|
|
|
else
|
|
|
|
r.seek(groupBegin + header.groupEndOff, athena::Begin);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void AudioGroupProject::BootstrapObjectIDs(const AudioGroupData& data)
|
|
|
|
{
|
|
|
|
athena::io::MemoryReader r(data.getProj(), data.getProjSize());
|
|
|
|
switch (data.getDataFormat())
|
|
|
|
{
|
|
|
|
case DataFormat::GCN:
|
|
|
|
default:
|
|
|
|
BootstrapObjectIDs(r, GCNDataTag{});
|
|
|
|
break;
|
|
|
|
case DataFormat::N64:
|
|
|
|
BootstrapObjectIDs<athena::Big>(r, data.getAbsoluteProjOffsets());
|
|
|
|
break;
|
|
|
|
case DataFormat::PC:
|
|
|
|
BootstrapObjectIDs<athena::Little>(r, data.getAbsoluteProjOffsets());
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-10 05:32:31 +00:00
|
|
|
const SongGroupIndex* AudioGroupProject::getSongGroupIndex(int groupId) const
|
|
|
|
{
|
|
|
|
auto search = m_songGroups.find(groupId);
|
2018-07-14 06:06:33 +00:00
|
|
|
if (search != m_songGroups.cend())
|
2018-07-26 03:41:48 +00:00
|
|
|
return search->second.get();
|
2018-07-14 06:06:33 +00:00
|
|
|
return nullptr;
|
2016-05-10 05:32:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const SFXGroupIndex* AudioGroupProject::getSFXGroupIndex(int groupId) const
|
|
|
|
{
|
|
|
|
auto search = m_sfxGroups.find(groupId);
|
2018-07-14 06:06:33 +00:00
|
|
|
if (search != m_sfxGroups.cend())
|
2018-07-26 03:41:48 +00:00
|
|
|
return search->second.get();
|
2018-07-14 06:06:33 +00:00
|
|
|
return nullptr;
|
2016-05-09 07:22:58 +00:00
|
|
|
}
|
2018-07-14 06:06:33 +00:00
|
|
|
|
2018-07-16 07:41:15 +00:00
|
|
|
bool AudioGroupProject::toYAML(SystemStringView groupPath) const
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
athena::io::YAMLDocWriter w("amuse::Project");
|
|
|
|
|
|
|
|
if (!m_songGroups.empty())
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
if (auto __v = w.enterSubRecord("songGroups"))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
for (const auto& p : SortUnorderedMap(m_songGroups))
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
char groupString[64];
|
|
|
|
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
|
|
|
|
if (auto __r = w.enterSubRecord(groupString))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
if (!p.second.get()->m_normPages.empty())
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
if (auto __v2 = w.enterSubRecord("normPages"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
for (const auto& pg : SortUnorderedMap(p.second.get()->m_normPages))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
snprintf(name, 16, "%d", pg.first);
|
|
|
|
if (auto __r2 = w.enterSubRecord(name))
|
|
|
|
{
|
|
|
|
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
|
|
|
pg.second.get().write(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-26 03:41:48 +00:00
|
|
|
if (!p.second.get()->m_drumPages.empty())
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
if (auto __v2 = w.enterSubRecord("drumPages"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
for (const auto& pg : SortUnorderedMap(p.second.get()->m_drumPages))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
char name[16];
|
|
|
|
snprintf(name, 16, "%d", pg.first);
|
|
|
|
if (auto __r2 = w.enterSubRecord(name))
|
|
|
|
{
|
|
|
|
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
|
|
|
pg.second.get().write(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-07-26 03:41:48 +00:00
|
|
|
if (!p.second.get()->m_midiSetups.empty())
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
if (auto __v2 = w.enterSubRecord("songs"))
|
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
for (const auto& song : SortUnorderedMap(p.second.get()->m_midiSetups))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
char songString[64];
|
|
|
|
snprintf(songString, 64, "%s/0x%04X", SongId::CurNameDB->resolveNameFromId(song.first).data(), int(song.first.id));
|
|
|
|
if (auto __v3 = w.enterSubVector(songString))
|
2018-07-15 06:10:50 +00:00
|
|
|
for (int i = 0; i < 16; ++i)
|
|
|
|
if (auto __r2 = w.enterSubRecord(nullptr))
|
|
|
|
{
|
|
|
|
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
|
|
|
song.second.get()[i].write(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!m_sfxGroups.empty())
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
if (auto __v = w.enterSubRecord("sfxGroups"))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
for (const auto& p : SortUnorderedMap(m_sfxGroups))
|
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
char groupString[64];
|
|
|
|
snprintf(groupString, 64, "%s/0x%04X", GroupId::CurNameDB->resolveNameFromId(p.first).data(), int(p.first.id));
|
|
|
|
if (auto __r = w.enterSubRecord(groupString))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
2018-07-26 03:41:48 +00:00
|
|
|
for (const auto& sfx : SortUnorderedMap(p.second.get()->m_sfxEntries))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
2018-07-17 04:48:38 +00:00
|
|
|
char sfxString[64];
|
|
|
|
snprintf(sfxString, 64, "%s/0x%04X", SFXId::CurNameDB->resolveNameFromId(sfx.first).data(), int(sfx.first.id));
|
|
|
|
if (auto __r2 = w.enterSubRecord(sfxString))
|
2018-07-15 06:10:50 +00:00
|
|
|
{
|
|
|
|
w.setStyle(athena::io::YAMLNodeStyle::Flow);
|
|
|
|
sfx.second.get().write(w);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-16 07:41:15 +00:00
|
|
|
SystemString projPath(groupPath);
|
|
|
|
projPath += _S("/!project.yaml");
|
|
|
|
athena::io::FileWriter fo(projPath);
|
|
|
|
return w.finish(&fo);
|
2018-07-15 06:10:50 +00:00
|
|
|
}
|
|
|
|
|
2016-05-09 07:22:58 +00:00
|
|
|
}
|