diff options
| author | inspectredc <inspectredc@gmail.com> | 2025-01-02 05:02:43 +0000 |
|---|---|---|
| committer | Lywx <kiritodev01@gmail.com> | 2025-01-03 11:05:00 -0600 |
| commit | 31ab0a91d0aa75909d6db77e3e60064c471fef99 (patch) | |
| tree | 3c5fe6d020eb1d52f41171d15bf9e9a790347ee6 /src | |
| parent | bdcdd4a4ea2f293be7c26bbca1ada91560182780 (diff) | |
Initial Course Factory
Diffstat (limited to 'src')
| -rw-r--r-- | src/Companion.cpp | 5 | ||||
| -rw-r--r-- | src/factories/ResourceType.h | 3 | ||||
| -rw-r--r-- | src/factories/fzerox/CourseFactory.cpp | 346 | ||||
| -rw-r--r-- | src/factories/fzerox/CourseFactory.h | 103 |
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 |
