diff options
| author | MegaMech <MegaMech@users.noreply.github.com> | 2024-04-06 15:36:03 -0600 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-04-06 15:36:03 -0600 |
| commit | 24633ebd7cc6866da3e3d06fddc6f0f49a741f1d (patch) | |
| tree | 357a563fae006b04ded941ee534b1d6522f3fc32 | |
| parent | ad667b705dad3476a1ae803f23588c55baab2ee4 (diff) | |
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: = <=>
| -rw-r--r-- | .gitignore | 3 | ||||
| -rw-r--r-- | lib/n64graphics/n64graphics.c | 115 | ||||
| -rw-r--r-- | lib/n64graphics/n64graphics.h | 10 | ||||
| -rw-r--r-- | src/Companion.cpp | 57 | ||||
| -rw-r--r-- | src/Companion.h | 8 | ||||
| -rw-r--r-- | src/factories/DisplayListFactory.cpp | 39 | ||||
| -rw-r--r-- | src/factories/GenericArrayFactory.cpp | 23 | ||||
| -rw-r--r-- | src/factories/GenericArrayFactory.h | 4 | ||||
| -rw-r--r-- | src/factories/IncludeFactory.cpp | 2 | ||||
| -rw-r--r-- | src/factories/LightsFactory.cpp | 2 | ||||
| -rw-r--r-- | src/factories/MtxFactory.cpp | 16 | ||||
| -rw-r--r-- | src/factories/MtxFactory.h | 2 | ||||
| -rw-r--r-- | src/factories/TextureFactory.cpp | 99 | ||||
| -rw-r--r-- | src/factories/VtxFactory.cpp | 2 | ||||
| -rw-r--r-- | src/factories/mk64/CourseMetadata.cpp | 375 | ||||
| -rw-r--r-- | src/factories/mk64/CourseMetadata.h | 70 | ||||
| -rw-r--r-- | src/factories/mk64/DrivingBehaviour.cpp | 4 | ||||
| -rw-r--r-- | src/factories/mk64/ItemCurve.cpp | 94 | ||||
| -rw-r--r-- | src/factories/mk64/ItemCurve.h | 42 | ||||
| -rw-r--r-- | src/types/Vec3D.cpp | 17 | ||||
| -rw-r--r-- | src/types/Vec3D.h | 11 | ||||
| -rw-r--r-- | src/utils/Decompressor.cpp | 2 |
22 files changed, 921 insertions, 76 deletions
@@ -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<MK64::TrackSectionsFactory>()); this->RegisterFactory("MK64:SPAWN_DATA", std::make_shared<MK64::SpawnDataFactory>()); this->RegisterFactory("MK64:DRIVING_BEHAVIOUR", std::make_shared<MK64::DrivingBehaviourFactory>()); + this->RegisterFactory("MK64:ITEM_CURVE", std::make_shared<MK64::ItemCurveFactory>()); // Item curve for decomp only + this->RegisterFactory("MK64:METADATA", std::make_shared<MK64::CourseMetadataFactory>()); // SF64 specific this->RegisterFactory("SF64:ANIM", std::make_shared<SF64::AnimFactory>()); @@ -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<YAML::Node> &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<std::vector<std::string>>(); + + for (const auto &dir : dirs) { + std::vector<YAML::Node> 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<std::vector<uint32_t>>(); for (int i = 0; i < segments.size(); i++) { @@ -1048,7 +1097,7 @@ CompressionType Companion::GetCompressionType(std::vector<uint8_t>& buffer, cons std::optional<Table> 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<YAML::Node> Companion::AddAsset(YAML::Node asset) { return std::nullopt; } + +void Companion::AddTlutTextureMap(std::string index, std::shared_ptr<TextureData> 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<std::string, std::vector<YAML::Node>> GetCourseMetadata() { return this->gCourseMetadata; } std::optional<std::string> GetEnumFromValue(const std::string& key, int id); std::optional<std::uint32_t> GetFileOffsetFromSegmentedAddr(uint8_t segment) const; @@ -111,6 +113,8 @@ public: CompressionType GetCurrCompressionType(void) const { return this->gCurrentCompressionType; }; CompressionType GetCompressionType(std::vector<uint8_t>& buffer, const uint32_t offset); std::optional<VRAMEntry> GetCurrentVRAM(void) const { return this->gCurrentVram; }; + std::unordered_map<std::string, std::shared_ptr<TextureData>> GetTlutTextureMap() { return this->TlutTextureMap; }; + void AddTlutTextureMap(std::string index, std::shared_ptr<TextureData> entry); std::optional<Table> SearchTable(uint32_t addr); static std::string CalculateHash(const std::vector<uint8_t>& data); @@ -132,6 +136,7 @@ private: bool gNodeForceProcessing = false; YAML::Node gHashNode; std::shared_ptr<N64::Cartridge> gCartridge; + std::unordered_map<std::string, std::vector<YAML::Node>> gCourseMetadata; std::unordered_map<std::string, std::unordered_map<int32_t, std::string>> gEnums; SWrapper* gCurrentWrapper; @@ -151,6 +156,7 @@ private: std::unordered_map<std::string, std::map<std::string, std::vector<WriteEntry>>> gWriteMap; std::unordered_map<std::string, std::map<std::string, std::pair<YAML::Node, bool>>> gAssetDependencies; std::unordered_map<std::string, std::unordered_map<uint32_t, std::tuple<std::string, YAML::Node>>> gAddrMap; + std::unordered_map<std::string, std::shared_ptr<TextureData>> 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<BaseFactory>& factory); void ExtractNode(YAML::Node& node, std::string& name, SWrapper* binary); + void ProcessTables(YAML::Node& rom); + void LoadYAMLRecursively(const std::string &dirPath, std::vector<YAML::Node> &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<std::shared_ptr<IParsedData>> DListFactory::parse(std::vector<uint } if(opcode == GBI(G_DL)) { + if (SEGMENT_NUMBER(node["offset"].as<uint32_t>()) == SEGMENT_NUMBER(w1)) { - std::optional<uint32_t> segment; + std::optional<uint32_t> 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<std::shared_ptr<IParsedData>> DListFactory::parse(std::vector<uint * Only generate lights on the second gsSPLight. * gsSPSetLights1(name) outputs three macros: * - * gsSPNumLights(NUMLIGHTS_1) - * gsSPLight(&name.l[0], G_MV_L0) - * gsSPLight(&name.a, G_MV_L1) <-- This ptr is used to generate the lights + * gsSPNumLights(NUMLIGHTS_1) + * gsSPLight(&name.l[0], G_MV_L0) + * gsSPLight(&name.a, G_MV_L1) <-- This ptr is used to generate the lights */ if (subcommand == GBI(G_MV_L1)) { light = true; diff --git a/src/factories/GenericArrayFactory.cpp b/src/factories/GenericArrayFactory.cpp index 81144f5..a8b502a 100644 --- a/src/factories/GenericArrayFactory.cpp +++ b/src/factories/GenericArrayFactory.cpp @@ -24,6 +24,7 @@ std::unordered_map<std::string, ArrayType> 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<ArrayType, size_t> 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<ArrayType, size_t> 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<ArrayDatum> data) : mData(std::move(data) mMaxWidth = std::max(mMaxWidth, (uint32_t)std::get<Vec3i>(datum).width()); break; } + case ArrayType::Vec3iu: { + mMaxWidth = std::max(mMaxWidth, (uint32_t)std::get<Vec3iu>(datum).width()); + break; + } case ArrayType::Vec4f: { mMaxWidth = std::max(mMaxWidth,(uint32_t)std::get<Vec4f>(datum).width()); mMaxPrec = std::max(mMaxPrec, (uint32_t)std::get<Vec4f>(datum).precision()); @@ -230,6 +237,9 @@ ExportResult ArrayCodeExporter::Export(std::ostream &write, std::shared_ptr<IPar case ArrayType::Vec3i: write << FORMAT_INT(std::get<Vec3i>(datum), array->mMaxWidth) << ", "; break; + case ArrayType::Vec3iu: + write << FORMAT_INT(std::get<Vec3iu>(datum), array->mMaxWidth) << ", "; + break; case ArrayType::Vec4f: write << FORMAT_FLOAT(std::get<Vec4f>(datum), array->mMaxWidth, array->mMaxPrec) << ", "; break; @@ -372,12 +382,13 @@ std::optional<std::shared_ptr<IParsedData>> 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<std::shared_ptr<IParsedData>> 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 <variant> #include <types/Vec3D.h> -typedef std::variant<uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, float, double, Vec2f, Vec3f, Vec3s, Vec3i, Vec4f, Vec4s> ArrayDatum; +typedef std::variant<uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, float, double, Vec2f, Vec3f, Vec3s, Vec3i, Vec3iu, Vec4f, Vec4s> 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<IP SPDLOG_INFO("writing INC"); write << ctype << " " << symbol << "[] = {\n"; - write << fourSpaceTab << "#include '" << file << "'\n"; + write << fourSpaceTab << "#include \"" << file << "\"\n"; write << "};\n\n"; diff --git a/src/factories/LightsFactory.cpp b/src/factories/LightsFactory.cpp index 83464c2..fd930aa 100644 --- a/src/factories/LightsFactory.cpp +++ b/src/factories/LightsFactory.cpp @@ -22,7 +22,7 @@ ExportResult LightsHeaderExporter::Export(std::ostream &write, std::shared_ptr<I return std::nullopt; } - write << "extern Lights1" << name << "[];\n"; + write << "extern Lights1 " << name << "[];\n"; } else { write << "extern Lights1 " << symbol << ";\n"; } diff --git a/src/factories/MtxFactory.cpp b/src/factories/MtxFactory.cpp index cce6f99..15ca03b 100644 --- a/src/factories/MtxFactory.cpp +++ b/src/factories/MtxFactory.cpp @@ -15,7 +15,7 @@ ExportResult MtxHeaderExporter::Export(std::ostream &write, std::shared_ptr<IPar return std::nullopt; } - write << "extern Mtx " << symbol << "[];\n"; + write << "extern Mtx " << symbol << ";\n"; return std::nullopt; } @@ -44,7 +44,7 @@ ExportResult MtxCodeExporter::Export(std::ostream &write, std::shared_ptr<IParse * 0.0, 0.0, 0.0, 1.0); */ - write << "Mtx " << symbol << "[] = {\n"; + write << "Mtx " << symbol << " = {\n"; for (int i = 0; i < m.size(); ++i) { @@ -53,10 +53,16 @@ ExportResult MtxCodeExporter::Export(std::ostream &write, std::shared_ptr<IParse for (int j = 0; j < 16; ++j) { // Turn 1, 3, and 6 into 1.0, 3.0, and 6.0. Unless it has a decimal number then leave it alone. + SPDLOG_INFO(m[i].mtx[j]); if (std::abs(m[i].mtx[j] - static_cast<int>(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<IParse #undef fiveFourSpaceTabs - return offset + m.size() * sizeof(MtxRaw); + return offset + sizeof(MtxRaw); } ExportResult MtxBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { @@ -134,7 +140,7 @@ std::optional<std::shared_ptr<IParsedData>> MtxFactory::parse(std::vector<uint8_ reader.SetEndianness(LUS::Endianness::Big); std::vector<MtxRaw> 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 <std::string, TextureFormat> 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<IP } if(start == offset){ - write << GetSafeNode<std::string>(node, "ctype", "static unsigned char") << " " << name << "[][" << (texture->mBuffer.size() / byteSize) << "] = {\n"; + write << GetSafeNode<std::string>(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<IP } } } else { - write << GetSafeNode<std::string>(node, "ctype", "static const u8") << " " << symbol << "[] = {\n"; + write << GetSafeNode<std::string>(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<std::string>(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<std::string>(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<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { auto offset = GetSafeNode<uint32_t>(node, "offset"); auto format = GetSafeNode<std::string>(node, "format"); + auto symbol = GetSafeNode<std::string>(node, "symbol"); uint32_t width; uint32_t height; uint32_t size; @@ -275,11 +286,11 @@ std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui 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<uint32_t>(node, "colors"); @@ -292,13 +303,10 @@ std::optional<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui if((format == "CI4" || format == "CI8") && node["tlut"] && node["colors"]) { YAML::Node tlutNode; const auto tlutOffset = GetSafeNode<uint32_t>(node, "tlut"); - if(node["symbol"]) { - const auto symbol = GetSafeNode<std::string>(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<std::shared_ptr<IParsedData>> TextureFactory::parse(std::vector<ui SPDLOG_INFO("Width: {}", width); SPDLOG_INFO("Height: {}", height); } - SPDLOG_INFO("Size: {}", size); + SPDLOG_INFO("Size: {}", size); SPDLOG_INFO("Offset: 0x{:X}", offset); if(result.size() == 0){ return std::nullopt; } + if(result.size() == 0){ + return std::nullopt; + } + + if (fmt.type == TextureType::TLUT) { + auto textureData = std::make_shared<TextureData>(fmt, width, height, result); + Companion::Instance->AddTlutTextureMap(symbol, textureData); + return textureData; + } + return std::make_shared<TextureData>(fmt, width, height, result); } @@ -349,11 +367,11 @@ std::optional<std::shared_ptr<IParsedData>> 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<uint32_t>(node, "colors"); height = 1; @@ -389,14 +407,31 @@ std::optional<std::shared_ptr<IParsedData>> 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<std::string>(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<IPar return std::nullopt; } - write << "extern Vtx" << name << "[][" << vtx.size() << "];\n"; + write << "extern Vtx " << name << "[][" << vtx.size() << "];\n"; } else { write << "extern Vtx " << symbol << "[];\n"; } diff --git a/src/factories/mk64/CourseMetadata.cpp b/src/factories/mk64/CourseMetadata.cpp new file mode 100644 index 0000000..083f553 --- /dev/null +++ b/src/factories/mk64/CourseMetadata.cpp @@ -0,0 +1,375 @@ +#include "CourseMetadata.h" + +#include "Companion.h" +#include "spdlog/spdlog.h" +#include <algorithm> + +#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<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto metadata = std::static_pointer_cast<MetadataData>(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<std::string>(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<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + //auto metadata = std::static_pointer_cast<MetadataData>(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<std::shared_ptr<IParsedData>> MK64::CourseMetadataFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) { + auto dir = GetSafeNode<std::string>(node, "input_directory"); + + auto m = Companion::Instance->GetCourseMetadata(); + SPDLOG_INFO("RUNNING"); + std::vector<CourseMetadata> 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<uint32_t>(metadata, "id"); + data.name = GetSafeNode<std::string>(metadata, "name"); + data.debugName = GetSafeNode<std::string>(metadata, "debug_name"); + data.cup = GetSafeNode<std::string>(metadata, "cup"); + data.cupIndex = GetSafeNode<int32_t>(metadata, "cup_index"); + data.courseLength = GetSafeNode<std::string>(metadata, "course_length"); + + data.kartAIBehaviourLUT = GetSafeNode<std::string>(metadata, "kart_ai_behaviour_ptr"); + data.kartAIMaximumSeparation = GetSafeNode<std::string>(metadata, "kart_ai_maximum_separation"); + data.kartAIMinimumSeparation = GetSafeNode<std::string>(metadata, "kart_ai_minimum_separation"); + + data.D_800DCBB4 = GetSafeNode<std::string>(metadata, "D_800DCBB4"); + data.steeringSensitivity = GetSafeNode<uint32_t>(metadata, "cpu_steering_sensitivity"); + SPDLOG_INFO("BEFORE"); + for (const auto& bombKart : GetSafeNode<YAML::Node>(metadata, "bomb_kart_spawns")) { + data.bombKartSpawns.push_back(BombKartSpawns({ + bombKart[0].as<uint16_t>(), + bombKart[1].as<uint16_t>(), + bombKart[2].as<std::string>(), // Parse as string because floating-point outputs incorrect values. + bombKart[3].as<float>(), + bombKart[4].as<float>(), + bombKart[5].as<float>(), + bombKart[6].as<float>(), + })); + } + + for (const auto& size : GetSafeNode<YAML::Node>(metadata, "path_sizes")) { + data.pathSizes.push_back(size.as<uint16_t>()); + } + + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "D_0D009418")) { + data.D_0D009418.push_back(value.as<std::string>()); + } + + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "D_0D009568")) { + data.D_0D009568.push_back(value.as<std::string>()); + } + + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "D_0D0096B8")) { + data.D_0D0096B8.push_back(value.as<std::string>()); + } + SPDLOG_INFO("MIDDLE"); + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "D_0D009808")) { + data.D_0D009808.push_back(value.as<std::string>()); + } + + for (const auto& str : GetSafeNode<YAML::Node>(metadata, "path_table")) { + data.pathTable.push_back(str.as<std::string>()); + } + + for (const auto& str : GetSafeNode<YAML::Node>(metadata, "path_table_unknown")) { + data.pathTableUnknown.push_back(str.as<std::string>()); + } + SPDLOG_INFO("BEFORE COLOUR"); + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "sky_colors")) { + data.skyColors.push_back(value.as<uint16_t>()); + } + SPDLOG_INFO("BEFORE COLOUR2"); + for (const auto& value : GetSafeNode<YAML::Node>(metadata, "sky_colors2")) { + data.skyColors2.push_back(value.as<uint16_t>()); + } + + yamlData.push_back(CourseMetadata( + {data} + )); + } + SPDLOG_INFO("END RUNNING"); + + return std::make_shared<MetadataData>(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> bombKartSpawns; + std::vector<uint16_t> pathSizes; + std::vector<std::string> D_0D009418; + std::vector<std::string> D_0D009568; + std::vector<std::string> D_0D0096B8; + std::vector<std::string> D_0D009808; + std::vector<std::string> pathTable; + std::vector<std::string> pathTableUnknown; + std::vector<uint16_t> skyColors; + std::vector<uint16_t> skyColors2; + }; + + class MetadataData : public IParsedData { + public: + std::vector<CourseMetadata> mMetadata; + + explicit MetadataData(std::vector<CourseMetadata> metadata) : mMetadata(metadata) {} + }; + + class CourseMetadataBinaryExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class CourseMetadataCodeExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class CourseMetadataFactory : 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, 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<IParsedData> 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<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) { + auto items = std::static_pointer_cast<ItemCurveData>(raw)->mItems; + const auto symbol = GetSafeNode(node, "symbol", entryName); + const auto offset = GetSafeNode<uint32_t>(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<std::string>(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<IParsedData> 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<std::shared_ptr<IParsedData>> MK64::ItemCurveFactory::parse(std::vector<uint8_t>& 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<uint8_t> 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<ItemCurveData>(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<uint8_t> mItems; + + explicit ItemCurveData(std::vector<uint8_t> items) : mItems(items) {} + }; + + class ItemCurveHeaderExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveBinaryExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveCodeExporter : public BaseExporter { + ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override; + }; + + class ItemCurveFactory : 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, 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<uint8_t>& 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)){ |
