summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorinspectredc <inspectredc@gmail.com>2025-04-03 16:39:23 +0100
committerLywx <kiritodev01@gmail.com>2025-04-23 07:54:00 -0600
commitdfaad4b3eac5c532b4dea999c484d5f43689bcae (patch)
tree0bfc98955acc8922032ae73e1590f80cc803ce20 /src
parent5c3ff9a40132d3d98211b2d37ad4cb7b6e208bd2 (diff)
Ghost Testing
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp2
-rw-r--r--src/factories/fzerox/GhostRecordFactory.cpp245
-rw-r--r--src/factories/fzerox/GhostRecordFactory.h28
-rw-r--r--src/factories/fzerox/ghost/Ghost.h385
4 files changed, 650 insertions, 10 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index a70f715..1864236 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -72,6 +72,7 @@
#ifdef FZERO_SUPPORT
#include "factories/fzerox/CourseFactory.h"
+#include "factories/fzerox/GhostRecordFactory.h"
#endif
#ifdef NAUDIO_SUPPORT
@@ -165,6 +166,7 @@ void Companion::Init(const ExportType type) {
#ifdef FZERO_SUPPORT
this->RegisterFactory("FZX:COURSE", std::make_shared<FZX::CourseFactory>());
+ this->RegisterFactory("FZX:GHOST", std::make_shared<FZX::GhostRecordFactory>());
#endif
#ifdef NAUDIO_SUPPORT
diff --git a/src/factories/fzerox/GhostRecordFactory.cpp b/src/factories/fzerox/GhostRecordFactory.cpp
index 9536d80..a72a6ed 100644
--- a/src/factories/fzerox/GhostRecordFactory.cpp
+++ b/src/factories/fzerox/GhostRecordFactory.cpp
@@ -1,9 +1,69 @@
#include "GhostRecordFactory.h"
+#include "ghost/Ghost.h"
#include "utils/Decompressor.h"
#include "spdlog/spdlog.h"
#include "Companion.h"
+#define ARRAY_COUNT(arr) (int32_t)(sizeof(arr) / sizeof(arr[0]))
+#define ALIGN16(val) (((val) + 0xF) & ~0xF)
+
+uint16_t FZX::GhostRecordData::Save_CalculateChecksum(void* data, int32_t size) {
+ uint8_t* dataPtr = (uint8_t*)data;
+ uint16_t checksum = 0;
+ int32_t i;
+
+ for (i = 0; i < size; i++) {
+ checksum += *dataPtr++;
+ }
+
+ return checksum;
+}
+
+uint16_t FZX::GhostRecordData::CalculateRecordChecksum(void) {
+ uint16_t checksum = 0;
+ int32_t recordChecksum = CalculateReplayChecksum();
+
+ checksum += Save_CalculateChecksum(&mGhostType, sizeof(mGhostType));
+ checksum += Save_CalculateChecksum(&recordChecksum, sizeof(recordChecksum));
+ checksum += Save_CalculateChecksum(&mCourseEncoding, sizeof(mCourseEncoding));
+ checksum += Save_CalculateChecksum(&mRaceTime, sizeof(mRaceTime));
+ checksum += Save_CalculateChecksum(&mUnk10, sizeof(mUnk10));
+ checksum += Save_CalculateChecksum((void*)mTrackName.c_str(), mTrackName.length());
+ checksum += Save_CalculateChecksum(&mGhostMachineInfo, sizeof(mGhostMachineInfo));
+
+ return checksum;
+}
+
+uint16_t FZX::GhostRecordData::CalculateDataChecksum(void) {
+ uint16_t checksum = 0;
+
+ checksum += Save_CalculateChecksum(&mReplayEnd, sizeof(mReplayEnd));
+ checksum += Save_CalculateChecksum(&mReplaySize, sizeof(mReplaySize));
+
+ for (auto lapTime : mLapTimes) {
+ checksum += Save_CalculateChecksum(&lapTime, sizeof(lapTime));
+ }
+
+ checksum += Save_CalculateChecksum(mReplayData.data(), mReplayData.size());
+
+ return checksum;
+}
+
+int32_t FZX::GhostRecordData::CalculateReplayChecksum(void) {
+ int32_t checksum = 0;
+ int32_t i = 0;
+
+ for (auto dataByte : mReplayData) {
+ checksum += ((uint32_t)(uint8_t)dataByte) << ((3 - i) * 8);
+
+ i++;
+ i %= 4;
+ }
+
+ return checksum;
+}
+
ExportResult FZX::GhostRecordHeaderExporter::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);
@@ -12,7 +72,9 @@ ExportResult FZX::GhostRecordHeaderExporter::Export(std::ostream &write, std::sh
return std::nullopt;
}
- write << "extern GhostRecord " << symbol << ";\n";
+ write << "extern GhostRecord " << symbol << "Record;\n";
+ write << "extern GhostReplayInfo " << symbol << "ReplayInfo;\n";
+ write << "extern s8 " << symbol << "Data[];\n";
return std::nullopt;
}
@@ -22,9 +84,111 @@ ExportResult FZX::GhostRecordCodeExporter::Export(std::ostream &write, std::shar
const auto offset = GetSafeNode<uint32_t>(node, "offset");
const auto record = std::static_pointer_cast<GhostRecordData>(raw);
- write << "GhostRecord " << symbol << " = {\n";
+ write << "GhostRecord " << symbol << "Record = {\n";
+
+ write << fourSpaceTab << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << record->CalculateRecordChecksum() << std::dec << ", /* Checksum */\n";
+
+ write << fourSpaceTab << static_cast<GhostType>(record->mGhostType) << ",\n";
+
+ write << fourSpaceTab << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << record->CalculateReplayChecksum() << std::dec << ", /* Replay Checksum */\n";
+
+ write << fourSpaceTab << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(8) << record->mCourseEncoding << std::dec << ", /* Course Encoding */\n";
+
+ write << fourSpaceTab << record->mRaceTime << ", /* Race Time */ \n";
+
+ write << fourSpaceTab << record->mUnk10 << ", { 0 },\n";
+
+ write << fourSpaceTab << "\"" << record->mTrackName << "\",\n";
+
+ write << fourSpaceTab << " {\n";
+ write << fourSpaceTab << fourSpaceTab << static_cast<Character>(record->mGhostMachineInfo.character) << ", ";
+ write << static_cast<CustomType>(record->mGhostMachineInfo.customType) << ",\n";
+
+ write << fourSpaceTab << fourSpaceTab << static_cast<FrontType>(record->mGhostMachineInfo.frontType) << ", ";
+ write << static_cast<RearType>(record->mGhostMachineInfo.rearType) << ", ";
+ write << static_cast<WingType>(record->mGhostMachineInfo.wingType) << ",\n";
+
+ write << fourSpaceTab << fourSpaceTab << static_cast<Logo>(record->mGhostMachineInfo.frontType) << ", ";
+ write << "MACHINE_NUMBER(" << (uint32_t)record->mGhostMachineInfo.number + 1 << "), ";
+ write << static_cast<Decal>(record->mGhostMachineInfo.decal) << ",\n";
+
+ write << fourSpaceTab << fourSpaceTab << (uint32_t)record->mGhostMachineInfo.bodyR << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.bodyG << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.bodyB << ", /* Body Color */\n";
+ write << fourSpaceTab << fourSpaceTab << (uint32_t)record->mGhostMachineInfo.numberR << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.numberG << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.numberB << ", /* Number Color */\n";
+ write << fourSpaceTab << fourSpaceTab << (uint32_t)record->mGhostMachineInfo.decalR << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.decalG << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.decalB << ", /* Decal Color */\n";
+ write << fourSpaceTab << fourSpaceTab << (uint32_t)record->mGhostMachineInfo.cockpitR << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.cockpitG << ", ";
+ write << (uint32_t)record->mGhostMachineInfo.cockpitB << " /* Cockpit Color */\n";
+ write << fourSpaceTab << "}\n";
+
+ write << "};\n\n";
+
+
+ write << "GhostReplayInfo " << symbol << "ReplayInfo = {\n";
+
+ write << fourSpaceTab << "0x" << std::hex << std::uppercase << std::setfill('0') << std::setw(4) << record->CalculateDataChecksum() << std::dec << ", /* Checksum */\n";
+
+ write << fourSpaceTab << "0,\n";
+
+ write << fourSpaceTab << "{ ";
- return offset;
+ for (int32_t i = 0; i < 3; i++) {
+ write << record->mLapTimes.at(i);
+ if (i != 2) {
+ write << ", ";
+ }
+ }
+
+ write << " },\n";
+
+ write << fourSpaceTab << record->mReplayEnd << ", ";
+
+ write << record->mReplaySize << ",\n";
+
+ write << fourSpaceTab << "0, 0\n";
+
+ write << "};\n\n";
+
+
+ write << "u8 " << symbol << "Data[] = {\n";
+
+ write << fourSpaceTab;
+
+ int32_t counter = 0;
+ for (uint32_t i = 0; i < record->mReplaySize; i++) {
+ int32_t replayData = (int32_t)record->mReplayData.at(i);
+
+ if (replayData == -0x80) {
+ write << "";
+
+ replayData = ((int32_t)record->mReplayData.at(i + 1) << 8) | (int32_t)record->mReplayData.at(i + 2);
+ write << "REPLAY_DATA_LARGE(" << replayData << ")";
+
+ i += 2;
+ } else {
+ write << replayData;
+ }
+
+ if (i != record->mReplaySize - 1) {
+ write << ", ";
+ } else {
+ write << "\n";
+ break;
+ }
+
+ if ((++counter % 3) == 0) {
+ write << "\n" << fourSpaceTab;
+ }
+ }
+
+ write << "};\n\n";
+
+ return offset + 0x20 + 0x40 + ALIGN16(record->mReplaySize);
}
ExportResult FZX::GhostRecordBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement) {
@@ -63,12 +227,78 @@ std::optional<std::shared_ptr<IParsedData>> FZX::GhostRecordFactory::parse(std::
reader.SetEndianness(Torch::Endianness::Big);
- return std::make_shared<GhostRecordData>();
+ // Record
+ uint16_t recordChecksum = reader.ReadInt16();
+ uint16_t ghostType = reader.ReadUInt16();
+ int32_t replayChecksum = reader.ReadInt32();
+ int32_t courseEncoding = reader.ReadInt32();
+ int32_t raceTime = reader.ReadInt32();
+
+ uint16_t unk_10 = reader.ReadInt16();
+ for (int32_t i = 0; i < 5; i++) {
+ reader.ReadInt8();
+ }
+
+ char trackNameBuffer[9];
+ for (int32_t i = 0; i < ARRAY_COUNT(trackNameBuffer); i++) {
+ trackNameBuffer[i] = reader.ReadChar();
+ }
+ std::string trackName(trackNameBuffer);
+
+ GhostMachineInfo ghostMachineInfo;
+
+ ghostMachineInfo.character = reader.ReadUByte();
+ ghostMachineInfo.customType = reader.ReadUByte();
+ ghostMachineInfo.frontType = reader.ReadUByte();
+ ghostMachineInfo.rearType = reader.ReadUByte();
+ ghostMachineInfo.wingType = reader.ReadUByte();
+ ghostMachineInfo.logo = reader.ReadUByte();
+ ghostMachineInfo.number = reader.ReadUByte();
+ ghostMachineInfo.decal = reader.ReadUByte();
+ ghostMachineInfo.bodyR = reader.ReadUByte();
+ ghostMachineInfo.bodyG = reader.ReadUByte();
+ ghostMachineInfo.bodyB = reader.ReadUByte();
+ ghostMachineInfo.numberR = reader.ReadUByte();
+ ghostMachineInfo.numberG = reader.ReadUByte();
+ ghostMachineInfo.numberB = reader.ReadUByte();
+ ghostMachineInfo.decalR = reader.ReadUByte();
+ ghostMachineInfo.decalG = reader.ReadUByte();
+ ghostMachineInfo.decalB = reader.ReadUByte();
+ ghostMachineInfo.cockpitR = reader.ReadUByte();
+ ghostMachineInfo.cockpitG = reader.ReadUByte();
+ ghostMachineInfo.cockpitB = reader.ReadUByte();
+
+ // Data
+ reader.Seek(0x40, LUS::SeekOffsetType::Start);
+
+ uint16_t dataChecksum = reader.ReadInt16();
+
+ reader.ReadInt16();
+
+ std::vector<int32_t> lapTimes;
+
+ for (int32_t i = 0; i < 3; i++) {
+ lapTimes.push_back(reader.ReadInt32());
+ }
+
+ int32_t replayEnd = reader.ReadInt32();
+ uint32_t replaySize = reader.ReadUInt32();
+
+ reader.ReadInt32();
+ reader.ReadInt32();
+
+ std::vector<int8_t> replayData;
+ for (uint32_t i = 0; i < replaySize; i++) {
+ replayData.push_back(reader.ReadInt8());
+ }
+
+ return std::make_shared<GhostRecordData>(ghostType, courseEncoding, raceTime, unk_10, trackName, ghostMachineInfo, lapTimes, replayEnd, replaySize, replayData);
}
+/*
std::optional<std::shared_ptr<IParsedData>> FZX::GhostRecordFactory::parse_modding(std::vector<uint8_t>& buffer, YAML::Node& node) {
YAML::Node assetNode;
-
+
try {
std::string text((char*) buffer.data(), buffer.size());
assetNode = YAML::Load(text.c_str());
@@ -77,8 +307,9 @@ std::optional<std::shared_ptr<IParsedData>> FZX::GhostRecordFactory::parse_moddi
SPDLOG_ERROR("{}", (char*) buffer.data());
return std::nullopt;
}
-
+
const auto info = assetNode.begin()->second;
-
+
return std::make_shared<GhostRecordData>();
}
+*/
diff --git a/src/factories/fzerox/GhostRecordFactory.h b/src/factories/fzerox/GhostRecordFactory.h
index aaf8ae1..3497629 100644
--- a/src/factories/fzerox/GhostRecordFactory.h
+++ b/src/factories/fzerox/GhostRecordFactory.h
@@ -29,14 +29,36 @@ typedef struct GhostMachineInfo {
class GhostRecordData : public IParsedData {
public:
+ // Records
uint16_t mGhostType;
- int32_t mReplayChecksum;
int32_t mCourseEncoding;
int32_t mRaceTime;
+ uint16_t mUnk10;
std::string mTrackName; // empty for normal tracks
GhostMachineInfo mGhostMachineInfo;
+
+ // Data
+ std::vector<int32_t> mLapTimes;
+ int32_t mReplayEnd;
+ uint32_t mReplaySize;
+ std::vector<int8_t> mReplayData;
- GhostRecordData(uint16_t ghostType, int32_t replayChecksum, int32_t courseEncoding, int32_t raceTime, std::string trackName, GhostMachineInfo& ghostMachineInfo) : mGhostType(ghostType), mReplayChecksum(replayChecksum), mCourseEncoding(courseEncoding), mRaceTime(raceTime), mTrackName(trackName), mGhostMachineInfo(ghostMachineInfo) {}
+ GhostRecordData(uint16_t ghostType, int32_t courseEncoding, int32_t raceTime, uint16_t unk_10, std::string trackName, GhostMachineInfo& ghostMachineInfo, std::vector<int32_t> lapTimes, int32_t replayEnd, uint32_t replaySize, std::vector<int8_t> replayData) :
+ mGhostType(ghostType),
+ mCourseEncoding(courseEncoding),
+ mRaceTime(raceTime),
+ mUnk10(unk_10),
+ mTrackName(trackName),
+ mGhostMachineInfo(ghostMachineInfo),
+ mLapTimes(std::move(lapTimes)),
+ mReplayEnd(replayEnd),
+ mReplaySize(replaySize),
+ mReplayData(std::move(replayData)) {}
+
+ uint16_t Save_CalculateChecksum(void* data, int32_t size);
+ uint16_t CalculateRecordChecksum(void);
+ uint16_t CalculateDataChecksum(void);
+ int32_t CalculateReplayChecksum(void);
};
class GhostRecordHeaderExporter : public BaseExporter {
@@ -58,7 +80,7 @@ class GhostRecordModdingExporter : public BaseExporter {
class GhostRecordFactory : 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;
+ // std::optional<std::shared_ptr<IParsedData>> parse_modding(std::vector<uint8_t>& buffer, YAML::Node& data) override;
inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
return {
REGISTER(Code, GhostRecordCodeExporter)
diff --git a/src/factories/fzerox/ghost/Ghost.h b/src/factories/fzerox/ghost/Ghost.h
new file mode 100644
index 0000000..efaf923
--- /dev/null
+++ b/src/factories/fzerox/ghost/Ghost.h
@@ -0,0 +1,385 @@
+#pragma once
+
+#include <string>
+#include <spdlog/spdlog.h>
+#include <spdlog/fmt/ostr.h>
+
+namespace FZX {
+enum class GhostType {
+ /* 0 */ GHOST_NONE,
+ /* 1 */ GHOST_PLAYER,
+ /* 2 */ GHOST_STAFF,
+ /* 3 */ GHOST_CELEBRITY,
+ /* 4 */ GHOST_CHAMP,
+};
+
+enum class Character {
+ /* 00 */ CAPTAIN_FALCON,
+ /* 01 */ DR_STEWART,
+ /* 02 */ PICO,
+ /* 03 */ SAMURAI_GOROH,
+ /* 04 */ JODY_SUMMER,
+ /* 05 */ MIGHTY_GAZELLE,
+ /* 06 */ MR_EAD,
+ /* 07 */ BABA,
+ /* 08 */ OCTOMAN,
+ /* 09 */ GOMAR_AND_SHIOH,
+ /* 10 */ KATE_ALEN,
+ /* 11 */ ROGER_BUSTER,
+ /* 12 */ JAMES_MCCLOUD,
+ /* 13 */ LEON,
+ /* 14 */ ANTONIO_GUSTER,
+ /* 15 */ BLACK_SHADOW,
+ /* 16 */ MICHAEL_CHAIN,
+ /* 17 */ JACK_LEVIN,
+ /* 18 */ SUPER_ARROW,
+ /* 19 */ MRS_ARROW,
+ /* 20 */ JOHN_TANAKA,
+ /* 21 */ BEASTMAN,
+ /* 22 */ ZODA,
+ /* 23 */ DR_CLASH,
+ /* 24 */ SILVER_NEELSEN,
+ /* 25 */ BIO_REX,
+ /* 26 */ DRAQ,
+ /* 27 */ BILLY,
+ /* 28 */ THE_SKULL,
+ /* 29 */ BLOOD_FALCON,
+};
+
+enum class CustomType {
+ /* 0 */ CUSTOM_MACHINE_DEFAULT,
+ /* 1 */ CUSTOM_MACHINE_EDITED,
+ /* 2 */ CUSTOM_MACHINE_SUPER_FALCON,
+ /* 3 */ CUSTOM_MACHINE_SUPER_STINGRAY,
+ /* 4 */ CUSTOM_MACHINE_SUPER_CAT,
+};
+
+enum class FrontType {
+ /* 0 */ FRONT_0,
+ /* 1 */ FRONT_1,
+ /* 2 */ FRONT_2,
+ /* 3 */ FRONT_3,
+ /* 4 */ FRONT_4,
+ /* 5 */ FRONT_5,
+ /* 6 */ FRONT_6,
+};
+
+enum class RearType {
+ /* 0 */ REAR_0,
+ /* 1 */ REAR_1,
+ /* 2 */ REAR_2,
+ /* 3 */ REAR_3,
+ /* 4 */ REAR_4,
+ /* 5 */ REAR_5,
+ /* 6 */ REAR_6,
+};
+
+enum class WingType {
+ /* 0 */ WING_NONE,
+ /* 1 */ WING_1,
+ /* 2 */ WING_2,
+ /* 3 */ WING_3,
+ /* 4 */ WING_4,
+ /* 5 */ WING_5,
+ /* 6 */ WING_6,
+};
+
+enum class Logo {
+ /* 0 */ LOGO_SHIELD,
+ /* 1 */ LOGO_ARROW_PLANE,
+ /* 2 */ LOGO_CIRCLE,
+ /* 3 */ LOGO_SKULL,
+ /* 4 */ LOGO_YELLOW_GREEN,
+ /* 5 */ LOGO_KANJI,
+ /* 6 */ LOGO_X,
+ /* 7 */ LOGO_N64,
+};
+
+enum class Decal {
+ /* 0 */ DECAL_STRIPE,
+ /* 1 */ DECAL_THIN_STRIPE,
+ /* 2 */ DECAL_DOUBLE_STRIPE,
+ /* 3 */ DECAL_TRIPLE_STRIPE_UNEVEN,
+ /* 4 */ DECAL_BLOCK,
+};
+
+
+inline std::ostream& operator<<(std::ostream& out, const GhostType& ghostType) {
+ std::string output;
+ switch (ghostType) {
+ case GhostType::GHOST_NONE:
+ output = "GHOST_NONE";
+ break;
+ case GhostType::GHOST_PLAYER:
+ output = "GHOST_PLAYER";
+ break;
+ case GhostType::GHOST_STAFF:
+ output = "GHOST_STAFF";
+ break;
+ case GhostType::GHOST_CELEBRITY:
+ output = "GHOST_CELEBRITY";
+ break;
+ case GhostType::GHOST_CHAMP:
+ output = "GHOST_CHAMP";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const Character& character) {
+ std::string output;
+ switch (character) {
+ case Character::CAPTAIN_FALCON:
+ output = "CAPTAIN_FALCON";
+ break;
+ case Character::DR_STEWART:
+ output = "DR_STEWART";
+ break;
+ case Character::PICO:
+ output = "PICO";
+ break;
+ case Character::SAMURAI_GOROH:
+ output = "SAMURAI_GOROH";
+ break;
+ case Character::JODY_SUMMER:
+ output = "JODY_SUMMER";
+ break;
+ case Character::MIGHTY_GAZELLE:
+ output = "MIGHTY_GAZELLE";
+ break;
+ case Character::MR_EAD:
+ output = "MR_EAD";
+ break;
+ case Character::BABA:
+ output = "BABA";
+ break;
+ case Character::OCTOMAN:
+ output = "OCTOMAN";
+ break;
+ case Character::GOMAR_AND_SHIOH:
+ output = "GOMAR_AND_SHIOH";
+ break;
+ case Character::KATE_ALEN:
+ output = "KATE_ALEN";
+ break;
+ case Character::ROGER_BUSTER:
+ output = "ROGER_BUSTER";
+ break;
+ case Character::JAMES_MCCLOUD:
+ output = "JAMES_MCCLOUD";
+ break;
+ case Character::LEON:
+ output = "LEON";
+ break;
+ case Character::ANTONIO_GUSTER:
+ output = "ANTONIO_GUSTER";
+ break;
+ case Character::BLACK_SHADOW:
+ output = "BLACK_SHADOW";
+ break;
+ case Character::MICHAEL_CHAIN:
+ output = "MICHAEL_CHAIN";
+ break;
+ case Character::JACK_LEVIN:
+ output = "JACK_LEVIN";
+ break;
+ case Character::SUPER_ARROW:
+ output = "SUPER_ARROW";
+ break;
+ case Character::MRS_ARROW:
+ output = "MRS_ARROW";
+ break;
+ case Character::JOHN_TANAKA:
+ output = "JOHN_TANAKA";
+ break;
+ case Character::BEASTMAN:
+ output = "BEASTMAN";
+ break;
+ case Character::ZODA:
+ output = "ZODA";
+ break;
+ case Character::DR_CLASH:
+ output = "DR_CLASH";
+ break;
+ case Character::SILVER_NEELSEN:
+ output = "SILVER_NEELSEN";
+ break;
+ case Character::BIO_REX:
+ output = "BIO_REX";
+ break;
+ case Character::DRAQ:
+ output = "DRAQ";
+ break;
+ case Character::BILLY:
+ output = "BILLY";
+ break;
+ case Character::THE_SKULL:
+ output = "THE_SKULL";
+ break;
+ case Character::BLOOD_FALCON:
+ output = "BLOOD_FALCON";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const CustomType& customType) {
+ std::string output;
+ switch (customType) {
+ case CustomType::CUSTOM_MACHINE_DEFAULT:
+ output = "CUSTOM_MACHINE_DEFAULT";
+ break;
+ case CustomType::CUSTOM_MACHINE_EDITED:
+ output = "CUSTOM_MACHINE_EDITED";
+ break;
+ case CustomType::CUSTOM_MACHINE_SUPER_FALCON:
+ output = "CUSTOM_MACHINE_SUPER_FALCON";
+ break;
+ case CustomType::CUSTOM_MACHINE_SUPER_STINGRAY:
+ output = "CUSTOM_MACHINE_SUPER_STINGRAY";
+ break;
+ case CustomType::CUSTOM_MACHINE_SUPER_CAT:
+ output = "CUSTOM_MACHINE_SUPER_CAT";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const FrontType& frontType) {
+ std::string output;
+ switch (frontType) {
+ case FrontType::FRONT_0:
+ output = "FRONT_0";
+ break;
+ case FrontType::FRONT_1:
+ output = "FRONT_1";
+ break;
+ case FrontType::FRONT_2:
+ output = "FRONT_2";
+ break;
+ case FrontType::FRONT_3:
+ output = "FRONT_3";
+ break;
+ case FrontType::FRONT_4:
+ output = "FRONT_4";
+ break;
+ case FrontType::FRONT_5:
+ output = "FRONT_5";
+ break;
+ case FrontType::FRONT_6:
+ output = "FRONT_6";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const RearType rearType) {
+ std::string output;
+ switch (rearType) {
+ case RearType::REAR_0:
+ output = "REAR_0";
+ break;
+ case RearType::REAR_1:
+ output = "REAR_1";
+ break;
+ case RearType::REAR_2:
+ output = "REAR_2";
+ break;
+ case RearType::REAR_3:
+ output = "REAR_3";
+ break;
+ case RearType::REAR_4:
+ output = "REAR_4";
+ break;
+ case RearType::REAR_5:
+ output = "REAR_5";
+ break;
+ case RearType::REAR_6:
+ output = "REAR_6";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const WingType& wingType) {
+ std::string output;
+ switch (wingType) {
+ case WingType::WING_NONE:
+ output = "WING_NONE";
+ break;
+ case WingType::WING_1:
+ output = "WING_1";
+ break;
+ case WingType::WING_2:
+ output = "WING_2";
+ break;
+ case WingType::WING_3:
+ output = "WING_3";
+ break;
+ case WingType::WING_4:
+ output = "WING_4";
+ break;
+ case WingType::WING_5:
+ output = "WING_5";
+ break;
+ case WingType::WING_6:
+ output = "WING_6";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const Logo& logo) {
+ std::string output;
+ switch (logo) {
+ case Logo::LOGO_SHIELD:
+ output = "LOGO_SHIELD";
+ break;
+ case Logo::LOGO_ARROW_PLANE:
+ output = "LOGO_ARROW_PLANE";
+ break;
+ case Logo::LOGO_CIRCLE:
+ output = "LOGO_CIRCLE";
+ break;
+ case Logo::LOGO_SKULL:
+ output = "LOGO_SKULL";
+ break;
+ case Logo::LOGO_YELLOW_GREEN:
+ output = "LOGO_YELLOW_GREEN";
+ break;
+ case Logo::LOGO_KANJI:
+ output = "LOGO_KANJI";
+ break;
+ case Logo::LOGO_X:
+ output = "LOGO_X";
+ break;
+ case Logo::LOGO_N64:
+ output = "LOGO_N64";
+ break;
+ }
+ return out << output;
+}
+
+inline std::ostream& operator<<(std::ostream& out, const Decal& decal) {
+ std::string output;
+ switch (decal) {
+ case Decal::DECAL_STRIPE:
+ output = "DECAL_STRIPE";
+ break;
+ case Decal::DECAL_THIN_STRIPE:
+ output = "DECAL_THIN_STRIPE";
+ break;
+ case Decal::DECAL_DOUBLE_STRIPE:
+ output = "DECAL_DOUBLE_STRIPE";
+ break;
+ case Decal::DECAL_TRIPLE_STRIPE_UNEVEN:
+ output = "DECAL_TRIPLE_STRIPE_UNEVEN";
+ break;
+ case Decal::DECAL_BLOCK:
+ output = "DECAL_BLOCK";
+ break;
+ }
+ return out << output;
+}
+
+}