Files
Diddy-Kong-Racing/tools/dkr_assets_tool_classes/compiler/compiler.cpp
T

254 lines
9.5 KiB
C++

#include "compiler.h"
// Needs to be defined here.
#include "../libs/zip_file.hpp"
const std::string JSON_WRITE_MODE_REPLACE = "replace";
const std::string JSON_WRITE_MODE_MERGE = "merge";
const std::string DEFAULT_JSON_WRITE_MODE = JSON_WRITE_MODE_REPLACE;
const std::string BASE_DIR_NAME = "vanilla";
const std::string EXT_DIR_NAME = "packages";
void print_text(std::string text) {
std::cout << text << std::endl;
}
void print_coloured_text(std::string color, std::string text) {
std::cout << color << text << RESET_TEXT << std::endl;
}
AssetCompiler::AssetCompiler() {
print_text("Compiling assets...");
std::string assetsDirectory = get_asset_folder_path();
std::string version = get_version();
std::string vanillaDirPath = assetsDirectory + "/" + BASE_DIR_NAME;
std::string outDirPath = assetsDirectory + "/" + version;
std::string packagesDirPath = assetsDirectory + "/" + EXT_DIR_NAME + "/" + version;
std::string tempDirPath = assetsDirectory + "/_temp";
try {
// 1.) Create temporary directory for working files.
create_directory(tempDirPath);
// 2.) Create "compiled" directory for output files
std::string compiledDir = tempDirPath + "/__compiled__";
// Copy vanilla folder to the compiled directory as the baseline.
print_text("Copying vanilla files...");
recursively_copy_directory(vanillaDirPath + "/" + version, compiledDir);
print_text("Reading package order...");
std::string packageOrderFilepath = packagesDirPath + "/order.json";
bool isVanilla = true;
if(path_exists(packageOrderFilepath)) {
std::vector<std::string> packageOrder = get_array_from_json(packageOrderFilepath);
for(std::string &packageName : packageOrder) {
print_text("Applying package \"" + packageName + "\"...");
if(packageName.length() == 0) {
display_error_and_abort("The package \"\" does not exist!");
}
apply_package(packagesDirPath, packageName, tempDirPath, compiledDir);
isVanilla = false;
}
}
if(isVanilla) {
print_coloured_text(CYAN_TEXT, "Assets are vanilla.");
} else {
print_coloured_text(CYAN_TEXT, "Assets are modified.");
}
print_text("Merging modified files...");
merge_output(compiledDir, outDirPath);
// Remove the temp directory
print_text("Cleaning up...");
delete_directory(tempDirPath);
} catch (int e) {
delete_directory(tempDirPath);
throw e;
}
print_text("Done!");
}
AssetCompiler::~AssetCompiler() {
}
void ensure_meta_json_has_key(std::string &packageName, std::string &metaJsonFilepath, std::string key) {
if(!json_has_key(metaJsonFilepath, key)) {
display_error_and_abort("\"", key, "\" not defined in ", packageName, "'s meta.json file!");
}
}
void AssetCompiler::check_meta_json(std::string &packageName, std::string metaJsonFilepath) {
if(!path_exists(metaJsonFilepath)) {
display_error_and_abort("The package \"", packageName, "\" is missing a meta.json file!");
}
ensure_meta_json_has_key(packageName, metaJsonFilepath, "name");
ensure_meta_json_has_key(packageName, metaJsonFilepath, "pkg-version");
ensure_meta_json_has_key(packageName, metaJsonFilepath, "authors");
ensure_meta_json_has_key(packageName, metaJsonFilepath, "revision");
ensure_meta_json_has_key(packageName, metaJsonFilepath, "revision-minor");
int revision = get_int_from_json(metaJsonFilepath, "revision");
int revisionMinor = get_int_from_json(metaJsonFilepath, "revision-minor");
if(revision != REVISION_NUMBER || revisionMinor != REVISION_MINOR_NUMBER) {
display_error_and_abort("The package \"", packageName, "\" is not supported. It's revision number is ", revision, ".", revisionMinor, ", when it should be ", REVISION_NUMBER, ".", REVISION_MINOR_NUMBER, "!");
}
}
void AssetCompiler::apply_package(std::string &packagesDirPath, std::string &packageName, std::string &tempDirPath, std::string &outDirPath) {
std::string packageDir = packagesDirPath + "/" + packageName;
bool isZip = false;
if(ends_with(packageName, ".zip")) {
packageName = packageName.substr(0, packageName.length() - 4);
isZip = true;
} else if(!path_exists(packageDir) && path_exists(packageDir + ".zip")) {
packageDir += ".zip";
isZip = true;
} else if(!path_exists(packageDir)) {
display_error_and_abort("The package \"", packageName, "\" does not exist!");
}
if(isZip) {
miniz_cpp::zip_file file(packageDir);
packageDir = tempDirPath + "/" + packageName;
// Make sure a subdirectory with the name of the package exists in the temp directory.
if(file.has_file("meta.json")) {
create_directory(packageDir);
file.extractall(packageDir);
} else {
file.extractall(tempDirPath);
}
}
check_meta_json(packageName, packageDir + "/meta.json");
std::vector<std::string> jsonFiles = get_filenames_from_directory_with_extension(packageDir, ".json");
std::vector<std::string> otherFiles = get_filenames_from_directory_without_extension(packageDir, ".json");
for(int i = 0; i < jsonFiles.size(); i++) {
std::string localPath = jsonFiles[i].substr(packageDir.length() + 1);
if(localPath == "meta.json") {
continue; // Skip meta.json file.
}
std::string outPath = outDirPath + "/" + localPath;
std::string write_mode = get_string_from_json(jsonFiles[i], "pkg-write");
make_lowercase(write_mode);
if(write_mode == "") {
write_mode = DEFAULT_JSON_WRITE_MODE;
}
if(write_mode == JSON_WRITE_MODE_REPLACE) {
copy_file(jsonFiles[i], outPath);
} else if(write_mode == JSON_WRITE_MODE_MERGE) {
combine_json_files(outPath, jsonFiles[i], outPath);
} else {
display_error_and_abort("Unknown json write mode: \"", write_mode, "\"");
}
}
for(int i = 0; i < otherFiles.size(); i++) {
if(path_is_directory(otherFiles[i])) {
std::string localPath = otherFiles[i].substr(packageDir.length() + 1);
if(localPath == "meta") {
continue; // Skip meta folder if it exists.
}
std::string outPath = outDirPath + "/" + localPath;
create_directory(outPath);
} else {
std::string localPath = otherFiles[i].substr(packageDir.length() + 1);
if(starts_with(localPath, "meta/")) {
continue; // Skip files within the meta folder.
}
std::string outPath = outDirPath + "/" + localPath;
copy_file(otherFiles[i], outPath);
}
}
}
void AssetCompiler::merge_output(std::string &tempDir, std::string &outDir) {
std::vector<std::string> tempFiles = get_filenames_from_directory(tempDir);
std::vector<std::string> curFiles = get_filenames_from_directory(outDir);
std::vector<std::string> sameFiles, filesToAdd, filesToRemove;
for(int i = 0; i < tempFiles.size(); i++) {
std::string tempFilepath = tempFiles[i];
std::string localPath = tempFilepath.substr(tempDir.length() + 1);
if(path_exists(outDir + "/" + localPath)) {
sameFiles.push_back(localPath);
} else {
filesToAdd.push_back(tempFilepath);
}
}
for(int i = 0; i < curFiles.size(); i++) {
std::string curOutFilepath = curFiles[i];
std::string localPath = curOutFilepath.substr(outDir.length() + 1);
if(!path_exists(tempDir + "/" + localPath)) {
filesToRemove.push_back(curOutFilepath);
}
}
for(std::string &pathToRemove : filesToRemove) {
delete_path(pathToRemove);
}
for(std::string &pathToAdd : filesToAdd) {
std::string localPath = pathToAdd.substr(tempDir.length() + 1);
std::string outAddPath = outDir + "/" + localPath;
if(path_is_directory(pathToAdd)) {
create_directory(outAddPath);
} else {
copy_file(pathToAdd, outAddPath);
}
}
for(std::string &sameFileLocalPath : sameFiles) {
std::string tempFilePath = tempDir + "/" + sameFileLocalPath;
std::string curFilePath = outDir + "/" + sameFileLocalPath;
if(!path_is_directory(tempFilePath)) {
bool doHashCheck = true;
if(ends_with(tempFilePath, ".json")) {
std::string pkgRebuild = "onchange";
if(json_has_key(tempFilePath, "pkg-rebuild")) {
pkgRebuild = get_string_from_json(tempFilePath, "pkg-rebuild");
make_lowercase(pkgRebuild);
}
if(pkgRebuild == "always") {
doHashCheck = false;
} else if(pkgRebuild == "onchange") {
doHashCheck = true;
} else {
display_error_and_abort("Unknown pkg-rebuild value \"", pkgRebuild, "\".");
}
}
if(doHashCheck) {
std::string tempFileHash = calculate_file_md5(tempFilePath);
std::string curFileHash = calculate_file_md5(curFilePath);
if(tempFileHash != curFileHash) {
copy_file(tempFilePath, curFilePath);
}
} else {
copy_file(tempFilePath, curFilePath);
}
}
}
}