summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2023-09-03 20:11:35 -0600
committerKiritoDv <kiritodev01@gmail.com>2023-09-03 20:11:35 -0600
commit063224c69d564d6e10dd4febf25caa9f59fccfda (patch)
treec1edbd90c120d7b846490159540f68adfddfbb5a /src
parent113472302904ac84d4ddf70df32ca245ad156bf2 (diff)
Fixed AIFC Extraction
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp6
-rw-r--r--src/audio/AIFCWriter.cpp48
-rw-r--r--src/audio/AIFCWriter.h36
-rw-r--r--src/audio/AudioManager.cpp231
-rw-r--r--src/audio/AudioManager.h23
-rw-r--r--src/factories/AudioFactory.cpp9
6 files changed, 309 insertions, 44 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index fe47842..1d2f371 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -51,7 +51,7 @@ void Companion::ProcessAssets() {
for( auto& [asset, data] : assets.items() ) {
std::string extension = fs::path(asset).extension().string();
if( gFactories.find(extension) == gFactories.end() ) {
- // std::cout << "No factory found for " << asset << '\n';
+ std::cout << "No factory found for " << asset << '\n';
continue;
}
LUS::BinaryWriter write = LUS::BinaryWriter();
@@ -63,7 +63,7 @@ void Companion::ProcessAssets() {
RawFactory* factory = gFactories.at(extension);
if(!factory->process(&write, entry, this->gRomData)){
- // std::cout << "Failed to process " << asset << '\n';
+ std::cout << "Failed to process " << asset << '\n';
continue;
}
@@ -84,7 +84,7 @@ void Companion::ProcessAssets() {
output.close();
}
- // std::cout << "Processed " << path << std::endl;
+ std::cout << "Processed " << path << std::endl;
write.Close();
}
diff --git a/src/audio/AIFCWriter.cpp b/src/audio/AIFCWriter.cpp
new file mode 100644
index 0000000..1df5813
--- /dev/null
+++ b/src/audio/AIFCWriter.cpp
@@ -0,0 +1,48 @@
+#include "AIFCWriter.h"
+
+#include "hj/pyutils.h"
+
+#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al)
+
+std::vector<char> AIFCWriter::pack(const std::string& str) {
+ std::vector<char> result;
+ result.insert(result.end(), str.begin(), str.end());
+ return result;
+}
+
+std::vector<char> AIFCWriter::pstring(const std::vector<char>& data) {
+ std::vector<char> result;
+ result.insert(result.end(), data.begin(), data.end());
+ if (data.size() % 2 == 0) {
+ result.push_back('\0');
+ }
+ return result;
+}
+
+void AIFCWriter::add_section(AIFC::MagicValues magic, const std::vector<char>& data) {
+ sections.emplace_back(magic, data);
+ total_size += ALIGN(data.size(), 2) + 8;
+}
+
+void AIFCWriter::add_custom_section(const std::string& section, const std::vector<char>& data) {
+ this->add_section(AIFC::MagicValues::AAPL, pack("stoc") + pstring(pack(section)) + data);
+}
+
+void AIFCWriter::finish() {
+ // total_size isn't used and is regularly wrong.
+ // In particular, vadpcm_enc preserves the size of the input file...
+ total_size += 4;
+
+ out.Write(BSWAP32(AIFC::MagicValues::FORM));
+ out.Write((uint32_t) BSWAP32(total_size));
+ out.Write(BSWAP32(AIFC::MagicValues::AIFC));
+
+ for (const auto& section : sections) {
+ out.Write((uint32_t) BSWAP32(section.first));
+ out.Write((uint32_t) BSWAP32(section.second.size()));
+ out.Write((char*) section.second.data(), section.second.size());
+ if(section.second.size() % 2) {
+ out.Write('\0');
+ }
+ }
+}
diff --git a/src/audio/AIFCWriter.h b/src/audio/AIFCWriter.h
new file mode 100644
index 0000000..9ea7843
--- /dev/null
+++ b/src/audio/AIFCWriter.h
@@ -0,0 +1,36 @@
+#pragma once
+
+#include <iostream>
+#include <vector>
+#include <string>
+#include <cstdint>
+#include "binarytools/BinaryWriter.h"
+
+namespace AIFC {
+ enum MagicValues {
+ FORM = 0x464f524d,
+ AIFC = 0x41494643,
+ COMM = 0x434f4d4d,
+ INST = 0x494e5354,
+ VAPC = 0x56415043,
+ SSND = 0x53534e44,
+ AAPL = 0x4150504c,
+ stoc = 0x73746f63,
+ };
+}
+
+class AIFCWriter {
+public:
+ AIFCWriter(LUS::BinaryWriter& out) : out(out), total_size(0) {}
+
+ static std::vector<char> pack(const std::string& str);
+ static std::vector<char> pstring(const std::vector<char>& data);
+ void add_section(AIFC::MagicValues, const std::vector<char>& data);
+ void add_custom_section(const std::string& section, const std::vector<char>& data);
+ void finish();
+
+private:
+ LUS::BinaryWriter& out;
+ std::vector<std::pair<AIFC::MagicValues, std::vector<char>>> sections;
+ std::size_t total_size;
+}; \ No newline at end of file
diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp
index 1f3e382..f241a9b 100644
--- a/src/audio/AudioManager.cpp
+++ b/src/audio/AudioManager.cpp
@@ -8,35 +8,43 @@
#include "hj/pyutils.h"
#include "binarytools/BinaryReader.h"
#include "hj/zip.h"
+#include "AIFCWriter.h"
#include <nlohmann/json.hpp>
std::unordered_map<std::string, uint32_t> name_table;
AudioManager* AudioManager::Instance;
+std::vector<uint32_t> PyUtils::range(uint32_t start, uint32_t end) {
+ std::vector<uint32_t> result;
+ for (uint32_t i = start; i < end; ++i) {
+ result.push_back(i);
+ }
+ return result;
+}
+
std::string gen_name(const std::string& prefix){
if(!name_table.contains(prefix)){
name_table[prefix] = 0;
}
- name_table[prefix] += 1;
- return prefix + "-" + std::to_string(name_table[prefix]);
+ return prefix + std::to_string(name_table[prefix]++);
}
-AifcEntry SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){
+AifcEntry* SampleBank::AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop){
assert(sampleSize % 2 == 0);
if(sampleSize % 9 != 0){
assert(sampleSize % 9 == 1);
sampleSize -= 1;
}
- AifcEntry entry;
+ AifcEntry* entry;
if(this->entries.contains(addr)){
entry = this->entries[addr];
- assert(entry.book == book);
- assert(entry.loop == loop);
- assert(entry.data.size() == sampleSize);
+ assert(entry->book == book);
+ assert(entry->loop == loop);
+ assert(entry->data.size() == sampleSize);
} else {
- entry = { gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop };
+ entry = new AifcEntry{ gen_name("aifc"), PyUtils::slice(this->data, addr, addr + sampleSize), book, loop };
this->entries[addr] = entry;
}
@@ -51,8 +59,8 @@ void Bank::print() const {
std::cout << "Envelopes: " << std::to_string(envelopes.size()) << std::endl;
std::cout << "All Instruments: " << std::to_string(allInsts.size()) << std::endl;
std::cout << "Inst Offsets: " << std::to_string(instOffsets.size()) << std::endl;
- std::cout << "Sample Bank: " << sampleBank.name << std::endl;
- std::cout << "Sample Bank Offset: " << std::to_string(sampleBank.offset) << std::endl;
+ std::cout << "Sample Bank: " << sampleBank->name << std::endl;
+ std::cout << "Sample Bank Offset: " << std::to_string(sampleBank->offset) << std::endl;
}
std::vector<Entry> AudioManager::parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL){
@@ -198,7 +206,7 @@ Book AudioManager::parse_book(uint32_t addr, std::vector<uint8_t>& bankData){
return book;
}
-AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank){
+AifcEntry* AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank* sampleBank){
LUS::BinaryReader reader((char*) data.data(), data.size());
reader.SetEndianness(LUS::Endianness::Big);
@@ -215,7 +223,7 @@ AifcEntry AudioManager::parse_sample(std::vector<uint8_t>& data, std::vector<uin
Book bookData = parse_book(book, bankData);
reader.Close();
- return sampleBank.AddSample(addr, sampleSize, bookData, loopData);
+ return sampleBank->AddSample(addr, sampleSize, bookData, loopData);
}
@@ -239,7 +247,7 @@ std::vector<EnvelopeData> AudioManager::parse_envelope(uint32_t addr, std::vecto
return entries;
}
-Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index) {
+Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank* bank, uint32_t index) {
name_table.clear();
std::ostringstream ss;
ss << std::hex << std::setw(2) << std::setfill('0') << index;
@@ -333,13 +341,13 @@ Bank AudioManager::parse_ctl(CTLHeader header, std::vector<uint8_t> data, Sample
allInsts.emplace_back(drums);
}
- std::unordered_map<uint32_t, AifcEntry> samples;
+ std::unordered_map<uint32_t, AifcEntry*> samples;
std::sort(sampleOffsets.begin(), sampleOffsets.end());
for(auto &offset : sampleOffsets){
auto rSample = PyUtils::slice(data, offset, offset + 20);
- AifcEntry sample = parse_sample(rSample, data, bank);
+ AifcEntry* sample = parse_sample(rSample, data, bank);
for(auto &tuning : tunings){
- sample.tunings.push_back(tuning.second);
+ sample->tunings.push_back(tuning.second);
}
samples[offset] = sample;
}
@@ -392,11 +400,11 @@ TBLFile AudioManager::parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>&
for(auto &entry : entries){
if(!cache.contains(entry.offset)){
std::string name = gen_name("sample_bank");
- SampleBank sampleBank = {
- name, entry.offset, PyUtils::slice(data, entry.offset, entry.offset + entry.length)
+ auto* sampleBank = new SampleBank{
+ name, entry.offset, PyUtils::slice(data, entry.offset, entry.offset + entry.length)
};
tbl.banks.push_back(sampleBank);
- tbl.map[name] = tbl.banks.size() - 1;
+ tbl.map[name] = sampleBank;
cache[entry.offset] = name;
}
tbl.tbls.push_back(cache[entry.offset]);
@@ -419,24 +427,189 @@ void AudioManager::initialize(std::vector<uint8_t>& buffer) {
std::vector<uint8_t> tbl_data = PyUtils::slice(buffer, sound_tbl[0], (size_t) sound_tbl[0] + (size_t) sound_tbl[1]);
std::vector<uint8_t> ctl_data = PyUtils::slice(buffer, sound_ctl[0], (size_t) sound_ctl[0] + (size_t) sound_ctl[1]);
- TBLFile tbl_file = parse_tbl(tbl_data, tbl);
+ this->loaded_tbl = parse_tbl(tbl_data, tbl);
- std::cout << "TBLs: " << tbl_file.tbls.size() << std::endl;
- std::cout << "Banks: " << tbl_file.banks.size() << std::endl;
- std::cout << "Map: " << tbl_file.map.size() << std::endl;
+ std::cout << "TBLs: " << this->loaded_tbl.tbls.size() << std::endl;
+ std::cout << "Banks: " << this->loaded_tbl.banks.size() << std::endl;
+ std::cout << "Map: " << this->loaded_tbl.map.size() << std::endl;
- auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, tbl_file.tbls);
+ auto zipped = zip(PyUtils::range(0, ctl.size()), ctl, this->loaded_tbl.tbls);
- std::cout << "================================" << std::endl;
+// std::cout << "================================" << std::endl;
for (const auto& item : zipped) {
auto [index, ctrl, sample_bank_name] = item;
- auto sample_bank = tbl_file.map[sample_bank_name];
+ auto sample_bank = this->loaded_tbl.map[sample_bank_name];
auto entry = PyUtils::slice(ctl_data, ctrl.offset, ctrl.offset + ctrl.length);
auto headerRaw = PyUtils::slice(entry, 0, 16);
auto header = parse_ctl_header(headerRaw);
- auto bank = parse_ctl(header, PyUtils::slice(entry, 16), tbl_file.banks[sample_bank], index);
+ auto bank = parse_ctl(header, PyUtils::slice(entry, 16), sample_bank, index);
banks.push_back(bank);
- bank.print();
- std::cout << "================================" << std::endl;
+// bank.print();
+// std::cout << "================================" << std::endl;
+ }
+
+
+}
+
+void serialize_f80(double num, LUS::BinaryWriter &writer) {
+ // Convert the input double to a uint64_t
+ std::uint64_t f64;
+ std::memcpy(&f64, &num, sizeof(double));
+
+ std::uint64_t f64_sign_bit = f64 & (std::uint64_t) pow(2, 63);
+ if (num == 0.0) {
+ if (f64_sign_bit) {
+ writer.Write(0x80000000);
+ } else {
+ writer.Write(0x00000000);
+ }
+ }
+
+ std::uint64_t exponent = ((f64 ^ f64_sign_bit) >> 52);
+
+ assert(exponent != 0);
+ assert(exponent != 0x7FF);
+
+ exponent -= 1023;
+ uint64_t f64_mantissa_bits = f64 & (uint64_t) pow(2, 52) - 1;
+ uint64_t f80_sign_bit = f64_sign_bit << (80 - 64);
+ uint64_t f80_exponent = (exponent + 0x3FFF) << 64;
+ uint64_t f80_mantissa_bits = (uint64_t) pow(2, 63) | (f64_mantissa_bits << (63 - 52));
+ uint64_t f80 = f80_sign_bit | f80_exponent | f80_mantissa_bits;
+
+ // Split the f80 representation into two parts (high and low)
+ uint16_t high = BSWAP16((uint16_t) f80 >> 64);
+ writer.Write((char*) &high, 2);
+ uint64_t low = BSWAP64(f80 & ((uint64_t) pow(2, 64) - 1));
+ writer.Write((char*) &low, 8);
+}
+
+#define START_SECTION(section) \
+ { \
+ out.Write(BSWAP32(section)); \
+ LUS::BinaryWriter tmp = LUS::BinaryWriter(); \
+ tmp.SetEndianness(LUS::Endianness::Big); \
+
+#define START_CUSTOM_SECTION(section) \
+ { \
+ LUS::BinaryWriter tmp = LUS::BinaryWriter(); \
+ tmp.SetEndianness(LUS::Endianness::Big); \
+ out.Write(BSWAP32(AIFC::MagicValues::AAPL)); \
+ tmp.Write(AIFC::MagicValues::stoc); \
+ tmp.Write(section, false); \
+
+#define END_SECTION() \
+ auto odata = tmp.ToVector(); \
+ size_t size = odata.size(); \
+ len += ALIGN(size, 2) + 8; \
+ out.Write(BSWAP32((uint32_t) size)); \
+ out.Write(odata.data(), odata.size()); \
+ if(size % 2){ \
+ out.WriteByte(0); \
+ } \
+ } \
+
+void AudioManager::write_aifc(AifcEntry* entry, LUS::BinaryWriter &out) {
+ int16_t num_channels = 1;
+ auto data = entry->data;
+ size_t len = 0;
+ assert(data.size() % 9 == 0);
+ if(data.size() % 2 == 1){
+ data.push_back('\0');
+ }
+ uint32_t num_frames = data.size() * 16 / 9;
+ int16_t sample_size = 16;
+
+ uint32_t sample_rate = -1;
+ if(entry->tunings.size() == 1){
+ sample_rate = 32000 * entry->tunings[0];
+ } else {
+ float tmin = PyUtils::min(entry->tunings);
+ float tmax = PyUtils::max(entry->tunings);
+
+ if(tmin <= 0.5f <= tmax){
+ sample_rate = 16000;
+ } else if(tmin <= 1.0f <= tmax){
+ sample_rate = 32000;
+ } else if(tmin <= 1.5f <= tmax){
+ sample_rate = 48000;
+ } else if(tmin <= 2.5f <= tmax){
+ sample_rate = 80000;
+ } else {
+ sample_rate = 16000 * (tmin + tmax);
+ }
+ }
+
+ out.Write(BSWAP32(AIFC::MagicValues::FORM));
+ // This should be where the size is, but we need to write it later
+ out.Write((uint32_t) 0);
+ out.Write(BSWAP32(AIFC::MagicValues::AIFC));
+
+ START_SECTION(AIFC::MagicValues::COMM);
+
+ tmp.Write((uint16_t) num_channels);
+ tmp.Write((uint32_t) num_frames);
+
+ tmp.Write((uint16_t) sample_size);
+ serialize_f80(sample_rate, tmp);
+ tmp.Write(AIFC::MagicValues::VAPC);
+ tmp.Write("\u000BVADPCM ~4-1", false);
+
+ END_SECTION();
+
+ START_SECTION(AIFC::MagicValues::INST)
+ tmp.Write(std::string(20, '\0'), false);
+ END_SECTION();
+
+ START_CUSTOM_SECTION("\u000BVADPCMCODES")
+ tmp.Write((uint16_t) 1);
+ tmp.Write((uint16_t) entry->book.order);
+ tmp.Write((uint16_t) entry->book.npredictors);
+
+ for(auto x : entry->book.table){
+ tmp.Write((int16_t) x);
+ }
+ END_SECTION();
+
+ START_SECTION(AIFC::MagicValues::SSND)
+ uint32_t zero = 0;
+ tmp.Write((char*) &zero, 4);
+ tmp.Write((char*) &zero, 4);
+ tmp.Write((char*) data.data(), data.size());
+ END_SECTION();
+
+ if(entry->loop.count != 0){
+ START_CUSTOM_SECTION("\u000BVADPCMLOOPS")
+ uint16_t one = BSWAP16(1);
+ tmp.Write((char*) &one, 2);
+ tmp.Write((char*) &one, 2);
+ tmp.Write((uint32_t) entry->loop.start);
+ tmp.Write((uint32_t) entry->loop.end);
+ tmp.Write((int32_t) entry->loop.count);
+ for(size_t i = 0; i < 16; i++){
+ int16_t loop = BSWAP16(entry->loop.state.value()[i]);
+ tmp.Write((char*) &loop, 2);
+ }
+ END_SECTION();
+ }
+
+ len += 4;
+ out.Seek(4, LUS::SeekOffsetType::Start);
+ out.Write((uint32_t) BSWAP32(len));
+
+}
+
+void AudioManager::create_aifc(int32_t index, LUS::BinaryWriter &out) {
+ int32_t idx = -1;
+ for(auto &sample_bank : this->loaded_tbl.banks){
+ auto offsets = PyUtils::keys(sample_bank->entries);
+ std::sort(offsets.begin(), offsets.end());
+
+ for(auto &offset : offsets){
+ if(++idx == index){
+ write_aifc(sample_bank->entries[offset], out);
+ return;
+ }
+ }
}
}
diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h
index decb0b5..6a1c559 100644
--- a/src/audio/AudioManager.h
+++ b/src/audio/AudioManager.h
@@ -4,6 +4,7 @@
#include <string>
#include <iostream>
#include <unordered_map>
+#include "binarytools/BinaryWriter.h"
#define NONE 0xFFFF
#define ALIGN(val, al) (size_t) ((val + (al - 1)) & -al)
@@ -77,15 +78,15 @@ struct SampleBank {
std::string name;
uint32_t offset;
std::vector<uint8_t> data;
- std::unordered_map<uint32_t, AifcEntry> entries;
+ std::unordered_map<uint32_t, AifcEntry*> entries;
- AifcEntry AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop);
+ AifcEntry* AddSample(uint32_t addr, size_t sampleSize, const Book& book, const Loop& loop);
};
struct TBLFile {
- std::vector<SampleBank> banks;
+ std::vector<SampleBank*> banks;
std::vector<std::string> tbls;
- std::unordered_map<std::string, uint32_t> map;
+ std::unordered_map<std::string, SampleBank*> map;
};
struct CTLHeader {
@@ -103,13 +104,13 @@ struct Envelope {
struct Bank {
std::string name;
- SampleBank sampleBank;
+ SampleBank* sampleBank;
std::vector<Inst> insts;
std::vector<Drum> drums;
std::vector<std::variant<Inst, std::vector<Drum>>> allInsts;
std::vector<uint32_t> instOffsets;
std::unordered_map<uint32_t, Envelope> envelopes;
- std::unordered_map<uint32_t, AifcEntry> samples;
+ std::unordered_map<uint32_t, AifcEntry*> samples;
void print() const;
};
@@ -117,8 +118,12 @@ class AudioManager {
public:
static AudioManager* Instance;
void initialize(std::vector<uint8_t>& buffer);
+ void create_aifc(int32_t index, LUS::BinaryWriter& writer);
private:
+ std::vector<Bank> banks;
+ TBLFile loaded_tbl;
+
static std::vector<Entry> parse_seq_file(std::vector<uint8_t>& buffer, uint32_t offset, bool isCTL);
static CTLHeader parse_ctl_header(std::vector<uint8_t>& data);
static std::optional<Sound> parse_sound(std::vector<uint8_t> data);
@@ -126,10 +131,10 @@ private:
static Inst parse_inst(std::vector<uint8_t>& data, uint32_t addr);
static Loop parse_loop(uint32_t addr, std::vector<uint8_t>& bankData);
static Book parse_book(uint32_t addr, std::vector<uint8_t>& bankData);
- static AifcEntry parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank& sampleBank);
+ static AifcEntry* parse_sample(std::vector<uint8_t>& data, std::vector<uint8_t>& bankData, SampleBank* sampleBank);
static std::vector<EnvelopeData> parse_envelope(uint32_t addr, std::vector<uint8_t>& dataBank);
- static Bank parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank bank, uint32_t index);
+ static Bank parse_ctl(CTLHeader header, std::vector<uint8_t> data, SampleBank* bank, uint32_t index);
static TBLFile parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>& entries);
- std::vector<Bank> banks;
+ static void write_aifc(AifcEntry* entry, LUS::BinaryWriter& writer);
}; \ No newline at end of file
diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp
index 185d740..64c940e 100644
--- a/src/factories/AudioFactory.cpp
+++ b/src/factories/AudioFactory.cpp
@@ -1,6 +1,7 @@
#include "AudioFactory.h"
#include <vector>
+#include "audio/AudioManager.h"
#include "binarytools/BinaryReader.h"
bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) {
@@ -17,11 +18,13 @@ bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std:
WRITE_I16(BSWAP16(value));
}
writer->Write(raw + 0x46, 0x5A);
+ return true;
}
- if(path.ends_with(".aiff")){
-
+ if(!metadata[1].contains("us")){
+ return false;
}
- return false;
+ AudioManager::Instance->create_aifc(metadata[1]["us"][1], *writer);
+ return true;
}