From 714b555d13ee1f2cfe107353dbfd76b81c993b62 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Sun, 24 Nov 2024 03:00:38 -0600 Subject: [PATCH 1/6] Added XML Extraction --- CMakeLists.txt | 19 ++- src/Companion.cpp | 170 +++++++++------------- src/Companion.h | 2 + src/factories/BaseFactory.h | 6 +- src/factories/naudio/v1/AudioContext.cpp | 57 +++++++- src/factories/naudio/v1/AudioContext.h | 4 + src/factories/naudio/v1/SampleFactory.cpp | 58 ++++++++ src/factories/naudio/v1/SampleFactory.h | 5 + src/main.cpp | 9 +- 9 files changed, 219 insertions(+), 111 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 365feca..fdbe317 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -165,20 +165,17 @@ endif() add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/binarytools) add_dependencies(${PROJECT_NAME} BinaryTools) -target_link_libraries(${PROJECT_NAME} PRIVATE BinaryTools) # Link n64graphics add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lib/n64graphics) add_dependencies(${PROJECT_NAME} N64Graphics) -target_link_libraries(${PROJECT_NAME} PRIVATE N64Graphics) # Link StormLib if (BUILD_STORMLIB) set(STORMLIB_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/StormLib) add_subdirectory(${STORMLIB_DIR}) - target_link_libraries(${PROJECT_NAME} PRIVATE storm) endif() # Link YamlCpp @@ -193,7 +190,8 @@ FetchContent_Declare( ) set(YAML_CPP_BUILD_TESTS OFF) FetchContent_MakeAvailable(yaml-cpp) -target_link_libraries(${PROJECT_NAME} PRIVATE yaml-cpp) + +#Link Spdlog FetchContent_Declare( spdlog @@ -202,7 +200,18 @@ FetchContent_Declare( ) FetchContent_MakeAvailable(spdlog) -target_link_libraries(${PROJECT_NAME} PRIVATE spdlog) + +# Link TinyXML2 +set(tinyxml2_BUILD_TESTING OFF) +FetchContent_Declare( + tinyxml2 + GIT_REPOSITORY https://github.com/leethomason/tinyxml2.git + GIT_TAG 10.0.0 + OVERRIDE_FIND_PACKAGE +) +FetchContent_MakeAvailable(tinyxml2) + +target_link_libraries(${PROJECT_NAME} PRIVATE spdlog tinyxml2 yaml-cpp storm N64Graphics BinaryTools) if((CMAKE_SYSTEM_NAME MATCHES "Windows") AND ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang")) include(../cmake/HandleCompilerRT.cmake) diff --git a/src/Companion.cpp b/src/Companion.cpp index e16c670..6b813b5 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -454,6 +454,7 @@ std::string ExportTypeToString(ExportType type) { case ExportType::Header: return "Header"; case ExportType::Code: return "Code"; case ExportType::Modding: return "Modding"; + case ExportType::XML: return "XML"; default: throw std::runtime_error("Invalid ExportType"); } @@ -559,66 +560,31 @@ void Companion::ProcessFile(YAML::Node root) { auto node = asset->second; auto entryName = asset->first.as(); - // Parse horizontal assets - if(node["files"]){ - auto segment = node["segment"] ? node["segment"].as() : -1; - const auto files = node["files"]; - for (const auto& file : files) { - auto assetNode = file.as(); - auto childName = assetNode["name"].as(); - auto output = (this->gCurrentDirectory / entryName / childName).string(); - std::replace(output.begin(), output.end(), '\\', '/'); + auto output = (this->gCurrentDirectory / entryName).string(); + std::replace(output.begin(), output.end(), '\\', '/'); - if(assetNode["type"]){ - const auto type = GetSafeNode(assetNode, "type"); - if(type == "SAMPLE"){ - AudioManager::Instance->bind_sample(assetNode, output); - } - } - - if(!assetNode["offset"]) { - continue; - } - - if(segment != -1 || gCurrentSegmentNumber) { - assetNode["offset"] = (segment << 24) | assetNode["offset"].as(); - } - - if(!gCurrentVirtualPath.empty()) { - node["path"] = gCurrentVirtualPath; - } - - this->gAddrMap[this->gCurrentFile][assetNode["offset"].as()] = std::make_tuple(output, assetNode); + if(node["type"]){ + const auto type = GetSafeNode(node, "type"); + if(type == "SAMPLE"){ + AudioManager::Instance->bind_sample(node, output); } - } else { - - auto output = (this->gCurrentDirectory / entryName).string(); - std::replace(output.begin(), output.end(), '\\', '/'); - - - if(node["type"]){ - const auto type = GetSafeNode(node, "type"); - if(type == "SAMPLE"){ - AudioManager::Instance->bind_sample(node, output); - } - } - - if(!node["offset"]) { - continue; - } - - if(gCurrentSegmentNumber) { - if (IS_SEGMENTED(node["offset"].as()) == false) { - node["offset"] = (gCurrentSegmentNumber << 24) | node["offset"].as(); - } - } - - if(!gCurrentVirtualPath.empty()) { - node["path"] = gCurrentVirtualPath; - } - - this->gAddrMap[this->gCurrentFile][node["offset"].as()] = std::make_tuple(output, node); } + + if(!node["offset"]) { + continue; + } + + if(gCurrentSegmentNumber) { + if (IS_SEGMENTED(node["offset"].as()) == false) { + node["offset"] = (gCurrentSegmentNumber << 24) | node["offset"].as(); + } + } + + if(!gCurrentVirtualPath.empty()) { + node["path"] = gCurrentVirtualPath; + } + + this->gAddrMap[this->gCurrentFile][node["offset"].as()] = std::make_tuple(output, node); } // Stupid hack because the iteration broke the assets @@ -656,53 +622,23 @@ void Companion::ProcessFile(YAML::Node root) { continue; } - // Parse horizontal assets - if(assetNode["files"]){ - auto segment = assetNode["segment"] ? assetNode["segment"].as() : -1; - auto files = assetNode["files"]; - for (const auto& file : files) { - auto node = file.as(); - auto childName = node["name"].as(); - - if(!node["offset"]) { - continue; - } - - if(segment != -1 || gCurrentFileOffset) { - node["offset"] = (segment << 24) | node["offset"].as(); - } - - if(!gCurrentVirtualPath.empty()) { - node["path"] = gCurrentVirtualPath; - } - - auto output = (this->gCurrentDirectory / entryName / childName).string(); - std::replace(output.begin(), output.end(), '\\', '/'); - this->gConfig.segment.temporal.clear(); - auto result = this->ParseNode(node, output); - if(result.has_value()) { - this->gParseResults[this->gCurrentFile].push_back(result.value()); - } - } - } else { - if(gCurrentFileOffset && assetNode["offset"]) { - const auto offset = assetNode["offset"].as(); - if (!IS_SEGMENTED(offset)) { - assetNode["offset"] = (gCurrentSegmentNumber << 24) | offset; - } + if(gCurrentFileOffset && assetNode["offset"]) { + const auto offset = assetNode["offset"].as(); + if (!IS_SEGMENTED(offset)) { + assetNode["offset"] = (gCurrentSegmentNumber << 24) | offset; } + } - if(!gCurrentVirtualPath.empty()) { - assetNode["path"] = gCurrentVirtualPath; - } + if(!gCurrentVirtualPath.empty()) { + assetNode["path"] = gCurrentVirtualPath; + } - std::string output = (this->gCurrentDirectory / entryName).string(); - std::replace(output.begin(), output.end(), '\\', '/'); - this->gConfig.segment.temporal.clear(); - auto result = this->ParseNode(assetNode, output); - if(result.has_value()) { - this->gParseResults[this->gCurrentFile].push_back(result.value()); - } + std::string output = (this->gCurrentDirectory / entryName).string(); + std::replace(output.begin(), output.end(), '\\', '/'); + this->gConfig.segment.temporal.clear(); + auto result = this->ParseNode(assetNode, output); + if(result.has_value()) { + this->gParseResults[this->gCurrentFile].push_back(result.value()); } spdlog::set_pattern(regular); @@ -730,8 +666,16 @@ void Companion::ProcessFile(YAML::Node root) { exporter->get()->Export(stream, data, result.name, result.node, &result.name); auto data = stream.str(); this->gCurrentWrapper->CreateFile(result.name, std::vector(data.begin(), data.end())); + + for(auto& entry : this->gCompanionFiles){ + auto output = (this->gCurrentDirectory / entry.first).string(); + std::replace(output.begin(), output.end(), '\\', '/'); + this->gCurrentWrapper->CreateFile(output, entry.second); + } + break; } + case ExportType::XML: case ExportType::Modding: { stream.str(""); stream.clear(); @@ -748,12 +692,24 @@ void Companion::ProcessFile(YAML::Node root) { create_directories(fs::path(dpath).parent_path()); } - SPDLOG_INFO(ogname); this->gModdedAssetPaths[ogname] = result.name; std::ofstream file(dpath, std::ios::binary); file.write(data.c_str(), data.size()); file.close(); + + for(auto& entry : this->gCompanionFiles){ + auto cpath = (Instance->GetOutputPath() / this->gCurrentDirectory / entry.first).string(); + std::replace(cpath.begin(), cpath.end(), '\\', '/'); + if(!exists(fs::path(cpath).parent_path())){ + create_directories(fs::path(cpath).parent_path()); + } + + std::ofstream cfile(cpath, std::ios::binary); + cfile.write(entry.second.data(), entry.second.size()); + cfile.close(); + } + break; } default: { @@ -762,6 +718,8 @@ void Companion::ProcessFile(YAML::Node root) { } } + this->gCompanionFiles.clear(); + if(result.node["offset"]) { auto alignment = GetSafeNode(result.node, "alignment", impl->GetAlignment()); if(!endptr.has_value()) { @@ -807,7 +765,7 @@ void Companion::ProcessFile(YAML::Node root) { auto fsout = fs::path(this->gConfig.outputPath); - if(this->gConfig.exporterType == ExportType::Modding) { + if(this->gConfig.exporterType == ExportType::Modding || this->gConfig.exporterType == ExportType::XML) { fsout /= "modding.yml"; YAML::Node modding; @@ -1048,6 +1006,7 @@ void Companion::Process() { this->gConfig.outputPath = opath && opath["code"] ? opath["code"].as() : "code"; break; } + case ExportType::XML: case ExportType::Modding: { this->gConfig.outputPath = modding_path; break; @@ -1420,6 +1379,11 @@ std::optional>> Companion::GetNo } +void Companion::RegisterCompanionFile(const std::string path, std::vector data) { + this->gCompanionFiles[path] = data; + SPDLOG_INFO("Registered companion file {}", path); +} + std::string Companion::NormalizeAsset(const std::string& name) const { auto path = fs::path(this->gCurrentFile).stem().string() + "_" + name; return path; diff --git a/src/Companion.h b/src/Companion.h index eb51783..6f14708 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -154,6 +154,7 @@ public: static void Pack(const std::string& folder, const std::string& output); std::string NormalizeAsset(const std::string& name) const; std::string RelativePath(const std::string& path) const; + void RegisterCompanionFile(const std::string path, std::vector data); TorchConfig& GetConfig() { return this->gConfig; } SWrapper* GetCurrentWrapper() { return this->gCurrentWrapper; } @@ -190,6 +191,7 @@ private: std::vector gCurrentExternalFiles; std::unordered_set gProcessedFiles; + std::unordered_map> gCompanionFiles; std::unordered_map> gParseResults; std::unordered_map gModdedAssetPaths; diff --git a/src/factories/BaseFactory.h b/src/factories/BaseFactory.h index 0320605..f3f30aa 100644 --- a/src/factories/BaseFactory.h +++ b/src/factories/BaseFactory.h @@ -11,10 +11,13 @@ #include #include #include +#include #include #include "lib/binarytools/BinaryWriter.h" #include "lib/binarytools/BinaryReader.h" +namespace fs = std::filesystem; + #define REGISTER(type, c) { ExportType::type, std::make_shared() }, #define SEGMENT_OFFSET(a) ((uint32_t)(a) & 0x00FFFFFF) @@ -37,7 +40,8 @@ enum class ExportType { Header, Code, Binary, - Modding + Modding, + XML }; template diff --git a/src/factories/naudio/v1/AudioContext.cpp b/src/factories/naudio/v1/AudioContext.cpp index 7954aef..ce434ac 100644 --- a/src/factories/naudio/v1/AudioContext.cpp +++ b/src/factories/naudio/v1/AudioContext.cpp @@ -63,7 +63,7 @@ TunedSample AudioContext::LoadTunedSample(LUS::BinaryReader& reader, uint32_t pa node["offset"] = parent + sampleAddr; node["tuning"] = tuning; node["sampleBankId"] = sampleBankId; - node["symbol"] = StringHelper::Sprintf("Sample_P_%X_O_%X_B_%d", parent, parent + sampleAddr, sampleBankId); + node["symbol"] = StringHelper::Sprintf("%d_Sample_P_%X_B_%d", parent + sampleAddr, parent, sampleBankId); Companion::Instance->AddAsset(node); return { parent + sampleAddr, sampleBankId, tuning }; @@ -84,4 +84,59 @@ uint64_t AudioContext::GetPathByAddr(uint32_t addr) { SPDLOG_INFO("Failed to find path for 0x{:X}", addr); throw std::runtime_error("Failed to find node by addr"); } +} + +const char* AudioContext::GetMediumStr(uint8_t medium) { + switch (medium) { + case 0: + return "Ram"; + case 1: + return "Unk"; + case 2: + return "Cart"; + case 3: + return "Disk"; + case 5: + return "RamUnloaded"; + default: + return "ERROR"; + } +} + +const char* AudioContext::GetCachePolicyStr(uint8_t policy) { + switch (policy) { + case 0: + return "Temporary"; + case 1: + return "Persistent"; + case 2: + return "Either"; + case 3: + return "Permanent"; + default: + return "ERROR"; + } +} + +const char* AudioContext::GetCodecStr(uint8_t codec) { + switch (codec) { + case 0: + return "ADPCM"; + case 1: + return "S8"; + case 2: + return "S16MEM"; + case 3: + return "ADPCMSMALL"; + case 4: + return "REVERB"; + case 5: + return "S16"; + case 6: + return "UNK6"; + case 7: + return "UNK7"; + default: + return "ERROR"; + } } \ No newline at end of file diff --git a/src/factories/naudio/v1/AudioContext.h b/src/factories/naudio/v1/AudioContext.h index 84e5337..a965f10 100644 --- a/src/factories/naudio/v1/AudioContext.h +++ b/src/factories/naudio/v1/AudioContext.h @@ -20,6 +20,10 @@ public: static LUS::BinaryReader MakeReader(AudioTableType type, uint32_t offset); static TunedSample LoadTunedSample(LUS::BinaryReader& reader, uint32_t parent, uint32_t sampleBankId); static uint64_t GetPathByAddr(uint32_t addr); + + static const char* GetMediumStr(uint8_t medium); + static const char* GetCachePolicyStr(uint8_t policy); + static const char* GetCodecStr(uint8_t codec); }; class AudioContextFactory : public BaseFactory { diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index b35467e..58e9afb 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -1,6 +1,9 @@ #include "SampleFactory.h" #include "AudioConverter.h" #include "Companion.h" +#include +#include "LoopFactory.h" +#include "BookFactory.h" #include ExportResult NSampleHeaderExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement) { @@ -58,6 +61,61 @@ ExportResult NSampleModdingExporter::Export(std::ostream &write, std::shared_ptr return std::nullopt; } +ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto aiff = LUS::BinaryWriter(); + auto entry = std::static_pointer_cast(raw); + auto loop = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(entry->loop)->data.value()); + auto book = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(entry->book)->data.value()); + + auto path = fs::path(*replacement); + + *replacement += ".meta"; + + tinyxml2::XMLDocument sample; + tinyxml2::XMLElement* root = sample.NewElement("Sample"); + root->SetAttribute("Codec", AudioContext::GetCodecStr(entry->codec)); + root->SetAttribute("Medium", AudioContext::GetMediumStr(entry->medium)); + root->SetAttribute("Unk", entry->unk); + // root->SetAttribute("Relocated", entry->isRelocated); + + tinyxml2::XMLElement* adpcmLoop = sample.NewElement("ADPCMLoop"); + adpcmLoop->SetAttribute("Start", loop->start); + adpcmLoop->SetAttribute("End", loop->end); + adpcmLoop->SetAttribute("Count", loop->count); + if (loop->count != 0) { + for (size_t i = 0; i < 16; i++) { + tinyxml2::XMLElement* loopEntry = adpcmLoop->InsertNewChildElement("Predictor"); + loopEntry->SetAttribute("State", loop->predictorState[i]); + adpcmLoop->InsertEndChild(loopEntry); + } + } + root->InsertEndChild(adpcmLoop); + + tinyxml2::XMLElement* adpcmBook = sample.NewElement("ADPCMBook"); + adpcmBook->SetAttribute("Order", book->order); + adpcmBook->SetAttribute("Npredictors", book->numPredictors); + + for (size_t i = 0; i < book->book.size(); i++) { + tinyxml2::XMLElement* bookEntry = adpcmBook->InsertNewChildElement("Book"); + bookEntry->SetAttribute("Page", book->book[i]); + adpcmBook->InsertEndChild(bookEntry); + } + root->InsertEndChild(adpcmBook); + sample.InsertEndChild(root); + + tinyxml2::XMLPrinter printer; + sample.Accept(&printer); + write.write(printer.CStr(), printer.CStrSize() - 1); + + auto tableEntry = AudioContext::tableData[AudioTableType::SAMPLE_TABLE]->entries[entry->sampleBankId]; + auto buffer = AudioContext::data[AudioTableType::SAMPLE_TABLE]; + auto sampleData = buffer.data() + tableEntry.addr + entry->sampleAddr; + std::vector data(sampleData, sampleData + entry->size); + Companion::Instance->RegisterCompanionFile(path.filename(), data); + + return std::nullopt; +} + std::optional> NSampleFactory::parse(std::vector& buffer, YAML::Node& node) { auto offset = GetSafeNode(node, "offset"); auto parent = GetSafeNode(node, "parent"); diff --git a/src/factories/naudio/v1/SampleFactory.h b/src/factories/naudio/v1/SampleFactory.h index d447cc2..eb7cff5 100644 --- a/src/factories/naudio/v1/SampleFactory.h +++ b/src/factories/naudio/v1/SampleFactory.h @@ -36,6 +36,10 @@ class NSampleModdingExporter : public BaseExporter { ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; +class NSampleXMLExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + class NSampleFactory : public BaseFactory { public: std::optional> parse(std::vector& buffer, YAML::Node& data) override; @@ -45,6 +49,7 @@ public: REGISTER(Binary, NSampleBinaryExporter) REGISTER(Code, NSampleCodeExporter) REGISTER(Modding, NSampleModdingExporter) + REGISTER(XML, NSampleXMLExporter) }; } bool SupportModdedAssets() override { return true; } diff --git a/src/main.cpp b/src/main.cpp index ee1d92a..33ca27a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -98,11 +98,18 @@ int main(int argc, char *argv[]) { } }); + modding_export->add_option("mode", mode, "xml or binary")->required(); modding_export->add_option("", filename, "")->required()->check(CLI::ExistingFile); modding_export->parse_complete_callback([&] { const auto instance = Companion::Instance = new Companion(filename, false, debug); - instance->Init(ExportType::Modding); + if (mode == "binary") { + instance->Init(ExportType::Modding); + } else if (mode == "xml") { + instance->Init(ExportType::XML); + } else { + std::cout << "Invalid mode" << std::endl; + } }); try { From ac3f20a78ce518c4076f00d13bf586d9fc27c5d0 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 27 Nov 2024 21:25:49 -0600 Subject: [PATCH 2/6] Test audio xmls --- src/Companion.cpp | 5 +- src/factories/naudio/v1/AudioContext.cpp | 3 +- src/factories/naudio/v1/InstrumentFactory.cpp | 64 +++++++++++++++++++ src/factories/naudio/v1/InstrumentFactory.h | 5 ++ src/factories/naudio/v1/SampleFactory.cpp | 4 +- 5 files changed, 74 insertions(+), 7 deletions(-) diff --git a/src/Companion.cpp b/src/Companion.cpp index 6b813b5..07f0845 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -1230,9 +1230,7 @@ std::optional> Companion::RegisterAsset(cons auto entry = std::make_tuple(output, node); this->gAddrMap[this->gCurrentFile][node["offset"].as()] = entry; - - - + return entry; } @@ -1418,7 +1416,6 @@ std::optional Companion::AddAsset(YAML::Node asset) { const auto symbol = GetSafeNode(asset, "symbol", ""); const auto decl = this->GetNodeByAddr(offset); - if(decl.has_value()) { return std::get<1>(decl.value()); } diff --git a/src/factories/naudio/v1/AudioContext.cpp b/src/factories/naudio/v1/AudioContext.cpp index ce434ac..b4f72e2 100644 --- a/src/factories/naudio/v1/AudioContext.cpp +++ b/src/factories/naudio/v1/AudioContext.cpp @@ -62,8 +62,9 @@ TunedSample AudioContext::LoadTunedSample(LUS::BinaryReader& reader, uint32_t pa node["parent"] = parent; node["offset"] = parent + sampleAddr; node["tuning"] = tuning; + SPDLOG_INFO("Tuning {}", tuning); node["sampleBankId"] = sampleBankId; - node["symbol"] = StringHelper::Sprintf("%d_Sample_P_%X_B_%d", parent + sampleAddr, parent, sampleBankId); + // node["symbol"] = StringHelper::Sprintf("%d_Sample_P_%X_B_%d", parent + sampleAddr, parent, sampleBankId); Companion::Instance->AddAsset(node); return { parent + sampleAddr, sampleBankId, tuning }; diff --git a/src/factories/naudio/v1/InstrumentFactory.cpp b/src/factories/naudio/v1/InstrumentFactory.cpp index acfada1..5315a78 100644 --- a/src/factories/naudio/v1/InstrumentFactory.cpp +++ b/src/factories/naudio/v1/InstrumentFactory.cpp @@ -1,6 +1,9 @@ #include "InstrumentFactory.h" #include "utils/Decompressor.h" #include "Companion.h" +#include "EnvelopeFactory.h" +#include "SampleFactory.h" +#include ExportResult InstrumentHeaderExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement) { const auto symbol = GetSafeNode(node, "symbol", entryName); @@ -40,6 +43,67 @@ ExportResult InstrumentBinaryExporter::Export(std::ostream &write, std::shared_p return std::nullopt; } +ExportResult InstrumentXMLExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto writer = LUS::BinaryWriter(); + auto instrument = std::static_pointer_cast(raw); + + auto envelopeData = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(instrument->envelope)->data.value()); + + auto path = fs::path(*replacement); + + *replacement += ".meta"; + + tinyxml2::XMLDocument inst; + tinyxml2::XMLElement* root = inst.NewElement("Instrument"); + root->SetAttribute("NormalRangeLo", instrument->normalRangeLo); + root->SetAttribute("NormalRangeHi", instrument->normalRangeLo); + root->SetAttribute("ReleaseRate", instrument->adsrDecayIndex); + + tinyxml2::XMLElement* envelopes = inst.NewElement("Envelopes"); + for(auto& point : envelopeData->points){ + tinyxml2::XMLElement* pointEntry = envelopes->InsertNewChildElement("Point"); + pointEntry->SetAttribute("Delay", point.delay); + pointEntry->SetAttribute("Arg", point.arg); + envelopes->InsertEndChild(pointEntry); + } + + auto lowSample = instrument->lowPitchTunedSample; + auto normSample = instrument->normalPitchTunedSample; + auto highSample = instrument->highPitchTunedSample; + + if(lowSample.sample != 0 && lowSample.tuning != 0.0f){ + tinyxml2::XMLElement* low = inst.NewElement("LowPitchSample"); + low->SetAttribute("Tuning", lowSample.tuning); + low->SetAttribute("Path", std::get(Companion::Instance->GetNodeByAddr(lowSample.sample).value()).c_str()); + + root->InsertEndChild(low); + } + + if(normSample.sample != 0 && normSample.tuning != 0.0f) { + tinyxml2::XMLElement* normal = inst.NewElement("NormalPitchSample"); + normal->SetAttribute("Tuning", normSample.tuning); + normal->SetAttribute("Path", std::get(Companion::Instance->GetNodeByAddr(normSample.sample).value()).c_str()); + + root->InsertEndChild(normal); + } + + if(highSample.sample != 0 && highSample.tuning != 0.0f) { + tinyxml2::XMLElement* high = inst.NewElement("HighPitchSample"); + high->SetAttribute("Tuning", highSample.tuning); + high->SetAttribute("Path", std::get(Companion::Instance->GetNodeByAddr(highSample.sample).value()).c_str()); + + root->InsertEndChild(high); + } + + inst.InsertEndChild(root); + + tinyxml2::XMLPrinter printer; + inst.Accept(&printer); + write.write(printer.CStr(), printer.CStrSize() - 1); + + return std::nullopt; +} + std::optional> InstrumentFactory::parse(std::vector& buffer, YAML::Node& node) { auto offset = GetSafeNode(node, "offset"); auto parent = GetSafeNode(node, "parent"); diff --git a/src/factories/naudio/v1/InstrumentFactory.h b/src/factories/naudio/v1/InstrumentFactory.h index 9af9295..ab0f623 100644 --- a/src/factories/naudio/v1/InstrumentFactory.h +++ b/src/factories/naudio/v1/InstrumentFactory.h @@ -27,6 +27,10 @@ class InstrumentCodeExporter : public BaseExporter { ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; +class InstrumentXMLExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + class InstrumentFactory : public BaseFactory { public: std::optional> parse(std::vector& buffer, YAML::Node& data) override; @@ -35,6 +39,7 @@ public: REGISTER(Header, InstrumentHeaderExporter) REGISTER(Binary, InstrumentBinaryExporter) REGISTER(Code, InstrumentCodeExporter) + REGISTER(XML, InstrumentXMLExporter) }; } diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index 58e9afb..ec8dbe2 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -76,7 +76,7 @@ ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptrSetAttribute("Codec", AudioContext::GetCodecStr(entry->codec)); root->SetAttribute("Medium", AudioContext::GetMediumStr(entry->medium)); root->SetAttribute("Unk", entry->unk); - // root->SetAttribute("Relocated", entry->isRelocated); + root->SetAttribute("Tuning", entry->tuning); tinyxml2::XMLElement* adpcmLoop = sample.NewElement("ADPCMLoop"); adpcmLoop->SetAttribute("Start", loop->start); @@ -119,7 +119,7 @@ ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptr> NSampleFactory::parse(std::vector& buffer, YAML::Node& node) { auto offset = GetSafeNode(node, "offset"); auto parent = GetSafeNode(node, "parent"); - auto tuning = GetSafeNode(node, "tuning", 1.0f); + auto tuning = GetSafeNode(node, "tuning", 0.0f); auto sampleRate = GetSafeNode(node, "sampleRate", 0); auto sampleBankId = GetSafeNode(node, "sampleBankId"); From 0efd51b717fe7ffef8a8c93a4c25efcb8ef3f919 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 25 Dec 2024 00:40:19 -0600 Subject: [PATCH 3/6] Modified XML format --- src/factories/naudio/v1/AudioTableFactory.cpp | 9 +- src/factories/naudio/v1/InstrumentFactory.cpp | 1 - src/factories/naudio/v1/InstrumentFactory.h | 1 - src/factories/naudio/v1/SampleFactory.cpp | 15 +- src/factories/naudio/v1/SoundFontFactory.cpp | 135 ++++++++++++++++++ src/factories/naudio/v1/SoundFontFactory.h | 5 + 6 files changed, 157 insertions(+), 9 deletions(-) diff --git a/src/factories/naudio/v1/AudioTableFactory.cpp b/src/factories/naudio/v1/AudioTableFactory.cpp index 96d6954..00f0f5c 100644 --- a/src/factories/naudio/v1/AudioTableFactory.cpp +++ b/src/factories/naudio/v1/AudioTableFactory.cpp @@ -124,7 +124,6 @@ std::optional> AudioTableFactory::parse(std::vector SPDLOG_INFO("addr: {}", baddr); std::vector entries; - auto parent = AudioContext::tableOffsets[AudioTableType::SEQ_TABLE]; for(size_t i = 0; i < count; i++){ auto addr = reader.ReadUInt32(); @@ -141,12 +140,20 @@ std::optional> AudioTableFactory::parse(std::vector YAML::Node font; font["type"] = "NAUDIO:V1:SOUND_FONT"; font["offset"] = addr; + font["id"] = (uint32_t) i; + font["medium"] = (int32_t) medium; + font["policy"] = (int32_t) policy; + font["sd1"] = (int32_t) sd1; + font["sd2"] = (int32_t) sd2; + font["sd3"] = (int32_t) sd3; + Companion::Instance->AddAsset(font); std::string path = font["vpath"].as(); crc = CRC64(path.c_str()); break; } case AudioTableType::SEQ_TABLE: { + auto parent = AudioContext::tableOffsets[AudioTableType::SEQ_TABLE]; if(size != 0){ YAML::Node seq; seq["type"] = "BLOB"; diff --git a/src/factories/naudio/v1/InstrumentFactory.cpp b/src/factories/naudio/v1/InstrumentFactory.cpp index 5315a78..10ba852 100644 --- a/src/factories/naudio/v1/InstrumentFactory.cpp +++ b/src/factories/naudio/v1/InstrumentFactory.cpp @@ -2,7 +2,6 @@ #include "utils/Decompressor.h" #include "Companion.h" #include "EnvelopeFactory.h" -#include "SampleFactory.h" #include ExportResult InstrumentHeaderExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement) { diff --git a/src/factories/naudio/v1/InstrumentFactory.h b/src/factories/naudio/v1/InstrumentFactory.h index ab0f623..245b6d9 100644 --- a/src/factories/naudio/v1/InstrumentFactory.h +++ b/src/factories/naudio/v1/InstrumentFactory.h @@ -39,7 +39,6 @@ public: REGISTER(Header, InstrumentHeaderExporter) REGISTER(Binary, InstrumentBinaryExporter) REGISTER(Code, InstrumentCodeExporter) - REGISTER(XML, InstrumentXMLExporter) }; } diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index ec8dbe2..18d539a 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -62,7 +62,6 @@ ExportResult NSampleModdingExporter::Export(std::ostream &write, std::shared_ptr } ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { - auto aiff = LUS::BinaryWriter(); auto entry = std::static_pointer_cast(raw); auto loop = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(entry->loop)->data.value()); auto book = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(entry->book)->data.value()); @@ -73,19 +72,23 @@ ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptrSetAttribute("Version", 0); root->SetAttribute("Codec", AudioContext::GetCodecStr(entry->codec)); root->SetAttribute("Medium", AudioContext::GetMediumStr(entry->medium)); - root->SetAttribute("Unk", entry->unk); + root->SetAttribute("bit26", entry->unk); root->SetAttribute("Tuning", entry->tuning); + root->SetAttribute("Size", entry->size); + root->SetAttribute("Relocated", 0); + root->SetAttribute("Path", path.c_str()); tinyxml2::XMLElement* adpcmLoop = sample.NewElement("ADPCMLoop"); adpcmLoop->SetAttribute("Start", loop->start); adpcmLoop->SetAttribute("End", loop->end); adpcmLoop->SetAttribute("Count", loop->count); if (loop->count != 0) { - for (size_t i = 0; i < 16; i++) { + for (auto& state : loop->predictorState) { tinyxml2::XMLElement* loopEntry = adpcmLoop->InsertNewChildElement("Predictor"); - loopEntry->SetAttribute("State", loop->predictorState[i]); + loopEntry->SetAttribute("State", state); adpcmLoop->InsertEndChild(loopEntry); } } @@ -95,9 +98,9 @@ ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptrSetAttribute("Order", book->order); adpcmBook->SetAttribute("Npredictors", book->numPredictors); - for (size_t i = 0; i < book->book.size(); i++) { + for (auto& page : book->book) { tinyxml2::XMLElement* bookEntry = adpcmBook->InsertNewChildElement("Book"); - bookEntry->SetAttribute("Page", book->book[i]); + bookEntry->SetAttribute("Page", page); adpcmBook->InsertEndChild(bookEntry); } root->InsertEndChild(adpcmBook); diff --git a/src/factories/naudio/v1/SoundFontFactory.cpp b/src/factories/naudio/v1/SoundFontFactory.cpp index ca84065..81419fa 100644 --- a/src/factories/naudio/v1/SoundFontFactory.cpp +++ b/src/factories/naudio/v1/SoundFontFactory.cpp @@ -3,6 +3,10 @@ #include "Companion.h" #include "spdlog/spdlog.h" #include "utils/Decompressor.h" +#include +#include "DrumFactory.h" +#include "EnvelopeFactory.h" +#include "InstrumentFactory.h" ExportResult SoundFontHeaderExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement) { const auto symbol = GetSafeNode(node, "symbol", entryName); @@ -45,6 +49,137 @@ ExportResult SoundFontBinaryExporter::Export(std::ostream &write, std::shared_pt return std::nullopt; } +void WriteInstrument(tinyxml2::XMLElement* parent, uint32_t offset){ + auto instrument = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(offset)->data.value()); + auto envelopeData = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(instrument->envelope)->data.value()); + + tinyxml2::XMLElement* root = parent->InsertNewChildElement("Instrument"); + root->SetAttribute("NormalRangeLo", instrument->normalRangeLo); + root->SetAttribute("NormalRangeHi", instrument->normalRangeLo); + root->SetAttribute("ReleaseRate", instrument->adsrDecayIndex); + + tinyxml2::XMLElement* envelopes = root->InsertNewChildElement("Envelopes"); + for(size_t i = 0; i < envelopeData->points.size(); i++){ + auto point = envelopeData->points[i]; + tinyxml2::XMLElement* pointEntry = envelopes->InsertNewChildElement("Envelope"); + pointEntry->SetAttribute("Delay", point.delay); + pointEntry->SetAttribute("Arg", point.arg); + envelopes->InsertEndChild(pointEntry); + } + + auto lowSample = instrument->lowPitchTunedSample; + auto normSample = instrument->normalPitchTunedSample; + auto highSample = instrument->highPitchTunedSample; + + if(lowSample.sample != 0 && lowSample.tuning != 0.0f){ + tinyxml2::XMLElement* low = root->InsertNewChildElement("LowPitchSample"); + low->SetAttribute("Tuning", lowSample.tuning); + low->SetAttribute("SampleRef", std::get(Companion::Instance->GetNodeByAddr(lowSample.sample).value()).c_str()); + + root->InsertEndChild(low); + } + + if(normSample.sample != 0 && normSample.tuning != 0.0f) { + tinyxml2::XMLElement* normal = root->InsertNewChildElement("NormalPitchSample"); + normal->SetAttribute("Tuning", normSample.tuning); + normal->SetAttribute("SampleRef", std::get(Companion::Instance->GetNodeByAddr(normSample.sample).value()).c_str()); + + root->InsertEndChild(normal); + } + + if(highSample.sample != 0 && highSample.tuning != 0.0f) { + tinyxml2::XMLElement* high = root->InsertNewChildElement("HighPitchSample"); + high->SetAttribute("Tuning", highSample.tuning); + high->SetAttribute("SampleRef", std::get(Companion::Instance->GetNodeByAddr(highSample.sample).value()).c_str()); + + root->InsertEndChild(high); + } + + parent->InsertEndChild(root); +} + +void WriteDrum(tinyxml2::XMLElement* parent, uint32_t offset){ + auto drum = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(offset)->data.value()); + auto envelopeData = std::static_pointer_cast(Companion::Instance->GetParseDataByAddr(drum->envelope)->data.value()); + auto sample = drum->tunedSample; + + tinyxml2::XMLElement* root = parent->InsertNewChildElement("Drum"); + root->SetAttribute("ReleaseRate", drum->adsrDecayIndex); + root->SetAttribute("Pan", drum->pan); + root->SetAttribute("Loaded", 0); + root->SetAttribute("SampleRef", std::get(Companion::Instance->GetNodeByAddr(sample.sample).value()).c_str()); + root->SetAttribute("Tuning", sample.tuning); + + tinyxml2::XMLElement* envelopes = root->InsertNewChildElement("Envelopes"); + envelopes->SetAttribute("Count", (uint32_t) envelopeData->points.size()); + for(size_t i = 0; i < envelopeData->points.size(); i++){ + auto point = envelopeData->points[i]; + tinyxml2::XMLElement* pointEntry = envelopes->InsertNewChildElement("Envelope"); + pointEntry->SetAttribute("Delay", point.delay); + pointEntry->SetAttribute("Arg", point.arg); + envelopes->InsertEndChild(pointEntry); + } + + parent->InsertEndChild(root); +} + +ExportResult SoundFontXMLExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto font = std::static_pointer_cast(raw); + auto id = GetSafeNode(node, "id"); + auto medium = (int8_t) GetSafeNode(node, "medium"); + auto policy = (int8_t) GetSafeNode(node, "policy"); + auto sd1 = (int16_t) GetSafeNode(node, "sd1"); + auto sd2 = (int16_t) GetSafeNode(node, "sd2"); + auto sd3 = (int16_t) GetSafeNode(node, "sd3"); + + *replacement += ".meta"; + + tinyxml2::XMLDocument doc; + tinyxml2::XMLElement* root = doc.NewElement("SoundFont"); + root->SetAttribute("Version", 0); + root->SetAttribute("Num", id); + root->SetAttribute("Medium", AudioContext::GetMediumStr(medium)); + root->SetAttribute("CachePolicy", AudioContext::GetCachePolicyStr(policy)); + root->SetAttribute("Data1", sd1); + root->SetAttribute("Data2", sd2); + root->SetAttribute("Data3", sd3); + + tinyxml2::XMLElement* drums = doc.NewElement("Drums"); + drums->SetAttribute("Count", font->numDrums); + + for(auto& drum : font->drums){ + if(drum == 0){ + continue; + } + WriteDrum(drums, drum); + } + root->InsertEndChild(drums); + + tinyxml2::XMLElement* insts = doc.NewElement("Instruments"); + insts->SetAttribute("Count", font->numInstruments); + + for(auto& inst : font->instruments){ + if(inst == 0){ + continue; + } + WriteInstrument(insts, inst); + } + root->InsertEndChild(insts); + + // This is ignored since it's an exclusive feature of OoT and MM + tinyxml2::XMLElement* sfx = doc.NewElement("SfxTable"); + sfx->SetAttribute("Count", 0); + root->InsertEndChild(sfx); + + doc.InsertEndChild(root); + + tinyxml2::XMLPrinter printer; + doc.Accept(&printer); + write.write(printer.CStr(), printer.CStrSize() - 1); + + return std::nullopt; +} + std::optional> SoundFontFactory::parse(std::vector& buffer, YAML::Node& node) { auto offset = GetSafeNode(node, "offset"); auto entry = AudioContext::tables[AudioTableType::FONT_TABLE].at(offset); diff --git a/src/factories/naudio/v1/SoundFontFactory.h b/src/factories/naudio/v1/SoundFontFactory.h index 256f2ce..72e1190 100644 --- a/src/factories/naudio/v1/SoundFontFactory.h +++ b/src/factories/naudio/v1/SoundFontFactory.h @@ -24,6 +24,10 @@ class SoundFontCodeExporter : public BaseExporter { ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; +class SoundFontXMLExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + class SoundFontFactory : public BaseFactory { public: std::optional> parse(std::vector& buffer, YAML::Node& data) override; @@ -32,6 +36,7 @@ public: REGISTER(Header, SoundFontHeaderExporter) REGISTER(Binary, SoundFontBinaryExporter) REGISTER(Code, SoundFontCodeExporter) + REGISTER(XML, SoundFontXMLExporter) }; } From f4b51fbb9f60c55886068d942500132dda278522 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 25 Dec 2024 00:54:24 -0600 Subject: [PATCH 4/6] Updated export argument --- src/main.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 33ca27a..d3b6ffb 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,6 +16,7 @@ int main(int argc, char *argv[]) { std::string target; std::string folder; bool otrMode = false; + bool xmlMode = false; bool debug = false; app.require_subcommand(); @@ -98,17 +99,15 @@ int main(int argc, char *argv[]) { } }); - modding_export->add_option("mode", mode, "xml or binary")->required(); + modding_export->add_flag("-x,--xml", xmlMode, "XML Mode"); modding_export->add_option("", filename, "")->required()->check(CLI::ExistingFile); modding_export->parse_complete_callback([&] { const auto instance = Companion::Instance = new Companion(filename, false, debug); - if (mode == "binary") { - instance->Init(ExportType::Modding); - } else if (mode == "xml") { + if (xmlMode) { instance->Init(ExportType::XML); } else { - std::cout << "Invalid mode" << std::endl; + instance->Init(ExportType::Modding); } }); From 03a0780ce6b2d4fbe13367bd6a8c4fb4e1d5145a Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 25 Dec 2024 11:56:36 -0600 Subject: [PATCH 5/6] Fixed windows compilation --- src/factories/naudio/v1/SampleFactory.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/factories/naudio/v1/SampleFactory.cpp b/src/factories/naudio/v1/SampleFactory.cpp index a89888f..e8c7fc9 100644 --- a/src/factories/naudio/v1/SampleFactory.cpp +++ b/src/factories/naudio/v1/SampleFactory.cpp @@ -114,7 +114,7 @@ ExportResult NSampleXMLExporter::Export(std::ostream &write, std::shared_ptrsampleAddr; std::vector data(sampleData, sampleData + entry->size); - Companion::Instance->RegisterCompanionFile(path.filename(), data); + Companion::Instance->RegisterCompanionFile(path.filename().string(), data); return std::nullopt; } From a3fcaf8ee2247c2add6e89cc1e6379819a193b67 Mon Sep 17 00:00:00 2001 From: KiritoDv Date: Wed, 25 Dec 2024 13:12:23 -0600 Subject: [PATCH 6/6] Changed log level on RegisterCompanionFile --- src/Companion.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Companion.cpp b/src/Companion.cpp index f56b2ed..7181cd1 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -1418,7 +1418,7 @@ std::optional>> Companion::GetNo void Companion::RegisterCompanionFile(const std::string path, std::vector data) { this->gCompanionFiles[path] = data; - SPDLOG_INFO("Registered companion file {}", path); + SPDLOG_TRACE("Registered companion file {}", path); } std::string Companion::NormalizeAsset(const std::string& name) const {