Initial Course Factory

This commit is contained in:
inspectredc
2025-01-03 11:05:00 -06:00
committed by Lywx
parent bdcdd4a4ea
commit 31ab0a91d0
4 changed files with 457 additions and 0 deletions
+5
View File
@@ -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>());
+3
View File
@@ -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
+346
View File
@@ -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);
}
+103
View File
@@ -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