summaryrefslogtreecommitdiff
path: root/tools/src/asset_processor/assets/midi.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/src/asset_processor/assets/midi.cpp')
-rw-r--r--tools/src/asset_processor/assets/midi.cpp156
1 files changed, 156 insertions, 0 deletions
diff --git a/tools/src/asset_processor/assets/midi.cpp b/tools/src/asset_processor/assets/midi.cpp
new file mode 100644
index 00000000..6444b05e
--- /dev/null
+++ b/tools/src/asset_processor/assets/midi.cpp
@@ -0,0 +1,156 @@
+#include "midi.h"
+#include "reader.h"
+#include "util.h"
+#include <filesystem>
+#include <iostream>
+#include <fstream>
+#include <nlohmann/json.hpp>
+
+extern std::string gBaseromPath;
+
+std::filesystem::path MidiAsset::generateAssetPath() {
+ std::filesystem::path path = this->path;
+ return path.replace_extension(".mid");
+}
+
+std::filesystem::path MidiAsset::generateBuildPath() {
+ std::filesystem::path path = this->path;
+ return path.replace_extension(".s");
+}
+
+void MidiAsset::extractBinary(const std::vector<char>& baserom) {
+ // Custom extraction as we need a label in the middle.
+ std::string label = this->path.stem();
+
+ std::filesystem::path tracksPath = this->path;
+ tracksPath.replace_filename(label + "_tracks.bin");
+ std::filesystem::path headerPath = this->path;
+ headerPath.replace_filename(label + "_header.bin");
+
+ int headerOffset = this->asset["options"]["headerOffset"];
+
+ // Extract tracks
+ {
+ auto first = baserom.begin() + this->start;
+ auto last = baserom.begin() + this->start + headerOffset;
+ std::vector<char> data(first, last);
+ std::fstream file(tracksPath, std::ios::out | std::ios::binary);
+ file.write(&data[0], data.size());
+ file.close();
+ }
+ // Extract header
+ {
+ auto first = baserom.begin() + this->start + headerOffset;
+ auto last = baserom.begin() + this->start + this->size;
+ std::vector<char> data(first, last);
+ std::fstream file(headerPath, std::ios::out | std::ios::binary);
+ file.write(&data[0], data.size());
+ file.close();
+ }
+
+ // Create dummy .s file.
+ std::ofstream out(this->buildPath);
+ out << "\t.incbin " << tracksPath << "\n";
+ out << label << "::\n";
+ out << "\t.incbin " << headerPath << "\n";
+ out.close();
+}
+
+void MidiAsset::parseOptions(std::vector<std::string>& commonParams, std::vector<std::string>& agb2midParams) {
+ bool exactGateTime = true;
+
+ for (const auto& it : this->asset["options"].items()) {
+ const std::string& key = it.key();
+ if (key == "group" || key == "G") {
+ commonParams.push_back("-G");
+ commonParams.push_back(to_string(it.value()));
+ } else if (key == "priority" || key == "P") {
+ commonParams.push_back("-P");
+ commonParams.push_back(to_string(it.value()));
+ } else if (key == "reverb" || key == "R") {
+ commonParams.push_back("-R");
+ commonParams.push_back(to_string(it.value()));
+ } else if (key == "nominator") {
+ agb2midParams.push_back("-n");
+ agb2midParams.push_back(to_string(it.value()));
+ } else if (key == "denominator") {
+ agb2midParams.push_back("-d");
+ agb2midParams.push_back(to_string(it.value()));
+ } else if (key == "timeChanges") {
+ const nlohmann::json& value = it.value();
+ if (value.is_array()) {
+ // Multiple time changes
+ for (const auto& change : value) {
+ agb2midParams.push_back("-t");
+ agb2midParams.push_back(to_string(change["nominator"]));
+ agb2midParams.push_back(to_string(change["denominator"]));
+ agb2midParams.push_back(to_string(change["time"]));
+ }
+ } else {
+ agb2midParams.push_back("-t");
+ agb2midParams.push_back(to_string(value["nominator"]));
+ agb2midParams.push_back(to_string(value["denominator"]));
+ agb2midParams.push_back(to_string(value["time"]));
+ }
+ } else if (key == "exactGateTime") {
+ if (it.value().get<int>() == 1) {
+ exactGateTime = true;
+ } else {
+ exactGateTime = false;
+ }
+ } else if (key == "headerOffset") {
+ // ignore here
+ } else {
+ commonParams.push_back("-" + key);
+ commonParams.push_back(to_string(it.value()));
+ }
+ }
+
+ if (exactGateTime) {
+ commonParams.push_back("-E");
+ }
+}
+
+void MidiAsset::convertToHumanReadable(const std::vector<char>& baserom) {
+ (void)baserom;
+
+ // Convert the options
+ std::vector<std::string> commonParams;
+ std::vector<std::string> agb2midParams;
+ this->parseOptions(commonParams, agb2midParams);
+
+ int headerOffset = this->asset["options"]["headerOffset"];
+
+ std::filesystem::path toolPath = "tools";
+ std::vector<std::string> cmd;
+ cmd.push_back(toolPath / "agb2mid" / "agb2mid");
+ cmd.push_back(gBaseromPath);
+ cmd.push_back(string_format("0x%x", this->start + headerOffset));
+ cmd.push_back(gBaseromPath); // TODO deduplicate?
+ cmd.push_back(this->assetPath);
+ cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
+ cmd.insert(cmd.end(), agb2midParams.begin(), agb2midParams.end());
+ check_call(cmd);
+
+ // We also need to build the mid to an s file here, so we get shiftability after converting.
+ cmd.clear();
+ cmd.push_back(toolPath / "mid2agb" / "mid2agb");
+ cmd.push_back(this->assetPath);
+ cmd.push_back(this->buildPath);
+ cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
+ check_call(cmd);
+}
+
+void MidiAsset::buildToBinary() {
+ // Convert the options
+ std::vector<std::string> commonParams;
+ std::vector<std::string> agb2midParams;
+ this->parseOptions(commonParams, agb2midParams);
+ std::filesystem::path toolPath = "tools";
+ std::vector<std::string> cmd;
+ cmd.push_back(toolPath / "mid2agb" / "mid2agb");
+ cmd.push_back(this->assetPath);
+ cmd.push_back(this->buildPath);
+ cmd.insert(cmd.end(), commonParams.begin(), commonParams.end());
+ check_call(cmd);
+} \ No newline at end of file