From 24633ebd7cc6866da3e3d06fddc6f0f49a741f1d Mon Sep 17 00:00:00 2001 From: MegaMech Date: Sat, 6 Apr 2024 15:36:03 -0600 Subject: [PATCH] Introduce Metadata and ItemCurve Factories (#27) * Introduce metadata * Metadata works now * Cleanup * Fixes * Cleanup 2 * yaml improvement * Comment update * Rename tables to metadata * Update * Fix warnings * Implement ItemCurve * Remove unused enum * Replace static with const * Fix bug * Add const * More fixes * Fix quote bug in IncludeFactory * Fix header in ItemCurve * Fixes * Fix texture factory * updates * Clearer debug info * Fixes * Working ci8 png * Fix size output * Fix * Add gitignore for modding * Fix precision for mtx * Remove header exporter from metadata * Remove hardcoded enum * Remove tabs * Remove tabs * tlut temp fix --------- Co-authored-by: = <=> --- .gitignore | 3 +- lib/n64graphics/n64graphics.c | 115 +++++++- lib/n64graphics/n64graphics.h | 10 +- src/Companion.cpp | 57 +++- src/Companion.h | 8 + src/factories/DisplayListFactory.cpp | 39 +-- src/factories/GenericArrayFactory.cpp | 23 +- src/factories/GenericArrayFactory.h | 4 +- src/factories/IncludeFactory.cpp | 2 +- src/factories/LightsFactory.cpp | 2 +- src/factories/MtxFactory.cpp | 16 +- src/factories/MtxFactory.h | 2 +- src/factories/TextureFactory.cpp | 99 +++++-- src/factories/VtxFactory.cpp | 2 +- src/factories/mk64/CourseMetadata.cpp | 375 ++++++++++++++++++++++++ src/factories/mk64/CourseMetadata.h | 70 +++++ src/factories/mk64/DrivingBehaviour.cpp | 4 +- src/factories/mk64/ItemCurve.cpp | 94 ++++++ src/factories/mk64/ItemCurve.h | 42 +++ src/types/Vec3D.cpp | 17 ++ src/types/Vec3D.h | 11 + src/utils/Decompressor.cpp | 2 +- 22 files changed, 921 insertions(+), 76 deletions(-) create mode 100644 src/factories/mk64/CourseMetadata.cpp create mode 100644 src/factories/mk64/CourseMetadata.h create mode 100644 src/factories/mk64/ItemCurve.cpp create mode 100644 src/factories/mk64/ItemCurve.h diff --git a/.gitignore b/.gitignore index be72f4d..6a42dc7 100644 --- a/.gitignore +++ b/.gitignore @@ -13,4 +13,5 @@ headers/ build/ code/ .vscode/ -tools/ \ No newline at end of file +tools/ +modding/* \ No newline at end of file diff --git a/lib/n64graphics/n64graphics.c b/lib/n64graphics/n64graphics.c index 39db002..9008bab 100644 --- a/lib/n64graphics/n64graphics.c +++ b/lib/n64graphics/n64graphics.c @@ -25,6 +25,8 @@ #define SCALE_3_8(VAL_) ((VAL_) * 0x24) #define SCALE_8_3(VAL_) ((VAL_) / 0x24) +unsigned short magicFiller = 0x07FE; + typedef struct { enum { IMG_FORMAT_RGBA, @@ -125,8 +127,8 @@ ia* raw2ia(const uint8_t* raw, int width, int height, int depth) { return img; } -ci* raw2ci_torch(const uint8_t* raw, int width, int height, int depth) { - ci* img = NULL; +ci *raw2ci_torch(const uint8_t* raw, int width, int height, int depth) { + ci *img = NULL; int img_size; img_size = width * height * sizeof(*img); @@ -308,6 +310,79 @@ int ia2raw(uint8_t* raw, const ia* img, int width, int height, int depth) { return size; } + +/** + * Check 2 rgba structs for equality. 0 if unequal, 1 if equal +**/ +int comp_rgba(const rgba left, const rgba right) { + if ((left.red != right.red) || (left.green != right.green) || (left.blue != right.blue) || (left.alpha != right.alpha)) { + return 0; + } else { + return 1; + } +} + +/** + * Check if a given rgba (comp) is in a given palette (pal, represented as an array of rgba structs) + * If found, return the index in pal it was found out + * Otherwise, return -1 +**/ +int get_color_index(const rgba comp, const rgba *pal, int mask_value, int pal_size) { + int pal_idx; + // The starting values used here are super specific to MK64, they're not really portable to anything else + if (mask_value == 0) { + pal_idx = 0; + } else { + pal_idx = 0xC0; + } + for (; pal_idx < pal_size; pal_idx++) { + if (comp_rgba(comp, pal[pal_idx]) == 1) return pal_idx; + } + ERROR("Could not find a color in the palette\n"); + ERROR("comp: %x%x%x%x\n", comp.red, comp.green, comp.blue, comp.alpha); + return -1; +} + +/** + * Takes an image (img, an array of rgba structs) and a palette (pal, also an array of rgba structs) + * Sets the values of rawci (8 bit color index array) to the appropriate index in pal that each entry in img can be found at + * If a value in img is not found in pal, return 0, indicating an error + * Returns 1 if all values in img are found somewhere in pal +**/ +int imgpal2rawci(uint8_t *rawci, const rgba *img, const rgba *pal, const uint8_t *wheel_mask, int raw_size, int ci_depth, int img_size, int pal_size) { + int img_idx; + int pal_idx; + int mask_value; + memset(rawci, 0, raw_size); + + for (img_idx = 0; img_idx < img_size; img_idx++) { + if (wheel_mask != NULL) { + mask_value = wheel_mask[img_idx]; + } else { + mask_value = 0; + } + pal_idx = get_color_index(img[img_idx], pal, mask_value, pal_size); + if (pal_idx != -1) { + switch (ci_depth) { + case 8: + rawci[img_idx] = pal_idx; + break; + case 4: + { + int byte_idx = img_idx / 2; + int nibble = 1 - (img_idx % 2); + uint8_t mask = 0xF << (4 * (1 - nibble)); + rawci[byte_idx] = (rawci[byte_idx] & mask) | (pal_idx << (4 * nibble)); + break; + } + } + } else { + return 0; + } + } + return 1; +} + int i2raw(uint8_t* raw, const ia* img, int width, int height, int depth) { int size = width * height * depth / 8; INFO("Converting I%d %dx%d to raw\n", depth, width, height); @@ -338,7 +413,7 @@ int i2raw(uint8_t* raw, const ia* img, int width, int height, int depth) { return size; } -int ci2raw_torch(uint8_t* raw, const ci* img, int width, int height, int depth) { +int ci2raw_torch(uint8_t *raw, const ci *img, int width, int height, int depth) { int size = width * height * depth / 8; INFO("Converting I%d %dx%d to raw\n", depth, width, height); @@ -420,7 +495,7 @@ int ia2png(unsigned char** png_output, int* size_output, const ia* img, int widt return ret; } -int ci2png(unsigned char** png_output, int* size_output, const ci* img, int width, int height) { +int ci2png(unsigned char **png_output, int *size_output, const ci *img, int width, int height) { int ret = 0; // convert to format stb_image_write expects @@ -743,4 +818,36 @@ const char* n64graphics_get_read_version(void) { const char* n64graphics_get_write_version(void) { return "stb_image_write 1.09"; +} + +/** + * Converts binary ci8 + palette to a single .png + */ +int convert_raw_to_ci8(unsigned char **png_output, int *size_output, uint8_t *texture, uint8_t *palette, int format, int width, int height, int depth) { +FILE *pal_fp; + uint8_t *pal; + uint8_t *raw_fmt; + rgba *imgr; + ia *imgi; + int pal_size; + int res; + + raw_fmt = ci2raw(texture, palette, width, height, depth); + switch (format) { + case IMG_FORMAT_RGBA: + INFO("Converting raw to RGBA16\n"); + imgr = raw2rgba(raw_fmt, width, height, depth); + res = rgba2png(png_output, size_output, imgr, width, height); + break; + case IMG_FORMAT_IA: + INFO("Converting raw to IA16\n"); + imgi = raw2ia(raw_fmt, width, height, depth); + //res = ia2png(name, imgi, width, height); + break; + default: + //ERROR("Unsupported palette format: %s\n", format2str(&config.pal_format)); + return EXIT_FAILURE; + } + free(raw_fmt); + free(pal); } \ No newline at end of file diff --git a/lib/n64graphics/n64graphics.h b/lib/n64graphics/n64graphics.h index 225169e..1488f79 100644 --- a/lib/n64graphics/n64graphics.h +++ b/lib/n64graphics/n64graphics.h @@ -51,7 +51,11 @@ ia *raw2ia(const uint8_t *raw, int width, int height, int depth); ia *raw2i(const uint8_t *raw, int width, int height, int depth); // N64 raw CI4/CI8 -> intermediate CI -ci* raw2ci_torch(const uint8_t* raw, int width, int height, int depth); +ci *raw2ci_torch(const uint8_t* raw, int width, int height, int depth); + +int convert_raw_to_ci8(unsigned char **png_output, int *size_output, uint8_t *texture, uint8_t *palette, int format, int width, int height, int depth); + +int imgpal2rawci(uint8_t *rawci, const rgba *img, const rgba *pal, const uint8_t *wheel_mask, int raw_size, int ci_depth, int img_size, int pal_size); //--------------------------------------------------------- // intermediate RGBA/IA -> N64 RGBA/IA/I/CI @@ -70,7 +74,6 @@ int i2raw(uint8_t *raw, const ia *img, int width, int height, int depth); // intermediate CI -> N64 raw CI4/CI8 int ci2raw_torch(uint8_t* raw, const ci* img, int width, int height, int depth); - //--------------------------------------------------------- // N64 CI <-> N64 RGBA16/IA16 //--------------------------------------------------------- @@ -93,7 +96,6 @@ int ia2png(unsigned char** png_output, int* size_output, const ia* img, int widt int ci2png(unsigned char** png_output, int* size_output, const ci* img, int width, int height); - //--------------------------------------------------------- // PNG -> intermediate RGBA/IA //--------------------------------------------------------- @@ -110,6 +112,8 @@ ia *png2ia(unsigned char* png_input, int size_input, int *width, int *height); // PNG file -> intermediate CI ci* png2ci(unsigned char* png_input, int size_input, int* width, int* height); +// Adds colours to palette data +static int pal_add_color(palette_t* pal, uint16_t val); //--------------------------------------------------------- // version diff --git a/src/Companion.cpp b/src/Companion.cpp index 1ad9e64..9a3c233 100644 --- a/src/Companion.cpp +++ b/src/Companion.cpp @@ -40,6 +40,8 @@ #include "factories/mk64/TrackSections.h" #include "factories/mk64/SpawnData.h" #include "factories/mk64/DrivingBehaviour.h" +#include "factories/mk64/ItemCurve.h" +#include "factories/mk64/CourseMetadata.h" #include "factories/sf64/ColPolyFactory.h" #include "factories/sf64/MessageFactory.h" @@ -96,6 +98,8 @@ void Companion::Init(const ExportType type) { this->RegisterFactory("MK64:TRACK_SECTIONS", std::make_shared()); this->RegisterFactory("MK64:SPAWN_DATA", std::make_shared()); this->RegisterFactory("MK64:DRIVING_BEHAVIOUR", std::make_shared()); + this->RegisterFactory("MK64:ITEM_CURVE", std::make_shared()); // Item curve for decomp only + this->RegisterFactory("MK64:METADATA", std::make_shared()); // SF64 specific this->RegisterFactory("SF64:ANIM", std::make_shared()); @@ -116,7 +120,7 @@ void Companion::ParseEnums(std::string& header) { std::ifstream file(header); if (!file.is_open()) { - throw std::runtime_error("Failed to open file"); + throw std::runtime_error("Failed to open header files for enums node in config"); } std::regex enumRegex(R"(enum\s+(\w+)\s*(?:\s*:\s*(\w+))?[\s\n\r]*\{)"); @@ -460,6 +464,47 @@ bool Companion::NodeHasChanges(const std::string& path) { return true; } +void Companion::LoadYAMLRecursively(const std::string &dirPath, std::vector &result, bool skipRoot) { + for (const auto &entry : std::filesystem::directory_iterator(dirPath)) { + if (entry.is_directory()) { + // Skip the root directory if specified + if (skipRoot && entry.path() == dirPath) { + continue; + } + + // Recursive call for subdirectories + LoadYAMLRecursively(entry.path(), result, false); + } else if (entry.path().extension() == ".yaml" || entry.path().extension() == ".yml") { + // Load YAML file and add it to the result vector + result.push_back(YAML::LoadFile(entry.path().string())); + } + } +} + +/** + * Config yaml requires tables: [assets/courses] + * Activate the factory using a normal asset yaml with type, input_directory, and output_directory nodes. + */ +void Companion::ProcessTables(YAML::Node& rom) { + auto dirs = rom["metadata"].as>(); + + for (const auto &dir : dirs) { + std::vector configNodes; + LoadYAMLRecursively(dir, configNodes, true); + gCourseMetadata[dir] = configNodes; + } + + // Write yaml data to console + if (this->IsDebug()) { + SPDLOG_INFO("------ Metadata ouptut ------"); + for (auto &node : gCourseMetadata[dirs[0]]) { + std::cout << node << std::endl; + SPDLOG_INFO("------------"); + } + SPDLOG_INFO("------ Metadata end ------"); + } +} + void Companion::Process() { if(!fs::exists("config.yml")) { @@ -491,6 +536,10 @@ void Companion::Process() { return; } + if (rom["metadata"]) { + ProcessTables(rom); + } + if(rom["segments"]) { auto segments = rom["segments"].as>(); for (int i = 0; i < segments.size(); i++) { @@ -1048,7 +1097,7 @@ CompressionType Companion::GetCompressionType(std::vector& buffer, cons std::optional Companion::SearchTable(uint32_t addr){ for(auto& table : this->gTables){ - if(addr >= table.start || addr <= table.end){ + if(addr >= table.start && addr <= table.end){ return table; } } @@ -1186,3 +1235,7 @@ std::optional Companion::AddAsset(YAML::Node asset) { return std::nullopt; } + +void Companion::AddTlutTextureMap(std::string index, std::shared_ptr entry) { + this->TlutTextureMap[index] = entry; +} \ No newline at end of file diff --git a/src/Companion.h b/src/Companion.h index 4319587..8d9b9e8 100644 --- a/src/Companion.h +++ b/src/Companion.h @@ -10,6 +10,7 @@ #include "factories/BaseFactory.h" #include "n64/Cartridge.h" #include "utils/Decompressor.h" +#include "factories/TextureFactory.h" class SWrapper; namespace fs = std::filesystem; @@ -99,6 +100,7 @@ public: GBIVersion GetGBIVersion() const { return this->gConfig.gbi.version; } GBIMinorVersion GetGBIMinorVersion() const { return this->gConfig.gbi.subversion; } + std::unordered_map> GetCourseMetadata() { return this->gCourseMetadata; } std::optional GetEnumFromValue(const std::string& key, int id); std::optional GetFileOffsetFromSegmentedAddr(uint8_t segment) const; @@ -111,6 +113,8 @@ public: CompressionType GetCurrCompressionType(void) const { return this->gCurrentCompressionType; }; CompressionType GetCompressionType(std::vector& buffer, const uint32_t offset); std::optional GetCurrentVRAM(void) const { return this->gCurrentVram; }; + std::unordered_map> GetTlutTextureMap() { return this->TlutTextureMap; }; + void AddTlutTextureMap(std::string index, std::shared_ptr entry); std::optional
SearchTable(uint32_t addr); static std::string CalculateHash(const std::vector& data); @@ -132,6 +136,7 @@ private: bool gNodeForceProcessing = false; YAML::Node gHashNode; std::shared_ptr gCartridge; + std::unordered_map> gCourseMetadata; std::unordered_map> gEnums; SWrapper* gCurrentWrapper; @@ -151,6 +156,7 @@ private: std::unordered_map>> gWriteMap; std::unordered_map>> gAssetDependencies; std::unordered_map>> gAddrMap; + std::unordered_map> TlutTextureMap; void ParseEnums(std::string& file); void ParseHash(); @@ -158,4 +164,6 @@ private: void ParseCurrentFileConfig(YAML::Node node); void RegisterFactory(const std::string& type, const std::shared_ptr& factory); void ExtractNode(YAML::Node& node, std::string& name, SWrapper* binary); + void ProcessTables(YAML::Node& rom); + void LoadYAMLRecursively(const std::string &dirPath, std::vector &result, bool skipRoot); }; diff --git a/src/factories/DisplayListFactory.cpp b/src/factories/DisplayListFactory.cpp index b5d71dd..965645e 100644 --- a/src/factories/DisplayListFactory.cpp +++ b/src/factories/DisplayListFactory.cpp @@ -313,32 +313,35 @@ std::optional> DListFactory::parse(std::vector()) == SEGMENT_NUMBER(w1)) { - std::optional segment; + std::optional segment; - if ((w0 >> 16) & G_DL_NO_PUSH) { - SPDLOG_INFO("Branch List Command Found"); - processing = false; + if ((w0 >> 16) & G_DL_NO_PUSH) { + SPDLOG_INFO("Branch List Command Found"); + processing = false; + } + + YAML::Node gfx; + gfx["type"] = "GFX"; + gfx["offset"] = w1; + + Companion::Instance->AddAsset(gfx); } - - YAML::Node gfx; - gfx["type"] = "GFX"; - gfx["offset"] = w1; - Companion::Instance->AddAsset(gfx); } - // This opcode is generally used as part of multiple macros such as gsSPSetLights1. - // We need to process gsSPLight which is a subcommand inside G_MOVEMEM (0x03). + // This opcode is generally used as part of multiple macros such as gsSPSetLights1. + // We need to process gsSPLight which is a subcommand inside G_MOVEMEM (0x03). if(opcode == GBI(G_MOVEMEM)) { - // 0x03860000 or 0x03880000 subcommand will contain 0x86/0x88 for G_MV_L0 and G_MV_L1. Other subcommands also exist. - uint8_t subcommand = (w0 >> 16) & 0xFF; + // 0x03860000 or 0x03880000 subcommand will contain 0x86/0x88 for G_MV_L0 and G_MV_L1. Other subcommands also exist. + uint8_t subcommand = (w0 >> 16) & 0xFF; uint8_t index = 0; uint8_t offset = 0; bool light = false; switch (Companion::Instance->GetGBIVersion()) { - // If needing light generation on G_MV_L0 then we'll need to walk the DL ptr forward/backward to check for 0xBC - // Otherwise mk64 will break. + // If needing light generation on G_MV_L0 then we'll need to walk the DL ptr forward/backward to check for 0xBC + // Otherwise mk64 will break. // PD: Mega, this works for sm64 too, why you didn't implement it? >:( // PD: Im jk, <3 case GBIVersion::f3d: @@ -347,9 +350,9 @@ std::optional> DListFactory::parse(std::vector arrayTypeMap = { { "Vec3f", ArrayType::Vec3f }, { "Vec3s", ArrayType::Vec3s }, { "Vec3i", ArrayType::Vec3i }, + { "Vec3iu", ArrayType::Vec3iu }, { "Vec4f", ArrayType::Vec4f }, { "Vec4s", ArrayType::Vec4s }, }; @@ -42,6 +43,7 @@ std::unordered_map typeSizeMap = { { ArrayType::Vec3f, 12 }, { ArrayType::Vec3s, 6 }, { ArrayType::Vec3i, 12 }, + { ArrayType::Vec3iu, 12 }, { ArrayType::Vec4f, 16 }, { ArrayType::Vec4s, 8 }, }; @@ -60,6 +62,7 @@ std::unordered_map structCountMap = { { ArrayType::Vec3f, 3 }, { ArrayType::Vec3s, 3 }, { ArrayType::Vec3i, 3 }, + { ArrayType::Vec3iu, 3 }, { ArrayType::Vec4f, 4 }, { ArrayType::Vec4s, 4 }, }; @@ -140,6 +143,10 @@ GenericArray::GenericArray(std::vector data) : mData(std::move(data) mMaxWidth = std::max(mMaxWidth, (uint32_t)std::get(datum).width()); break; } + case ArrayType::Vec3iu: { + mMaxWidth = std::max(mMaxWidth, (uint32_t)std::get(datum).width()); + break; + } case ArrayType::Vec4f: { mMaxWidth = std::max(mMaxWidth,(uint32_t)std::get(datum).width()); mMaxPrec = std::max(mMaxPrec, (uint32_t)std::get(datum).precision()); @@ -230,6 +237,9 @@ ExportResult ArrayCodeExporter::Export(std::ostream &write, std::shared_ptr(datum), array->mMaxWidth) << ", "; break; + case ArrayType::Vec3iu: + write << FORMAT_INT(std::get(datum), array->mMaxWidth) << ", "; + break; case ArrayType::Vec4f: write << FORMAT_FLOAT(std::get(datum), array->mMaxWidth, array->mMaxPrec) << ", "; break; @@ -372,12 +382,13 @@ std::optional> GenericArrayFactory::parse(std::vect for (int i = 0; i < count; i++) { switch (arrayType) { case ArrayType::u8: { - auto x = reader.ReadUByte(); + uint8_t x = reader.ReadUByte(); data.emplace_back(x); + SPDLOG_INFO("HERE"); break; } case ArrayType::s8: { - auto x = reader.ReadInt8(); + int8_t x = reader.ReadInt8(); data.emplace_back(x); break; } @@ -447,6 +458,14 @@ std::optional> GenericArrayFactory::parse(std::vect data.emplace_back(Vec3i(vx, vy, vz)); break; } + case ArrayType::Vec3iu: { + auto vx = reader.ReadUInt32(); + auto vy = reader.ReadUInt32(); + auto vz = reader.ReadUInt32(); + + data.emplace_back(Vec3iu(vx, vy, vz)); + break; + } case ArrayType::Vec4f: { auto vx = reader.ReadFloat(); auto vy = reader.ReadFloat(); diff --git a/src/factories/GenericArrayFactory.h b/src/factories/GenericArrayFactory.h index 4f7688e..1781211 100644 --- a/src/factories/GenericArrayFactory.h +++ b/src/factories/GenericArrayFactory.h @@ -5,10 +5,10 @@ #include #include -typedef std::variant ArrayDatum; +typedef std::variant ArrayDatum; enum class ArrayType { - u8, s8, u16, s16, u32, s32, u64, f32, f64, Vec2f, Vec3f, Vec3s, Vec3i, Vec4f, Vec4s, + u8, s8, u16, s16, u32, s32, u64, f32, f64, Vec2f, Vec3f, Vec3s, Vec3i, Vec3iu, Vec4f, Vec4s, }; class GenericArray : public IParsedData { diff --git a/src/factories/IncludeFactory.cpp b/src/factories/IncludeFactory.cpp index bf3d268..ab38c28 100644 --- a/src/factories/IncludeFactory.cpp +++ b/src/factories/IncludeFactory.cpp @@ -27,7 +27,7 @@ ExportResult IncludeCodeExporter::Export(std::ostream &write, std::shared_ptr(m[i].mtx[j])) < 1e-6) { write << std::fixed << std::setprecision(1) << m[i].mtx[j]; } else { - write << std::fixed << std::setprecision(6) << m[i].mtx[j]; + // Stupid hack to get matching precision so this value outputs 0.0000153 instead. + if (std::fabs(m[i].mtx[j] - 0.000015) < 0.000001) { + write << std::fixed << std::setprecision(7) << m[i].mtx[j]; + } else { + write << std::fixed << std::setprecision(6) << m[i].mtx[j]; + } } // Add comma for all but the last arg @@ -93,7 +99,7 @@ ExportResult MtxCodeExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { @@ -134,7 +140,7 @@ std::optional> MtxFactory::parse(std::vector matrix; - #define FIXTOF(x) ((double)((x) / 65536.0)) + #define FIXTOF(x) ((float)((x) / 65536.0f)) // Reads the inteer portion, the fractional portion, puts each together into a fixed-point value, and finally converts to float. for(size_t i = 0; i < count; i++) { diff --git a/src/factories/MtxFactory.h b/src/factories/MtxFactory.h index cad0ed2..cc3f7df 100644 --- a/src/factories/MtxFactory.h +++ b/src/factories/MtxFactory.h @@ -3,7 +3,7 @@ #include "BaseFactory.h" struct MtxRaw { - double mtx[16]; + float mtx[16]; }; class MtxData : public IParsedData { diff --git a/src/factories/TextureFactory.cpp b/src/factories/TextureFactory.cpp index a293bfc..5d7ebc0 100644 --- a/src/factories/TextureFactory.cpp +++ b/src/factories/TextureFactory.cpp @@ -20,9 +20,9 @@ static const std::unordered_map gTextureFormats = { { "I4", { TextureType::Grayscale4bpp, 4 } }, { "I8", { TextureType::Grayscale8bpp, 8 } }, { "IA1", { TextureType::GrayscaleAlpha1bpp, 1 } }, - { "IA4", { TextureType::GrayscaleAlpha4bpp, 4 } }, - { "IA8", { TextureType::GrayscaleAlpha8bpp, 8 } }, - { "IA16", { TextureType::GrayscaleAlpha16bpp, 16 } }, + { "IA4", { TextureType::GrayscaleAlpha4bpp, 4 } }, + { "IA8", { TextureType::GrayscaleAlpha8bpp, 8 } }, + { "IA16", { TextureType::GrayscaleAlpha16bpp, 16 } }, { "TLUT", { TextureType::TLUT, 16 } }, }; @@ -156,7 +156,7 @@ ExportResult TextureCodeExporter::Export(std::ostream &write, std::shared_ptr(node, "ctype", "static unsigned char") << " " << name << "[][" << (texture->mBuffer.size() / byteSize) << "] = {\n"; + write << GetSafeNode(node, "ctype", "u8") << " " << name << "[][" << (texture->mBuffer.size() / byteSize) << "] = {\n"; } write << tab << "{\n"; @@ -170,7 +170,7 @@ ExportResult TextureCodeExporter::Export(std::ostream &write, std::shared_ptr(node, "ctype", "static const u8") << " " << symbol << "[] = {\n"; + write << GetSafeNode(node, "ctype", "u8") << " " << symbol << "[] = {\n"; write << tab << "#include \"" << Companion::Instance->GetOutputPath() + "/" << *replacement << ".inc.c\"\n"; write << "};\n"; @@ -235,11 +235,21 @@ ExportResult TextureModdingExporter::Export(std::ostream&write, std::shared_ptr< } case TextureType::Palette8bpp: case TextureType::Palette4bpp: { - ci* imgi = raw2ci_torch(texture->mBuffer.data(), texture->mWidth, texture->mHeight, format.depth); - if(ci2png(&raw, &size, imgi, texture->mWidth, texture->mHeight)) { - throw std::runtime_error("Failed to convert texture to PNG"); + // This check needed until sf64 has tluts fixed. + if (node["tlut_symbol"]) { + auto tlut = GetSafeNode(node,"tlut_symbol"); + auto tlutTextureMap = Companion::Instance->GetTlutTextureMap(); + auto palettePtr = tlutTextureMap[tlut]; + + if (palettePtr) { + convert_raw_to_ci8(&raw, &size, texture->mBuffer.data(), (uint8_t *)palettePtr->mBuffer.data(), 0, texture->mWidth, texture->mHeight, palettePtr->mFormat.depth); + + } else { + auto symbol = GetSafeNode(node, "symbol"); + throw std::runtime_error("Could not convert ci8 '"+symbol+"' the tlut symbol name is probably wrong for tlut_symbol node"); + } + break; } - break; } case TextureType::Grayscale8bpp: case TextureType::Grayscale4bpp: { @@ -262,6 +272,7 @@ ExportResult TextureModdingExporter::Export(std::ostream&write, std::shared_ptr< std::optional> TextureFactory::parse(std::vector& buffer, YAML::Node& node) { auto offset = GetSafeNode(node, "offset"); auto format = GetSafeNode(node, "format"); + auto symbol = GetSafeNode(node, "symbol"); uint32_t width; uint32_t height; uint32_t size; @@ -275,11 +286,11 @@ std::optional> TextureFactory::parse(std::vector(node, "colors"); @@ -292,13 +303,10 @@ std::optional> TextureFactory::parse(std::vector(node, "tlut"); - if(node["symbol"]) { - const auto symbol = GetSafeNode(node, "symbol"); - const auto tlutSymbol = GetSafeNode(node, "tlut_symbol", symbol + "_tlut"); - std::ostringstream offsetSeg; - offsetSeg << std::uppercase << std::hex << tlutOffset; - tlutNode["symbol"] = std::regex_replace(tlutSymbol, std::regex(R"(OFFSET)"), offsetSeg.str()); - } + const auto tlutSymbol = GetSafeNode(node, "tlut_symbol", symbol + "_tlut"); + std::ostringstream offsetSeg; + offsetSeg << std::uppercase << std::hex << tlutOffset; + tlutNode["symbol"] = std::regex_replace(tlutSymbol, std::regex(R"(OFFSET)"), offsetSeg.str()); tlutNode["type"] = "TEXTURE"; tlutNode["format"] = "TLUT"; tlutNode["offset"] = tlutOffset; @@ -325,13 +333,23 @@ std::optional> TextureFactory::parse(std::vector(fmt, width, height, result); + Companion::Instance->AddTlutTextureMap(symbol, textureData); + return textureData; + } + return std::make_shared(fmt, width, height, result); } @@ -349,11 +367,11 @@ std::optional> TextureFactory::parse_modding(std::v return std::nullopt; } - if(!gTextureFormats.contains(format)) { - return std::nullopt; - } + if(!gTextureFormats.contains(format)) { + return std::nullopt; + } - TextureFormat fmt = gTextureFormats.at(format); + TextureFormat fmt = gTextureFormats.at(format); if(fmt.type == TextureType::TLUT){ width = GetSafeNode(node, "colors"); height = 1; @@ -389,14 +407,31 @@ std::optional> TextureFactory::parse_modding(std::v } case TextureType::Palette8bpp: case TextureType::Palette4bpp: { - SPDLOG_WARN("Converting PNG to CI texture is kind of broken, use at your own risk!"); - const auto imgi = png2ci(buffer.data(), buffer.size(), &width, &height); - size = width * height * fmt.depth / 8; - raw = new uint8_t[size]; + // This implementation is not correct. + // The process should be: + // png2rgba --> imgpal2rawci - if(ci2raw_torch(raw, imgi, width, height, fmt.depth) <= 0){ - throw std::runtime_error("Failed to convert PNG to texture"); - } + // todo: Add wheel palette input + // Implement so that it works. + + // auto tlut = GetSafeNode(node,"tlut_symbol"); + // auto tlutTextureMap = Companion::Instance->GetTlutTextureMap(); + // auto palettePtr = tlutTextureMap[tlut]; + + // if (palettePtr) { + + // auto imgi = png2rgba(buffer.data(), buffer.size(), &width, &height); + // auto pal = png2rgba(palettePtr->mBuffer.data(), (palettePtr->mWidth * palettePtr->mWidth * palettePtr->mFormat.depth * 2), &width, &height); + + // size = width * height * fmt.depth / 8; + // raw = new uint8_t[size]; + + // if(imgpal2rawci(raw, imgi, pal, 0, 0, width, height, fmt.depth) <= 0){ + // throw std::runtime_error("Failed to convert PNG to texture"); + // } + // } else { + + // } break; } case TextureType::Grayscale8bpp: diff --git a/src/factories/VtxFactory.cpp b/src/factories/VtxFactory.cpp index 000b274..450ad3c 100644 --- a/src/factories/VtxFactory.cpp +++ b/src/factories/VtxFactory.cpp @@ -26,7 +26,7 @@ ExportResult VtxHeaderExporter::Export(std::ostream &write, std::shared_ptr + +#define NUM(x) std::dec << std::setfill(' ') << std::setw(6) << x +#define COL(c) "0x" << std::hex << std::setw(2) << std::setfill('0') << c + +ExportResult MK64::CourseMetadataCodeExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto metadata = std::static_pointer_cast(raw)->mMetadata; + + if (metadata.empty()) { + throw std::runtime_error("Course metadata null"); + } + + // Sort the data by id, 0 to 20 and beyond. + std::sort(metadata.begin(), metadata.end(), + [this](const CourseMetadata& a, const CourseMetadata& b) { + return a.id < b.id; + }); + + std::ofstream file; + auto outDir = GetSafeNode(node, "out_directory") + "/"; + + file.open(outDir+"gCourseNames.inc.c"); + if (file.is_open()) { + // file << "char *gCourseNames[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + if (m.name == "null") { continue; } + // Remove debug line once proven that sort worked right (start at id 0 and go up) + SPDLOG_INFO("Processing Course Id: "+std::to_string(m.id)); + file << '"' << m.name << "\", "; + } + // file << "\n};\n\n"; + file.close(); + } else if (file.fail()) { + throw std::runtime_error("Course metadata output folder is likely bad or the file is in-use"); + } + + file.open(outDir+"gCourseDebugNames.inc.c"); + if (file.is_open()) { + // file << "char *gDebugCourseNames[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + if (m.name == "null") { continue; } + file << '"' << m.debugName << "\", "; + } + //file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"gCupSelectionByCourseId.inc.c"); + if (file.is_open()) { + //file << "char *gCupSelectionByCourseId[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + if (m.cup == "null") { continue; } + file << m.cup << ", "; + } + // file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"gPerCupIndexByCourseId.inc.c"); + if (file.is_open()) { + //file << "const u8 gPerCupIndexByCourseId[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + if (m.cupIndex == -1) { continue; } + file << m.cupIndex << ", "; + } + //file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"sCourseLengths.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + if (m.courseLength == "null") { continue; } + file << '"' << m.courseLength << "\", "; + } + file.close(); + } + + file.open(outDir+"gKartAIBehaviourLUT.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << m.kartAIBehaviourLUT << ", "; + } + file << 0; // @WARNING TRAILING ZERO IN ARRAY + file.close(); + } + + file.open(outDir+"gKartAICourseMaximumSeparation.inc.c"); + if (file.is_open()) { + // file << "f32 gWaypointWidth[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + file << m.kartAIMaximumSeparation << ", "; + } + // file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"gKartAICourseMinimumSeparation.inc.c"); + if (file.is_open()) { + // file << "f32 gWaypointWidth2[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + file << m.kartAIMinimumSeparation << ", "; + } + // file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"D_800DCBB4.inc.c"); + if (file.is_open()) { + //file << "uintptr_t *D_800DCBB4[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + file << m.D_800DCBB4 << ", "; + } + //file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"gCPUSteeringSensitivity.inc.c"); + + // @WARNING THIS FILE HAS A TRAILING ZERO + if (file.is_open()) { + //file << "u16 gCPUSteeringSensitivity[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + file << m.steeringSensitivity << ", "; + } + file << 0; + //file << "\n};\n\n"; + file.close(); + } + + file.open(outDir+"gBombKartSpawns.inc.c"); + if (file.is_open()) { + //file << "u16 gCPUSteeringSensitivity[] = {\n" << fourSpaceTab; + for (const auto& m : metadata) { + file << "{ // " << m.name << "\n"; + for (const auto& bombKart : m.bombKartSpawns) { + file << "{ "; + file << bombKart.waypointIndex << ", "; + file << bombKart.startingState << ", "; + file << bombKart.unk_04 << ", "; + file << bombKart.x << ", "; + file << bombKart.z << ", "; + file << bombKart.unk10 << ", "; + file << bombKart.unk14; + file << " },\n"; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"gCoursePathSizes.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.pathSizes) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"D_0D009418.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto size : m.D_0D009418) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"D_0D009568.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.D_0D009568) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"D_0D0096B8.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.D_0D0096B8) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"D_0D009808.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.D_0D009808) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"gCoursePathTable.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.pathTable) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"gCoursePathTableUnknown.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.pathTableUnknown) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"sSkyColors.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.skyColors) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + + file.open(outDir+"sSkyColors2.inc.c"); + if (file.is_open()) { + for (const auto& m : metadata) { + file << "// " << m.name << "\n"; + file << "{ "; + for (const auto& size : m.skyColors2) { + file << size << ", "; + } + file << "},\n"; + } + file.close(); + } + return std::nullopt; +} + +ExportResult MK64::CourseMetadataBinaryExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + //auto metadata = std::static_pointer_cast(raw)->mMetadata; + //auto writer = LUS::BinaryWriter(); + + throw std::runtime_error("CourseMetadata not implemented for OTR"); + + // WriteHeader(writer, LUS::ResourceType::Metadata, 0); + // writer.Write((uint32_t) metadata.size()); + //writer.Finish(write); + return std::nullopt; +} + +std::optional> MK64::CourseMetadataFactory::parse(std::vector& buffer, YAML::Node& node) { + auto dir = GetSafeNode(node, "input_directory"); + + auto m = Companion::Instance->GetCourseMetadata(); + SPDLOG_INFO("RUNNING"); + std::vector yamlData; + for (const auto &yamls : m[dir]) { + + if (!yamls["course"]) { + for (auto &node : yamls) { + std::cout << node << std::endl; + } + throw std::runtime_error("Course yaml missing root label of course\nEx. course:"); + } + + auto metadata = yamls["course"]; + + CourseMetadata data; + + data.id = GetSafeNode(metadata, "id"); + data.name = GetSafeNode(metadata, "name"); + data.debugName = GetSafeNode(metadata, "debug_name"); + data.cup = GetSafeNode(metadata, "cup"); + data.cupIndex = GetSafeNode(metadata, "cup_index"); + data.courseLength = GetSafeNode(metadata, "course_length"); + + data.kartAIBehaviourLUT = GetSafeNode(metadata, "kart_ai_behaviour_ptr"); + data.kartAIMaximumSeparation = GetSafeNode(metadata, "kart_ai_maximum_separation"); + data.kartAIMinimumSeparation = GetSafeNode(metadata, "kart_ai_minimum_separation"); + + data.D_800DCBB4 = GetSafeNode(metadata, "D_800DCBB4"); + data.steeringSensitivity = GetSafeNode(metadata, "cpu_steering_sensitivity"); + SPDLOG_INFO("BEFORE"); + for (const auto& bombKart : GetSafeNode(metadata, "bomb_kart_spawns")) { + data.bombKartSpawns.push_back(BombKartSpawns({ + bombKart[0].as(), + bombKart[1].as(), + bombKart[2].as(), // Parse as string because floating-point outputs incorrect values. + bombKart[3].as(), + bombKart[4].as(), + bombKart[5].as(), + bombKart[6].as(), + })); + } + + for (const auto& size : GetSafeNode(metadata, "path_sizes")) { + data.pathSizes.push_back(size.as()); + } + + for (const auto& value : GetSafeNode(metadata, "D_0D009418")) { + data.D_0D009418.push_back(value.as()); + } + + for (const auto& value : GetSafeNode(metadata, "D_0D009568")) { + data.D_0D009568.push_back(value.as()); + } + + for (const auto& value : GetSafeNode(metadata, "D_0D0096B8")) { + data.D_0D0096B8.push_back(value.as()); + } + SPDLOG_INFO("MIDDLE"); + for (const auto& value : GetSafeNode(metadata, "D_0D009808")) { + data.D_0D009808.push_back(value.as()); + } + + for (const auto& str : GetSafeNode(metadata, "path_table")) { + data.pathTable.push_back(str.as()); + } + + for (const auto& str : GetSafeNode(metadata, "path_table_unknown")) { + data.pathTableUnknown.push_back(str.as()); + } + SPDLOG_INFO("BEFORE COLOUR"); + for (const auto& value : GetSafeNode(metadata, "sky_colors")) { + data.skyColors.push_back(value.as()); + } + SPDLOG_INFO("BEFORE COLOUR2"); + for (const auto& value : GetSafeNode(metadata, "sky_colors2")) { + data.skyColors2.push_back(value.as()); + } + + yamlData.push_back(CourseMetadata( + {data} + )); + } + SPDLOG_INFO("END RUNNING"); + + return std::make_shared(yamlData); +} \ No newline at end of file diff --git a/src/factories/mk64/CourseMetadata.h b/src/factories/mk64/CourseMetadata.h new file mode 100644 index 0000000..05264ba --- /dev/null +++ b/src/factories/mk64/CourseMetadata.h @@ -0,0 +1,70 @@ +#pragma once + +#include "../BaseFactory.h" + +namespace MK64 { + + struct BombKartSpawns { + uint16_t waypointIndex; + uint16_t startingState; + std::string unk_04; + float x; + float z; + float unk10; + float unk14; + }; + + struct CourseMetadata { + uint32_t id; + std::string name; + std::string debugName; + std::string cup; + int32_t cupIndex; + std::string courseLength; + std::string kartAIBehaviourLUT; + std::string kartAIMaximumSeparation; + std::string kartAIMinimumSeparation; + std::string D_800DCBB4; + uint32_t steeringSensitivity; + std::vector bombKartSpawns; + std::vector pathSizes; + std::vector D_0D009418; + std::vector D_0D009568; + std::vector D_0D0096B8; + std::vector D_0D009808; + std::vector pathTable; + std::vector pathTableUnknown; + std::vector skyColors; + std::vector skyColors2; + }; + + class MetadataData : public IParsedData { + public: + std::vector mMetadata; + + explicit MetadataData(std::vector metadata) : mMetadata(metadata) {} + }; + + class CourseMetadataBinaryExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class CourseMetadataCodeExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class CourseMetadataFactory : public BaseFactory { + public: + std::optional> parse(std::vector& buffer, YAML::Node& data) override; + std::optional> parse_modding(std::vector& buffer, YAML::Node& data) override { + return std::nullopt; + } + inline std::unordered_map> GetExporters() override { + return { + REGISTER(Code, CourseMetadataCodeExporter) + REGISTER(Binary, CourseMetadataBinaryExporter) + }; + } + bool SupportModdedAssets() override { return false; } + }; +} \ No newline at end of file diff --git a/src/factories/mk64/DrivingBehaviour.cpp b/src/factories/mk64/DrivingBehaviour.cpp index 76e355a..80d31d0 100644 --- a/src/factories/mk64/DrivingBehaviour.cpp +++ b/src/factories/mk64/DrivingBehaviour.cpp @@ -15,7 +15,7 @@ ExportResult MK64::DrivingBehaviourHeaderExporter::Export(std::ostream &write, s return std::nullopt; } - write << "extern DrivingBehaviour " << symbol << "[];\n"; + write << "extern CPUBehaviour " << symbol << "[];\n"; return std::nullopt; } @@ -35,7 +35,7 @@ ExportResult MK64::DrivingBehaviourCodeExporter::Export(std::ostream &write, std write << "// 0x" << std::hex << std::uppercase << offset << "\n"; } - write << "DrivingBehaviour " << symbol << "[] = {\n"; + write << "CPUBehaviour " << symbol << "[] = {\n"; for(auto b : bhv->mBhvs) { diff --git a/src/factories/mk64/ItemCurve.cpp b/src/factories/mk64/ItemCurve.cpp new file mode 100644 index 0000000..99ba99c --- /dev/null +++ b/src/factories/mk64/ItemCurve.cpp @@ -0,0 +1,94 @@ +#include "ItemCurve.h" +#include "spdlog/spdlog.h" + +#include "Companion.h" +#include "utils/Decompressor.h" + +#define NUM(x) std::dec << std::setfill(' ') << std::setw(6) << x +#define COL(c) "0x" << std::hex << std::setw(2) << std::setfill('0') << c + +ExportResult MK64::ItemCurveHeaderExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement) { + const auto symbol = GetSafeNode(node, "symbol", entryName); + + if(Companion::Instance->IsOTRMode()){ + write << "static const char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n"; + return std::nullopt; + } + + write << "extern u8 " << symbol << "[][100];\n"; + return std::nullopt; +} + +ExportResult MK64::ItemCurveCodeExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto items = std::static_pointer_cast(raw)->mItems; + const auto symbol = GetSafeNode(node, "symbol", entryName); + const auto offset = GetSafeNode(node, "offset"); + + + const auto searchTable = Companion::Instance->SearchTable(offset); + + if(searchTable.has_value()){ + const auto [name, start, end, mode] = searchTable.value(); + + if(start == offset){ + write << GetSafeNode(node, "ctype", "u8") << " " << name << "[][" << items.size() << "] = {\n"; + } + + write << fourSpaceTab << "{"; + for (size_t i = 0; i < items.size(); ++i) { + uint8_t value = items[i]; + auto enumName = Companion::Instance->GetEnumFromValue("ITEMS", value).value_or(std::to_string(value)); + + if (i % 10 == 0) { + write << "\n" << fourSpaceTab << fourSpaceTab << enumName << ", "; + } else { + write << enumName << ", "; + } + } + write << "\n" << fourSpaceTab << "},\n"; + + if(end == offset){ + write << "};\n\n"; + } + + } else { + + write << "u8 " << symbol << "[][100] = {\n"; + write << fourSpaceTab << "{"; + + for (size_t i = 0; i < items.size(); ++i) { + uint8_t value = items[i]; + auto enumName = Companion::Instance->GetEnumFromValue("ITEMS", value).value_or(std::to_string(value)); + + if (i % 10 == 0) { + write << "\n" << fourSpaceTab << enumName << ", "; + } else { + write << enumName << ", "; + } + } + write << "\n" << fourSpaceTab << "},\n"; + write << "};\n\n"; + } + + return offset + items.size() * sizeof(uint8_t); +} + +ExportResult MK64::ItemCurveBinaryExporter::Export(std::ostream &write, std::shared_ptr raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + throw std::runtime_error("Decomp ItemCurve is only implemented in decomp.\nuk64 and port use a new system for ease of modding and bug fixes."); + return std::nullopt; +} + +std::optional> MK64::ItemCurveFactory::parse(std::vector& buffer, YAML::Node& node) { + auto [_, segment] = Decompressor::AutoDecode(node, buffer); + LUS::BinaryReader reader(segment.data, (10 * 10) * sizeof(uint8_t)); + + reader.SetEndianness(LUS::Endianness::Big); + std::vector items; + + // Each array is size of 10*10. + for(size_t i = 0; i < 10*10; i++) { + items.push_back(reader.ReadUByte()); + } + + return std::make_shared(items); +} \ No newline at end of file diff --git a/src/factories/mk64/ItemCurve.h b/src/factories/mk64/ItemCurve.h new file mode 100644 index 0000000..c58e825 --- /dev/null +++ b/src/factories/mk64/ItemCurve.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../BaseFactory.h" + +namespace MK64 { + + class ItemCurveData : public IParsedData { + public: + std::vector mItems; + + explicit ItemCurveData(std::vector items) : mItems(items) {} + }; + + class ItemCurveHeaderExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveBinaryExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveCodeExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveFactory : public BaseFactory { + public: + std::optional> parse(std::vector& buffer, YAML::Node& data) override; + std::optional> parse_modding(std::vector& buffer, YAML::Node& data) override { + return std::nullopt; + } + inline std::unordered_map> GetExporters() override { + return { + REGISTER(Code, ItemCurveCodeExporter) + REGISTER(Header, ItemCurveHeaderExporter) + REGISTER(Binary, ItemCurveBinaryExporter) + }; + } + bool SupportModdedAssets() override { return false; } + }; + +} \ No newline at end of file diff --git a/src/types/Vec3D.cpp b/src/types/Vec3D.cpp index c70e540..ab263f8 100644 --- a/src/types/Vec3D.cpp +++ b/src/types/Vec3D.cpp @@ -86,6 +86,23 @@ std::ostream& operator<< (std::ostream& stream, const Vec3i& vec) { return stream; } +Vec3iu::Vec3iu(uint32_t xv, uint32_t yv, uint32_t zv) : x(xv), y(yv), z(zv) {} + +int Vec3iu::width() { + auto wx = GetMagnitude(this->x); + auto wy = GetMagnitude(this->y); + auto wz = GetMagnitude(this->z); + + return std::max(wx, std::max(wy, wz)); +} + +std::ostream& operator<< (std::ostream& stream, const Vec3iu& vec) { + int width = stream.width(); + + stream << std::setw(0) << "{" << std::setw(width) << vec.x << ", " << std::setw(width) << vec.y << ", " << std::setw(width) << vec.z << "}"; + return stream; +} + Vec2f::Vec2f(float xv, float zv) : x(xv), z(zv) {} int Vec2f::precision() { diff --git a/src/types/Vec3D.h b/src/types/Vec3D.h index acb6eea..76a8d5d 100644 --- a/src/types/Vec3D.h +++ b/src/types/Vec3D.h @@ -36,6 +36,17 @@ public: friend std::ostream& operator<< (std::ostream& stream, const Vec3i& vec); }; +class Vec3iu { +public: + uint32_t x; + uint32_t y; + uint32_t z; + + Vec3iu(uint32_t xv = 0, uint32_t yv = 0, uint32_t zv = 0); + int width(); + friend std::ostream& operator<< (std::ostream& stream, const Vec3iu& vec); +}; + class Vec2f { public: float x; diff --git a/src/utils/Decompressor.cpp b/src/utils/Decompressor.cpp index 9570ab3..1dd326e 100644 --- a/src/utils/Decompressor.cpp +++ b/src/utils/Decompressor.cpp @@ -18,7 +18,7 @@ DataChunk* Decompressor::Decode(const std::vector& buffer, const uint32 const unsigned char* in_buf = buffer.data() + offset; - switch (type) { + switch (type) { case CompressionType::MIO0: { mio0_header_t head; if(!mio0_decode_header(in_buf, &head)){