Modding support

This commit is contained in:
inspectredc
2025-01-03 11:05:00 -06:00
committed by Lywx
parent fdc2a4164c
commit 9ab2dee1f0
2 changed files with 239 additions and 17 deletions
+231 -11
View File
@@ -29,7 +29,7 @@ uint32_t FZX::CourseData::CalculateChecksum(void) {
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.dash * (counter + 0x10));
checksum += (controlPointInfo.dirt * (counter + 0x80));
checksum += (controlPointInfo.ice * (counter + 0x100));
checksum += (controlPointInfo.jump * (counter + 0x800));
@@ -57,10 +57,10 @@ ExportResult FZX::CourseHeaderExporter::Export(std::ostream &write, std::shared_
return std::nullopt;
}
ExportResult FZX::CourseCodeExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement ) {
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);
const auto course = std::static_pointer_cast<CourseData>(raw);
size_t controlPointCount = course->mControlPointInfos.size();
write << "CourseData " << symbol << " = {\n";
@@ -74,8 +74,12 @@ ExportResult FZX::CourseCodeExporter::Export(std::ostream &write, std::shared_pt
write << fourSpaceTab << (int32_t)course->mFlag << ", /* Flag */\n";
write << fourSpaceTab << "\"" << course->mFileName << std::hex;
for (size_t i = 0; i < 22 - course->mFileName.length(); i++) {
write << "\\x" << FORMAT_HEX((uint32_t)(uint8_t)course->mFileNameExtra.at(i), 2);
for (int32_t i = 0; i < 22 - course->mFileName.length(); i++) {
if (i < (int32_t)course->mFileNameExtra.size() - 1) {
write << "\\x" << FORMAT_HEX((uint32_t)(uint8_t)course->mFileNameExtra.at(i), 2);
} else {
write << "\\x00";
}
}
write << std::dec << "\", /* File Name */\n";
@@ -128,7 +132,7 @@ ExportResult FZX::CourseCodeExporter::Export(std::ostream &write, std::shared_pt
}
write << "\n" << fourSpaceTab << "},\n";
write << fourSpaceTab << "{ /* Boost */";
write << fourSpaceTab << "{ /* Dash */";
for (size_t i = 0; i < 64; i++) {
if ((i % 16) == 0) {
write << "\n" << fourSpaceTab << fourSpaceTab;
@@ -136,7 +140,7 @@ ExportResult FZX::CourseCodeExporter::Export(std::ostream &write, std::shared_pt
if (i < controlPointCount) {
const ControlPointInfo& controlPointInfo = course->mControlPointInfos.at(i);
write << (int32_t)controlPointInfo.boost << ", ";
write << (int32_t)controlPointInfo.dash << ", ";
} else {
write << "0, ";
}
@@ -252,9 +256,9 @@ ExportResult FZX::CourseCodeExporter::Export(std::ostream &write, std::shared_pt
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 ) {
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);
const auto course = std::static_pointer_cast<CourseData>(raw);
int8_t controlPointCount = (int8_t)course->mControlPointInfos.size();
WriteHeader(writer, Torch::ResourceType::CourseData, 0);
@@ -266,6 +270,7 @@ ExportResult FZX::CourseBinaryExporter::Export(std::ostream &write, std::shared_
writer.Write(course->CalculateChecksum());
writer.Write(course->mFlag);
writer.Write(course->mFileName);
writer.Write((uint32_t)course->mFileNameExtra.size());
for (const auto extra : course->mFileNameExtra) {
writer.Write(extra);
}
@@ -278,7 +283,7 @@ ExportResult FZX::CourseBinaryExporter::Export(std::ostream &write, std::shared_
writer.Write(controlPointInfo.controlPoint.trackSegmentInfo);
writer.Write(controlPointInfo.bankAngle);
writer.Write(controlPointInfo.pit);
writer.Write(controlPointInfo.boost);
writer.Write(controlPointInfo.dash);
writer.Write(controlPointInfo.dirt);
writer.Write(controlPointInfo.ice);
writer.Write(controlPointInfo.jump);
@@ -292,6 +297,119 @@ ExportResult FZX::CourseBinaryExporter::Export(std::ostream &write, std::shared_
return std::nullopt;
}
ExportResult FZX::CourseModdingExporter::Export(std::ostream &write, std::shared_ptr<IParsedData> raw, std::string& entryName, YAML::Node &node, std::string* replacement) {
const auto course = std::static_pointer_cast<CourseData>(raw);
const auto symbol = GetSafeNode(node, "symbol", entryName);
*replacement += ".yaml";
std::stringstream stream;
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << symbol;
out << YAML::Value;
out.SetIndent(2);
out << YAML::BeginMap;
out << YAML::Key << "CreatorId";
out << YAML::Value << course->mCreatorId;
out << YAML::Key << "Venue";
out << YAML::Value << course->mVenue;
out << YAML::Key << "Skybox";
out << YAML::Value << course->mSkybox;
out << YAML::Key << "Flag";
out << YAML::Value << course->mFlag;
out << YAML::Key << "Name";
out << YAML::Value << course->mFileName;
if (course->mFileNameExtra.size() > 1) {
out << YAML::Key << "NameExtra";
out << YAML::Value << YAML::Flow << YAML::BeginSeq;
for (size_t i = 0; i < course->mFileNameExtra.size() - 1; i++) {
out << YAML::Hex << (uint32_t)(uint8_t)course->mFileNameExtra.at(i);
}
out << YAML::EndSeq << YAML::Block << YAML::Dec;
}
out << YAML::Key << "ControlPoints";
out << YAML::Value << YAML::BeginSeq;
for (const auto& controlPointInfo : course->mControlPointInfos) {
out << YAML::BeginMap;
out << YAML::Key << "PosX";
out << YAML::Value << controlPointInfo.controlPoint.pos.x;
out << YAML::Key << "PosY";
out << YAML::Value << controlPointInfo.controlPoint.pos.y;
out << YAML::Key << "PosZ";
out << YAML::Value << controlPointInfo.controlPoint.pos.z;
out << YAML::Key << "RadiusLeft";
out << YAML::Value << controlPointInfo.controlPoint.radiusLeft;
out << YAML::Key << "RadiusRight";
out << YAML::Value << controlPointInfo.controlPoint.radiusRight;
out << YAML::Key << "TrackSegmentInfo";
out << YAML::Value << YAML::Hex << controlPointInfo.controlPoint.trackSegmentInfo << YAML::Dec;
// Default to 0
if (controlPointInfo.bankAngle != 0) {
out << YAML::Key << "BankAngle";
out << YAML::Value << controlPointInfo.bankAngle;
}
// Remaining Default to -1
if (controlPointInfo.pit != -1) {
out << YAML::Key << "Pit";
out << YAML::Value << controlPointInfo.pit;
}
if (controlPointInfo.dash != -1) {
out << YAML::Key << "Dash";
out << YAML::Value << controlPointInfo.dash;
}
if (controlPointInfo.dirt != -1) {
out << YAML::Key << "Dirt";
out << YAML::Value << controlPointInfo.dirt;
}
if (controlPointInfo.ice != -1) {
out << YAML::Key << "Ice";
out << YAML::Value << controlPointInfo.ice;
}
if (controlPointInfo.jump != -1) {
out << YAML::Key << "Jump";
out << YAML::Value << controlPointInfo.jump;
}
if (controlPointInfo.landmine != -1) {
out << YAML::Key << "Landmine";
out << YAML::Value << controlPointInfo.landmine;
}
if (controlPointInfo.gate != -1) {
out << YAML::Key << "Gate";
out << YAML::Value << controlPointInfo.gate;
}
if (controlPointInfo.building != -1) {
out << YAML::Key << "Building";
out << YAML::Value << controlPointInfo.building;
}
if (controlPointInfo.sign != -1) {
out << YAML::Key << "Sign";
out << YAML::Value << controlPointInfo.sign;
}
out << YAML::EndMap;
}
out << YAML::EndMap;
out << YAML::EndMap;
write.write(out.c_str(), out.size());
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);
@@ -333,7 +451,7 @@ std::optional<std::shared_ptr<IParsedData>> FZX::CourseFactory::parse(std::vecto
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();
controlPointInfo.dash = 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);
@@ -353,3 +471,105 @@ std::optional<std::shared_ptr<IParsedData>> FZX::CourseFactory::parse(std::vecto
return std::make_shared<CourseData>(creatorId, venue, skybox, flag, fileName, fileNameExtra, controlPointInfos);
}
std::optional<std::shared_ptr<IParsedData>> FZX::CourseFactory::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());
} catch (YAML::ParserException& e) {
SPDLOG_ERROR("Failed to parse message data: {}", e.what());
SPDLOG_ERROR("{}", (char*) buffer.data());
return std::nullopt;
}
const auto info = assetNode.begin()->second;
int8_t creatorId = info["CreatorId"].as<int8_t>();
int8_t venue = info["Venue"].as<int8_t>();
int8_t skybox = info["Skybox"].as<int8_t>();
int8_t flag = info["Flag"].as<int8_t>();
std::string fileName = info["Name"].as<std::string>();
std::vector<int8_t> fileNameExtra;
if (info["NameExtra"]) {
auto nameExtra = info["NameExtra"];
for (YAML::iterator it = nameExtra.begin(); it != nameExtra.end(); ++it) {
fileNameExtra.push_back((int8_t)(*it).as<uint8_t>());
}
}
std::vector<ControlPointInfo> controlPointInfos;
auto controlPoints = info["ControlPoints"];
for (YAML::iterator it = controlPoints.begin(); it != controlPoints.end(); ++it) {
const YAML::Node& controlPointNode = *it;
ControlPointInfo controlPointInfo;
controlPointInfo.controlPoint.pos.x = controlPointNode["PosX"].as<float>();
controlPointInfo.controlPoint.pos.y = controlPointNode["PosY"].as<float>();
controlPointInfo.controlPoint.pos.z = controlPointNode["PosZ"].as<float>();
controlPointInfo.controlPoint.radiusLeft = controlPointNode["RadiusLeft"].as<int16_t>();
controlPointInfo.controlPoint.radiusRight = controlPointNode["RadiusRight"].as<int16_t>();
controlPointInfo.controlPoint.trackSegmentInfo = controlPointNode["TrackSegmentInfo"].as<int32_t>();
if (controlPointNode["BankAngle"]) {
controlPointInfo.bankAngle = controlPointNode["BankAngle"].as<int16_t>();
} else {
controlPointInfo.bankAngle = 0;
}
if (controlPointNode["Pit"]) {
controlPointInfo.pit = controlPointNode["Pit"].as<int8_t>();
} else {
controlPointInfo.pit = -1;
}
if (controlPointNode["Dash"]) {
controlPointInfo.dash = controlPointNode["Dash"].as<int8_t>();
} else {
controlPointInfo.dash = -1;
}
if (controlPointNode["Dirt"]) {
controlPointInfo.dirt = controlPointNode["Dirt"].as<int8_t>();
} else {
controlPointInfo.dirt = -1;
}
if (controlPointNode["Ice"]) {
controlPointInfo.ice = controlPointNode["Ice"].as<int8_t>();
} else {
controlPointInfo.ice = -1;
}
if (controlPointNode["Jump"]) {
controlPointInfo.jump = controlPointNode["Jump"].as<int8_t>();
} else {
controlPointInfo.jump = -1;
}
if (controlPointNode["Landmine"]) {
controlPointInfo.landmine = controlPointNode["Landmine"].as<int8_t>();
} else {
controlPointInfo.landmine = -1;
}
if (controlPointNode["Gate"]) {
controlPointInfo.gate = controlPointNode["Gate"].as<int8_t>();
} else {
controlPointInfo.gate = -1;
}
if (controlPointNode["Building"]) {
controlPointInfo.building = controlPointNode["Building"].as<int8_t>();
} else {
controlPointInfo.building = -1;
}
if (controlPointNode["Sign"]) {
controlPointInfo.sign = controlPointNode["Sign"].as<int8_t>();
} else {
controlPointInfo.sign = -1;
}
controlPointInfos.push_back(controlPointInfo);
}
return std::make_shared<CourseData>(creatorId, venue, skybox, flag, fileName, fileNameExtra, controlPointInfos);
}
+8 -6
View File
@@ -27,7 +27,7 @@ typedef struct CourseRawData {
ControlPoint controlPoint[64];
int16_t bankAngle[64];
int8_t pit[64];
int8_t boost[64];
int8_t dash[64];
int8_t dirt[64];
int8_t ice[64];
int8_t jump[64];
@@ -42,7 +42,7 @@ typedef struct ControlPointInfo {
ControlPoint controlPoint;
int16_t bankAngle;
int8_t pit;
int8_t boost;
int8_t dash;
int8_t dirt;
int8_t ice;
int8_t jump;
@@ -86,20 +86,22 @@ 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 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;
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, CourseCodeExporter)
REGISTER(Header, CourseHeaderExporter)
REGISTER(Binary, CourseBinaryExporter)
// REGISTER(Modding, CourseModdingExporter)
REGISTER(Modding, CourseModdingExporter)
};
}
bool SupportModdedAssets() override { return true; }
};
} // namespace FZX