summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorinspectredc <inspectredc@gmail.com>2025-01-02 05:02:43 +0000
committerLywx <kiritodev01@gmail.com>2025-01-03 11:05:00 -0600
commit31ab0a91d0aa75909d6db77e3e60064c471fef99 (patch)
tree3c5fe6d020eb1d52f41171d15bf9e9a790347ee6 /src
parentbdcdd4a4ea2f293be7c26bbca1ada91560182780 (diff)
Initial Course Factory
Diffstat (limited to 'src')
-rw-r--r--src/Companion.cpp5
-rw-r--r--src/factories/ResourceType.h3
-rw-r--r--src/factories/fzerox/CourseFactory.cpp346
-rw-r--r--src/factories/fzerox/CourseFactory.h103
4 files changed, 457 insertions, 0 deletions
diff --git a/src/Companion.cpp b/src/Companion.cpp
index 7181cd1..2921b8f 100644
--- a/src/Companion.cpp
+++ b/src/Companion.cpp
@@ -63,6 +63,8 @@
#include "factories/sf64/ObjInitFactory.h"
#include "factories/sf64/TriangleFactory.h"
+#include "factories/fzerox/CourseFactory.h"
+
#include "factories/naudio/v0/AudioHeaderFactory.h"
#include "factories/naudio/v0/BankFactory.h"
#include "factories/naudio/v0/SampleFactory.h"
@@ -149,6 +151,9 @@ void Companion::Init(const ExportType type) {
this->RegisterFactory("SF64:COLPOLY", std::make_shared<SF64::ColPolyFactory>());
this->RegisterFactory("SF64:TRIANGLE", std::make_shared<SF64::TriangleFactory>());
+ // F-Zero X specific
+ this->RegisterFactory("FZX:COURSE", std::make_shared<FZX::CourseFactory>());
+
// NAudio specific
this->RegisterFactory("NAUDIO:V0:AUDIO_HEADER", std::make_shared<AudioHeaderFactory>());
this->RegisterFactory("NAUDIO:V0:SEQUENCE", std::make_shared<SequenceFactory>());
diff --git a/src/factories/ResourceType.h b/src/factories/ResourceType.h
index b92be30..5758e9d 100644
--- a/src/factories/ResourceType.h
+++ b/src/factories/ResourceType.h
@@ -61,6 +61,9 @@ enum class ResourceType {
Hitbox = 0x48544258, // HTBX
ObjectInit = 0x4F42494E, // OBIN
+ // F-ZERO X
+ CourseData = 0x58435253, // XCRS
+
// NAudio v0
Bank = 0x42414E4B, // BANK
Sample = 0x41554643, // AIFC
diff --git a/src/factories/fzerox/CourseFactory.cpp b/src/factories/fzerox/CourseFactory.cpp
new file mode 100644
index 0000000..ba763a9
--- /dev/null
+++ b/src/factories/fzerox/CourseFactory.cpp
@@ -0,0 +1,346 @@
+#include "CourseFactory.h"
+
+#include "utils/Decompressor.h"
+#include "spdlog/spdlog.h"
+#include "Companion.h"
+#include <types/Vec3D.h>
+
+#define ARRAY_COUNT(arr) (int32_t)(sizeof(arr) / sizeof(arr[0]))
+
+#define TRACK_SEGMENT_JOINT_MASK 0x600
+#define TRACK_SEGMENT_FORM_MASK 0x38000
+#define TRACK_SEGMENT_FLAG_CONTINUE 0x40000000
+
+#define FORMAT_FLOAT(x, w, p) std::dec << std::setfill(' ') << std::fixed << std::setprecision(p) << std::setw(w) << x
+
+uint32_t FZX::CourseData::CalculateChecksum(void) {
+ uint32_t checksum = mControlPointInfos.size();
+ int32_t counter = 0;
+
+ for (const auto& controlPointInfo : mControlPointInfos) {
+ int32_t trackSegmentInfo = controlPointInfo.controlPoint.trackSegmentInfo;
+ trackSegmentInfo &= ~TRACK_SEGMENT_JOINT_MASK;
+ trackSegmentInfo &= ~TRACK_SEGMENT_FORM_MASK;
+ trackSegmentInfo &= ~TRACK_SEGMENT_FLAG_CONTINUE;
+
+ checksum += (int32_t) ((controlPointInfo.controlPoint.pos.x + ((1.1f + (0.7f * counter)) * controlPointInfo.controlPoint.pos.y)) +
+ ((2.2f + (1.2f * counter)) * controlPointInfo.controlPoint.pos.z * (4.4f + (0.9f * counter))) +
+ controlPointInfo.controlPoint.radiusLeft + ((5.5f + (0.8f * counter)) * controlPointInfo.controlPoint.radiusRight * 4.8f)) +
+ trackSegmentInfo * (0xFE - counter) + controlPointInfo.bankAngle * (0x93DE - counter * 2);
+ checksum += (controlPointInfo.pit * counter);
+ checksum += (controlPointInfo.boost * (counter + 0x10));
+ checksum += (controlPointInfo.dirt * (counter + 0x80));
+ checksum += (controlPointInfo.ice * (counter + 0x100));
+ checksum += (controlPointInfo.jump * (counter + 0x800));
+ checksum += (controlPointInfo.landmine * (counter + 0x1000));
+ checksum += (controlPointInfo.gate * (counter + 0x8000));
+ checksum += (controlPointInfo.building * (counter + 0x10000));
+ checksum += (controlPointInfo.sign * (counter + 0x80000));
+ counter++;
+ }
+
+
+ return checksum;
+}
+
+ExportResult FZX::CourseHeaderExporter::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 ALIGN_ASSET(2) char " << symbol << "[] = \"__OTR__" << (*replacement) << "\";\n\n";
+ return std::nullopt;
+ }
+
+ write << "extern CourseData " << symbol << ";\n";
+
+ return std::nullopt;
+}
+
+ExportResult FZX::CourseCodeExporter::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);
+ const auto offset = GetSafeNode<uint32_t>(node, "offset");
+ auto course = std::static_pointer_cast<CourseData>(raw);
+ size_t controlPointCount = course->mControlPointInfos.size();
+
+ write << "CourseData " << symbol << " = {\n";
+
+ write << std::dec;
+ write << fourSpaceTab << (int32_t)course->mCreatorId << ", /* Creator Id */\n";
+ write << fourSpaceTab << (int32_t)controlPointCount << ", /* Control Point Count */\n";
+ write << fourSpaceTab << (int32_t)course->mVenue << ", /* Venue */\n";
+ write << fourSpaceTab << (int32_t)course->mSkybox << ", /* Skybox */\n";
+ write << fourSpaceTab << "0x" << std::hex << std::uppercase << course->CalculateChecksum() << std::dec << ", /* Checksum */\n";
+ write << fourSpaceTab << (int32_t)course->mFlag << ", /* Flag */\n";
+
+ write << fourSpaceTab << "\"" << course->mFileName;
+ for (size_t i = 0; i < 22 - course->mFileName.length(); i++) {
+ write << "\\0";
+ }
+ write << "\", /* File Name */\n";
+
+ write << fourSpaceTab << "{ /* Control Points */\n";
+
+ for (size_t i = 0; i < 64; i++) {
+ write << fourSpaceTab << fourSpaceTab;
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ Vec3f pos(controlPointInfo.controlPoint.pos.x, controlPointInfo.controlPoint.pos.y, controlPointInfo.controlPoint.pos.z);
+ write << "{ { " << FORMAT_FLOAT(pos, 6, 1) << "}, ";
+ write << controlPointInfo.controlPoint.radiusLeft << ", ";
+ write << controlPointInfo.controlPoint.radiusRight << ", ";
+ write << "0x" << std::hex << controlPointInfo.controlPoint.trackSegmentInfo << std::dec;
+ write << " },\n";
+
+ } else {
+ write << "{ 0 },\n";
+ }
+ }
+ write << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Bank Angle */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << controlPointInfo.bankAngle << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Pit */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.pit << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Boost */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.boost << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Dirt */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.dirt << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Ice */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.ice << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Jump */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.jump << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Landmine */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.landmine << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Gate */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.gate << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Building */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.building << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "},\n";
+
+ write << fourSpaceTab << "{ /* Sign */";
+ for (size_t i = 0; i < 64; i++) {
+ if ((i % 16) == 0) {
+ write << "\n" << fourSpaceTab << fourSpaceTab;
+ }
+
+ if (i < controlPointCount) {
+ const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
+ write << (int32_t)controlPointInfo.sign << ", ";
+ } else {
+ write << "0, ";
+ }
+ }
+ write << "\n" << fourSpaceTab << "}\n";
+ write << "};\n\n";
+
+ return offset + sizeof(CourseRawData);
+}
+
+ExportResult FZX::CourseBinaryExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
+ auto writer = LUS::BinaryWriter();
+ auto course = std::static_pointer_cast<CourseData>(raw);
+ int8_t controlPointCount = (int8_t)course->mControlPointInfos.size();
+
+ WriteHeader(writer, Torch::ResourceType::CourseData, 0);
+
+ writer.Write(course->mCreatorId);
+ writer.Write(controlPointCount);
+ writer.Write(course->mVenue);
+ writer.Write(course->mSkybox);
+ writer.Write(course->CalculateChecksum());
+ writer.Write(course->mFlag);
+ writer.Write(course->mFileName);
+ for (const auto& controlPointInfo : course->mControlPointInfos) {
+ writer.Write(controlPointInfo.controlPoint.pos.x);
+ writer.Write(controlPointInfo.controlPoint.pos.y);
+ writer.Write(controlPointInfo.controlPoint.pos.z);
+ writer.Write(controlPointInfo.controlPoint.radiusLeft);
+ writer.Write(controlPointInfo.controlPoint.radiusRight);
+ writer.Write(controlPointInfo.controlPoint.trackSegmentInfo);
+ writer.Write(controlPointInfo.bankAngle);
+ writer.Write(controlPointInfo.pit);
+ writer.Write(controlPointInfo.boost);
+ writer.Write(controlPointInfo.dirt);
+ writer.Write(controlPointInfo.ice);
+ writer.Write(controlPointInfo.jump);
+ writer.Write(controlPointInfo.landmine);
+ writer.Write(controlPointInfo.gate);
+ writer.Write(controlPointInfo.building);
+ writer.Write(controlPointInfo.sign);
+ }
+
+ writer.Finish(write);
+ return std::nullopt;
+}
+
+std::optional<std::shared_ptr<IParsedData>> FZX::CourseFactory::parse(std::vector<uint8_t>& buffer, YAML::Node& node) {
+ auto [_, segment] = Decompressor::AutoDecode(node, buffer);
+ LUS::BinaryReader reader(segment.data, segment.size);
+
+ reader.SetEndianness(Torch::Endianness::Big);
+
+ int8_t creatorId = reader.ReadInt8();
+ int8_t controlPointCount = reader.ReadInt8();
+ int8_t venue = reader.ReadInt8();
+ int8_t skybox = reader.ReadInt8();
+ uint32_t checksum = reader.ReadUInt32();
+ int8_t flag = reader.ReadInt8();
+ char nameBuffer[23];
+
+ for (int32_t i = 0; i < ARRAY_COUNT(nameBuffer); i++) {
+ nameBuffer[i] = reader.ReadInt8();
+ }
+
+ std::string fileName(nameBuffer);
+ std::vector<ControlPointInfo> controlPointInfos;
+
+ for (int8_t i = 0; i < controlPointCount; i++) {
+ ControlPointInfo controlPointInfo;
+
+ reader.Seek(0x020 + i * sizeof(ControlPoint), LUS::SeekOffsetType::Start);
+ controlPointInfo.controlPoint.pos.x = reader.ReadFloat();
+ controlPointInfo.controlPoint.pos.y = reader.ReadFloat();
+ controlPointInfo.controlPoint.pos.z = reader.ReadFloat();
+ controlPointInfo.controlPoint.radiusLeft = reader.ReadInt16();
+ controlPointInfo.controlPoint.radiusRight = reader.ReadInt16();
+ controlPointInfo.controlPoint.trackSegmentInfo = reader.ReadInt32();
+ reader.Seek(0x520 + i * sizeof(int16_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.bankAngle = reader.ReadInt16();
+ reader.Seek(0x5A0 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.pit = reader.ReadInt8();
+ reader.Seek(0x5E0 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.boost = reader.ReadInt8();
+ reader.Seek(0x620 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.dirt = reader.ReadInt8();
+ reader.Seek(0x660 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.ice = reader.ReadInt8();
+ reader.Seek(0x6A0 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.jump = reader.ReadInt8();
+ reader.Seek(0x6E0 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.landmine = reader.ReadInt8();
+ reader.Seek(0x720 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.gate = reader.ReadInt8();
+ reader.Seek(0x760 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.building = reader.ReadInt8();
+ reader.Seek(0x7A0 + i * sizeof(int8_t), LUS::SeekOffsetType::Start);
+ controlPointInfo.sign = reader.ReadInt8();
+ controlPointInfos.push_back(controlPointInfo);
+ }
+
+ return std::make_shared<CourseData>(creatorId, venue, skybox, flag, fileName, controlPointInfos);
+}
diff --git a/src/factories/fzerox/CourseFactory.h b/src/factories/fzerox/CourseFactory.h
new file mode 100644
index 0000000..f19ad01
--- /dev/null
+++ b/src/factories/fzerox/CourseFactory.h
@@ -0,0 +1,103 @@
+#pragma once
+
+#include <factories/BaseFactory.h>
+
+namespace FZX {
+
+// In-Game Structs
+typedef struct Vec3fStruct {
+ float x, y, z;
+} Vec3fStruct;
+
+typedef struct ControlPoint {
+ Vec3fStruct pos;
+ int16_t radiusLeft;
+ int16_t radiusRight;
+ int32_t trackSegmentInfo;
+} ControlPoint; // size = 0x14
+
+typedef struct CourseRawData {
+ int8_t creatorId;
+ int8_t controlPointCount;
+ int8_t venue;
+ int8_t skybox;
+ uint32_t checksum;
+ int8_t flag;
+ char fileName[23];
+ ControlPoint controlPoint[64];
+ int16_t bankAngle[64];
+ int8_t pit[64];
+ int8_t boost[64];
+ int8_t dirt[64];
+ int8_t ice[64];
+ int8_t jump[64];
+ int8_t landmine[64];
+ int8_t gate[64];
+ int8_t building[64];
+ int8_t sign[64];
+} CourseRawData; // size = 0x7E0
+
+// Factory Structs
+typedef struct ControlPointInfo {
+ ControlPoint controlPoint;
+ int16_t bankAngle;
+ int8_t pit;
+ int8_t boost;
+ int8_t dirt;
+ int8_t ice;
+ int8_t jump;
+ int8_t landmine;
+ int8_t gate;
+ int8_t building;
+ int8_t sign;
+} ControlPointInfo;
+
+class CourseData : public IParsedData {
+public:
+ int8_t mCreatorId;
+ int8_t mVenue;
+ int8_t mSkybox;
+ int8_t mFlag;
+ std::string mFileName;
+ std::vector<ControlPointInfo> mControlPointInfos;
+
+ CourseData(int8_t creatorId, int8_t venue, int8_t skybox, int8_t flag, std::string fileName, std::vector<ControlPointInfo> controlPointInfos) :
+ mCreatorId(creatorId),
+ mVenue(venue),
+ mSkybox(skybox),
+ mFlag(flag),
+ mFileName(std::move(fileName)),
+ mControlPointInfos(std::move(controlPointInfos)) {}
+
+ uint32_t CalculateChecksum(void);
+};
+
+class CourseHeaderExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class CourseBinaryExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+class CourseCodeExporter : public BaseExporter {
+ ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+};
+
+// class CourseModdingExporter : public BaseExporter {
+// ExportResult Export(std::ostream& write, std::shared_ptr<IParsedData> data, std::string& entryName, YAML::Node& node, std::string* replacement) override;
+// };
+
+class CourseFactory : public BaseFactory {
+public:
+ std::optional<std::shared_ptr<IParsedData>> parse(std::vector<uint8_t>& buffer, YAML::Node& data) override;
+ inline std::unordered_map<ExportType, std::shared_ptr<BaseExporter>> GetExporters() override {
+ return {
+ REGISTER(Code, CourseCodeExporter)
+ REGISTER(Header, CourseHeaderExporter)
+ REGISTER(Binary, CourseBinaryExporter)
+ // REGISTER(Modding, CourseModdingExporter)
+ };
+ }
+};
+} // namespace FZX