diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2024-02-21 12:51:47 -0600 |
|---|---|---|
| committer | Lywx <kiritodev01@gmail.com> | 2024-02-21 20:38:05 -0600 |
| commit | 70a6bb7cf1a93609bcb92f14673c064d98ff9cf5 (patch) | |
| tree | 6d30a15ac7ce377343422798b2c9b9bcffca4b5b /src | |
| parent | 3845d02cf603dbba49b44a160b4e1958c86c738e (diff) | |
Added asset mods support
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.cpp | 92 | ||||
| -rw-r--r-- | src/Companion.h | 13 | ||||
| -rw-r--r-- | src/factories/AudioHeaderFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/BankFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/BaseFactory.h | 3 | ||||
| -rw-r--r-- | src/factories/BlobFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/DisplayListFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/LightsFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/SampleFactory.h | 5 | ||||
| -rw-r--r-- | src/factories/SequenceFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/TextureFactory.cpp | 176 | ||||
| -rw-r--r-- | src/factories/TextureFactory.h | 16 | ||||
| -rw-r--r-- | src/factories/VtxFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/mk64/WaypointFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/sm64/AnimationFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/sm64/DialogFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/sm64/DictionaryFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/sm64/GeoLayoutFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/sm64/TextFactory.h | 4 | ||||
| -rw-r--r-- | src/main.cpp | 19 | ||||
| -rw-r--r-- | src/n64/Cartridge.cpp | 4 |
21 files changed, 350 insertions, 30 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp index c7be9b6..a6e9aa3 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -18,6 +18,7 @@ #include "factories/LightsFactory.h" #include "factories/mk64/WaypointFactory.h" #include "spdlog/spdlog.h" +#include "hj/sha1.h" #include <fstream> #include <iostream> @@ -79,7 +80,29 @@ void Companion::ExtractNode(YAML::Node& node, std::string& name, SWrapper* binar return; } - auto result = factory->get()->parse(this->gRomData, node); + auto impl = factory->get(); + + std::optional<std::shared_ptr<IParsedData>> result; + if(this->gConfig.modding) { + if(impl->SupportModdedAssets() && this->gModdedAssetPaths.contains(name)) { + + auto path = fs::path(this->gConfig.moddingPath) / this->gModdedAssetPaths[name]; + if(!fs::exists(path)) { + SPDLOG_ERROR("Modded asset {} not found", this->gModdedAssetPaths[name]); + return; + } + + std::ifstream input(path, std::ios::binary); + std::vector<uint8_t> data = std::vector<uint8_t>( std::istreambuf_iterator( input ), {}); + input.close(); + + result = factory->get()->parse_modding(data, node); + } else { + result = factory->get()->parse(this->gRomData, node); + } + } else { + result = factory->get()->parse(this->gRomData, node); + } if(!result.has_value()){ SPDLOG_ERROR("Failed to process {}", name); return; @@ -117,6 +140,29 @@ void Companion::ExtractNode(YAML::Node& node, std::string& name, SWrapper* binar binary->CreateFile(name, std::vector(data.begin(), data.end())); break; } + case ExportType::Modding: { + stream.str(""); + stream.clear(); + std::string ogname = name; + exporter->get()->Export(stream, result.value(), name, node, &name); + + auto data = stream.str(); + if(data.empty()) { + break; + } + + std::string dpath = Instance->GetOutputPath() + "/" + name; + if(!exists(fs::path(dpath).parent_path())){ + create_directories(fs::path(dpath).parent_path()); + } + + this->gModdedAssetPaths[ogname] = name; + + std::ofstream file(dpath, std::ios::binary); + file.write(data.c_str(), data.size()); + file.close(); + break; + } default: { exporter->get()->Export(stream, result.value(), name, node, &name); @@ -141,6 +187,20 @@ void Companion::ExtractNode(YAML::Node& node, std::string& name, SWrapper* binar } } +void Companion::ParseModdingConfig() { + auto path = fs::path(this->gConfig.moddingPath) / "modding.yml"; + if(!fs::exists(path)) { + throw std::runtime_error("No modding config found, please run in export mode first"); + } + auto modding = YAML::LoadFile(path); + for(auto assets = modding["assets"].begin(); assets != modding["assets"].end(); ++assets) { + auto name = assets->first.as<std::string>(); + auto asset = assets->second.as<std::string>(); + this->gModdedAssetPaths[name] = asset; + } +} + + void Companion::ParseCurrentFileConfig(YAML::Node node) { if(node["segments"]) { for(auto segment = node["segments"].begin(); segment != node["segments"].end(); ++segment) { @@ -214,7 +274,9 @@ void Companion::Process() { auto path = rom["path"].as<std::string>(); auto opath = cfg["output"]; auto gbi = cfg["gbi"]; + auto modding_path = opath && opath["modding"] ? opath["modding"].as<std::string>() : "modding"; + this->gConfig.moddingPath = modding_path; switch (this->gConfig.exporterType) { case ExportType::Binary: { this->gConfig.outputPath = opath && opath["binary"] ? opath["binary"].as<std::string>() : "generic.otr"; @@ -228,6 +290,10 @@ void Companion::Process() { this->gConfig.outputPath = opath && opath["code"] ? opath["code"].as<std::string>() : "code"; break; } + case ExportType::Modding: { + this->gConfig.outputPath = modding_path; + break; + } } if(gbi) { @@ -264,6 +330,10 @@ void Companion::Process() { }; } + if(this->gConfig.exporterType == ExportType::Code && this->gConfig.modding) { + this->ParseModdingConfig(); + } + if(std::holds_alternative<std::vector<std::string>>(this->gWriteOrder)) { for (auto& [key, _] : this->gFactories) { auto entries = std::get<std::vector<std::string>>(this->gWriteOrder); @@ -415,8 +485,20 @@ void Companion::Process() { spdlog::set_pattern(line); } - if(this->gConfig.exporterType != ExportType::Binary){ - auto fsout = fs::path(this->gConfig.outputPath); + auto fsout = fs::path(this->gConfig.outputPath); + + if(this->gConfig.exporterType == ExportType::Modding) { + fsout /= "modding.yml"; + YAML::Node modding; + + for (const auto& [key, value] : this->gModdedAssetPaths) { + modding["assets"][key] = value; + } + + std::ofstream file(fsout.string(), std::ios::binary); + file << modding; + file.close(); + } else if(this->gConfig.exporterType != ExportType::Binary){ std::string filename = this->gCurrentDirectory.filename().string(); switch (this->gConfig.exporterType) { @@ -627,4 +709,8 @@ std::optional<std::tuple<std::string, YAML::Node>> Companion::GetNodeByAddr(cons std::string Companion::NormalizeAsset(const std::string& name) const { auto path = fs::path(this->gCurrentFile).stem().string() + "_" + name; return path; +} + +std::string Companion::CalculateHash(const std::vector<uint8_t>& data) { + return Chocobo1::SHA1().addData(data).finalize().toString(); }
\ No newline at end of file diff --git a/src/Companion.h b/src/Companion.h index 8af92f0..b82b83d 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -42,18 +42,21 @@ struct TorchConfig { GBIConfig gbi; SegmentConfig segment; std::string outputPath; + std::string moddingPath; ExportType exporterType; bool otrMode; bool debug; + bool modding; }; class Companion { public: static Companion* Instance; - explicit Companion(std::filesystem::path rom, const bool otr, const bool debug) : gRomPath(std::move(rom)), gCartridge(nullptr) { + explicit Companion(std::filesystem::path rom, const bool otr, const bool debug, const bool modding = false) : gRomPath(std::move(rom)), gCartridge(nullptr) { this->gConfig.otrMode = otr; this->gConfig.debug = debug; + this->gConfig.modding = modding; } void Init(ExportType type); @@ -73,6 +76,7 @@ public: std::optional<std::tuple<std::string, YAML::Node>> GetNodeByAddr(uint32_t addr); std::optional<std::shared_ptr<BaseFactory>> GetFactory(const std::string& type); + static std::string CalculateHash(const std::vector<uint8_t>& data); static void Pack(const std::string& folder, const std::string& output); std::string NormalizeAsset(const std::string& name) const; @@ -81,6 +85,7 @@ public: std::optional<std::tuple<std::string, YAML::Node>> RegisterAsset(const std::string& name, YAML::Node& node); private: TorchConfig gConfig; + YAML::Node gModdingConfig; fs::path gCurrentDirectory; std::vector<uint8_t> gRomData; std::filesystem::path gRomPath; @@ -92,11 +97,13 @@ private: uint32_t gCurrentPad = 0; std::variant<std::vector<std::string>, std::string> gWriteOrder; std::unordered_map<std::string, std::shared_ptr<BaseFactory>> gFactories; - std::map<std::string, std::map<std::string, std::pair<YAML::Node, bool>>> gAssetDependencies; - std::map<std::string, std::map<std::string, std::vector<std::pair<uint32_t, std::string>>>> gWriteMap; + std::unordered_map<std::string, std::string> gModdedAssetPaths; + std::unordered_map<std::string, std::map<std::string, std::pair<YAML::Node, bool>>> gAssetDependencies; + std::unordered_map<std::string, std::map<std::string, std::vector<std::pair<uint32_t, std::string>>>> gWriteMap; std::unordered_map<std::string, std::unordered_map<uint32_t, std::tuple<std::string, YAML::Node>>> gAddrMap; void ParseCurrentFileConfig(YAML::Node node); + void ParseModdingConfig(); void RegisterFactory(const std::string& type, const std::shared_ptr<BaseFactory>& factory); void ExtractNode(YAML::Node& node, std::string& name, SWrapper* binary); }; diff --git a/src/factories/AudioHeaderFactory.h b/src/factories/AudioHeaderFactory.h index 5372eb5..4726068 100644 --- a/src/factories/AudioHeaderFactory.h +++ b/src/factories/AudioHeaderFactory.h @@ -5,7 +5,11 @@ class AudioHeaderFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return {}; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/BankFactory.h b/src/factories/BankFactory.h index b5298de..4699c92 100644 --- a/src/factories/BankFactory.h +++ b/src/factories/BankFactory.h @@ -20,9 +20,13 @@ class BankBinaryExporter : public BaseExporter { class BankFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, BankBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/BaseFactory.h b/src/factories/BaseFactory.h index 3cbb0d9..0ccf6dc 100644 --- a/src/factories/BaseFactory.h +++ b/src/factories/BaseFactory.h @@ -26,6 +26,7 @@ enum class ExportType { Header, Code, Binary, + Modding, }; template<typename T> @@ -72,6 +73,7 @@ public: class BaseFactory { public: virtual std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) = 0; + virtual std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) = 0; std::optional<std::shared_ptr<BaseExporter>> GetExporter(ExportType type) { auto exporters = this->GetExporters(); if (exporters.find(type) != exporters.end()) { @@ -79,6 +81,7 @@ public: } return std::nullopt; } + virtual bool SupportModdedAssets() = 0; private: virtual std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() = 0; };
\ No newline at end of file diff --git a/src/factories/BlobFactory.h b/src/factories/BlobFactory.h index c199618..9dd1c0d 100644 --- a/src/factories/BlobFactory.h +++ b/src/factories/BlobFactory.h @@ -13,10 +13,14 @@ class BlobCodeExporter : public BaseExporter { class BlobFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, BlobBinaryExporter) REGISTER(Code, BlobCodeExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/DisplayListFactory.h b/src/factories/DisplayListFactory.h index f6eb730..3c9116e 100644 --- a/src/factories/DisplayListFactory.h +++ b/src/factories/DisplayListFactory.h @@ -25,6 +25,9 @@ class DListCodeExporter : public BaseExporter { class DListFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Header, DListHeaderExporter) @@ -32,4 +35,5 @@ public: REGISTER(Code, DListCodeExporter) }; } + bool SupportModdedAssets() override { return false; } }; diff --git a/src/factories/LightsFactory.h b/src/factories/LightsFactory.h index 9c3bcf1..a063ddf 100644 --- a/src/factories/LightsFactory.h +++ b/src/factories/LightsFactory.h @@ -56,6 +56,9 @@ class LightsCodeExporter : public BaseExporter { class LightsFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Code, LightsCodeExporter) @@ -63,4 +66,5 @@ public: REGISTER(Binary, LightsBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/SampleFactory.h b/src/factories/SampleFactory.h index 0a006ca..19f26ee 100644 --- a/src/factories/SampleFactory.h +++ b/src/factories/SampleFactory.h @@ -19,10 +19,13 @@ class SampleBinaryExporter : public BaseExporter { class SampleFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; - + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, SampleBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/SequenceFactory.h b/src/factories/SequenceFactory.h index 1f82d75..40f19c8 100644 --- a/src/factories/SequenceFactory.h +++ b/src/factories/SequenceFactory.h @@ -21,9 +21,13 @@ class SequenceBinaryExporter : public BaseExporter { class SequenceFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, SequenceBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/TextureFactory.cpp b/src/factories/TextureFactory.cpp index 5a0caad..6f1de04 100644 --- a/src/factories/TextureFactory.cpp +++ b/src/factories/TextureFactory.cpp @@ -4,18 +4,22 @@ #include "Companion.h" #include <iomanip> -static const std::unordered_map <std::string, TextureType> gTextureTypes = { - { "RGBA16", TextureType::RGBA16bpp }, - { "RGBA32", TextureType::RGBA32bpp }, - { "CI4", TextureType::Palette4bpp }, - { "CI8", TextureType::Palette8bpp }, - { "I4", TextureType::Grayscale4bpp }, - { "I8", TextureType::Grayscale8bpp }, - { "IA1", TextureType::GrayscaleAlpha1bpp }, - { "IA4", TextureType::GrayscaleAlpha4bpp }, - { "IA8", TextureType::GrayscaleAlpha8bpp }, - { "IA16", TextureType::GrayscaleAlpha16bpp }, - { "TLUT", TextureType::TLUT }, +extern "C" { +#include "n64graphics/n64graphics.h" +} + +static const std::unordered_map <std::string, TextureFormat> gTextureTypes = { + { "RGBA16", { TextureType::RGBA16bpp, 16 } }, + { "RGBA32", { TextureType::RGBA32bpp, 32 } }, + { "CI4", { TextureType::Palette4bpp, 4 } }, + { "CI8", { TextureType::Palette8bpp, 8 } }, + { "I4", { TextureType::Grayscale4bpp, 4 } }, + { "I8", { TextureType::Grayscale8bpp, 8 } }, + { "IA1", { TextureType::GrayscaleAlpha1bpp, 1 } }, + { "IA4", { TextureType::GrayscaleAlpha4bpp, 4 } }, + { "IA8", { TextureType::GrayscaleAlpha8bpp, 8 } }, + { "IA16", { TextureType::GrayscaleAlpha16bpp, 16 } }, + { "TLUT", { TextureType::TLUT, 0 } }, }; size_t CalculateTextureSize(TextureType type, uint32_t width, uint32_t height) { @@ -134,7 +138,7 @@ void TextureBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedD WriteHeader(writer, LUS::ResourceType::Texture, 0); - writer.Write((uint32_t) texture->mType); + writer.Write((uint32_t) texture->mType.type); writer.Write(texture->mWidth); writer.Write(texture->mHeight); @@ -143,6 +147,53 @@ void TextureBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedD writer.Finish(write); } +void TextureModdingExporter::Export(std::ostream&write, std::shared_ptr<IParsedData> data, std::string&entryName, YAML::Node&node, std::string* replacement) { + auto texture = std::static_pointer_cast<TextureData>(data); + auto format = texture->mType; + uint8_t* raw = new uint8_t[CalculateTextureSize(format.type, texture->mWidth, texture->mHeight) * 2]; + int size = 0; + + auto ext = GetSafeNode<std::string>(node, "format"); + + std::transform(ext.begin(), ext.end(), ext.begin(), tolower); + (*replacement) += "." + ext + ".png"; + + switch (format.type) { + case TextureType::RGBA16bpp: + case TextureType::RGBA32bpp: { + rgba* imgr = raw2rgba(texture->mBuffer.data(), texture->mWidth, texture->mHeight, format.depth); + if(rgba2png(&raw, &size, imgr, texture->mWidth, texture->mHeight)) { + throw std::runtime_error("Failed to convert texture to PNG"); + } + break; + } + case TextureType::GrayscaleAlpha16bpp: + case TextureType::GrayscaleAlpha8bpp: + case TextureType::GrayscaleAlpha4bpp: + case TextureType::GrayscaleAlpha1bpp: { + ia* imgia = raw2ia(texture->mBuffer.data(), texture->mWidth, texture->mHeight, format.depth); + if(ia2png(&raw, &size, imgia, texture->mWidth, texture->mHeight)) { + throw std::runtime_error("Failed to convert texture to PNG"); + } + break; + } + case TextureType::Grayscale8bpp: + case TextureType::Grayscale4bpp: { + ia* imgi = raw2i(texture->mBuffer.data(), texture->mWidth, texture->mHeight, format.depth); + if(ia2png(&raw, &size, imgi, texture->mWidth, texture->mHeight)) { + throw std::runtime_error("Failed to convert texture to PNG"); + } + break; + } + default: { + SPDLOG_ERROR("Unsupported texture format for modding: {}", ext); + } + } + + write.write(reinterpret_cast<char*>(raw), size); +} + + std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { auto format = GetSafeNode<std::string>(node, "format"); uint32_t width; @@ -161,9 +212,9 @@ std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui return std::nullopt; } - TextureType type = gTextureTypes.at(format); + TextureFormat fmt = gTextureTypes.at(format); - if(type == TextureType::TLUT){ + if(fmt.type == TextureType::TLUT){ width = GetSafeNode<uint32_t>(node, "colors"); height = 1; } else { @@ -171,18 +222,105 @@ std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui height = GetSafeNode<uint32_t>(node, "height"); } - size = GetSafeNode<uint32_t>(node, "size", CalculateTextureSize(gTextureTypes.at(format), width, height)); + size = GetSafeNode<uint32_t>(node, "size", CalculateTextureSize(gTextureTypes.at(format).type, width, height)); auto [_, segment] = Decompressor::AutoDecode(node, buffer, size); std::vector<uint8_t> result; - if(type == TextureType::GrayscaleAlpha1bpp){ + if(fmt.type == TextureType::GrayscaleAlpha1bpp){ result = alloc_ia8_text_from_i1(reinterpret_cast<uint16_t*>(segment.data), 8, 16); } else { result = std::vector(segment.data, segment.data + segment.size); } SPDLOG_INFO("Texture: {}", format); - if(type == TextureType::TLUT){ + if(fmt.type == TextureType::TLUT){ + SPDLOG_INFO("Colors: {}", width); + } else { + SPDLOG_INFO("Width: {}", width); + SPDLOG_INFO("Height: {}", height); + } + SPDLOG_INFO("Size: {}", size); + SPDLOG_INFO("Offset: 0x{:X}", offset); + SPDLOG_INFO("Is Compressed: {}", Decompressor::IsCompressed(node) ? "true" : "false"); + + if(result.size() == 0){ + return std::nullopt; + } + + return std::make_shared<TextureData>(fmt, width, height, result); +} + +std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse_modding(std::vector<uint8_t>& buffer, YAML::Node& node) { + auto format = GetSafeNode<std::string>(node, "format"); + int width; + int height; + uint32_t size; + auto offset = GetSafeNode<uint32_t>(node, "offset"); + + if (format.empty()) { + SPDLOG_ERROR("Texture entry at {:X} in yaml missing format node\n\ + Please add one of the following formats\n\ + rgba16, rgba32, ia16, ia8, ia4, i8, i4, ci8, ci4, 1bpp, tlut", offset); + return std::nullopt; + } + + if(!gTextureTypes.contains(format)) { + return std::nullopt; + } + + TextureFormat fmt = gTextureTypes.at(format); + if(fmt.type == TextureType::TLUT){ + width = GetSafeNode<uint32_t>(node, "colors"); + height = 1; + } else { + width = GetSafeNode<uint32_t>(node, "width"); + height = GetSafeNode<uint32_t>(node, "height"); + } + + uint8_t* raw; + switch (fmt.type) { + case TextureType::RGBA16bpp: + case TextureType::RGBA32bpp: { + const auto imgr = png2rgba(buffer.data(), buffer.size(), &width, &height); + size = width * height * fmt.depth / 8; + raw = new uint8_t[size]; + if(rgba2raw(raw, imgr, width, height, fmt.depth) <= 0){ + throw std::runtime_error("Failed to convert PNG to texture"); + } + break; + } + case TextureType::GrayscaleAlpha16bpp: + case TextureType::GrayscaleAlpha8bpp: + case TextureType::GrayscaleAlpha4bpp: + case TextureType::GrayscaleAlpha1bpp: { + const auto imgia = png2ia(buffer.data(), buffer.size(), &width, &height); + size = width * height * fmt.depth / 8; + raw = new uint8_t[size]; + if(ia2raw(raw, imgia, width, height, fmt.depth) <= 0){ + throw std::runtime_error("Failed to convert PNG to texture"); + } + break; + } + case TextureType::Grayscale8bpp: + case TextureType::Grayscale4bpp: { + const auto imgi = png2ia(buffer.data(), buffer.size(), &width, &height); + size = width * height * fmt.depth / 8; + raw = new uint8_t[size]; + if(i2raw(raw, imgi, width, height, fmt.depth) <= 0){ + throw std::runtime_error("Failed to convert PNG to texture"); + } + break; + } + default: { + SPDLOG_ERROR("Unsupported texture format for modding: {}", format); + return std::nullopt; + } + } + + auto result = std::vector(raw, raw + size); + + SPDLOG_INFO("Texture: {}", format); + if(fmt.type == TextureType::TLUT){ SPDLOG_INFO("Colors: {}", width); } else { SPDLOG_INFO("Width: {}", width); @@ -196,5 +334,5 @@ std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui return std::nullopt; } - return std::make_shared<TextureData>(type, width, height, result); + return std::make_shared<TextureData>(fmt, width, height, result); } diff --git a/src/factories/TextureFactory.h b/src/factories/TextureFactory.h index 2bffc9a..e684061 100644 --- a/src/factories/TextureFactory.h +++ b/src/factories/TextureFactory.h @@ -17,14 +17,19 @@ enum class TextureType { TLUT }; +struct TextureFormat { + TextureType type; + uint32_t depth; +}; + class TextureData : public IParsedData { public: - TextureType mType; + TextureFormat mType; uint32_t mWidth; uint32_t mHeight; std::vector<uint8_t> mBuffer; - TextureData(TextureType type, uint32_t width, uint32_t height, std::vector<uint8_t>& buffer) : mType(type), mWidth(width), mHeight(height), mBuffer(std::move(buffer)) {} + TextureData(TextureFormat type, uint32_t width, uint32_t height, std::vector<uint8_t>& buffer) : mType(type), mWidth(width), mHeight(height), mBuffer(std::move(buffer)) {} }; class TextureHeaderExporter : public BaseExporter { @@ -39,14 +44,21 @@ class TextureBinaryExporter : public BaseExporter { void Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; }; +class TextureModdingExporter : public BaseExporter { + void Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; +}; + class TextureFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override; inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Header, TextureHeaderExporter) REGISTER(Binary, TextureBinaryExporter) REGISTER(Code, TextureCodeExporter) + REGISTER(Modding, TextureModdingExporter) }; } + bool SupportModdedAssets() override { return true; } };
\ No newline at end of file diff --git a/src/factories/VtxFactory.h b/src/factories/VtxFactory.h index 22ec7dd..3ab1f52 100644 --- a/src/factories/VtxFactory.h +++ b/src/factories/VtxFactory.h @@ -31,6 +31,9 @@ class VtxCodeExporter : public BaseExporter { class VtxFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Code, VtxCodeExporter) @@ -38,4 +41,5 @@ public: REGISTER(Binary, VtxBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } };
\ No newline at end of file diff --git a/src/factories/mk64/WaypointFactory.h b/src/factories/mk64/WaypointFactory.h index 1268e6c..a42ed60 100644 --- a/src/factories/mk64/WaypointFactory.h +++ b/src/factories/mk64/WaypointFactory.h @@ -33,6 +33,9 @@ namespace MK64 { class WaypointFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Code, WaypointCodeExporter) @@ -40,5 +43,6 @@ namespace MK64 { REGISTER(Binary, WaypointBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; }
\ No newline at end of file diff --git a/src/factories/sm64/AnimationFactory.h b/src/factories/sm64/AnimationFactory.h index 82b073a..9658c50 100644 --- a/src/factories/sm64/AnimationFactory.h +++ b/src/factories/sm64/AnimationFactory.h @@ -25,8 +25,12 @@ class AnimationBinaryExporter : public BaseExporter { class AnimationFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, AnimationBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; }
\ No newline at end of file diff --git a/src/factories/sm64/DialogFactory.h b/src/factories/sm64/DialogFactory.h index 75160af..dc456f9 100644 --- a/src/factories/sm64/DialogFactory.h +++ b/src/factories/sm64/DialogFactory.h @@ -21,8 +21,12 @@ class DialogBinaryExporter : public BaseExporter { class DialogFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, DialogBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; }
\ No newline at end of file diff --git a/src/factories/sm64/DictionaryFactory.h b/src/factories/sm64/DictionaryFactory.h index acb7429..4e94aec 100644 --- a/src/factories/sm64/DictionaryFactory.h +++ b/src/factories/sm64/DictionaryFactory.h @@ -18,8 +18,12 @@ class DictionaryBinaryExporter : public BaseExporter { class DictionaryFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, DictionaryBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; }
\ No newline at end of file diff --git a/src/factories/sm64/GeoLayoutFactory.h b/src/factories/sm64/GeoLayoutFactory.h index 406babc..0b743af 100644 --- a/src/factories/sm64/GeoLayoutFactory.h +++ b/src/factories/sm64/GeoLayoutFactory.h @@ -32,11 +32,15 @@ class GeoBinaryExporter : public BaseExporter { class GeoLayoutFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Header, GeoHeaderExporter) REGISTER(Binary, GeoBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; } diff --git a/src/factories/sm64/TextFactory.h b/src/factories/sm64/TextFactory.h index 9c030b7..3813587 100644 --- a/src/factories/sm64/TextFactory.h +++ b/src/factories/sm64/TextFactory.h @@ -11,8 +11,12 @@ class TextBinaryExporter : public BaseExporter { class TextFactory : public BaseFactory { public: std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override; + std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override { + return std::nullopt; + } inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override { return { REGISTER(Binary, TextBinaryExporter) }; } + bool SupportModdedAssets() override { return false; } }; };
\ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 173fe79..7b4d6f9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -76,6 +76,25 @@ int main(int argc, char *argv[]) { } }); + /* Generate modding files */ + const auto modding_root = app.add_subcommand("modding", "Modding - Generates modding files like png\n"); + const auto modding_import = modding_root->add_subcommand("import", "Import - Import modified files to generate C code\n"); + const auto modding_export = modding_root->add_subcommand("export", "Export - Export modified files to a folder\n"); + + modding_import->add_option("<baserom.z64>", filename, "")->required()->check(CLI::ExistingFile); + + modding_import->parse_complete_callback([&] { + const auto instance = Companion::Instance = new Companion(filename, false, debug, true); + instance->Init(ExportType::Code); + }); + + 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); + }); + try { app.parse(argc, argv); } catch (const CLI::ParseError &e) { diff --git a/src/n64/Cartridge.cpp b/src/n64/Cartridge.cpp index 97fdef4..f725f86 100644 --- a/src/n64/Cartridge.cpp +++ b/src/n64/Cartridge.cpp @@ -1,7 +1,7 @@ #include "Cartridge.h" -#include "hj/sha1.h" #include "binarytools/BinaryReader.h" +#include <Companion.h> void N64::Cartridge::Initialize() { LUS::BinaryReader reader((char*) this->gRomData.data(), this->gRomData.size()); @@ -13,7 +13,7 @@ void N64::Cartridge::Initialize() { reader.Seek(0x3E, LUS::SeekOffsetType::Start); uint8_t country = reader.ReadUByte(); this->gVersion = reader.ReadUByte(); - this->gHash = Chocobo1::SHA1().addData(this->gRomData).finalize().toString(); + this->gHash = Companion::CalculateHash(this->gRomData); switch (country) { case 'J': this->gCountryCode = CountryCode::Japan; |
