Torch
Loading...
Searching...
No Matches
Companion.h
1#pragma once
2
3#include <string>
4#include <optional>
5#include <filesystem>
6#include <vector>
7#include <atomic>
8#include <array>
9#include <fstream>
10#include <unordered_map>
11#include <unordered_set>
12#include <variant>
13#include <functional>
14#include "factories/BaseFactory.h"
15#include "n64/Cartridge.h"
16#include "utils/Decompressor.h"
17#include "factories/TextureFactory.h"
18
19#define CONTAINS(_map, value) ((_map).find(value) != (_map).end())
20
21class BinaryWrapper;
22namespace fs = std::filesystem;
23
24enum class ParseMode {
25 Default,
26 Directory
27};
28
29enum class GBIVersion {
30 f3db,
31 f3d,
32 f3dex,
33 f3dexb,
34 f3dex2,
35};
36
37enum class GBIMinorVersion {
38 None,
39 Mk64,
40 SM64,
41 PM64,
42 OoT,
43 BK64
44};
45
46enum class TableMode {
47 Reference,
48 Append
49};
50
51enum class ArchiveType {
52 None,
53 OTR,
54 O2R,
55};
56
58 std::unordered_map<uint32_t, uint32_t> global;
59 std::unordered_map<uint32_t, uint32_t> local;
60 std::unordered_map<uint32_t, uint32_t> temporal;
61 std::optional<uint32_t> primaryVirtual;
62 std::unordered_map<std::string, std::unordered_map<uint32_t, std::pair<std::uint32_t, std::uint32_t>>> compressed;
63};
64
65struct Table {
66 std::string name;
67 uint32_t start;
68 uint32_t end;
69 TableMode mode;
70 int32_t index_size;
71};
72
73struct VRAMEntry {
74 uint32_t addr;
75 uint32_t offset;
76};
77
79 uint32_t addr;
80 enum Kind {
81 Segmented, // addr has a segment number (e.g. 0x06001CC4)
82 Absolute, // addr is a physical ROM address (e.g. 0x00D269A0)
83 FileRelative, // addr is a plain offset within the file (e.g. 0x00005CC8)
84 Unknown, // addr has bit 31 set but we can't determine if it's
85 // segment-0x80 or VRAM for another file
86 } kind;
87};
88
89struct WriteEntry {
90 std::string name;
91 uint32_t addr;
92 uint32_t alignment;
93 std::string buffer;
94 std::optional<std::string> comment;
95 std::optional<uint32_t> endptr;
96};
97
98struct GBIConfig {
99 GBIVersion version = GBIVersion::f3d;
100 GBIMinorVersion subversion = GBIMinorVersion::None;
101 bool useFloats = false;
102};
103
105 GBIConfig gbi;
106 SegmentConfig segment;
107 std::string outputPath;
108 std::string moddingPath;
109 ExportType exporterType;
110 ParseMode parseMode;
111 ArchiveType otrMode;
112 bool debug;
113 bool modding;
114 bool textureDefines;
115 bool strictDeclarations;
116 bool includeAutogen;
117 bool dialogPack = false;
118 // Default model-preview shading mode name (e.g. "textured + lit"); empty
119 // uses each viewer's built-in fallback. See UI::ShadeSetupIndexByName.
120 std::string defaultShading;
121};
122
124 std::string name;
125 std::string type;
126 YAML::Node node;
127 std::optional<std::shared_ptr<IParsedData>> data;
128
129 uint32_t GetOffset() {
130 return GetSafeNode<uint32_t>(node, "offset");
131 }
132
133 std::optional<std::string> GetSymbol() {
134 return GetSafeNode<std::string>(node, "symbol");
135 }
136};
137
138class Companion {
139public:
140 static Companion* Instance;
141
142 explicit Companion(std::filesystem::path rom, const ArchiveType otr, const bool debug, const bool modding = false,
143 const std::string& srcDir = "", const std::string& destPath = "") : gCartridge(nullptr),
144 gSourceDirectory(srcDir), gDestinationDirectory(destPath) {
145 this->gRomPath = rom;
146 this->gConfig.otrMode = otr;
147 this->gConfig.debug = debug;
148 this->gConfig.modding = modding;
149 this->gConfig.textureDefines = false;
150 this->gConfig.includeAutogen = false;
151 }
152
153 explicit Companion(std::vector<uint8_t> rom, const ArchiveType otr, const bool debug, const bool modding = false,
154 const std::string& srcDir = "", const std::string& destPath = "") : gCartridge(nullptr),
155 gSourceDirectory(srcDir), gDestinationDirectory(destPath) {
156 this->gRomData = rom;
157 this->gConfig.otrMode = otr;
158 this->gConfig.debug = debug;
159 this->gConfig.modding = modding;
160 this->gConfig.textureDefines = false;
161 this->gConfig.includeAutogen = false;
162 }
163
164 void SetRomPath(std::filesystem::path path) { this->gRomPath = std::move(path); }
165
166 explicit Companion(std::filesystem::path rom, const ArchiveType otr, const bool debug, const std::string& srcDir = "", const std::string& destPath = "") :
167 Companion(rom, otr, debug, false, srcDir, destPath) {}
168
169 explicit Companion(std::vector<uint8_t> rom, const ArchiveType otr, const bool debug, const std::string& srcDir = "", const std::string& destPath = "") :
170 Companion(rom, otr, debug, false, srcDir, destPath) {}
171
172 void Init(ExportType type);
173 void Init(ExportType type, std::atomic<size_t>& assetCount, bool shouldProcess);
174
175 bool NodeHasChanges(const std::string& string);
176
177 void Process(std::atomic<size_t>& assetCount);
178
179 bool IsOTRMode() const { return (this->gConfig.otrMode != ArchiveType::None); }
180 bool IsDebug() const { return this->gConfig.debug; }
181 bool AddTextureDefines() const { return this->gConfig.textureDefines; }
182
183 N64::Cartridge* GetCartridge() const { return this->gCartridge.get(); }
184 // Survives cartridge teardown so the UI can key game-specific behavior.
185 const std::string& GetGameTitle() const { return this->gGameTitle; }
186 std::vector<uint8_t>& GetRomData() { return this->gRomData; }
187 std::string GetOutputPath() { return this->gConfig.outputPath; }
188 const std::string& GetAssetPath() const { return this->gAssetPath; }
189 std::string GetDestRelativeOutputPath() { return RelativePathToDestDir(GetOutputPath()); }
190
191 GBIVersion GetGBIVersion() const { return this->gConfig.gbi.version; }
192 GBIMinorVersion GetGBIMinorVersion() const { return this->gConfig.gbi.subversion; }
193 std::unordered_map<std::string, std::vector<YAML::Node>> GetCourseMetadata() { return this->gCourseMetadata; }
194 std::optional<std::string> GetEnumFromValue(const std::string& key, int id);
195 bool IsUsingIndividualIncludes() const { return this->gIndividualIncludes; }
196
197 std::optional<ParseResultData> GetParseDataByAddr(uint32_t addr);
198 std::optional<ParseResultData> GetParseDataBySymbol(const std::string& symbol);
199
200 std::optional<std::uint32_t> GetFileOffsetFromSegmentedAddr(uint8_t segment) const;
201 std::optional<std::pair<std::uint32_t, std::uint32_t>> GetFileOffsetFromCompressedSegmentedAddr(uint8_t segment) const;
202 std::optional<std::shared_ptr<BaseFactory>> GetFactory(const std::string& type);
203 uint32_t PatchVirtualAddr(uint32_t addr);
204 ResolvedAddr ResolveVirtualAddr(uint32_t addr);
205 std::optional<std::tuple<std::string, YAML::Node>> GetNodeByAddr(uint32_t addr);
206 // File-scoped variant for use outside the parse/export passes, where
207 // gCurrentFile no longer points at the owning file.
208 std::optional<std::tuple<std::string, YAML::Node>> GetNodeByAddr(uint32_t addr, const std::string& file);
209 std::optional<std::string> GetStringByAddr(uint32_t addr);
210 std::optional<std::tuple<std::string, YAML::Node>> GetSafeNodeByAddr(const uint32_t addr, std::string type);
211 std::optional<std::string> GetSafeStringByAddr(const uint32_t addr, std::string type);
212 std::optional<std::vector<std::tuple<std::string, YAML::Node>>> GetNodesByType(const std::string& type, bool includeAutogen = false);
213 // Same as GetNodesByType but returns a pointer into the internal cache to
214 // avoid copying. The pointer is valid until the cache is invalidated; do not
215 // hold across AddAsset/RegisterAsset calls. Returns nullptr if the current
216 // file has no nodes registered.
217 const std::vector<std::tuple<std::string, YAML::Node>>* GetNodesByTypeRef(const std::string& type, bool includeAutogen = false);
218 std::string GetSymbolFromAddr(uint32_t addr, bool validZero = false);
219
220 std::optional<std::uint32_t> GetFileOffset(void) const { return this->gCurrentFileOffset; };
221 std::optional<std::uint32_t> GetCurrSegmentNumber(void) const { return this->gCurrentSegmentNumber; };
222 CompressionType GetCurrCompressionType(void) const { return this->gCurrentCompressionType; };
223 std::optional<std::uint32_t> GetCurrentCompressedSize(void) const { return this->gCurrentCompressedSize; };
224 std::optional<VRAMEntry> GetCurrentVRAM(void) const { return this->gCurrentVram; };
225 std::optional<Table> SearchTable(uint32_t addr);
226
227 static std::vector<char> ParseVersionString(const std::string& version);
228 static std::string CalculateHash(const std::vector<uint8_t>& data);
229 static void Pack(const std::string& folder, const std::string& output, const ArchiveType otrMode, const std::string& version = "");
230 std::string NormalizeAsset(const std::string& name) const;
231 std::string RelativePath(const std::string& path) const;
232 std::string RelativePathToSrcDir(const std::string& path) const;
233 std::string RelativePathToDestDir(const std::string& path) const;
234 void RegisterCompanionFile(const std::string path, std::vector<char> data);
235 void RegisterArchiveFile(const std::string& name, std::vector<char> data);
236 void SetAdditionalFiles(const std::vector<std::string>& files) { this->gAdditionalFiles = files; }
237 void SetVersion(const std::string& version) { this->gVersion = version; }
238
239 std::string GetCurrentAssetName() const { return this->gCurrentAssetName; }
240 void SetAssetTotal(std::atomic<size_t>* total) { this->gAssetTotal = total; }
241 void SetPhaseCallback(std::function<void(int)> cb) { this->gPhaseCallback = cb; }
242
243 void SetProcess(bool shouldProcess);
244 TorchConfig& GetConfig() { return this->gConfig; }
245 BinaryWrapper* GetCurrentWrapper() { return this->gCurrentWrapper; }
246 const std::unordered_map<std::string, std::string>& GetModdedAssetPaths() const { return this->gModdedAssetPaths; }
247
248 std::optional<std::tuple<std::string, YAML::Node>> RegisterAsset(const std::string& name, YAML::Node& node);
249 std::optional<YAML::Node> AddSubFileAsset(YAML::Node asset, std::string newFileName, CompressionType newCompressionType, uint32_t compressedSize = 0);
250 std::optional<YAML::Node> AddAsset(YAML::Node asset);
251 std::string GetCurrentDirectory() const { return gCurrentDirectory.generic_string(); }
252 void SetCompressedSegment(uint32_t segmentId, uint32_t compressedFileOffset, uint32_t offset);
253 bool GetCompressedSegmentOffset(uint32_t* addr);
254
255 void SetSingleYMLPath(const std::string& path) { this->gSingleYMLPath = path; }
256
257#ifdef BUILD_UI
258 void RegisterUIFactory(const std::string& type, const std::shared_ptr<BaseFactoryUI>& factory);
259 std::optional<std::shared_ptr<BaseFactoryUI>> GetUIFactory(const std::string& type);
260 const std::unordered_map<std::string, std::vector<ParseResultData>>& GetParseResults() { return this->gParseResults; }
261#endif
262private:
263 TorchConfig gConfig;
264 YAML::Node gModdingConfig;
265 fs::path gSourceDirectory;
266 fs::path gDestinationDirectory;
267 fs::path gCurrentDirectory;
268 std::string gCurrentHash;
269 std::string gAssetPath;
270 std::string gVersion;
271 std::string gSingleYMLPath;
272 std::vector<uint8_t> gRomData;
273 std::optional<std::filesystem::path> gRomPath;
274 bool gNodeForceProcessing = false;
275 bool gIndividualIncludes = false;
276 YAML::Node gHashNode;
277 std::shared_ptr<N64::Cartridge> gCartridge;
278 std::string gGameTitle;
279 std::unordered_map<std::string, std::vector<YAML::Node>> gCourseMetadata;
280 std::unordered_map<std::string, std::unordered_map<int32_t, std::string>> gEnums;
281 BinaryWrapper* gCurrentWrapper;
282
283 // Temporal Variables
284 std::string gCurrentAssetName;
285 std::atomic<size_t>* gAssetCounter = nullptr;
286 std::atomic<size_t>* gAssetTotal = nullptr;
287 std::function<void(int)> gPhaseCallback;
288 std::string gCurrentFile;
289 std::vector<std::string> gSubFileList;
290 std::string gCurrentVirtualPath;
291 std::string gFileHeader;
292 bool gEnablePadGen = false;
293 bool mShouldProcess = true;
294 uint32_t gCurrentPad = 0;
295 uint32_t gCurrentFileOffset;
296 uint32_t gCurrentSegmentNumber;
297 std::optional<VRAMEntry> gCurrentVram;
298 CompressionType gCurrentCompressionType = CompressionType::None;
299 std::optional<std::uint32_t> gCurrentCompressedSize;
300 std::vector<Table> gTables;
301 std::vector<std::string> gCurrentExternalFiles;
302 std::unordered_map<int, std::string> gManualSegments;
303 std::unordered_set<std::string> gProcessedFiles;
304
305 std::unordered_map<std::string, std::vector<char>> gCompanionFiles;
306 std::vector<std::pair<std::string, std::vector<char>>> gArchiveFiles;
307 std::unordered_map<std::string, std::vector<ParseResultData>> gParseResults;
308 std::vector<std::string> gAdditionalFiles;
309
310 std::unordered_map<std::string, std::string> gModdedAssetPaths;
311 std::variant<std::vector<std::string>, std::string> gWriteOrder;
312 std::unordered_map<std::string, std::shared_ptr<BaseFactory>> gFactories;
313#ifdef BUILD_UI
314 std::unordered_map<std::string, std::shared_ptr<BaseFactoryUI>> gUIFactories;
315#endif
316 std::unordered_map<std::string, std::map<std::string, std::vector<WriteEntry>>> gWriteMap;
317 std::unordered_map<std::string, std::tuple<uint32_t, uint32_t>> gVirtualAddrMap;
318 std::unordered_map<std::string, std::unordered_map<uint32_t, std::tuple<std::string, YAML::Node>>> gAddrMap;
319 // Cached results of GetNodesByType keyed by [file][type][includeAutogen?].
320 // Built lazily; invalidated on RegisterAsset for the affected (file, type).
321 // Index 0 = !includeAutogen, index 1 = includeAutogen.
322 std::unordered_map<std::string,
323 std::unordered_map<std::string, std::array<std::vector<std::tuple<std::string, YAML::Node>>, 2>>>
324 gNodesByTypeCache;
325 // Validity bitmap; bit 0 = !includeAutogen, bit 1 = includeAutogen.
326 std::unordered_map<std::string, std::unordered_map<std::string, uint8_t>> gNodesByTypeCacheValid;
327
328 void ProcessParseFile(YAML::Node root, std::atomic<size_t>& assetCount);
329 void ProcessExportFile();
330 void ProcessFile(YAML::Node root);
331 void ProcessFile(YAML::Node root, std::atomic<size_t>& assetCount);
332 std::optional<std::tuple<std::string, YAML::Node>> FindNodeInOverlaySegments(uint32_t addr, const std::string& file);
333 std::optional<std::tuple<std::string, YAML::Node>> FindInExternalByVRAM(uint32_t addr, const std::string& file);
334 void ParseEnums(std::string& file);
335 void ParseHash();
336 void ParseModdingConfig();
337 void ParseCurrentFileConfig(YAML::Node node, std::atomic<size_t>& assetCount);
338 void RegisterFactory(const std::string& type, const std::shared_ptr<BaseFactory>& factory);
339 void ExtractNode(YAML::Node& node, std::string& name, BinaryWrapper* binary);
340 void ProcessTables(YAML::Node& rom);
341 void LoadYAMLRecursively(const std::string &dirPath, std::vector<YAML::Node> &result, bool skipRoot);
342 std::vector<fs::directory_entry> GetAssetYMLs(YAML::Node& rom) const;
343 std::optional<ParseResultData> ParseNode(YAML::Node& node, std::string& name);
344};
Definition BinaryWrapper.h:7
Definition Cartridge.h:15
Definition Companion.h:98
Definition Companion.h:123
Definition Companion.h:78
Definition Companion.h:57
Definition Companion.h:65
Definition Companion.h:104
Definition Companion.h:73
Definition Companion.h:89