Added XML Extraction

This commit is contained in:
KiritoDv
2024-11-24 03:00:38 -06:00
parent de6b8fa447
commit 714b555d13
9 changed files with 219 additions and 111 deletions
+14 -5
View File
@@ -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)
+67 -103
View File
@@ -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<std::string>();
// Parse horizontal assets
if(node["files"]){
auto segment = node["segment"] ? node["segment"].as<uint8_t>() : -1;
const auto files = node["files"];
for (const auto& file : files) {
auto assetNode = file.as<YAML::Node>();
auto childName = assetNode["name"].as<std::string>();
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<std::string>(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<uint32_t>();
}
if(!gCurrentVirtualPath.empty()) {
node["path"] = gCurrentVirtualPath;
}
this->gAddrMap[this->gCurrentFile][assetNode["offset"].as<uint32_t>()] = std::make_tuple(output, assetNode);
if(node["type"]){
const auto type = GetSafeNode<std::string>(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<std::string>(node, "type");
if(type == "SAMPLE"){
AudioManager::Instance->bind_sample(node, output);
}
}
if(!node["offset"]) {
continue;
}
if(gCurrentSegmentNumber) {
if (IS_SEGMENTED(node["offset"].as<uint32_t>()) == false) {
node["offset"] = (gCurrentSegmentNumber << 24) | node["offset"].as<uint32_t>();
}
}
if(!gCurrentVirtualPath.empty()) {
node["path"] = gCurrentVirtualPath;
}
this->gAddrMap[this->gCurrentFile][node["offset"].as<uint32_t>()] = std::make_tuple(output, node);
}
if(!node["offset"]) {
continue;
}
if(gCurrentSegmentNumber) {
if (IS_SEGMENTED(node["offset"].as<uint32_t>()) == false) {
node["offset"] = (gCurrentSegmentNumber << 24) | node["offset"].as<uint32_t>();
}
}
if(!gCurrentVirtualPath.empty()) {
node["path"] = gCurrentVirtualPath;
}
this->gAddrMap[this->gCurrentFile][node["offset"].as<uint32_t>()] = 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<uint8_t>() : -1;
auto files = assetNode["files"];
for (const auto& file : files) {
auto node = file.as<YAML::Node>();
auto childName = node["name"].as<std::string>();
if(!node["offset"]) {
continue;
}
if(segment != -1 || gCurrentFileOffset) {
node["offset"] = (segment << 24) | node["offset"].as<uint32_t>();
}
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<uint32_t>();
if (!IS_SEGMENTED(offset)) {
assetNode["offset"] = (gCurrentSegmentNumber << 24) | offset;
}
if(gCurrentFileOffset && assetNode["offset"]) {
const auto offset = assetNode["offset"].as<uint32_t>();
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<uint32_t>(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<std::string>() : "code";
break;
}
case ExportType::XML:
case ExportType::Modding: {
this->gConfig.outputPath = modding_path;
break;
@@ -1420,6 +1379,11 @@ std::optional<std::vector<std::tuple<std::string, YAML::Node>>> Companion::GetNo
}
void Companion::RegisterCompanionFile(const std::string path, std::vector<char> 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;
+2
View File
@@ -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<char> data);
TorchConfig& GetConfig() { return this->gConfig; }
SWrapper* GetCurrentWrapper() { return this->gCurrentWrapper; }
@@ -190,6 +191,7 @@ private:
std::vector<std::string> gCurrentExternalFiles;
std::unordered_set<std::string> gProcessedFiles;
std::unordered_map<std::string, std::vector<char>> gCompanionFiles;
std::unordered_map<std::string, std::vector<ParseResultData>> gParseResults;
std::unordered_map<std::string, std::string> gModdedAssetPaths;
+5 -1
View File
@@ -11,10 +11,13 @@
#include <variant>
#include <optional>
#include <yaml-cpp/yaml.h>
#include <filesystem>
#include <strhash64/StrHash64.h>
#include "lib/binarytools/BinaryWriter.h"
#include "lib/binarytools/BinaryReader.h"
namespace fs = std::filesystem;
#define REGISTER(type, c) { ExportType::type, std::make_shared<c>() },
#define SEGMENT_OFFSET(a) ((uint32_t)(a) & 0x00FFFFFF)
@@ -37,7 +40,8 @@ enum class ExportType {
Header,
Code,
Binary,
Modding
Modding,
XML
};
template<typename T>
+56 -1
View File
@@ -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";
}
}
+4
View File
@@ -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 {
+58
View File
@@ -1,6 +1,9 @@
#include "SampleFactory.h"
#include "AudioConverter.h"
#include "Companion.h"
#include <tinyxml2.h>
#include "LoopFactory.h"
#include "BookFactory.h"
#include <factories/naudio/v0/AIFCDecode.h>
ExportResult NSampleHeaderExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> 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<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto aiff = LUS::BinaryWriter();
auto entry = std::static_pointer_cast<NSampleData>(raw);
auto loop = std::static_pointer_cast<ADPCMLoopData>(Companion::Instance->GetParseDataByAddr(entry->loop)->data.value());
auto book = std::static_pointer_cast<ADPCMBookData>(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<char> data(sampleData, sampleData + entry->size);
Companion::Instance->RegisterCompanionFile(path.filename(), data);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> NSampleFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto offset = GetSafeNode<uint32_t>(node, "offset");
auto parent = GetSafeNode<uint32_t>(node, "parent");
+5
View File
@@ -36,6 +36,10 @@ class NSampleModdingExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class NSampleXMLExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class NSampleFactory : public BaseFactory {
public:
std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& 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; }
+8 -1
View File
@@ -98,11 +98,18 @@ int main(int argc, char *argv[]) {
}
});
modding_export->add_option("mode", mode, "xml or binary")->required();
modding_export->add_option("<baserom.z64>", 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 {