#pragma once #include #include #include #include #include #include #include #include #include #include #include #include "factories/BaseFactory.h" #include "n64/Cartridge.h" #include "utils/Decompressor.h" #include "factories/TextureFactory.h" #define CONTAINS(_map, value) ((_map).find(value) != (_map).end()) class BinaryWrapper; namespace fs = std::filesystem; enum class ParseMode { Default, Directory }; enum class GBIVersion { f3db, f3d, f3dex, f3dexb, f3dex2, }; enum class GBIMinorVersion { None, Mk64, SM64, PM64, BK64 }; enum class TableMode { Reference, Append }; enum class ArchiveType { None, OTR, O2R, }; struct SegmentConfig { std::unordered_map global; std::unordered_map local; std::unordered_map temporal; std::unordered_map>> compressed; }; struct Table { std::string name; uint32_t start; uint32_t end; TableMode mode; int32_t index_size; }; struct VRAMEntry { uint32_t addr; uint32_t offset; }; struct WriteEntry { std::string name; uint32_t addr; uint32_t alignment; std::string buffer; std::optional comment; std::optional endptr; }; struct GBIConfig { GBIVersion version = GBIVersion::f3d; GBIMinorVersion subversion = GBIMinorVersion::None; bool useFloats = false; }; struct TorchConfig { GBIConfig gbi; SegmentConfig segment; std::string outputPath; std::string moddingPath; ExportType exporterType; ParseMode parseMode; ArchiveType otrMode; bool debug; bool modding; bool textureDefines; bool includeAutogen; bool dialogPack = false; // Default model-preview shading mode name (e.g. "textured + lit"); empty // uses each viewer's built-in fallback. See UI::ShadeSetupIndexByName. std::string defaultShading; }; struct ParseResultData { std::string name; std::string type; YAML::Node node; std::optional> data; uint32_t GetOffset() { return GetSafeNode(node, "offset"); } std::optional GetSymbol() { return GetSafeNode(node, "symbol"); } }; class Companion { public: static Companion* Instance; explicit Companion(std::filesystem::path rom, const ArchiveType otr, const bool debug, const bool modding = false, const std::string& srcDir = "", const std::string& destPath = "") : gCartridge(nullptr), gSourceDirectory(srcDir), gDestinationDirectory(destPath) { this->gRomPath = rom; this->gConfig.otrMode = otr; this->gConfig.debug = debug; this->gConfig.modding = modding; this->gConfig.textureDefines = false; this->gConfig.includeAutogen = false; } explicit Companion(std::vector rom, const ArchiveType otr, const bool debug, const bool modding = false, const std::string& srcDir = "", const std::string& destPath = "") : gCartridge(nullptr), gSourceDirectory(srcDir), gDestinationDirectory(destPath) { this->gRomData = rom; this->gConfig.otrMode = otr; this->gConfig.debug = debug; this->gConfig.modding = modding; this->gConfig.textureDefines = false; this->gConfig.includeAutogen = false; } void SetRomPath(std::filesystem::path path) { this->gRomPath = std::move(path); } explicit Companion(std::filesystem::path rom, const ArchiveType otr, const bool debug, const std::string& srcDir = "", const std::string& destPath = "") : Companion(rom, otr, debug, false, srcDir, destPath) {} explicit Companion(std::vector rom, const ArchiveType otr, const bool debug, const std::string& srcDir = "", const std::string& destPath = "") : Companion(rom, otr, debug, false, srcDir, destPath) {} void Init(ExportType type); void Init(ExportType type, std::atomic& assetCount, bool shouldProcess); bool NodeHasChanges(const std::string& string); void Process(std::atomic& assetCount); bool IsOTRMode() const { return (this->gConfig.otrMode != ArchiveType::None); } bool IsDebug() const { return this->gConfig.debug; } bool AddTextureDefines() const { return this->gConfig.textureDefines; } N64::Cartridge* GetCartridge() const { return this->gCartridge.get(); } // Survives cartridge teardown so the UI can key game-specific behavior. const std::string& GetGameTitle() const { return this->gGameTitle; } std::vector& GetRomData() { return this->gRomData; } std::string GetOutputPath() { return this->gConfig.outputPath; } const std::string& GetAssetPath() const { return this->gAssetPath; } std::string GetDestRelativeOutputPath() { return RelativePathToDestDir(GetOutputPath()); } 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); bool IsUsingIndividualIncludes() const { return this->gIndividualIncludes; } std::optional GetParseDataByAddr(uint32_t addr); std::optional GetParseDataBySymbol(const std::string& symbol); std::optional GetFileOffsetFromSegmentedAddr(uint8_t segment) const; std::optional> GetFileOffsetFromCompressedSegmentedAddr(uint8_t segment) const; std::optional> GetFactory(const std::string& type); uint32_t PatchVirtualAddr(uint32_t addr); std::optional> GetNodeByAddr(uint32_t addr); // File-scoped variant for use outside the parse/export passes, where // gCurrentFile no longer points at the owning file. std::optional> GetNodeByAddr(uint32_t addr, const std::string& file); std::optional GetStringByAddr(uint32_t addr); std::optional> GetSafeNodeByAddr(const uint32_t addr, std::string type); std::optional GetSafeStringByAddr(const uint32_t addr, std::string type); std::optional>> GetNodesByType(const std::string& type, bool includeAutogen = false); // Same as GetNodesByType but returns a pointer into the internal cache to // avoid copying. The pointer is valid until the cache is invalidated; do not // hold across AddAsset/RegisterAsset calls. Returns nullptr if the current // file has no nodes registered. const std::vector>* GetNodesByTypeRef(const std::string& type, bool includeAutogen = false); std::string GetSymbolFromAddr(uint32_t addr, bool validZero = false); std::optional GetFileOffset(void) const { return this->gCurrentFileOffset; }; std::optional GetCurrSegmentNumber(void) const { return this->gCurrentSegmentNumber; }; CompressionType GetCurrCompressionType(void) const { return this->gCurrentCompressionType; }; std::optional GetCurrentCompressedSize(void) const { return this->gCurrentCompressedSize; }; std::optional GetCurrentVRAM(void) const { return this->gCurrentVram; }; std::optional SearchTable(uint32_t addr); static std::vector ParseVersionString(const std::string& version); static std::string CalculateHash(const std::vector& data); static void Pack(const std::string& folder, const std::string& output, const ArchiveType otrMode, const std::string& version = ""); std::string NormalizeAsset(const std::string& name) const; std::string RelativePath(const std::string& path) const; std::string RelativePathToSrcDir(const std::string& path) const; std::string RelativePathToDestDir(const std::string& path) const; void RegisterCompanionFile(const std::string path, std::vector data); void RegisterArchiveFile(const std::string& name, std::vector data); void SetAdditionalFiles(const std::vector& files) { this->gAdditionalFiles = files; } void SetVersion(const std::string& version) { this->gVersion = version; } std::string GetCurrentAssetName() const { return this->gCurrentAssetName; } void SetAssetTotal(std::atomic* total) { this->gAssetTotal = total; } void SetPhaseCallback(std::function cb) { this->gPhaseCallback = cb; } void SetProcess(bool shouldProcess); TorchConfig& GetConfig() { return this->gConfig; } BinaryWrapper* GetCurrentWrapper() { return this->gCurrentWrapper; } const std::unordered_map& GetModdedAssetPaths() const { return this->gModdedAssetPaths; } std::optional> RegisterAsset(const std::string& name, YAML::Node& node); std::optional AddSubFileAsset(YAML::Node asset, std::string newFileName, CompressionType newCompressionType, uint32_t compressedSize = 0); std::optional AddAsset(YAML::Node asset); void SetCompressedSegment(uint32_t segmentId, uint32_t compressedFileOffset, uint32_t offset); bool GetCompressedSegmentOffset(uint32_t* addr); #ifdef BUILD_UI void RegisterUIFactory(const std::string& type, const std::shared_ptr& factory); std::optional> GetUIFactory(const std::string& type); const std::unordered_map>& GetParseResults() { return this->gParseResults; } #endif private: TorchConfig gConfig; YAML::Node gModdingConfig; fs::path gSourceDirectory; fs::path gDestinationDirectory; fs::path gCurrentDirectory; std::string gCurrentHash; std::string gAssetPath; std::string gVersion; std::vector gRomData; std::optional gRomPath; bool gNodeForceProcessing = false; bool gIndividualIncludes = false; YAML::Node gHashNode; std::shared_ptr gCartridge; std::string gGameTitle; std::unordered_map> gCourseMetadata; std::unordered_map> gEnums; BinaryWrapper* gCurrentWrapper; // Temporal Variables std::string gCurrentAssetName; std::atomic* gAssetCounter = nullptr; std::atomic* gAssetTotal = nullptr; std::function gPhaseCallback; std::string gCurrentFile; std::vector gSubFileList; std::string gCurrentVirtualPath; std::string gFileHeader; bool gEnablePadGen = false; bool mShouldProcess = true; uint32_t gCurrentPad = 0; uint32_t gCurrentFileOffset; uint32_t gCurrentSegmentNumber; std::optional gCurrentVram; CompressionType gCurrentCompressionType = CompressionType::None; std::optional gCurrentCompressedSize; std::vector
gTables; std::vector gCurrentExternalFiles; std::unordered_map gManualSegments; std::unordered_set gProcessedFiles; std::unordered_map> gCompanionFiles; std::vector>> gArchiveFiles; std::unordered_map> gParseResults; std::vector gAdditionalFiles; std::unordered_map gModdedAssetPaths; std::variant, std::string> gWriteOrder; std::unordered_map> gFactories; #ifdef BUILD_UI std::unordered_map> gUIFactories; #endif std::unordered_map>> gWriteMap; std::unordered_map> gVirtualAddrMap; std::unordered_map>> gAddrMap; // Cached results of GetNodesByType keyed by [file][type][includeAutogen?]. // Built lazily; invalidated on RegisterAsset for the affected (file, type). // Index 0 = !includeAutogen, index 1 = includeAutogen. std::unordered_map>, 2>>> gNodesByTypeCache; // Validity bitmap; bit 0 = !includeAutogen, bit 1 = includeAutogen. std::unordered_map> gNodesByTypeCacheValid; void ProcessParseFile(YAML::Node root, std::atomic& assetCount); void ProcessExportFile(); void ProcessFile(YAML::Node root); void ProcessFile(YAML::Node root, std::atomic& assetCount); void ParseEnums(std::string& file); void ParseHash(); void ParseModdingConfig(); void ParseCurrentFileConfig(YAML::Node node, std::atomic& assetCount); void RegisterFactory(const std::string& type, const std::shared_ptr& factory); void ExtractNode(YAML::Node& node, std::string& name, BinaryWrapper* binary); void ProcessTables(YAML::Node& rom); void LoadYAMLRecursively(const std::string &dirPath, std::vector &result, bool skipRoot); std::optional ParseNode(YAML::Node& node, std::string& name); };