diff options
| author | octorock <79596758+octorock@users.noreply.github.com> | 2021-11-07 18:38:04 +0100 |
|---|---|---|
| committer | octorock <79596758+octorock@users.noreply.github.com> | 2021-11-07 18:38:04 +0100 |
| commit | e44d0e3fc9c417fb9c2c5943065b38e2707072bd (patch) | |
| tree | b8a7322297490b23cf698a15bb473dbd12d221df /tools/asset_processor/main.cpp | |
| parent | 473bc5f2600fe8ef31f32df8439074211dafd2d2 (diff) | |
Restructure asset processor
Diffstat (limited to 'tools/asset_processor/main.cpp')
| -rw-r--r-- | tools/asset_processor/main.cpp | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/tools/asset_processor/main.cpp b/tools/asset_processor/main.cpp new file mode 100644 index 00000000..4dc30249 --- /dev/null +++ b/tools/asset_processor/main.cpp @@ -0,0 +1,297 @@ +#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 <filesystem> +#include <fstream> +#include <iostream> +#include <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 << "Unrecognized argument `" << args[0] << "`" << std::endl; + std::exit(1); + } + } + + if (argCount != 3) { + std::cerr << "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 << "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 << "Unsupported variant `" << gVariant << "`" << std::endl; + std::exit(1); + } + + gAssetsFolder = args[2]; + gBaseromPath = baseroms[gVariant]; + + // 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); + + 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("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: { + if (shouldExtractAsset(path, configModified)) { + if (gVerbose) { + std::cout << "Extracting " << path << "..." << std::endl; + } + + std::unique_ptr<BaseAsset> assetHandler = getAssetHandlerByType(path, asset, currentOffset); + extractAsset(assetHandler, baserom); + } + 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 << "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(); +}
\ No newline at end of file |
