Ghost Testing

This commit is contained in:
inspectredc
2025-04-23 07:54:00 -06:00
committed by Lywx
parent 5c3ff9a401
commit dfaad4b3ea
4 changed files with 650 additions and 10 deletions
+2
View File
@@ -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
+238 -7
View File
@@ -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";
return offset;
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 << "{ ";
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>();
}
*/
+25 -3
View File
@@ -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)
+385
View File
@@ -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;
}
}