mirror of
https://github.com/zeldaret/tmc
synced 2026-07-09 23:01:43 -04:00
moved asset_processor to new location
This commit is contained in:
@@ -0,0 +1 @@
|
||||
add_subdirectory(asset_processor)
|
||||
@@ -0,0 +1,5 @@
|
||||
file(GLOB_RECURSE sources *.cpp)
|
||||
|
||||
add_executable(asset_processor ${sources})
|
||||
target_include_directories(asset_processor PRIVATE .)
|
||||
target_link_libraries(asset_processor PRIVATE nlohmann_json::nlohmann_json filesystem)
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "aif.h"
|
||||
#include "util.h"
|
||||
|
||||
std::filesystem::path AifAsset::generateAssetPath() {
|
||||
std::filesystem::path path = this->path;
|
||||
return path.replace_extension(".aif");
|
||||
}
|
||||
|
||||
void AifAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
(void)baserom;
|
||||
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "aif2pcm" / "aif2pcm");
|
||||
cmd.push_back(this->path);
|
||||
cmd.push_back(this->assetPath);
|
||||
check_call(cmd);
|
||||
}
|
||||
|
||||
void AifAsset::buildToBinary() {
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "aif2pcm" / "aif2pcm");
|
||||
cmd.push_back(this->assetPath);
|
||||
cmd.push_back(this->path);
|
||||
check_call(cmd);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "asset.h"
|
||||
|
||||
class AifAsset : public BaseAsset {
|
||||
public:
|
||||
using BaseAsset::BaseAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
virtual void buildToBinary();
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
#include "animation.h"
|
||||
#include "reader.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
void AnimationAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
Reader reader(baserom, this->start, this->size);
|
||||
std::vector<std::string> lines;
|
||||
bool end_of_animation = false;
|
||||
while (!end_of_animation && reader.cursor + 3 < this->size) {
|
||||
u8 frame_index = reader.read_u8();
|
||||
u8 keyframe_duration = reader.read_u8();
|
||||
u8 bitfield = reader.read_u8();
|
||||
u8 bitfield2 = reader.read_u8();
|
||||
end_of_animation = (bitfield2 & 0x80) != 0;
|
||||
lines.push_back(string_format("\tkeyframe frame_index=%d", frame_index));
|
||||
lines.push_back(opt_param(", duration=%d", 0, keyframe_duration));
|
||||
lines.push_back(opt_param(", bitfield=0x%x", 0, bitfield));
|
||||
lines.push_back(opt_param(", bitfield2=0x%x", 0, bitfield2));
|
||||
lines.push_back("\n");
|
||||
}
|
||||
|
||||
if (!end_of_animation) {
|
||||
lines.push_back("@ TODO why no terminator?\n");
|
||||
}
|
||||
|
||||
while (reader.cursor < this->size) {
|
||||
u8 keyframe_count = reader.read_u8();
|
||||
lines.push_back(string_format("\t.byte %d @ keyframe count\n", keyframe_count));
|
||||
}
|
||||
std::ofstream out(this->assetPath);
|
||||
for (const auto& line : lines) {
|
||||
out << line;
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "macroasm.h"
|
||||
|
||||
class AnimationAsset : public BaseMacroAsmAsset {
|
||||
public:
|
||||
using BaseMacroAsmAsset::BaseMacroAsmAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "asset.h"
|
||||
#include <fstream>
|
||||
|
||||
void BaseAsset::extractBinary(const std::vector<char>& baserom) {
|
||||
auto first = baserom.begin() + this->start;
|
||||
auto last = baserom.begin() + this->start + this->size;
|
||||
std::vector<char> data(first, last);
|
||||
std::fstream file(this->path, std::ios::out | std::ios::binary);
|
||||
file.write(&data[0], data.size());
|
||||
file.close();
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
#ifndef ASSET_H
|
||||
#define ASSET_H
|
||||
|
||||
#include "util.h"
|
||||
#include <filesystem>
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
class BaseAsset {
|
||||
public:
|
||||
BaseAsset(std::filesystem::path path, int start, int size, const nlohmann::json& asset)
|
||||
: path(path), start(start), size(size), asset(asset) {
|
||||
}
|
||||
|
||||
virtual ~BaseAsset() = default;
|
||||
|
||||
void setup() {
|
||||
// Cannot call virtual functions in constructor, so another function call is necessary
|
||||
assetPath = this->generateAssetPath();
|
||||
buildPath = this->generateBuildPath();
|
||||
}
|
||||
|
||||
// Extract the binary segment for this asset from the baserom and store it in a separate file.
|
||||
virtual void extractBinary(const std::vector<char>& baserom);
|
||||
|
||||
// Convert the binary data for this asset to a human readable form.
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
(void)baserom;
|
||||
}
|
||||
|
||||
// Build the asset from the human readable form back to the binary.
|
||||
virtual void buildToBinary() {
|
||||
}
|
||||
|
||||
// Returns the path to the binary file extracted from the baserom.
|
||||
std::filesystem::path getPath() const {
|
||||
return path;
|
||||
}
|
||||
// Returns the path to the human readable asset file.
|
||||
std::filesystem::path getAssetPath() const {
|
||||
return assetPath;
|
||||
}
|
||||
// Returns the path to the resulting file after building.
|
||||
std::filesystem::path getBuildPath() const {
|
||||
return buildPath;
|
||||
}
|
||||
|
||||
// Returns the base of the filename of the asset.
|
||||
std::string getSymbol() const {
|
||||
// Need to get the stem twice to remove both of the .4bpp.lz extensions.
|
||||
return (this->path.stem()).stem();
|
||||
}
|
||||
|
||||
// Returns the start address of this asset.
|
||||
int getStart() const {
|
||||
return start;
|
||||
}
|
||||
|
||||
|
||||
protected:
|
||||
std::filesystem::path path;
|
||||
std::filesystem::path assetPath;
|
||||
std::filesystem::path buildPath;
|
||||
int start;
|
||||
int size;
|
||||
const nlohmann::json& asset;
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath() {
|
||||
return this->path;
|
||||
}
|
||||
virtual std::filesystem::path generateBuildPath() {
|
||||
return this->path;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,48 @@
|
||||
#include "exitlist.h"
|
||||
#include "reader.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
void ExitListAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
Reader reader(baserom, this->start, this->size);
|
||||
std::vector<std::string> lines;
|
||||
while (reader.cursor < this->size) {
|
||||
u16 transition_type = reader.read_u16();
|
||||
u16 x_pos = reader.read_u16();
|
||||
u16 y_pos = reader.read_u16();
|
||||
u16 dest_x = reader.read_u16();
|
||||
u16 dest_y = reader.read_u16();
|
||||
u8 screen_edge = reader.read_u8();
|
||||
u8 dest_area = reader.read_u8();
|
||||
u8 dest_room = reader.read_u8();
|
||||
u8 unknown_2 = reader.read_u8();
|
||||
u8 unknown_3 = reader.read_u8();
|
||||
u8 unknown_4 = reader.read_u8();
|
||||
u16 unknown_5 = reader.read_u16();
|
||||
u16 padding_1 = reader.read_u16();
|
||||
if (transition_type == 0xffff) {
|
||||
lines.push_back("\texit_list_end\n");
|
||||
break;
|
||||
}
|
||||
lines.push_back(string_format("\texit transition=%d", transition_type));
|
||||
lines.push_back(opt_param(", x=0x%x", 0, x_pos));
|
||||
lines.push_back(opt_param(", y=0x%x", 0, y_pos));
|
||||
lines.push_back(opt_param(", destX=0x%x", 0, dest_x));
|
||||
lines.push_back(opt_param(", destY=0x%x", 0, dest_y));
|
||||
lines.push_back(opt_param(", screenEdge=0x%x", 0, screen_edge));
|
||||
lines.push_back(opt_param(", destArea=0x%x", 0, dest_area));
|
||||
lines.push_back(opt_param(", destRoom=0x%x", 0, dest_room));
|
||||
lines.push_back(opt_param(", unknown=0x%x", 0, unknown_2));
|
||||
lines.push_back(opt_param(", unknown2=0x%x", 0, unknown_3));
|
||||
lines.push_back(opt_param(", unknown3=0x%x", 0, unknown_4));
|
||||
lines.push_back(opt_param(", unknown4=0x%x", 0, unknown_5));
|
||||
lines.push_back(opt_param(", padding=0x%x", 0, padding_1));
|
||||
lines.push_back("\n");
|
||||
}
|
||||
|
||||
std::ofstream out(this->assetPath);
|
||||
for (const auto& line : lines) {
|
||||
out << line;
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "macroasm.h"
|
||||
|
||||
class ExitListAsset : public BaseMacroAsmAsset {
|
||||
public:
|
||||
using BaseMacroAsmAsset::BaseMacroAsmAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "frameobjlists.h"
|
||||
#include "reader.h"
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
void FrameObjListsAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
Reader reader(baserom, this->start, this->size);
|
||||
|
||||
std::vector<int> first_level;
|
||||
std::vector<int> second_level;
|
||||
|
||||
std::vector<std::string> lines;
|
||||
lines.push_back("@ First level of offsets\n");
|
||||
|
||||
while (reader.cursor < this->size) {
|
||||
if (std::find(first_level.begin(), first_level.end(), reader.cursor) != first_level.end()) {
|
||||
// End of first level
|
||||
break;
|
||||
}
|
||||
|
||||
u32 pointer = reader.read_u32();
|
||||
first_level.push_back(pointer);
|
||||
lines.push_back(string_format("\t.4byte 0x%x\n", pointer));
|
||||
}
|
||||
|
||||
lines.push_back("\n@ Second level of offsets\n");
|
||||
|
||||
while (reader.cursor < this->size) {
|
||||
if (std::find(second_level.begin(), second_level.end(), reader.cursor) != second_level.end()) {
|
||||
// End of second level
|
||||
break;
|
||||
}
|
||||
u32 pointer = reader.read_u32();
|
||||
second_level.push_back(pointer);
|
||||
lines.push_back(string_format("\t.4byte 0x%x\n", pointer));
|
||||
}
|
||||
|
||||
int max_second_level = *std::max_element(second_level.begin(), second_level.end());
|
||||
|
||||
while (reader.cursor < this->size) {
|
||||
if (reader.cursor > max_second_level) {
|
||||
break;
|
||||
}
|
||||
if (std::find(second_level.begin(), second_level.end(), reader.cursor) == second_level.end()) {
|
||||
// Find nearest next value that is in the second level
|
||||
int next = -1;
|
||||
for (const auto& i : second_level) {
|
||||
if (i > reader.cursor && (next == -1 || i < next)) {
|
||||
next = i;
|
||||
}
|
||||
}
|
||||
int diff = next - reader.cursor;
|
||||
lines.push_back(string_format("@ Skipping %d bytes\n", diff));
|
||||
for (int i = 0; i < diff; i++) {
|
||||
u8 byte = reader.read_u8();
|
||||
lines.push_back(string_format("\t.byte %d\n", byte));
|
||||
}
|
||||
}
|
||||
u8 num_objects = reader.read_u8();
|
||||
lines.push_back(string_format("\t.byte %d @ num_objs\n", num_objects));
|
||||
|
||||
for (int i = 0; i < num_objects; i++) {
|
||||
s8 x_offset = reader.read_s8();
|
||||
s8 y_offset = reader.read_s8();
|
||||
u8 bitfield = reader.read_u8();
|
||||
u16 bitfield2 = reader.read_u16();
|
||||
lines.push_back(string_format("\tobj x=%d, y=%d", x_offset, y_offset));
|
||||
lines.push_back(opt_param(", bitfield=0x%x", 0, bitfield));
|
||||
lines.push_back(opt_param(", bitfield2=0x%x", 0, bitfield2));
|
||||
lines.push_back("\n");
|
||||
}
|
||||
}
|
||||
|
||||
std::ofstream out(this->assetPath);
|
||||
for (const auto& line : lines) {
|
||||
out << line;
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "macroasm.h"
|
||||
|
||||
class FrameObjListsAsset : public BaseMacroAsmAsset {
|
||||
public:
|
||||
using BaseMacroAsmAsset::BaseMacroAsmAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
};
|
||||
@@ -0,0 +1,38 @@
|
||||
#include "gfx.h"
|
||||
#include "util.h"
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
std::filesystem::path GfxAsset::generateAssetPath() {
|
||||
std::filesystem::path pngPath = this->path;
|
||||
if (pngPath.extension() == ".lz") {
|
||||
pngPath.replace_extension("");
|
||||
}
|
||||
pngPath.replace_extension(".png");
|
||||
return pngPath;
|
||||
}
|
||||
|
||||
void GfxAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
(void)baserom;
|
||||
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->path);
|
||||
cmd.push_back(this->assetPath);
|
||||
if (this->asset.contains("options")) {
|
||||
for (const auto& it : this->asset["options"].items()) {
|
||||
cmd.push_back("-" + it.key());
|
||||
cmd.push_back(to_string(it.value()));
|
||||
}
|
||||
}
|
||||
check_call(cmd);
|
||||
}
|
||||
|
||||
void GfxAsset::buildToBinary() {
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->assetPath);
|
||||
cmd.push_back(this->path);
|
||||
check_call(cmd);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "asset.h"
|
||||
|
||||
class GfxAsset : public BaseAsset {
|
||||
public:
|
||||
using BaseAsset::BaseAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
virtual void buildToBinary();
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "macroasm.h"
|
||||
#include <fstream>
|
||||
|
||||
std::filesystem::path BaseMacroAsmAsset::generateAssetPath() {
|
||||
std::filesystem::path path = this->path;
|
||||
return path.replace_extension(".s");
|
||||
}
|
||||
|
||||
std::filesystem::path BaseMacroAsmAsset::generateBuildPath() {
|
||||
std::filesystem::path path = this->path;
|
||||
return path.replace_extension(".s");
|
||||
}
|
||||
|
||||
void BaseMacroAsmAsset::extractBinary(const std::vector<char>& baserom) {
|
||||
BaseAsset::extractBinary(baserom);
|
||||
// Create dummy .s file that just incbins the .bin file.
|
||||
std::ofstream out(this->assetPath);
|
||||
out << "\t.incbin " << this->path << "\n";
|
||||
out.close();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
#ifndef MACROASM_H
|
||||
#define MACROASM_H
|
||||
|
||||
#include "asset.h"
|
||||
|
||||
// Common class for all assets that are converted to asm files consisting of macros.
|
||||
class BaseMacroAsmAsset : public BaseAsset {
|
||||
using BaseAsset::BaseAsset;
|
||||
|
||||
public:
|
||||
virtual void extractBinary(const std::vector<char>& baserom);
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
virtual std::filesystem::path generateBuildPath();
|
||||
};
|
||||
#endif
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#include "asset.h"
|
||||
|
||||
class MidiAsset : public BaseAsset {
|
||||
public:
|
||||
using BaseAsset::BaseAsset;
|
||||
virtual void extractBinary(const std::vector<char>& baserom);
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
virtual void buildToBinary();
|
||||
|
||||
private:
|
||||
void parseOptions(std::vector<std::string>& commonParams, std::vector<std::string>& agb2midParams);
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
virtual std::filesystem::path generateBuildPath();
|
||||
};
|
||||
@@ -0,0 +1,27 @@
|
||||
#include "palette.h"
|
||||
#include "util.h"
|
||||
|
||||
std::filesystem::path PaletteAsset::generateAssetPath() {
|
||||
std::filesystem::path path = this->path;
|
||||
return path.replace_extension(".pal");
|
||||
}
|
||||
|
||||
void PaletteAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
(void)baserom;
|
||||
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->path);
|
||||
cmd.push_back(this->assetPath);
|
||||
check_call(cmd);
|
||||
}
|
||||
|
||||
void PaletteAsset::buildToBinary() {
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->assetPath);
|
||||
cmd.push_back(this->path);
|
||||
check_call(cmd);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "asset.h"
|
||||
|
||||
class PaletteAsset : public BaseAsset {
|
||||
public:
|
||||
using BaseAsset::BaseAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
virtual void buildToBinary();
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
};
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "spriteframe.h"
|
||||
#include "reader.h"
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
void SpriteFrameAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
Reader reader(baserom, this->start, this->size);
|
||||
std::vector<std::string> lines;
|
||||
while (reader.cursor < this->size) {
|
||||
u8 num_gfx_tiles = reader.read_u8();
|
||||
u8 unk = reader.read_u8();
|
||||
u16 first_gfx_tile_index = reader.read_u16();
|
||||
|
||||
lines.push_back(string_format("\tsprite_frame first_tile_index=0x%x", first_gfx_tile_index));
|
||||
lines.push_back(opt_param(", num_tiles=%d", 0, num_gfx_tiles));
|
||||
lines.push_back(opt_param(", unknown=0x%x", 0, unk));
|
||||
lines.push_back("\n");
|
||||
}
|
||||
|
||||
std::ofstream out(this->assetPath);
|
||||
for (const auto& line : lines) {
|
||||
out << line;
|
||||
}
|
||||
out.close();
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include "macroasm.h"
|
||||
|
||||
class SpriteFrameAsset : public BaseMacroAsmAsset {
|
||||
public:
|
||||
using BaseMacroAsmAsset::BaseMacroAsmAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "tileset.h"
|
||||
#include "util.h"
|
||||
|
||||
std::filesystem::path TilesetAsset::generateAssetPath() {
|
||||
std::filesystem::path pngPath = this->path;
|
||||
if (pngPath.extension() == ".lz") {
|
||||
pngPath.replace_extension("");
|
||||
}
|
||||
pngPath.replace_extension(".png");
|
||||
return pngPath;
|
||||
}
|
||||
|
||||
void TilesetAsset::convertToHumanReadable(const std::vector<char>& baserom) {
|
||||
(void)baserom;
|
||||
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->path);
|
||||
cmd.push_back(this->assetPath);
|
||||
cmd.push_back("-mwidth");
|
||||
cmd.push_back("32");
|
||||
check_call(cmd);
|
||||
}
|
||||
|
||||
void TilesetAsset::buildToBinary() {
|
||||
std::filesystem::path toolsPath = "tools";
|
||||
std::vector<std::string> cmd;
|
||||
cmd.push_back(toolsPath / "gbagfx" / "gbagfx");
|
||||
cmd.push_back(this->assetPath);
|
||||
cmd.push_back(this->path);
|
||||
check_call(cmd);
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#include "asset.h"
|
||||
|
||||
class TilesetAsset : public BaseAsset {
|
||||
public:
|
||||
using BaseAsset::BaseAsset;
|
||||
virtual void convertToHumanReadable(const std::vector<char>& baserom);
|
||||
virtual void buildToBinary();
|
||||
|
||||
private:
|
||||
virtual std::filesystem::path generateAssetPath();
|
||||
};
|
||||
@@ -0,0 +1,313 @@
|
||||
#include "main.h"
|
||||
#include "assets/aif.h"
|
||||
#include "assets/animation.h"
|
||||
#include "assets/exitlist.h"
|
||||
#include "assets/frameobjlists.h"
|
||||
#include "assets/gfx.h"
|
||||
#include "assets/midi.h"
|
||||
#include "assets/palette.h"
|
||||
#include "assets/spriteframe.h"
|
||||
#include "assets/tileset.h"
|
||||
#include "offsets.h"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
using nlohmann::json;
|
||||
|
||||
// Does json array contain element? https://github.com/nlohmann/json/issues/399#issuecomment-844212174
|
||||
#define CONTAINS(list, value) (std::find(list.begin(), list.end(), value) != list.end())
|
||||
|
||||
void usage() {
|
||||
std::cout << "Usage: asset_processor [options] MODE VARIANT BUILD_PATH\n"
|
||||
<< "\n"
|
||||
<< "MODE Mode to execute\n"
|
||||
<< "VARIANT Variant to build. One of USA, JP, EU, DEMO_USA, DEMO_JP\n"
|
||||
<< "BUILD_PATH Path to the build folder\n"
|
||||
<< "\n"
|
||||
<< "Modes:\n"
|
||||
<< "extract Extract binary data from baserom\n"
|
||||
<< "convert Convert data to human readable format\n"
|
||||
<< "build Build binary data from assets\n"
|
||||
<< "\n"
|
||||
<< "Options:\n"
|
||||
<< "-v Print verbose output\n"
|
||||
<< "-h Show this help\n"
|
||||
<< "-f Force extraction of all assets (even if unmodified)\n";
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
bool gVerbose = false;
|
||||
bool gForceExtraction = false;
|
||||
Mode gMode;
|
||||
std::string gVariant;
|
||||
std::string gAssetsFolder;
|
||||
std::string gBaseromPath;
|
||||
|
||||
static std::map<std::string, std::string> baseroms = { { "USA", "baserom.gba" },
|
||||
{ "EU", "baserom_eu.gba" },
|
||||
{ "JP", "baserom_jp.gba" },
|
||||
{ "DEMO_USA", "baserom_demo.gba" },
|
||||
{ "DEMO_JP", "baserom_demo_jp.gba" } };
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
||||
// Parse options.
|
||||
int argCount = argc - 1; // Skip program name
|
||||
char** args = &argv[1];
|
||||
|
||||
while (argCount > 0 && args[0][0] == '-') {
|
||||
if (strcmp(args[0], "-v") == 0) {
|
||||
gVerbose = true;
|
||||
argCount--;
|
||||
args++;
|
||||
} else if (strcmp(args[0], "-h") == 0) {
|
||||
argCount--;
|
||||
args++;
|
||||
usage();
|
||||
} else if (strcmp(args[0], "-f") == 0) {
|
||||
gForceExtraction = true;
|
||||
argCount--;
|
||||
args++;
|
||||
} else {
|
||||
std::cerr << "Error: Unrecognized argument `" << args[0] << "`" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (argCount != 3) {
|
||||
std::cerr << "Error: Not enough arguments. expected: 3, actual: " << argCount << std::endl;
|
||||
usage();
|
||||
}
|
||||
|
||||
if (strcmp(args[0], "extract") == 0) {
|
||||
gMode = EXTRACT;
|
||||
} else if (strcmp(args[0], "convert") == 0) {
|
||||
gMode = CONVERT;
|
||||
} else if (strcmp(args[0], "build") == 0) {
|
||||
gMode = BUILD;
|
||||
} else {
|
||||
std::cerr << "Error: Unsupported mode `" << args[0] << "`" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
gVariant = args[1];
|
||||
if (gVariant != "USA" && gVariant != "EU" && gVariant != "JP" && gVariant != "DEMO_USA" && gVariant != "DEMO_JP") {
|
||||
std::cerr << "Error: Unsupported variant `" << gVariant << "`" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
gAssetsFolder = args[2];
|
||||
gBaseromPath = baseroms[gVariant];
|
||||
|
||||
if (!std::filesystem::exists(gBaseromPath)) {
|
||||
std::cerr << "Error: You need to provide a " << gVariant << " ROM as " << gBaseromPath << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
|
||||
// Read baserom.
|
||||
std::ifstream file(gBaseromPath, std::ios::binary | std::ios::ate);
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<char> baserom(size);
|
||||
if (!file.read(baserom.data(), size)) {
|
||||
std::cerr << "Could not read baserom " << gBaseromPath << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
file.close();
|
||||
|
||||
// Gather all json configs from assets folder.
|
||||
std::vector<std::filesystem::path> configs;
|
||||
std::string configFolder = "assets";
|
||||
for (const auto& entry : std::filesystem::directory_iterator(configFolder)) {
|
||||
const auto& path = entry.path();
|
||||
if (path.extension() == ".json") {
|
||||
configs.push_back(path);
|
||||
}
|
||||
}
|
||||
std::sort(configs.begin(), configs.end());
|
||||
|
||||
for (const auto& config : configs) {
|
||||
if (gVerbose) {
|
||||
std::cout << "Parsing " << config << "..." << std::endl;
|
||||
}
|
||||
|
||||
std::ifstream input(config);
|
||||
json assets;
|
||||
input >> assets;
|
||||
|
||||
std::filesystem::file_time_type configModified = std::filesystem::last_write_time(config);
|
||||
|
||||
std::unique_ptr<OffsetCalculator> offsetCalculator;
|
||||
|
||||
uint currentOffset = 0;
|
||||
for (const auto& asset : assets) {
|
||||
if (asset.contains("offsets")) { // Offset definition
|
||||
if (asset["offsets"].contains(gVariant)) {
|
||||
currentOffset = asset["offsets"][gVariant];
|
||||
}
|
||||
} else if (asset.contains("calculateOffsets")) { // Start offset calculation
|
||||
std::filesystem::path path = gAssetsFolder;
|
||||
path = path / asset["calculateOffsets"];
|
||||
int baseOffset = asset["start"].get<int>() + currentOffset;
|
||||
offsetCalculator = std::make_unique<OffsetCalculator>(path, baseOffset);
|
||||
} else if (asset.contains("path")) { // Asset definition
|
||||
|
||||
if (asset.contains("variants")) {
|
||||
if (!CONTAINS(asset["variants"], gVariant)) {
|
||||
// This asset is not used in the current variant
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
std::filesystem::path path = gAssetsFolder;
|
||||
path = path / asset["path"];
|
||||
|
||||
switch (gMode) {
|
||||
case EXTRACT: {
|
||||
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
||||
if (shouldExtractAsset(path, configModified)) {
|
||||
if (gVerbose) {
|
||||
std::cout << "Extracting " << path << "..." << std::endl;
|
||||
}
|
||||
|
||||
extractAsset(assetHandler, baserom);
|
||||
}
|
||||
if (offsetCalculator != nullptr) {
|
||||
offsetCalculator->addAsset(assetHandler->getStart(), assetHandler->getSymbol());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case CONVERT: {
|
||||
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
||||
if (shouldConvertAsset(assetHandler)) {
|
||||
if (gVerbose) {
|
||||
std::cout << "Converting " << assetHandler->getAssetPath() << "..." << std::endl;
|
||||
}
|
||||
convertAsset(assetHandler, baserom);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case BUILD: {
|
||||
std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset);
|
||||
if (shouldBuildAsset(assetHandler)) {
|
||||
if (gVerbose) {
|
||||
std::cout << "Building " << assetHandler->getAssetPath() << "..." << std::endl;
|
||||
}
|
||||
buildAsset(assetHandler);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (gVerbose) {
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<BaseAsset> getAssetHandlerByType(const std::filesystem::path& path, const json& asset,
|
||||
const int& currentOffset) {
|
||||
|
||||
int start = 0;
|
||||
if (asset.contains("start")) {
|
||||
// Apply offset to the start of the USA variant
|
||||
start = asset["start"].get<int>() + currentOffset;
|
||||
} else if (asset.contains("starts")) {
|
||||
// Use start for the current variant
|
||||
start = asset["starts"][gVariant];
|
||||
}
|
||||
|
||||
std::string type = "";
|
||||
if (asset.contains("type")) {
|
||||
type = asset["type"];
|
||||
}
|
||||
|
||||
int size = 0;
|
||||
if (asset.contains("size")) { // The asset has a size and want to be extracted first.
|
||||
size = asset["size"]; // TODO can different sizes for the different variants ever occur?
|
||||
}
|
||||
|
||||
std::unique_ptr<BaseAsset> assetHandler;
|
||||
if (type == "tileset") {
|
||||
assetHandler = std::make_unique<TilesetAsset>(path, start, size, asset);
|
||||
} else if (type == "animation") {
|
||||
assetHandler = std::make_unique<AnimationAsset>(path, start, size, asset);
|
||||
} else if (type == "sprite_frame") {
|
||||
assetHandler = std::make_unique<SpriteFrameAsset>(path, start, size, asset);
|
||||
} else if (type == "exit_list") {
|
||||
assetHandler = std::make_unique<ExitListAsset>(path, start, size, asset);
|
||||
} else if (type == "frame_obj_lists") {
|
||||
assetHandler = std::make_unique<FrameObjListsAsset>(path, start, size, asset);
|
||||
} else if (type == "midi") {
|
||||
assetHandler = std::make_unique<MidiAsset>(path, start, size, asset);
|
||||
} else if (type == "aif") {
|
||||
assetHandler = std::make_unique<AifAsset>(path, start, size, asset);
|
||||
} else if (type == "gfx") {
|
||||
assetHandler = std::make_unique<GfxAsset>(path, start, size, asset);
|
||||
} else if (type == "palette") {
|
||||
assetHandler = std::make_unique<PaletteAsset>(path, start, size, asset);
|
||||
} else if (type == "map_gfx" || type == "map_layer1" || type == "map_layer2" || type == "metatiles_tile_types1" ||
|
||||
type == "metatiles_tile_types2" || type == "metatiles_tileset1" || type == "metatiles_tileset2" ||
|
||||
type == "map_mapping1" || type == "map_mapping2" || type == "tileset_mapping3" ||
|
||||
type == "map_collision" || type == "unknown") {
|
||||
// TODO implement conversions
|
||||
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
||||
} else if (type == "") {
|
||||
// Unknown binary asset
|
||||
assetHandler = std::make_unique<BaseAsset>(path, start, size, asset);
|
||||
} else {
|
||||
std::cerr << "Error: Unimplemented asset type `" << type << "`" << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
assetHandler->setup();
|
||||
return assetHandler;
|
||||
}
|
||||
|
||||
bool shouldExtractAsset(const std::filesystem::path& path, const std::filesystem::file_time_type& configModified) {
|
||||
if (gForceExtraction) {
|
||||
return true;
|
||||
}
|
||||
if (std::filesystem::is_regular_file(path)) {
|
||||
std::filesystem::file_time_type fileModified = std::filesystem::last_write_time(path);
|
||||
if (fileModified < configModified) {
|
||||
// File was created before the config was modified, so it needs to be renewed.
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// Target file does not yet exist -> extract
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void extractAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom) {
|
||||
// Create the parent directory
|
||||
std::filesystem::path parentDir = std::filesystem::path(assetHandler->getPath());
|
||||
parentDir.remove_filename();
|
||||
std::filesystem::create_directories(parentDir);
|
||||
|
||||
assetHandler->extractBinary(baserom);
|
||||
}
|
||||
|
||||
bool shouldConvertAsset(const std::unique_ptr<BaseAsset>& assetHandler) {
|
||||
(void)assetHandler;
|
||||
return true; // TODO
|
||||
}
|
||||
|
||||
void convertAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom) {
|
||||
assetHandler->convertToHumanReadable(baserom);
|
||||
}
|
||||
|
||||
bool shouldBuildAsset(const std::unique_ptr<BaseAsset>& assetHandler) {
|
||||
(void)assetHandler;
|
||||
return true; // TODO
|
||||
}
|
||||
|
||||
void buildAsset(std::unique_ptr<BaseAsset>& assetHandler) {
|
||||
assetHandler->buildToBinary();
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
#ifndef MAIN_H
|
||||
#define MAIN_H
|
||||
|
||||
#include "util.h"
|
||||
#include "assets/asset.h"
|
||||
|
||||
std::unique_ptr<BaseAsset> getAssetHandlerByType(const std::filesystem::path& path, const nlohmann::json& asset,
|
||||
const int& currentOffset);
|
||||
bool shouldExtractAsset(const std::filesystem::path& path, const std::filesystem::file_time_type& configModified);
|
||||
void extractAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom);
|
||||
bool shouldConvertAsset(const std::unique_ptr<BaseAsset>& assetHandler);
|
||||
void convertAsset(std::unique_ptr<BaseAsset>& assetHandler, const std::vector<char>& baserom);
|
||||
bool shouldBuildAsset(const std::unique_ptr<BaseAsset>& assetHandler);
|
||||
void buildAsset(std::unique_ptr<BaseAsset>& assetHandler);
|
||||
|
||||
enum Mode { EXTRACT, CONVERT, BUILD };
|
||||
|
||||
// Arguments
|
||||
extern bool gVerbose;
|
||||
extern Mode gMode;
|
||||
extern std::string gVariant;
|
||||
extern std::string gAssetsFolder;
|
||||
extern std::string gBaseromPath;
|
||||
#endif
|
||||
@@ -0,0 +1,9 @@
|
||||
#include "offsets.h"
|
||||
|
||||
OffsetCalculator::OffsetCalculator(std::filesystem::path outputFile, int baseOffset): baseOffset(baseOffset) {
|
||||
output = std::ofstream(outputFile);
|
||||
}
|
||||
|
||||
void OffsetCalculator::addAsset(int start, std::string symbol) {
|
||||
this->output << "\t.equiv offset_" << symbol << ", " << start - this->baseOffset << std::endl;
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef OFFSETS_H
|
||||
#define OFFSETS_H
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
class OffsetCalculator {
|
||||
|
||||
public:
|
||||
OffsetCalculator(std::filesystem::path offsetsFile, int baseOffset);
|
||||
void addAsset(int start, std::string symbol);
|
||||
|
||||
private:
|
||||
std::ofstream output;
|
||||
int baseOffset;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,10 @@
|
||||
#include "reader.h"
|
||||
#include "util.h"
|
||||
#include <string>
|
||||
|
||||
std::string opt_param(const std::string& format, int defaultVal, int value) {
|
||||
if (value != defaultVal) {
|
||||
return string_format(format, value);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
#ifndef READER_H
|
||||
#define READER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <vector>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
class Reader {
|
||||
public:
|
||||
Reader(const std::vector<char>& baserom, int start, int size) {
|
||||
auto first = baserom.begin() + start;
|
||||
auto last = baserom.begin() + start + size;
|
||||
data = std::vector<char>(first, last);
|
||||
}
|
||||
|
||||
u8 read_u8() {
|
||||
return this->data[this->cursor++];
|
||||
}
|
||||
|
||||
s8 read_s8() {
|
||||
return (s8)this->read_u8();
|
||||
}
|
||||
|
||||
u16 read_u16() {
|
||||
u16 val = (u8)this->data[this->cursor] + (((u8)this->data[this->cursor + 1]) << 8);
|
||||
this->cursor += 2;
|
||||
return val;
|
||||
}
|
||||
|
||||
u32 read_u32() {
|
||||
u32 val = ((u8)this->data[this->cursor]) + (((u8)this->data[this->cursor + 1]) << 8) +
|
||||
(((u8)this->data[this->cursor + 2]) << 16) + (((u8)this->data[this->cursor + 3]) << 24);
|
||||
this->cursor += 4;
|
||||
return val;
|
||||
}
|
||||
int cursor = 0;
|
||||
|
||||
private:
|
||||
std::vector<char> data;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
#include "util.h"
|
||||
#include <iostream>
|
||||
|
||||
void check_call(const std::vector<std::string>& cmd) {
|
||||
std::string cmdstr;
|
||||
bool first = true;
|
||||
for (const auto& segment : cmd) {
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
cmdstr += " ";
|
||||
}
|
||||
cmdstr += segment;
|
||||
}
|
||||
int code = system(cmdstr.c_str());
|
||||
if (code != 0) {
|
||||
std::cerr << cmdstr << " failed with return code " << code << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
typedef uint8_t u8;
|
||||
typedef uint16_t u16;
|
||||
typedef uint32_t u32;
|
||||
typedef uint64_t u64;
|
||||
typedef int8_t s8;
|
||||
typedef int16_t s16;
|
||||
typedef int32_t s32;
|
||||
typedef int64_t s64;
|
||||
|
||||
void check_call(const std::vector<std::string>& cmd);
|
||||
|
||||
template <typename... Args> std::string string_format(const std::string& format, Args... args) {
|
||||
int size_s = std::snprintf(nullptr, 0, format.c_str(), args...) + 1; // Extra space for '\0'
|
||||
if (size_s <= 0) {
|
||||
throw std::runtime_error("Error during formatting.");
|
||||
}
|
||||
auto size = static_cast<size_t>(size_s);
|
||||
auto buf = std::make_unique<char[]>(size);
|
||||
std::snprintf(buf.get(), size, format.c_str(), args...);
|
||||
return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside
|
||||
}
|
||||
|
||||
std::string opt_param(const std::string& format, int defaultVal, int value);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user