summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorKiritoDv <kiritodev01@gmail.com>2024-02-21 12:51:47 -0600
committerLywx <kiritodev01@gmail.com>2024-02-21 20:38:05 -0600
commit70a6bb7cf1a93609bcb92f14673c064d98ff9cf5 (patch)
tree6d30a15ac7ce377343422798b2c9b9bcffca4b5b /src
parent3845d02cf603dbba49b44a160b4e1958c86c738e (diff)
Added asset mods support
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp92
-rw-r--r--src/Companion.h13
-rw-r--r--src/factories/AudioHeaderFactory.h4
-rw-r--r--src/factories/BankFactory.h4
-rw-r--r--src/factories/BaseFactory.h3
-rw-r--r--src/factories/BlobFactory.h4
-rw-r--r--src/factories/DisplayListFactory.h4
-rw-r--r--src/factories/LightsFactory.h4
-rw-r--r--src/factories/SampleFactory.h5
-rw-r--r--src/factories/SequenceFactory.h4
-rw-r--r--src/factories/TextureFactory.cpp176
-rw-r--r--src/factories/TextureFactory.h16
-rw-r--r--src/factories/VtxFactory.h4
-rw-r--r--src/factories/mk64/WaypointFactory.h4
-rw-r--r--src/factories/sm64/AnimationFactory.h4
-rw-r--r--src/factories/sm64/DialogFactory.h4
-rw-r--r--src/factories/sm64/DictionaryFactory.h4
-rw-r--r--src/factories/sm64/GeoLayoutFactory.h4
-rw-r--r--src/factories/sm64/TextFactory.h4
-rw-r--r--src/main.cpp19
-rw-r--r--src/n64/Cartridge.cpp4
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;