Add Mtx, float, and mk64 Factories (#43)

* Create MtxFactory.cpp

* Add mtx factory

* Implement mtx output

* Changed implementation

* Update comment

* Finish Mtx factory

* Add float factory

* Add runtime error for untested code

* Add DrivingBehaviour factory & fix bug

* Remove limit

* Add mk64 graphics

* Changes

* fix

* Update

* Improve error checking
This commit is contained in:
MegaMech
2024-03-23 01:57:53 -06:00
committed by GitHub
parent 5c6fa9c1f6
commit cd7dd6f59a
10 changed files with 545 additions and 7 deletions
+1 -6
View File
@@ -509,7 +509,7 @@ rgba* png2rgba(unsigned char* png_input, int size_input, int* width, int* height
}
rgb* png2rgb(unsigned char* png_input, int size_input, int* width, int* height) {
rgba* img = NULL;
rgb* img = NULL;
int w = 0;
int h = 0;
int channels = 0;
@@ -538,11 +538,6 @@ rgb* png2rgb(unsigned char* png_input, int size_input, int* width, int* height)
img[idx].red = data[channels * idx];
img[idx].green = data[channels * idx + 1];
img[idx].blue = data[channels * idx + 2];
if (channels == 4) {
img[idx].alpha = data[channels * idx + 3];
} else {
img[idx].alpha = 0xFF;
}
}
}
break;
+6
View File
@@ -13,6 +13,8 @@
#include "factories/SampleFactory.h"
#include "factories/SequenceFactory.h"
#include "factories/VtxFactory.h"
#include "factories/MtxFactory.h"
#include "factories/FloatFactory.h"
#include "factories/TextureFactory.h"
#include "factories/DisplayListFactory.h"
#include "factories/DisplayListOverrides.h"
@@ -23,6 +25,7 @@
#include "factories/mk64/Waypoints.h"
#include "factories/mk64/TrackSections.h"
#include "factories/mk64/SpawnData.h"
#include "factories/mk64/DrivingBehaviour.h"
#include "spdlog/spdlog.h"
#include "hj/sha1.h"
@@ -59,6 +62,8 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("BLOB", std::make_shared<BlobFactory>());
this->RegisterFactory("TEXTURE", std::make_shared<TextureFactory>());
this->RegisterFactory("VTX", std::make_shared<VtxFactory>());
this->RegisterFactory("MTX", std::make_shared<MtxFactory>());
this->RegisterFactory("F32", std::make_shared<FloatFactory>());
this->RegisterFactory("LIGHTS", std::make_shared<LightsFactory>());
this->RegisterFactory("GFX", std::make_shared<DListFactory>());
this->RegisterFactory("AUDIO:HEADER", std::make_shared<AudioHeaderFactory>());
@@ -79,6 +84,7 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("MK64:TRACK_WAYPOINTS", std::make_shared<MK64::WaypointsFactory>());
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>());
// SF64 specific
this->RegisterFactory("SF64:ANIM", std::make_shared<SF64::AnimFactory>());
+5 -1
View File
@@ -52,7 +52,11 @@ std::optional<T> GetNode(YAML::Node& node, const std::string& key) {
template<typename T>
T GetSafeNode(YAML::Node& node, const std::string& key) {
if(!node[key]) {
throw std::runtime_error("Failed to find " + key + " in yaml");
if (node["symbol"]) {
throw std::runtime_error("Yaml asset missing the '" + key + "' node for '" + node["symbol"].as<std::string>() + "'");
} else {
throw std::runtime_error("Yaml asset missing the '" + key + "' node");
}
}
return node[key].as<T>();
+102
View File
@@ -0,0 +1,102 @@
#include "FloatFactory.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) std::dec << std::setfill(' ') << std::setw(3) << c
ExportResult FloatHeaderExporter::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 f32 " << symbol << "[];\n";
return std::nullopt;
}
ExportResult FloatCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto f = std::static_pointer_cast<FloatData>(raw)->mFloats;
const auto symbol = GetSafeNode(node, "symbol", entryName);
auto offset = GetSafeNode<uint32_t>(node, "offset");
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
if (Companion::Instance->IsDebug()) {
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
write << "// 0x" << std::hex << std::uppercase << offset << "\n";
}
write << "f32 " << symbol << "[] = {";
/**
* f32 sym[] = {
* 0.1, 0.2, 0.3,
* };
*
*/
for (int i = 0; i < f.size(); ++i) {
// Make a new line every fourth iteration
if ((i % 4) == 0) {
write << "\n" << fourSpaceTab;
}
write << f[i] << ", ";
// if(i <= f.size() - 1) {
// write << fourSpaceTab;
// }
}
write << "\n};\n";
if (Companion::Instance->IsDebug()) {
write << "// count: " << std::to_string(f.size()) << " f32s\n";
write << "// 0x" << std::hex << std::uppercase << (offset + (sizeof(float) * f.size())) << "\n";
}
write << "\n";
return offset + f.size() * sizeof(float);
}
ExportResult FloatBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto f = std::static_pointer_cast<FloatData>(raw);
auto writer = LUS::BinaryWriter();
WriteHeader(writer, LUS::ResourceType::Float, 0);
writer.Write((uint32_t) f->mFloats.size());
for(auto fl : f->mFloats) {
writer.Write(fl);
}
throw std::runtime_error("Float factory untested for otr/o2r exporter");
writer.Finish(write);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> FloatFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto count = GetSafeNode<size_t>(node, "count");
auto [_, segment] = Decompressor::AutoDecode(node, buffer);
LUS::BinaryReader reader(segment.data, count * sizeof(float));
reader.SetEndianness(LUS::Endianness::Big);
std::vector<float> floats;
for(size_t i = 0; i < count; i++) {
auto f = reader.ReadFloat();
floats.push_back(f);
}
return std::make_shared<FloatData>(floats);
}
+37
View File
@@ -0,0 +1,37 @@
#pragma once
#include "BaseFactory.h"
class FloatData : public IParsedData {
public:
std::vector<float> mFloats;
explicit FloatData(std::vector<float> floats) : mFloats(floats) {}
};
class FloatHeaderExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class FloatBinaryExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class FloatCodeExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class FloatFactory : 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, FloatCodeExporter)
REGISTER(Header, FloatHeaderExporter)
REGISTER(Binary, FloatBinaryExporter)
};
}
};
+207
View File
@@ -0,0 +1,207 @@
#include "MtxFactory.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) std::dec << std::setfill(' ') << std::setw(3) << c
ExportResult MtxHeaderExporter::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 Mtx " << symbol << "[];\n";
return std::nullopt;
}
ExportResult MtxCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto m = std::static_pointer_cast<MtxData>(raw)->mMtxs;
const auto symbol = GetSafeNode(node, "symbol", entryName);
auto offset = GetSafeNode<uint32_t>(node, "offset");
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
if (Companion::Instance->IsDebug()) {
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
write << "// 0x" << std::hex << std::uppercase << offset << "\n";
}
#define fiveFourSpaceTabs fourSpaceTab << fourSpaceTab << fourSpaceTab << fourSpaceTab << fourSpaceTab << " "
/**
* toFixedPointMatrix(1.0, 0.0, 0.0, 0.0,
* 0.0, 1.0, 0.0, 0.0,
* 0.0, 0.0, 1.0, 0.0,
* 0.0, 0.0, 0.0, 1.0);
*/
write << "Mtx " << symbol << "[] = {\n";
for (int i = 0; i < m.size(); ++i) {
write << fourSpaceTab << "toFixedPointMatrix(";
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.
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];
}
// Add comma for all but the last arg
if (j < 15) {
write << ", ";
}
// Add closing bracket for last arg in matrix
if (j == 15) {
write << "),\n";
// Do not add an extra \n on the last iteration.
if (i < m.size() - 1) {
write << "\n";
}
break;
}
// Add lots of spaces for start of a new line
if ((j + 1) % 4 == 0) {
write << "\n" << fiveFourSpaceTabs;
}
}
}
write << "};\n";
if (Companion::Instance->IsDebug()) {
write << "// count: " << std::to_string(m.size()) << " Mtxs\n";
write << "// 0x" << std::hex << std::uppercase << (offset + (sizeof(MtxRaw) * m.size())) << "\n";
}
write << "\n";
#undef fiveFourSpaceTabs
return offset + m.size() * sizeof(MtxRaw);
}
ExportResult MtxBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto mtx = std::static_pointer_cast<MtxData>(raw);
auto writer = LUS::BinaryWriter();
WriteHeader(writer, LUS::ResourceType::Matrix, 0);
writer.Write((uint32_t) mtx->mMtxs.size());
for(auto m : mtx->mMtxs) {
writer.Write(m.mtx[0]);
writer.Write(m.mtx[1]);
writer.Write(m.mtx[2]);
writer.Write(m.mtx[3]);
writer.Write(m.mtx[4]);
writer.Write(m.mtx[5]);
writer.Write(m.mtx[6]);
writer.Write(m.mtx[7]);
writer.Write(m.mtx[8]);
writer.Write(m.mtx[9]);
writer.Write(m.mtx[10]);
writer.Write(m.mtx[11]);
writer.Write(m.mtx[12]);
writer.Write(m.mtx[13]);
writer.Write(m.mtx[14]);
writer.Write(m.mtx[15]);
}
throw std::runtime_error("Mtx not tested for otr/o2r.");
writer.Finish(write);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> MtxFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto count = GetSafeNode<size_t>(node, "count");
auto [_, segment] = Decompressor::AutoDecode(node, buffer);
LUS::BinaryReader reader(segment.data, count * sizeof(MtxRaw));
reader.SetEndianness(LUS::Endianness::Big);
std::vector<MtxRaw> matrix;
#define FIXTOF(x) ((double)((x) / 65536.0))
// 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++) {
// Read the integer portion of the fixed-point value (ex. 4)
auto i1 = reader.ReadInt16();
auto i2 = reader.ReadInt16();
auto i3 = reader.ReadInt16();
auto i4 = reader.ReadInt16();
auto i5 = reader.ReadInt16();
auto i6 = reader.ReadInt16();
auto i7 = reader.ReadInt16();
auto i8 = reader.ReadInt16();
auto i9 = reader.ReadInt16();
auto i10 = reader.ReadInt16();
auto i11 = reader.ReadInt16();
auto i12 = reader.ReadInt16();
auto i13 = reader.ReadInt16();
auto i14 = reader.ReadInt16();
auto i15 = reader.ReadInt16();
auto i16 = reader.ReadInt16();
// Read the fractional portion of the fixed-point value (ex. 0.45)
auto f1 = reader.ReadUInt16();
auto f2 = reader.ReadUInt16();
auto f3 = reader.ReadUInt16();
auto f4 = reader.ReadUInt16();
auto f5 = reader.ReadUInt16();
auto f6 = reader.ReadUInt16();
auto f7 = reader.ReadUInt16();
auto f8 = reader.ReadUInt16();
auto f9 = reader.ReadUInt16();
auto f10 = reader.ReadUInt16();
auto f11 = reader.ReadUInt16();
auto f12 = reader.ReadUInt16();
auto f13 = reader.ReadUInt16();
auto f14 = reader.ReadUInt16();
auto f15 = reader.ReadUInt16();
auto f16 = reader.ReadUInt16();
// Place the integer and fractional portions together (ex 4.45) and convert to floating-point
auto m1 = FIXTOF( (int32_t) ( (i1 << 16) | f1 ) );
auto m2 = FIXTOF( (int32_t) ( (i2 << 16) | f2 ) );
auto m3 = FIXTOF( (int32_t) ( (i3 << 16) | f3 ) );
auto m4 = FIXTOF( (int32_t) ( (i4 << 16) | f4 ) );
auto m5 = FIXTOF( (int32_t) ( (i5 << 16) | f5 ) );
auto m6 = FIXTOF( (int32_t) ( (i6 << 16) | f6 ) );
auto m7 = FIXTOF( (int32_t) ( (i7 << 16) | f7 ) );
auto m8 = FIXTOF( (int32_t) ( (i8 << 16) | f8 ) );
auto m9 = FIXTOF( (int32_t) ( (i9 << 16) | f9 ) );
auto m10 = FIXTOF( (int32_t) ( (i10 << 16) | f10 ) );
auto m11 = FIXTOF( (int32_t) ( (i11 << 16) | f11 ) );
auto m12 = FIXTOF( (int32_t) ( (i12 << 16) | f12 ) );
auto m13 = FIXTOF( (int32_t) ( (i13 << 16) | f13 ) );
auto m14 = FIXTOF( (int32_t) ( (i14 << 16) | f14 ) );
auto m15 = FIXTOF( (int32_t) ( (i15 << 16) | f15 ) );
auto m16 = FIXTOF( (int32_t) ( (i16 << 16) | f16 ) );
matrix.push_back(MtxRaw({
m1, m2, m3, m4,
m5, m6, m7, m8,
m9, m10, m11, m12,
m13, m14, m15, m16,
}));
}
#undef FIXTOF
return std::make_shared<MtxData>(matrix);
}
+41
View File
@@ -0,0 +1,41 @@
#pragma once
#include "BaseFactory.h"
struct MtxRaw {
double mtx[16];
};
class MtxData : public IParsedData {
public:
std::vector<MtxRaw> mMtxs;
explicit MtxData(std::vector<MtxRaw> mtxs) : mMtxs(mtxs) {}
};
class MtxHeaderExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class MtxBinaryExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class MtxCodeExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class MtxFactory : 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, MtxCodeExporter)
REGISTER(Header, MtxHeaderExporter)
REGISTER(Binary, MtxBinaryExporter)
};
}
};
+2
View File
@@ -11,6 +11,7 @@ enum class ResourceType {
DisplayList = 0x4F444C54, // ODLT
Vertex = 0x4F565458, // OVTX
Matrix = 0x4F4D5458, // OMTX
Float = 0x4F464C54, // OFLT
Array = 0x4F415252, // OARR
Blob = 0x4F424C42, // OBLB
Texture = 0x4F544558, // OTEX
@@ -31,6 +32,7 @@ enum class ResourceType {
Waypoints = 0x57505453, // WPTS
Metadata = 0x4D444154, // MDAT
SpawnData = 0x53444154, // SDAT
DrivingBehaviour = 0x44424856, // DBHV
// SF64
AnimData = 0x414E494D, // ANIM
+98
View File
@@ -0,0 +1,98 @@
#include "DrivingBehaviour.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) std::dec << std::setfill(' ') << std::setw(3) << c
ExportResult MK64::DrivingBehaviourHeaderExporter::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 DrivingBehaviour " << symbol << "[];\n";
return std::nullopt;
}
ExportResult MK64::DrivingBehaviourCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto bhv = std::static_pointer_cast<DrivingData>(raw);
const auto symbol = GetSafeNode(node, "symbol", entryName);
auto offset = GetSafeNode<uint32_t>(node, "offset");
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
if (Companion::Instance->IsDebug()) {
if (IS_SEGMENTED(offset)) {
offset = SEGMENT_OFFSET(offset);
}
write << "// 0x" << std::hex << std::uppercase << offset << "\n";
}
write << "DrivingBehaviour " << symbol << "[] = {\n";
for(auto b : bhv->mBhvs) {
auto w1 = b.waypoint1;
auto w2 = b.waypoint2;
auto id = b.bhv;
// { w1, w2, bhvId}
write << fourSpaceTab << "{" << NUM(w1) << ", " << NUM(w2) << ", " << NUM(id) << "},\n";
}
write << "};\n";
if (Companion::Instance->IsDebug()) {
write << "// count: " << std::to_string(bhv->mBhvs.size()) << " Entries\n";
write << "// 0x" << std::hex << std::uppercase << (offset + (sizeof(BhvRaw) * bhv->mBhvs.size())) << "\n";
}
write << "\n";
return offset + bhv->mBhvs.size() * sizeof(BhvRaw);
}
ExportResult MK64::DrivingBehaviourBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
auto bhv = std::static_pointer_cast<DrivingData>(raw);
auto writer = LUS::BinaryWriter();
WriteHeader(writer, LUS::ResourceType::DrivingBehaviour, 0);
writer.Write((uint32_t) bhv->mBhvs.size());
for(auto b : bhv->mBhvs) {
writer.Write(b.waypoint1);
writer.Write(b.waypoint2);
writer.Write(b.bhv);
}
writer.Finish(write);
return std::nullopt;
}
std::optional<std::shared_ptr<IParsedData>> MK64::DrivingBehaviourFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
auto [_, segment] = Decompressor::AutoDecode(node, buffer);
LUS::BinaryReader reader(segment.data, segment.size);
reader.SetEndianness(LUS::Endianness::Big);
std::vector<BhvRaw> behaviours;
while(1) {
auto w1 = reader.ReadInt16();
auto w2 = reader.ReadInt16();
auto id = reader.ReadInt32();
behaviours.push_back( BhvRaw( {w1, w2, id} ) );
// Magic number for ending of array
if ((w1 == -1) && (w2 == -1)) {
break;
}
}
return std::make_shared<DrivingData>(behaviours);
}
+46
View File
@@ -0,0 +1,46 @@
#pragma once
#include "../BaseFactory.h"
namespace MK64 {
struct BhvRaw {
int16_t waypoint1;
int16_t waypoint2;
int32_t bhv;
};
class DrivingData : public IParsedData {
public:
std::vector<BhvRaw> mBhvs;
explicit DrivingData(std::vector<BhvRaw> bhvs) : mBhvs(bhvs) {}
};
class DrivingBehaviourHeaderExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class DrivingBehaviourBinaryExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class DrivingBehaviourCodeExporter : public BaseExporter {
ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
};
class DrivingBehaviourFactory : 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, DrivingBehaviourCodeExporter)
REGISTER(Header, DrivingBehaviourHeaderExporter)
REGISTER(Binary, DrivingBehaviourBinaryExporter)
};
}
};
}