diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.cpp | 131 | ||||
| -rw-r--r-- | src/Companion.h | 12 | ||||
| -rw-r--r-- | src/audio/AudioManager.cpp | 20 | ||||
| -rw-r--r-- | src/audio/AudioManager.h | 3 | ||||
| -rw-r--r-- | src/factories/AnimFactory.cpp | 39 | ||||
| -rw-r--r-- | src/factories/AnimFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/AudioFactory.cpp | 191 | ||||
| -rw-r--r-- | src/factories/AudioFactory.h | 9 | ||||
| -rw-r--r-- | src/factories/AudioHeaderFactory.cpp | 10 | ||||
| -rw-r--r-- | src/factories/AudioHeaderFactory.h | 9 | ||||
| -rw-r--r-- | src/factories/BankFactory.cpp | 94 | ||||
| -rw-r--r-- | src/factories/BankFactory.h | 9 | ||||
| -rw-r--r-- | src/factories/BlobFactory.cpp | 25 | ||||
| -rw-r--r-- | src/factories/BlobFactory.h | 8 | ||||
| -rw-r--r-- | src/factories/RawFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/SampleFactory.cpp | 46 | ||||
| -rw-r--r-- | src/factories/SampleFactory.h | 9 | ||||
| -rw-r--r-- | src/factories/SequenceFactory.cpp | 21 | ||||
| -rw-r--r-- | src/factories/SequenceFactory.h | 9 | ||||
| -rw-r--r-- | src/factories/TextureFactory.cpp | 77 | ||||
| -rw-r--r-- | src/factories/TextureFactory.h | 15 | ||||
| -rw-r--r-- | src/main.cpp | 2 | ||||
| -rw-r--r-- | src/n64/Cartridge.cpp | 59 | ||||
| -rw-r--r-- | src/n64/Cartridge.h | 30 |
24 files changed, 467 insertions, 367 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp index df793be..d234ee7 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -5,92 +5,105 @@ #include "audio/AudioManager.h" #include "factories/RawFactory.h" #include "factories/BlobFactory.h" -#include "factories/AudioFactory.h" +#include "factories/AudioHeaderFactory.h" +#include "factories/SequenceFactory.h" +#include "factories/SampleFactory.h" +#include "factories/BankFactory.h" #include "factories/TextureFactory.h" #include "factories/AnimFactory.h" +#include "n64/Cartridge.h" #include <fstream> #include <iostream> #include <filesystem> -#include <nlohmann/json.hpp> -using json = nlohmann::json; namespace fs = std::filesystem; -static const std::unordered_map<std::string, RawFactory*> gFactories = { - { ".png", new TextureFactory() }, - { ".anim", new AnimFactory() }, - { ".aiff", new AudioFactory() }, - { ".bnk", new AudioFactory() }, - { ".bset", new AudioFactory() }, - { ".m64", new AudioFactory() }, - { ".bin", new BlobFactory(LUS::ResourceType::Blob) }, - { ".sbox", new BlobFactory(LUS::ResourceType::Blob, true) }, -}; +void Companion::Init() { -void Companion::Start() { - std::cout << "Super Mario 64 Rom Path: " << this->gRomPath << '\n'; + this->RegisterFactory("AUDIO:HEADER", new AudioHeaderFactory()); + this->RegisterFactory("SEQUENCE", new SequenceFactory()); + this->RegisterFactory("SAMPLE", new SampleFactory()); + this->RegisterFactory("BANK", new BankFactory()); + + this->RegisterFactory("TEXTURE", new TextureFactory()); + this->RegisterFactory("BLOB", new BlobFactory()); + + // SM64 Specific + + this->RegisterFactory("SM64:ANIM", new AnimFactory()); + this->Process(); +} + +void Companion::Process() { std::ifstream input( this->gRomPath, std::ios::binary ); this->gRomData = std::vector<uint8_t>( std::istreambuf_iterator<char>( input ), {} ); input.close(); - // TODO: Validate hash + N64::Cartridge cartridge = N64::Cartridge(this->gRomData); + cartridge.Initialize(); - std::cout << "Detected Rom Size: " << this->gRomData.size() << '\n'; + YAML::Node config = YAML::LoadFile("config.yml"); - AudioManager::Instance = new AudioManager(); - AudioManager::Instance->initialize(this->gRomData); - - this->ProcessAssets(); -} + if(!config[cartridge.GetHash()]){ + std::cout << "No config found for " << cartridge.GetHash() << '\n'; + return; + } -void Companion::ProcessAssets() { - json assets = json::parse( std::ifstream( "assets.json" ) ); + auto cfg = config[cartridge.GetHash()]; + auto path = cfg["path"].as<std::string>(); + auto otr = cfg["output"].as<std::string>(); - SWrapper wrapper = SWrapper("smcube.otr"); + auto wrapper = SWrapper(otr); - 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'; - continue; - } - LUS::BinaryWriter write = LUS::BinaryWriter(); - std::string path = asset.substr(0, asset.find_last_of('.')); - json entry = { - { "path", path }, - { "offsets", data } - }; - RawFactory* factory = gFactories.at(extension); - - if(!factory->process(&write, entry, this->gRomData)){ - std::cout << "Failed to process " << asset << '\n'; - continue; - } + for (const auto & entry : fs::directory_iterator(path)){ + std::cout << "Processing " << entry.path() << '\n'; + YAML::Node root = YAML::LoadFile(entry.path()); + for(auto asset = root.begin(); asset != root.end(); ++asset){ + auto type = asset->second["type"].as<std::string>(); - bool isTexture = typeid(*factory) == typeid(TextureFactory); + LUS::BinaryWriter write = LUS::BinaryWriter(); + RawFactory* factory = this->GetFactory(type); - auto buffer = write.ToVector(); - // TODO: Change this that we all know its going to crash with other assets - wrapper.CreateFile(isTexture ? path.substr(0, path.find_last_of('.')) : path, buffer); + if(!factory->process(&write, asset->second, this->gRomData)){ + std::cout << "Failed to process " << asset->first.as<std::string>() << '\n'; + continue; + } - if(!isTexture) { - // Write to file too - std::string dpath = "debug/" + path; - if(!fs::exists(fs::path(dpath).parent_path())){ - fs::create_directories(fs::path(dpath).parent_path()); + auto buffer = write.ToVector(); + auto output = entry.path().stem() / asset->first.as<std::string>(); + std::cout << "Writing " << output << '\n'; + wrapper.CreateFile(output, buffer); + + if(type != "TEXTURE") { + std::string dpath = "debug/" + path; + if(!fs::exists(fs::path(dpath).parent_path())){ + fs::create_directories(fs::path(dpath).parent_path()); + } + std::ofstream stream(dpath, std::ios::binary); + stream.write((char*)buffer.data(), buffer.size()); + stream.close(); } - std::ofstream output(dpath, std::ios::binary); - output.write((char*)buffer.data(), buffer.size()); - output.close(); - } - std::cout << "Processed " << path << std::endl; + std::cout << "Processed " << path << std::endl; - write.Close(); + write.Close(); + } } MIO0Decoder::ClearCache(); wrapper.Close(); -}
\ No newline at end of file +} + +void Companion::RegisterFactory(const std::string &extension, RawFactory *factory) { + this->gFactories[extension] = factory; +} + +RawFactory *Companion::GetFactory(const std::string &extension) { + if(!this->gFactories.contains(extension)){ + throw std::runtime_error("No factory found for " + extension); + } + + return this->gFactories[extension]; +} diff --git a/src/Companion.h b/src/Companion.h index 28a12eb..6bf6130 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -1,17 +1,23 @@ #pragma once #include <string> +#include <utility> #include <vector> #include <unordered_map> #include <filesystem> +#include "factories/RawFactory.h" class Companion { public: static Companion* Instance; - Companion(const std::filesystem::path& rom) : gRomPath(rom) {} - void Start(); - void ProcessAssets(); + explicit Companion(std::filesystem::path rom) : gRomPath(std::move(rom)) {} + void Init(); + void Process(); private: std::filesystem::path gRomPath; std::vector<uint8_t> gRomData; + std::unordered_map<std::string, RawFactory*> gFactories; + + void RegisterFactory(const std::string& type, RawFactory* factory); + RawFactory* GetFactory(const std::string& type); };
\ No newline at end of file diff --git a/src/audio/AudioManager.cpp b/src/audio/AudioManager.cpp index 2316141..2bd1960 100644 --- a/src/audio/AudioManager.cpp +++ b/src/audio/AudioManager.cpp @@ -8,7 +8,6 @@ #include "hj/pyutils.h" #include "binarytools/BinaryReader.h" #include "hj/zip.h" -#include <nlohmann/json.hpp> std::unordered_map<std::string, uint32_t> name_table; AudioManager* AudioManager::Instance; @@ -429,21 +428,22 @@ TBLFile AudioManager::parse_tbl(std::vector<uint8_t>& data, std::vector<Entry>& return tbl; } -void AudioManager::initialize(std::vector<uint8_t>& buffer) { - auto assets = nlohmann::json::parse(std::ifstream( "assets.json" )); - auto sound_data = assets["@sound_data"]["us"]; +void AudioManager::initialize(std::vector<uint8_t>& buffer, YAML::Node& data) { - auto sound_tbl = sound_data["sound_tbl"]; - auto sound_ctl = sound_data["sound_ctl"]; + auto ctlOffset = data["ctl"]["offset"].as<size_t>(); + auto ctlSize = data["ctl"]["size"].as<size_t>(); - std::vector<Entry> tbl = parse_seq_file(buffer, sound_tbl[0], false); - std::vector<Entry> ctl = parse_seq_file(buffer, sound_ctl[0], true); + auto tblOffset = data["tbl"]["offset"].as<size_t>(); + auto tblSize = data["tbl"]["size"].as<size_t>(); + + std::vector<Entry> tbl = parse_seq_file(buffer, tblOffset, false); + std::vector<Entry> ctl = parse_seq_file(buffer, ctlOffset, true); std::cout << "Table entries: " << tbl.size() << std::endl; std::cout << "Control entries: " << ctl.size() << std::endl; - 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]); + std::vector<uint8_t> tbl_data = PyUtils::slice(buffer, tblOffset, tblOffset + tblSize); + std::vector<uint8_t> ctl_data = PyUtils::slice(buffer, ctlOffset, ctlOffset + ctlSize); this->loaded_tbl = parse_tbl(tbl_data, tbl); std::cout << "TBLs: " << this->loaded_tbl.tbls.size() << std::endl; diff --git a/src/audio/AudioManager.h b/src/audio/AudioManager.h index 4b5d9ce..23aa875 100644 --- a/src/audio/AudioManager.h +++ b/src/audio/AudioManager.h @@ -4,6 +4,7 @@ #include <vector> #include <string> #include <iostream> +#include <yaml-cpp/yaml.h> #include "binarytools/BinaryWriter.h" #define NONE 0xFFFF @@ -134,7 +135,7 @@ struct Bank { class AudioManager { public: static AudioManager* Instance; - void initialize(std::vector<uint8_t>& buffer); + void initialize(std::vector<uint8_t>& buffer, YAML::Node& data); void create_aifc(int32_t index, LUS::BinaryWriter& writer); AudioBankSample get_aifc(int32_t index); std::map<uint32_t, Bank> get_banks(); diff --git a/src/factories/AnimFactory.cpp b/src/factories/AnimFactory.cpp index 7727220..5acd0ab 100644 --- a/src/factories/AnimFactory.cpp +++ b/src/factories/AnimFactory.cpp @@ -8,8 +8,11 @@ namespace fs = std::filesystem; -void WriteAnimationHeader(LUS::BinaryWriter* writer, nlohmann::json& offsets, std::vector<uint8_t>& buffer) { - LUS::BinaryReader reader((char*) buffer.data() + (size_t)offsets[0], offsets[1]); +void WriteAnimationHeader(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + auto offset = data["offset"].as<uint32_t>(); + auto size = data["size"].as<uint32_t>(); + + LUS::BinaryReader reader((char*) buffer.data() + offset, size); reader.SetEndianness(LUS::Endianness::Big); auto flags = reader.ReadInt16(); @@ -32,10 +35,13 @@ void WriteAnimationHeader(LUS::BinaryWriter* writer, nlohmann::json& offsets, st reader.Close(); } -void WriteAnimationIndex(LUS::BinaryWriter* writer, nlohmann::json& offsets, std::vector<uint8_t>& buffer) { - LUS::BinaryReader reader((char*) buffer.data() + (size_t)offsets[0], offsets[1]); +void WriteAnimationIndex(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + auto offset = data["offset"].as<uint32_t>(); + auto size = data["size"].as<uint32_t>(); + + LUS::BinaryReader reader((char*) buffer.data() + offset, size); reader.SetEndianness(LUS::Endianness::Big); - size_t entries = ((size_t) offsets[1]); + size_t entries = size / sizeof(uint16_t); WRITE_U32(entries); for(size_t id = 0; id < entries; id++) { WRITE_I16(reader.ReadInt16()); @@ -43,10 +49,13 @@ void WriteAnimationIndex(LUS::BinaryWriter* writer, nlohmann::json& offsets, std reader.Close(); } -void WriteAnimationValues(LUS::BinaryWriter* writer, nlohmann::json& offsets, std::vector<uint8_t>& buffer) { - LUS::BinaryReader reader((char*) buffer.data() + (size_t)offsets[0], offsets[1]); +void WriteAnimationValues(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + auto offset = data["offset"].as<uint32_t>(); + auto size = data["size"].as<uint32_t>(); + + LUS::BinaryReader reader((char*) buffer.data() + offset, size); reader.SetEndianness(LUS::Endianness::Big); - size_t entries = ((size_t) offsets[1]); + size_t entries = size / sizeof(uint16_t); WRITE_U32(entries); for(size_t id = 0; id < entries; id++) { WRITE_U16(reader.ReadUInt16()); @@ -54,14 +63,16 @@ void WriteAnimationValues(LUS::BinaryWriter* writer, nlohmann::json& offsets, st reader.Close(); } -bool AnimFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) { - auto metadata = data["offsets"]; - +bool AnimFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { WRITE_HEADER(LUS::ResourceType::Anim, 0); - WriteAnimationHeader(writer, metadata["header"], buffer); - WriteAnimationIndex( writer, metadata["indices"], buffer); - WriteAnimationValues(writer, metadata["values"], buffer); + auto header = data["header"]; + auto values = data["values"]; + auto index = data["indices"]; + + WriteAnimationHeader(writer, header, buffer); + WriteAnimationIndex(writer, values, buffer); + WriteAnimationValues(writer, index, buffer); return true; } diff --git a/src/factories/AnimFactory.h b/src/factories/AnimFactory.h index 7641099..c1ca50d 100644 --- a/src/factories/AnimFactory.h +++ b/src/factories/AnimFactory.h @@ -5,5 +5,5 @@ class AnimFactory : public RawFactory { public: AnimFactory() = default; - bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector<uint8_t>& buffer) override; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; };
\ No newline at end of file diff --git a/src/factories/AudioFactory.cpp b/src/factories/AudioFactory.cpp deleted file mode 100644 index bd91a20..0000000 --- a/src/factories/AudioFactory.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "AudioFactory.h" - -#include <vector> -#include "audio/AudioManager.h" -#include "audio/samples_table.h" -#include "binarytools/BinaryReader.h" - -bool AudioFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) { - auto metadata = data["offsets"]; - std::string path = data["path"]; - - if(path.find("bank_sets") != std::string::npos){ - WRITE_HEADER(LUS::ResourceType::Blob, 0); - char* raw = (char*) (buffer.data() + metadata[1]["us"][0]); - WRITE_U32(metadata[0]); - for(int i = 0; i < 0x23; i++){ - int16_t value; - memcpy(&value, raw + (i * 2), 2); - WRITE_I16(BSWAP16(value)); - } - writer->Write(raw + 0x46, 0x5A); - return true; - } - - if(path.find("sound/banks") != std::string::npos){ - auto banks = AudioManager::Instance->get_banks(); - auto bankId = metadata["us"][0]; - auto bank = banks[bankId]; - - if(bankId == 10){ - int bp = 1; - } - - WRITE_HEADER(LUS::ResourceType::Bank, 0); - - WRITE_U32(bank.insts.size()); - for(auto &instrument : bank.insts){ - WRITE_U8(instrument.valid); - if(!instrument.valid){ - continue; - } - WRITE_U8(instrument.releaseRate); - WRITE_U8(instrument.normalRangeLo); - WRITE_U8(instrument.normalRangeHi); - - auto envelope = bank.envelopes[instrument.envelope]; - if(!envelope.entries.empty()){ - WRITE_U32(envelope.entries.size()); - for(auto &entry : envelope.entries){ - writer->Write(entry.delay); - writer->Write(entry.arg); - } - } else { - WRITE_U32(0); - } - - uint32_t soundFlags = 0; - bool hasLo = instrument.soundLo.has_value(); - bool hasMed = instrument.soundMed.has_value(); - bool hasHi = instrument.soundHi.has_value(); - if(hasLo){ - soundFlags |= (1 << 0); - } - if(hasMed){ - soundFlags |= (1 << 1); - } - if(hasHi){ - soundFlags |= (1 << 2); - } - - WRITE_U32(soundFlags); - if(hasLo){ - auto value = instrument.soundLo.value(); - uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); - writer->Write(std::string(gSampleTable[idx])); - writer->Write(value.tuning); - } - if(hasMed){ - auto value = instrument.soundMed.value(); - uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); - writer->Write(std::string(gSampleTable[idx])); - writer->Write(value.tuning); - } - if(hasHi){ - auto value = instrument.soundHi.value(); - uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); - writer->Write(std::string(gSampleTable[idx])); - writer->Write(value.tuning); - } - - } - - WRITE_U32(bank.drums.size()); - for(auto &drum : bank.drums){ - WRITE_U8(drum.releaseRate); - WRITE_U8(drum.pan); - auto envelope = bank.envelopes[drum.envelope]; - if(!envelope.entries.empty()){ - std::cout << "Envelope entries: " << envelope.entries.size() << std::endl; - WRITE_U32(envelope.entries.size()); - for(auto &entry : envelope.entries){ - writer->Write(entry.delay); - writer->Write(entry.arg); - } - } else { - WRITE_U32(0); - } - - uint32_t idx = AudioManager::Instance->get_index(bank.samples[drum.sound.offset]); - std::string out = std::string(gSampleTable[idx]); - writer->Write(out); - writer->Write(drum.sound.tuning); - } - - return true; - } - - if(path.find("sound/sequences") != std::string::npos){ - - if(!metadata[1].contains("us")){ - return false; - } - - WRITE_HEADER(LUS::ResourceType::Sequence, 0); - - size_t size = metadata[0]; - auto offsets = metadata[1]["us"]; - - std::vector<uint8_t> bank = metadata[1]["banks"]; - - WRITE_U32(bank.size()); - WRITE_ARRAY(bank.data(), bank.size()); - - WRITE_U32(size); - WRITE_ARRAY(buffer.data() + (size_t)offsets[0], size); - - return true; - } - - if(path.find("sound/samples") == std::string::npos || metadata.size() < 2 || !metadata[1].contains("us")){ - return false; - } - - /* - * HINT: Uncomment this to write the aiff to a file - LUS::BinaryWriter aifcWriter; - AudioManager::Instance->create_aifc(metadata[1]["us"][1], aifcWriter); - std::vector<char> aifcData = aifcWriter.ToVector(); - aifcWriter.Close(); - write_aiff(aifcData, *writer); - */ - - WRITE_HEADER(LUS::ResourceType::Sample, 0); - - AudioBankSample entry = AudioManager::Instance->get_aifc(metadata[1]["us"][1]); - - // Write AdpcmLoop - WRITE_U32(entry.loop.start); - WRITE_U32(entry.loop.end); - WRITE_I32(entry.loop.count); - WRITE_U32(entry.loop.pad); - - if(entry.loop.state.has_value()){ - std::vector<int16_t> state = entry.loop.state.value(); - WRITE_U32(state.size()); - for(auto &value : state){ - writer->Write(value); - } - } else { - WRITE_U32(0); - } - - // Write AdpcmBook - WRITE_I32(entry.book.order); - WRITE_I32(entry.book.npredictors); - - WRITE_U32(entry.book.table.size()); - for(auto &value : entry.book.table){ - writer->Write(value); - } - - // Write sample - WRITE_I32(entry.data.size()); - for(auto &value : entry.data){ - WRITE_U8(value); - } - - writer->Write(entry.name); - - return true; -}
\ No newline at end of file diff --git a/src/factories/AudioFactory.h b/src/factories/AudioFactory.h deleted file mode 100644 index fdfdb9c..0000000 --- a/src/factories/AudioFactory.h +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "RawFactory.h" - -class AudioFactory : public RawFactory { -public: - AudioFactory() = default; - bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector<uint8_t>& buffer) override; -};
\ No newline at end of file diff --git a/src/factories/AudioHeaderFactory.cpp b/src/factories/AudioHeaderFactory.cpp new file mode 100644 index 0000000..b493458 --- /dev/null +++ b/src/factories/AudioHeaderFactory.cpp @@ -0,0 +1,10 @@ +#include "AudioHeaderFactory.h" + +#include <vector> +#include "audio/AudioManager.h" + +bool AudioHeaderFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + AudioManager::Instance = new AudioManager(); + AudioManager::Instance->initialize(buffer, data); + return false; +}
\ No newline at end of file diff --git a/src/factories/AudioHeaderFactory.h b/src/factories/AudioHeaderFactory.h new file mode 100644 index 0000000..bd7336e --- /dev/null +++ b/src/factories/AudioHeaderFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "RawFactory.h" + +class AudioHeaderFactory : public RawFactory { +public: + AudioHeaderFactory() = default; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; +};
\ No newline at end of file diff --git a/src/factories/BankFactory.cpp b/src/factories/BankFactory.cpp new file mode 100644 index 0000000..252fd48 --- /dev/null +++ b/src/factories/BankFactory.cpp @@ -0,0 +1,94 @@ +#include "BankFactory.h" + +#include <vector> +#include "audio/AudioManager.h" +#include "audio/samples_table.h" +#include "binarytools/BinaryReader.h" + +bool BankFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + auto banks = AudioManager::Instance->get_banks(); + auto bankId = data["id"].as<uint32_t>(); + auto bank = banks[bankId]; + + WRITE_HEADER(LUS::ResourceType::Bank, 0); + + WRITE_U32(bank.insts.size()); + for(auto &instrument : bank.insts){ + WRITE_U8(instrument.valid); + if(!instrument.valid){ + continue; + } + WRITE_U8(instrument.releaseRate); + WRITE_U8(instrument.normalRangeLo); + WRITE_U8(instrument.normalRangeHi); + + auto envelope = bank.envelopes[instrument.envelope]; + if(!envelope.entries.empty()){ + WRITE_U32(envelope.entries.size()); + for(auto &entry : envelope.entries){ + writer->Write(entry.delay); + writer->Write(entry.arg); + } + } else { + WRITE_U32(0); + } + + uint32_t soundFlags = 0; + bool hasLo = instrument.soundLo.has_value(); + bool hasMed = instrument.soundMed.has_value(); + bool hasHi = instrument.soundHi.has_value(); + if(hasLo){ + soundFlags |= (1 << 0); + } + if(hasMed){ + soundFlags |= (1 << 1); + } + if(hasHi){ + soundFlags |= (1 << 2); + } + + WRITE_U32(soundFlags); + if(hasLo){ + auto value = instrument.soundLo.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + if(hasMed){ + auto value = instrument.soundMed.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + if(hasHi){ + auto value = instrument.soundHi.value(); + uint32_t idx = AudioManager::Instance->get_index(bank.samples[value.offset]); + writer->Write(std::string(gSampleTable[idx])); + writer->Write(value.tuning); + } + + } + + WRITE_U32(bank.drums.size()); + for(auto &drum : bank.drums){ + WRITE_U8(drum.releaseRate); + WRITE_U8(drum.pan); + auto envelope = bank.envelopes[drum.envelope]; + if(!envelope.entries.empty()){ + WRITE_U32(envelope.entries.size()); + for(auto &entry : envelope.entries){ + writer->Write(entry.delay); + writer->Write(entry.arg); + } + } else { + WRITE_U32(0); + } + + uint32_t idx = AudioManager::Instance->get_index(bank.samples[drum.sound.offset]); + std::string out = std::string(gSampleTable[idx]); + writer->Write(out); + writer->Write(drum.sound.tuning); + } + + return true; +}
\ No newline at end of file diff --git a/src/factories/BankFactory.h b/src/factories/BankFactory.h new file mode 100644 index 0000000..c19f137 --- /dev/null +++ b/src/factories/BankFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "RawFactory.h" + +class BankFactory : public RawFactory { +public: + BankFactory() = default; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; +};
\ No newline at end of file diff --git a/src/factories/BlobFactory.cpp b/src/factories/BlobFactory.cpp index 30714b1..e020079 100644 --- a/src/factories/BlobFactory.cpp +++ b/src/factories/BlobFactory.cpp @@ -1,29 +1,22 @@ #include "BlobFactory.h" - -#include <iostream> -#include <filesystem> -#include "Companion.h" #include "utils/MIODecoder.h" namespace fs = std::filesystem; -bool BlobFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) { - auto metadata = data["offsets"]; - if(!metadata[1].contains("us")){ - return false; - } +bool BlobFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { - WRITE_HEADER(this->type, 0); + WRITE_HEADER(LUS::ResourceType::Blob, 0); - size_t size = metadata[0]; - auto offsets = metadata[1]["us"]; + auto size = data["size"].as<size_t>(); + auto offset = data["offset"].as<size_t>(); auto* blob = new uint8_t[size]; - if(this->isMIOChunk){ - auto mio0 = MIO0Decoder::Decode(buffer, offsets[0]); - memcpy(blob, mio0.data() + (size_t)offsets[1], size); + if(data["mio0"]){ + auto decoded = MIO0Decoder::Decode(buffer, offset); + auto cursor = data["mio0"].as<size_t>(); + memcpy(blob, decoded.data() + cursor, size); } else { - memcpy(blob, buffer.data() + (size_t)offsets[0], size); + memcpy(blob, buffer.data() + offset, size); } WRITE_U32(size); // Blob Data Size diff --git a/src/factories/BlobFactory.h b/src/factories/BlobFactory.h index 73cb80b..302fb01 100644 --- a/src/factories/BlobFactory.h +++ b/src/factories/BlobFactory.h @@ -4,10 +4,6 @@ class BlobFactory : public RawFactory { public: - BlobFactory(LUS::ResourceType type, bool isMIOChunk = false, bool bigEndian = false) : type(type), isMIOChunk(isMIOChunk), bigEndian(bigEndian) {} - bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector<uint8_t>& buffer) override; -private: - bool isMIOChunk = false; - bool bigEndian = false; - LUS::ResourceType type = LUS::ResourceType::Blob; + BlobFactory() = default; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; };
\ No newline at end of file diff --git a/src/factories/RawFactory.h b/src/factories/RawFactory.h index 8918b8b..fba32e0 100644 --- a/src/factories/RawFactory.h +++ b/src/factories/RawFactory.h @@ -4,7 +4,7 @@ #include <string> #include <vector> #include <binarytools/BinaryWriter.h> -#include <nlohmann/json.hpp> +#include <yaml-cpp/yaml.h> #define WRITE_HEADER(type, version) this->WriteHeader(writer, type, version) #define WRITE_BHEADER(type, version) this->WriteHeader(writer, type, version, true) @@ -21,6 +21,6 @@ class RawFactory { public: RawFactory() = default; - virtual bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector<uint8_t>& buffer) = 0; + virtual bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) = 0; void WriteHeader(LUS::BinaryWriter* write, LUS::ResourceType resType, int32_t version, bool bigEndian = false); };
\ No newline at end of file diff --git a/src/factories/SampleFactory.cpp b/src/factories/SampleFactory.cpp new file mode 100644 index 0000000..7e2d1a1 --- /dev/null +++ b/src/factories/SampleFactory.cpp @@ -0,0 +1,46 @@ +#include "SampleFactory.h" + +#include <vector> +#include "audio/AudioManager.h" + +bool SampleFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + WRITE_HEADER(LUS::ResourceType::Sample, 0); + + auto id = data["id"].as<int32_t>(); + AudioBankSample entry = AudioManager::Instance->get_aifc(id); + + // Write AdpcmLoop + WRITE_U32(entry.loop.start); + WRITE_U32(entry.loop.end); + WRITE_I32(entry.loop.count); + WRITE_U32(entry.loop.pad); + + if(entry.loop.state.has_value()){ + std::vector<int16_t> state = entry.loop.state.value(); + WRITE_U32(state.size()); + for(auto &value : state){ + writer->Write(value); + } + } else { + WRITE_U32(0); + } + + // Write AdpcmBook + WRITE_I32(entry.book.order); + WRITE_I32(entry.book.npredictors); + + WRITE_U32(entry.book.table.size()); + for(auto &value : entry.book.table){ + writer->Write(value); + } + + // Write sample + WRITE_I32(entry.data.size()); + for(auto &value : entry.data){ + WRITE_U8(value); + } + + writer->Write(entry.name); + + return true; +}
\ No newline at end of file diff --git a/src/factories/SampleFactory.h b/src/factories/SampleFactory.h new file mode 100644 index 0000000..c0ad4ef --- /dev/null +++ b/src/factories/SampleFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "RawFactory.h" + +class SampleFactory : public RawFactory { +public: + SampleFactory() = default; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; +};
\ No newline at end of file diff --git a/src/factories/SequenceFactory.cpp b/src/factories/SequenceFactory.cpp new file mode 100644 index 0000000..5e3151b --- /dev/null +++ b/src/factories/SequenceFactory.cpp @@ -0,0 +1,21 @@ +#include "SequenceFactory.h" + +#include "audio/AudioManager.h" +#include <vector> + +bool SequenceFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { + WRITE_HEADER(LUS::ResourceType::Sequence, 0); + + auto size = data["size"].as<size_t>(); + auto offset = data["offset"].as<size_t>(); + + auto bank = data["banks"].as<std::vector<uint8_t>>(); + + WRITE_U32(bank.size()); + WRITE_ARRAY(bank.data(), bank.size()); + + WRITE_U32(size); + WRITE_ARRAY(buffer.data() + offset, size); + + return true; +}
\ No newline at end of file diff --git a/src/factories/SequenceFactory.h b/src/factories/SequenceFactory.h new file mode 100644 index 0000000..93e9b3e --- /dev/null +++ b/src/factories/SequenceFactory.h @@ -0,0 +1,9 @@ +#pragma once + +#include "RawFactory.h" + +class SequenceFactory : public RawFactory { +public: + SequenceFactory() = default; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; +};
\ No newline at end of file diff --git a/src/factories/TextureFactory.cpp b/src/factories/TextureFactory.cpp index 4c37fd5..12d8540 100644 --- a/src/factories/TextureFactory.cpp +++ b/src/factories/TextureFactory.cpp @@ -1,77 +1,48 @@ #include "TextureFactory.h" -#include <iostream> -#include <filesystem> #include "Companion.h" #include "utils/MIODecoder.h" namespace fs = std::filesystem; -enum class TextureType { - Error, - RGBA32bpp, - RGBA16bpp, - Palette4bpp, - Palette8bpp, - Grayscale4bpp, - Grayscale8bpp, - GrayscaleAlpha4bpp, - GrayscaleAlpha8bpp, - GrayscaleAlpha16bpp, -}; - static const std::unordered_map <std::string, TextureType> gTextureTypes = { - { ".rgba16", TextureType::RGBA16bpp }, - { ".rgba32", TextureType::RGBA32bpp }, - { ".ia1", TextureType::GrayscaleAlpha4bpp }, - { ".ia4", TextureType::GrayscaleAlpha4bpp }, - { ".ia8", TextureType::GrayscaleAlpha8bpp }, - { ".ia16", TextureType::GrayscaleAlpha16bpp }, + { "RGBA16", TextureType::RGBA16bpp }, + { "RGBA32", TextureType::RGBA32bpp }, + { "IA1", TextureType::GrayscaleAlpha4bpp }, + { "IA4", TextureType::GrayscaleAlpha4bpp }, + { "IA8", TextureType::GrayscaleAlpha8bpp }, + { "IA16", TextureType::GrayscaleAlpha16bpp }, }; -bool TextureFactory::process(LUS::BinaryWriter* writer, nlohmann::json& data, std::vector<uint8_t>& buffer) { - std::string path = data["path"]; - std::string ext = fs::path(path).extension().string(); +bool TextureFactory::process(LUS::BinaryWriter* writer, YAML::Node& data, std::vector<uint8_t>& buffer) { - bool isTileTexture = path.find("cake") != std::string::npos || path.find("skyboxes") != std::string::npos; + auto format = data["format"].as<std::string>(); + auto width = data["width"].as<uint32_t>(); + auto height = data["height"].as<uint32_t>(); + auto size = data["size"].as<size_t>(); + auto offset = data["offset"].as<size_t>(); - if(isTileTexture) { + if(!gTextureTypes.contains(format)) { return false; } - if(!gTextureTypes.contains(ext)) { - return false; - } - - auto metadata = data["offsets"]; - if(!metadata[3].contains("us")){ - return false; - } - - // Path: [Width, Height, Size, { Country : [Rom Offset, MIO0 Size] }], - - TextureType type = isTileTexture ? TextureType::RGBA32bpp : gTextureTypes.at(ext); + TextureType type = gTextureTypes.at(format); WRITE_HEADER(LUS::ResourceType::Texture, 0); - WRITE_U32(type); // Texture Type - WRITE_U32(metadata[0]); // Width - WRITE_U32(metadata[1]); // Height - size_t size = metadata[2]; - auto offsets = metadata[3]["us"]; + WRITE_U32(type); + WRITE_U32(width); + WRITE_U32(height); - auto* texture = new uint8_t[size]; - - if(offsets.size() > 1){ - auto mio0 = MIO0Decoder::Decode(buffer, offsets[0]); - memcpy(texture, mio0.data() + (size_t)offsets[1], size); + if(data["mio0"]){ + auto mio0 = data["mio0"].as<size_t>(); + auto decoded = MIO0Decoder::Decode(buffer, offset); + WRITE_U32(size); + WRITE_ARRAY(decoded.data() + mio0, size); } else { - memcpy(texture, buffer.data() + (size_t)offsets[0], size); + WRITE_U32(size); + WRITE_ARRAY(buffer.data() + offset, size); } - WRITE_U32(size); // Texture Data Size - WRITE_ARRAY(texture, size); // Texture Data - - delete[] texture; return true; } diff --git a/src/factories/TextureFactory.h b/src/factories/TextureFactory.h index 88ebbdf..c0349d6 100644 --- a/src/factories/TextureFactory.h +++ b/src/factories/TextureFactory.h @@ -2,8 +2,21 @@ #include "RawFactory.h" +enum class TextureType { + Error, + RGBA32bpp, + RGBA16bpp, + Palette4bpp, + Palette8bpp, + Grayscale4bpp, + Grayscale8bpp, + GrayscaleAlpha4bpp, + GrayscaleAlpha8bpp, + GrayscaleAlpha16bpp, +}; + class TextureFactory : public RawFactory { public: TextureFactory() = default; - bool process(LUS::BinaryWriter* write, nlohmann::json& data, std::vector<uint8_t>& buffer) override; + bool process(LUS::BinaryWriter* write, YAML::Node& data, std::vector<uint8_t>& buffer) override; };
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index ad50334..ab1f8d3 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,6 @@ int main(int argc, char *argv[]) { } Companion::Instance = new Companion(filename); - Companion::Instance->Start(); + Companion::Instance->Init(); return 0; }
\ No newline at end of file diff --git a/src/n64/Cartridge.cpp b/src/n64/Cartridge.cpp new file mode 100644 index 0000000..96e150c --- /dev/null +++ b/src/n64/Cartridge.cpp @@ -0,0 +1,59 @@ +#include "Cartridge.h" + +#include "hj/sha1.h" +#include "binarytools/BinaryReader.h" + +void N64::Cartridge::Initialize() { + LUS::BinaryReader reader((char*) this->gRomData.data(), this->gRomData.size()); + reader.SetEndianness(LUS::Endianness::Big); + reader.Seek(0x20, LUS::SeekOffsetType::Start); + this->gGameTitle = std::string(reader.ReadCString()); + reader.Seek(0x3E, LUS::SeekOffsetType::Start); + uint8_t country = reader.ReadUByte(); + this->gVersion = reader.ReadUByte(); + this->gHash = Chocobo1::SHA1().addData(this->gRomData).finalize().toString(); + switch (country) { + case 'J': + this->gCountryCode = CountryCode::Japan; + break; + case 'E': + this->gCountryCode = CountryCode::NorthAmerica; + break; + case 'P': + this->gCountryCode = CountryCode::Europe; + break; + default: + this->gCountryCode = CountryCode::Unknown; + break; + } + reader.Close(); +} + +const std::string &N64::Cartridge::GetGameTitle() { + return this->gGameTitle; +} + +N64::CountryCode N64::Cartridge::GetCountry() { + return this->gCountryCode; +} + +uint8_t N64::Cartridge::GetVersion() const { + return this->gVersion; +} + +std::string N64::Cartridge::GetHash() { + return this->gHash; +} + +std::string N64::Cartridge::GetCountryCode() { + switch (this->gCountryCode) { + case CountryCode::Japan: + return "jp"; + case CountryCode::NorthAmerica: + return "us"; + case CountryCode::Europe: + return "eu"; + default: + return "unk"; + } +} diff --git a/src/n64/Cartridge.h b/src/n64/Cartridge.h new file mode 100644 index 0000000..e53b8ef --- /dev/null +++ b/src/n64/Cartridge.h @@ -0,0 +1,30 @@ +#pragma once + +#include <vector> +#include <string> + +namespace N64 { +enum class CountryCode { + Japan, + NorthAmerica, + Europe, + Unknown +}; + +class Cartridge { +public: + explicit Cartridge(const std::vector<uint8_t>& romData) : gRomData(romData), gCountryCode(CountryCode::Unknown), gVersion(0), gGameTitle("Unknown") {} + void Initialize(); + const std::string& GetGameTitle(); + std::string GetCountryCode(); + CountryCode GetCountry(); + uint8_t GetVersion() const; + std::string GetHash(); +private: + std::vector<uint8_t> gRomData; + CountryCode gCountryCode; + uint8_t gVersion; + std::string gGameTitle; + std::string gHash; +}; +}
\ No newline at end of file |
