2023-08-17 00:40:00 -06:00
|
|
|
#include "Companion.h"
|
|
|
|
|
|
|
|
|
|
#include "storm/SWrapper.h"
|
|
|
|
|
#include "utils/MIODecoder.h"
|
2023-11-15 15:11:15 -06:00
|
|
|
#include "factories/sm64/AnimationFactory.h"
|
|
|
|
|
#include "factories/sm64/DialogFactory.h"
|
|
|
|
|
#include "factories/sm64/DictionaryFactory.h"
|
|
|
|
|
#include "factories/sm64/TextFactory.h"
|
2023-11-21 00:27:23 -06:00
|
|
|
#include "factories/sm64/GeoLayoutFactory.h"
|
2023-09-17 23:55:59 -06:00
|
|
|
#include "factories/BankFactory.h"
|
2023-11-15 15:11:15 -06:00
|
|
|
#include "factories/AudioHeaderFactory.h"
|
|
|
|
|
#include "factories/SampleFactory.h"
|
|
|
|
|
#include "factories/SequenceFactory.h"
|
|
|
|
|
#include "factories/VtxFactory.h"
|
2023-08-17 00:40:00 -06:00
|
|
|
#include "factories/TextureFactory.h"
|
2023-11-15 15:11:15 -06:00
|
|
|
#include "factories/DisplayListFactory.h"
|
|
|
|
|
#include "factories/BlobFactory.h"
|
|
|
|
|
#include "factories/LightsFactory.h"
|
|
|
|
|
#include "factories/mk64/WaypointFactory.h"
|
2023-09-24 13:39:02 -06:00
|
|
|
#include "spdlog/spdlog.h"
|
2023-08-17 00:40:00 -06:00
|
|
|
|
|
|
|
|
#include <fstream>
|
|
|
|
|
#include <iostream>
|
|
|
|
|
#include <filesystem>
|
|
|
|
|
|
2023-09-24 14:06:18 -06:00
|
|
|
using namespace std::chrono;
|
2023-11-15 15:11:15 -06:00
|
|
|
namespace fs = std::filesystem;
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
static const std::string regular = "[%Y-%m-%d %H:%M:%S.%e] [%l] %v";
|
|
|
|
|
static const std::string line = "[%Y-%m-%d %H:%M:%S.%e] [%l] > %v";
|
|
|
|
|
|
|
|
|
|
void Companion::Init(const ExportType type) {
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-09-24 13:39:02 -06:00
|
|
|
spdlog::set_level(spdlog::level::debug);
|
|
|
|
|
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gExporterType = type;
|
|
|
|
|
this->RegisterFactory("BLOB", std::make_shared<BlobFactory>());
|
|
|
|
|
this->RegisterFactory("TEXTURE", std::make_shared<TextureFactory>());
|
|
|
|
|
this->RegisterFactory("VTX", std::make_shared<VtxFactory>());
|
|
|
|
|
this->RegisterFactory("LIGHTS", std::make_shared<LightsFactory>());
|
|
|
|
|
this->RegisterFactory("GFX", std::make_shared<DListFactory>());
|
|
|
|
|
this->RegisterFactory("AUDIO:HEADER", std::make_shared<AudioHeaderFactory>());
|
|
|
|
|
this->RegisterFactory("SEQUENCE", std::make_shared<SequenceFactory>());
|
|
|
|
|
this->RegisterFactory("SAMPLE", std::make_shared<SampleFactory>());
|
|
|
|
|
this->RegisterFactory("BANK", std::make_shared<BankFactory>());
|
2023-09-17 23:55:59 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
// SM64 specific
|
|
|
|
|
this->RegisterFactory("SM64:DIALOG", std::make_shared<SM64::DialogFactory>());
|
|
|
|
|
this->RegisterFactory("SM64:TEXT", std::make_shared<SM64::TextFactory>());
|
|
|
|
|
this->RegisterFactory("SM64:DICTIONARY", std::make_shared<SM64::DictionaryFactory>());
|
|
|
|
|
this->RegisterFactory("SM64:ANIM", std::make_shared<SM64::AnimationFactory>());
|
2023-11-21 00:27:23 -06:00
|
|
|
this->RegisterFactory("SM64:GEO_LAYOUT", std::make_shared<SM64::GeoLayoutFactory>());
|
2023-09-17 23:55:59 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
// MK64 specific
|
|
|
|
|
this->RegisterFactory("MK64:TRACKWAYPOINTS", std::make_shared<MK64::WaypointFactory>());
|
2023-09-21 09:52:11 -06:00
|
|
|
|
2023-09-17 23:55:59 -06:00
|
|
|
this->Process();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
void Companion::ExtractNode(YAML::Node& node, std::string& name, SWrapper* binary) {
|
|
|
|
|
std::ostringstream stream;
|
|
|
|
|
|
|
|
|
|
auto type = node["type"].as<std::string>();
|
|
|
|
|
std::transform(type.begin(), type.end(), type.begin(), ::toupper);
|
|
|
|
|
|
2023-11-21 00:27:23 -06:00
|
|
|
if(node["offset"]) {
|
|
|
|
|
auto offset = node["offset"].as<uint32_t>();
|
|
|
|
|
SPDLOG_INFO("[{}] Processing {} at 0x{:X}", type, name, offset);
|
|
|
|
|
} else {
|
|
|
|
|
SPDLOG_INFO("[{}] Processing {}", type, name);
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto factory = this->GetFactory(type);
|
|
|
|
|
if(!factory.has_value()){
|
|
|
|
|
SPDLOG_ERROR("No factory found for {}", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-22 10:49:30 -06:00
|
|
|
if(node["segments"]) {
|
|
|
|
|
for(auto segment = node["segments"].begin(); segment != node["segments"].end(); ++segment) {
|
|
|
|
|
auto id = std::stoi(segment->first.as<std::string>().substr(3));
|
|
|
|
|
auto replacement = segment->second.as<uint32_t>();
|
|
|
|
|
this->gTemporalSegments[id] = replacement;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto result = factory->get()->parse(this->gRomData, node);
|
|
|
|
|
if(!result.has_value()){
|
|
|
|
|
SPDLOG_ERROR("Failed to process {}", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
auto exporter = factory->get()->GetExporter(this->gExporterType);
|
|
|
|
|
if(!exporter.has_value()){
|
|
|
|
|
SPDLOG_ERROR("No exporter found for {}", name);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto [fst, snd] : this->gAssetDependencies[this->gCurrentFile]) {
|
|
|
|
|
if(snd.second) continue;
|
|
|
|
|
std::string doutput = (this->gCurrentDirectory / fst).string();
|
|
|
|
|
std::replace(doutput.begin(), doutput.end(), '\\', '/');
|
|
|
|
|
this->gAssetDependencies[this->gCurrentFile][fst].second = true;
|
|
|
|
|
this->ExtractNode(snd.first, doutput, binary);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (this->gExporterType) {
|
|
|
|
|
case ExportType::Binary: {
|
|
|
|
|
if(binary == nullptr) break;
|
|
|
|
|
stream.str("");
|
|
|
|
|
stream.clear();
|
|
|
|
|
exporter->get()->Export(stream, result.value(), name, node, &name);
|
|
|
|
|
auto data = stream.str();
|
|
|
|
|
binary->CreateFile(name, std::vector(data.begin(), data.end()));
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: {
|
|
|
|
|
exporter->get()->Export(stream, result.value(), name, node, &name);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
SPDLOG_INFO("Processed {}", name);
|
2023-11-21 00:27:23 -06:00
|
|
|
if(node["offset"]) {
|
|
|
|
|
this->gWriteMap[this->gCurrentFile][type].emplace_back(node["offset"].as<uint32_t>(), stream.str());
|
|
|
|
|
}
|
2023-11-15 15:11:15 -06:00
|
|
|
}
|
|
|
|
|
|
2023-09-17 23:55:59 -06:00
|
|
|
void Companion::Process() {
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
if(!fs::exists("config.yml")) {
|
|
|
|
|
SPDLOG_ERROR("No config file found");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2023-09-24 14:26:01 -06:00
|
|
|
|
2023-09-24 14:06:18 -06:00
|
|
|
auto start = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
|
|
|
|
|
2023-08-17 00:40:00 -06:00
|
|
|
std::ifstream input( this->gRomPath, std::ios::binary );
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gRomData = std::vector<uint8_t>( std::istreambuf_iterator( input ), {} );
|
2023-08-17 00:40:00 -06:00
|
|
|
input.close();
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gCartridge = new N64::Cartridge(this->gRomData);
|
|
|
|
|
gCartridge->Initialize();
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-09-17 23:55:59 -06:00
|
|
|
YAML::Node config = YAML::LoadFile("config.yml");
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
if(!config[gCartridge->GetHash()]){
|
|
|
|
|
SPDLOG_ERROR("No config found for {}", gCartridge->GetHash());
|
2023-09-17 23:55:59 -06:00
|
|
|
return;
|
|
|
|
|
}
|
2023-09-01 00:51:51 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto rom = config[gCartridge->GetHash()];
|
|
|
|
|
auto cfg = rom["config"];
|
|
|
|
|
|
|
|
|
|
if(!cfg) {
|
|
|
|
|
SPDLOG_ERROR("No config found for {}", gCartridge->GetHash());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(rom["segments"]) {
|
|
|
|
|
this->gSegments = rom["segments"].as<std::vector<uint32_t>>();
|
|
|
|
|
}
|
|
|
|
|
auto path = rom["path"].as<std::string>();
|
|
|
|
|
auto opath = cfg["output"];
|
|
|
|
|
auto gbi = cfg["gbi"];
|
|
|
|
|
|
|
|
|
|
switch (gExporterType) {
|
|
|
|
|
case ExportType::Binary: {
|
|
|
|
|
this->gOutputPath = opath && opath["binary"] ? opath["binary"].as<std::string>() : "generic.otr";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ExportType::Header: {
|
|
|
|
|
this->gOutputPath = opath && opath["headers"] ? opath["headers"].as<std::string>() : "headers";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ExportType::Code: {
|
|
|
|
|
this->gOutputPath = opath && opath["code"] ? opath["code"].as<std::string>() : "code";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(gbi) {
|
|
|
|
|
auto key = gbi.as<std::string>();
|
|
|
|
|
|
|
|
|
|
if(key == "F3D") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3d;
|
|
|
|
|
} else if(key == "F3DEX") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3dex;
|
|
|
|
|
} else if(key == "F3DB") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3db;
|
|
|
|
|
} else if(key == "F3DEX2") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3dex2;
|
|
|
|
|
} else if(key == "F3DEXB") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3dexb;
|
|
|
|
|
} else if (key == "F3DEX_MK64") {
|
|
|
|
|
this->gGBIVersion = GBIVersion::f3dex;
|
|
|
|
|
this->gGBIMinorVersion = GBIMinorVersion::Mk64;
|
|
|
|
|
} else {
|
|
|
|
|
SPDLOG_ERROR("Invalid GBI version");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(auto sort = cfg["sort"]) {
|
|
|
|
|
if(sort.IsSequence()) {
|
|
|
|
|
this->gWriteOrder = sort.as<std::vector<std::string>>();
|
|
|
|
|
} else {
|
|
|
|
|
this->gWriteOrder = sort.as<std::string>();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
this->gWriteOrder = std::vector<std::string> {
|
|
|
|
|
"LIGHTS", "TEXTURE", "VTX", "GFX"
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(std::holds_alternative<std::vector<std::string>>(this->gWriteOrder)) {
|
|
|
|
|
for (auto& [key, _] : this->gFactories) {
|
|
|
|
|
auto entries = std::get<std::vector<std::string>>(this->gWriteOrder);
|
|
|
|
|
|
|
|
|
|
if(std::find(entries.begin(), entries.end(), key) != entries.end()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
entries.push_back(key);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-09-24 14:06:18 -06:00
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(line);
|
2023-09-24 13:39:02 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
SPDLOG_INFO("Starting Torch...");
|
|
|
|
|
SPDLOG_INFO("Game: {}", gCartridge->GetGameTitle());
|
|
|
|
|
SPDLOG_INFO("CRC: {}", gCartridge->GetCRC());
|
|
|
|
|
SPDLOG_INFO("Version: {}", gCartridge->GetVersion());
|
|
|
|
|
SPDLOG_INFO("Country: [{}]", gCartridge->GetCountryCode());
|
|
|
|
|
SPDLOG_INFO("Hash: {}", gCartridge->GetHash());
|
2023-09-24 13:39:02 -06:00
|
|
|
SPDLOG_INFO("Assets: {}", path);
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto wrapper = this->gExporterType == ExportType::Binary ? new SWrapper(this->gOutputPath) : nullptr;
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-09-18 03:39:03 -06:00
|
|
|
auto vWriter = LUS::BinaryWriter();
|
|
|
|
|
vWriter.SetEndianness(LUS::Endianness::Big);
|
2023-11-15 15:11:15 -06:00
|
|
|
vWriter.Write(static_cast<uint8_t>(LUS::Endianness::Big));
|
|
|
|
|
vWriter.Write(gCartridge->GetCRC());
|
2023-09-18 03:39:03 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
for (const auto & entry : fs::recursive_directory_iterator(path)){
|
|
|
|
|
if(entry.is_directory()) continue;
|
2023-09-23 23:31:37 +01:00
|
|
|
YAML::Node root = YAML::LoadFile(entry.path().string());
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gCurrentDirectory = relative(entry.path(), path).replace_extension("");
|
|
|
|
|
this->gCurrentFile = entry.path().string();
|
2023-08-17 00:40:00 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
for(auto asset = root.begin(); asset != root.end(); ++asset){
|
|
|
|
|
auto node = asset->second;
|
|
|
|
|
if(!asset->second["offset"]) continue;
|
|
|
|
|
|
|
|
|
|
auto output = (this->gCurrentDirectory / asset->first.as<std::string>()).string();
|
|
|
|
|
std::replace(output.begin(), output.end(), '\\', '/');
|
|
|
|
|
|
|
|
|
|
this->gAddrMap[this->gCurrentFile][node["offset"].as<uint32_t>()] = std::make_tuple(output, node);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Stupid hack because the iteration broke the assets
|
|
|
|
|
root = YAML::LoadFile(entry.path().string());
|
|
|
|
|
|
|
|
|
|
for(auto asset = root.begin(); asset != root.end(); ++asset){
|
2023-08-19 23:55:59 -06:00
|
|
|
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(regular);
|
2023-09-24 13:39:02 -06:00
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(line);
|
2023-09-24 13:39:02 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto entryName = asset->first.as<std::string>();
|
|
|
|
|
std::string output = (this->gCurrentDirectory / entryName).string();
|
|
|
|
|
std::replace(output.begin(), output.end(), '\\', '/');
|
|
|
|
|
|
2023-11-22 10:49:30 -06:00
|
|
|
this->gTemporalSegments.clear();
|
2023-11-15 15:11:15 -06:00
|
|
|
this->ExtractNode(asset->second, output, wrapper);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(gExporterType != ExportType::Binary){
|
|
|
|
|
std::string output = (this->gOutputPath / this->gCurrentDirectory).string();
|
|
|
|
|
|
|
|
|
|
switch (gExporterType) {
|
|
|
|
|
case ExportType::Header: {
|
|
|
|
|
output += "/definition.h";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
case ExportType::Code: {
|
|
|
|
|
output += "/root.inc.c";
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
default: break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::ostringstream stream;
|
|
|
|
|
|
|
|
|
|
if(std::holds_alternative<std::string>(this->gWriteOrder)) {
|
|
|
|
|
auto sort = std::get<std::string>(this->gWriteOrder);
|
|
|
|
|
std::vector<std::pair<uint32_t, std::string>> outbuf;
|
|
|
|
|
for (const auto& [type, buffer] : this->gWriteMap[this->gCurrentFile]) {
|
|
|
|
|
outbuf.insert(outbuf.end(), buffer.begin(), buffer.end());
|
|
|
|
|
}
|
|
|
|
|
this->gWriteMap.clear();
|
|
|
|
|
|
|
|
|
|
if(sort == "OFFSET") {
|
|
|
|
|
std::sort(outbuf.begin(), outbuf.end(), [](const auto& a, const auto& b) {
|
|
|
|
|
return std::get<uint32_t>(a) < std::get<uint32_t>(b);
|
|
|
|
|
});
|
|
|
|
|
} else if(sort == "ROFFSET") {
|
|
|
|
|
std::sort(outbuf.begin(), outbuf.end(), [](const auto& a, const auto& b) {
|
|
|
|
|
return std::get<uint32_t>(a) > std::get<uint32_t>(b);
|
|
|
|
|
});
|
|
|
|
|
} else if(sort != "LINEAR") {
|
|
|
|
|
throw std::runtime_error("Invalid write order");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto& [symbol, buffer] : outbuf) {
|
|
|
|
|
stream << buffer;
|
|
|
|
|
}
|
|
|
|
|
outbuf.clear();
|
|
|
|
|
} else {
|
|
|
|
|
for (const auto& type : std::get<std::vector<std::string>>(this->gWriteOrder)) {
|
|
|
|
|
std::vector<std::pair<uint32_t, std::string>> outbuf = this->gWriteMap[this->gCurrentFile][type];
|
|
|
|
|
|
|
|
|
|
std::sort(outbuf.begin(), outbuf.end(), [](const auto& a, const auto& b) {
|
|
|
|
|
return std::get<uint32_t>(a) > std::get<uint32_t>(b);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
for (auto& [symbol, buffer] : outbuf) {
|
|
|
|
|
stream << buffer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string buffer = stream.str();
|
|
|
|
|
|
|
|
|
|
if(buffer.empty()) {
|
2023-09-17 23:55:59 -06:00
|
|
|
continue;
|
2023-08-30 21:26:38 -06:00
|
|
|
}
|
2023-09-17 23:55:59 -06:00
|
|
|
|
2023-09-24 03:28:51 -06:00
|
|
|
std::replace(output.begin(), output.end(), '\\', '/');
|
2023-11-15 15:11:15 -06:00
|
|
|
if(!exists(fs::path(output).parent_path())){
|
|
|
|
|
create_directories(fs::path(output).parent_path());
|
2023-09-17 23:55:59 -06:00
|
|
|
}
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
std::ofstream file(output, std::ios::binary);
|
2023-09-17 23:55:59 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
if(gExporterType == ExportType::Header) {
|
2023-11-17 09:39:13 -05:00
|
|
|
std::string symbol = entry.path().stem().string();
|
2023-11-15 15:11:15 -06:00
|
|
|
std::transform(symbol.begin(), symbol.end(), symbol.begin(), toupper);
|
|
|
|
|
file << "#ifndef " << symbol << "_H" << std::endl;
|
|
|
|
|
file << "#define " << symbol << "_H" << std::endl << std::endl;
|
|
|
|
|
file << buffer;
|
|
|
|
|
file << std::endl << "#endif" << std::endl;
|
|
|
|
|
} else {
|
|
|
|
|
file << buffer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
file.close();
|
2023-08-19 23:55:59 -06:00
|
|
|
}
|
2023-08-17 00:40:00 -06:00
|
|
|
}
|
|
|
|
|
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(regular);
|
2023-09-24 13:39:02 -06:00
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(line);
|
2023-11-15 15:11:15 -06:00
|
|
|
if(wrapper != nullptr) {
|
|
|
|
|
SPDLOG_INFO("Writing version file");
|
|
|
|
|
wrapper->CreateFile("version", vWriter.ToVector());
|
|
|
|
|
vWriter.Close();
|
|
|
|
|
wrapper->Close();
|
|
|
|
|
}
|
2023-09-24 14:06:18 -06:00
|
|
|
auto end = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
|
|
|
|
SPDLOG_INFO("Done! Took {}ms", end.count() - start.count());
|
2023-09-24 14:26:01 -06:00
|
|
|
spdlog::set_pattern(regular);
|
2023-09-24 13:39:02 -06:00
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-09-18 03:39:03 -06:00
|
|
|
|
2023-08-17 00:40:00 -06:00
|
|
|
MIO0Decoder::ClearCache();
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gCartridge = nullptr;
|
|
|
|
|
Instance = nullptr;
|
2023-09-17 23:55:59 -06:00
|
|
|
}
|
2023-10-07 21:07:04 -06:00
|
|
|
|
|
|
|
|
void Companion::Pack(const std::string& folder, const std::string& output) {
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
spdlog::set_level(spdlog::level::debug);
|
|
|
|
|
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
SPDLOG_INFO("Starting Torch...");
|
|
|
|
|
SPDLOG_INFO("Scanning {}", folder);
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto start = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
|
|
|
|
std::unordered_map<std::string, std::vector<char>> files;
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
for (const auto & entry : fs::recursive_directory_iterator(folder)){
|
|
|
|
|
if(entry.is_directory()) continue;
|
|
|
|
|
std::ifstream input( entry.path(), std::ios::binary );
|
|
|
|
|
auto data = std::vector( std::istreambuf_iterator( input ), {} );
|
|
|
|
|
input.close();
|
|
|
|
|
files[entry.path().string()] = data;
|
|
|
|
|
}
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto wrapper = SWrapper(output);
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
for(auto& [path, data] : files){
|
|
|
|
|
std::string normalized = path;
|
|
|
|
|
std::replace(normalized.begin(), normalized.end(), '\\', '/');
|
|
|
|
|
// Remove parent folder
|
|
|
|
|
normalized = normalized.substr(folder.length() + 1);
|
|
|
|
|
wrapper.CreateFile(normalized, data);
|
|
|
|
|
SPDLOG_INFO("> Added {}", normalized);
|
|
|
|
|
}
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
auto end = duration_cast<milliseconds>(system_clock::now().time_since_epoch());
|
|
|
|
|
SPDLOG_INFO("Done! Took {}ms", end.count() - start.count());
|
|
|
|
|
SPDLOG_INFO("Exported to {}", output);
|
|
|
|
|
spdlog::set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%l] %v");
|
|
|
|
|
SPDLOG_INFO("------------------------------------------------");
|
2023-10-07 21:07:04 -06:00
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
wrapper.Close();
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-21 00:27:23 -06:00
|
|
|
std::optional<std::tuple<std::string, YAML::Node>> Companion::RegisterAsset(const std::string& name, YAML::Node& node) {
|
|
|
|
|
if(!node["offset"]) return std::nullopt;
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
this->gAssetDependencies[this->gCurrentFile][name] = std::make_pair(node, false);
|
|
|
|
|
|
|
|
|
|
auto output = (this->gCurrentDirectory / name).string();
|
|
|
|
|
std::replace(output.begin(), output.end(), '\\', '/');
|
2023-11-21 00:27:23 -06:00
|
|
|
|
|
|
|
|
auto entry = std::make_tuple(output, node);
|
|
|
|
|
this->gAddrMap[this->gCurrentFile][node["offset"].as<uint32_t>()] = entry;
|
|
|
|
|
|
|
|
|
|
return entry;
|
2023-11-15 15:11:15 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Companion::RegisterFactory(const std::string& type, const std::shared_ptr<BaseFactory>& factory) {
|
|
|
|
|
this->gFactories[type] = factory;
|
|
|
|
|
SPDLOG_DEBUG("Registered factory for {}", type);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<std::shared_ptr<BaseFactory>> Companion::GetFactory(const std::string &type) {
|
|
|
|
|
if(!this->gFactories.contains(type)){
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this->gFactories[type];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<std::uint32_t> Companion::GetSegmentedAddr(const uint8_t segment) {
|
2023-11-22 10:49:30 -06:00
|
|
|
|
|
|
|
|
if(this->gTemporalSegments.contains(segment)) {
|
|
|
|
|
return this->gTemporalSegments[segment];
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-15 15:11:15 -06:00
|
|
|
if(segment >= this->gSegments.size()) {
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this->gSegments[segment];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::optional<std::tuple<std::string, YAML::Node>> Companion::GetNodeByAddr(const uint32_t addr){
|
|
|
|
|
if(!this->gAddrMap.contains(this->gCurrentFile)){
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if(!this->gAddrMap[this->gCurrentFile].contains(addr)){
|
|
|
|
|
return std::nullopt;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this->gAddrMap[this->gCurrentFile][addr];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string Companion::NormalizeAsset(const std::string& name) const {
|
|
|
|
|
auto path = fs::path(this->gCurrentFile).stem().string() + "_" + name;
|
|
|
|
|
return path;
|
2023-10-07 21:07:04 -06:00
|
|
|
}
|