From 35ea62bb63fe8a76e5cb391374d549316a099321 Mon Sep 17 00:00:00 2001 From: lisyarus Date: Fri, 28 Aug 2020 09:29:39 +0300 Subject: [PATCH] Add an embedded resource compiler tool --- CMakeLists.txt | 1 + tools/CMakeLists.txt | 6 ++ tools/resource/CMakeLists.txt | 29 ++++++++++ tools/resource/compiler.cpp | 102 ++++++++++++++++++++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 tools/CMakeLists.txt create mode 100644 tools/resource/CMakeLists.txt create mode 100644 tools/resource/compiler.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 97a8fbf7..49ace2e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ if(PSEMEK_BUILD_TYPE STREQUAL "DEBUG") add_definitions("-DPSEMEK_DEBUG=1") endif() +add_subdirectory(tools) add_subdirectory(libs) add_library(psemek todo.md) diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt new file mode 100644 index 00000000..f4ee27eb --- /dev/null +++ b/tools/CMakeLists.txt @@ -0,0 +1,6 @@ +file(GLOB PSEMEK_TOOLS RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/*") +list(REMOVE_ITEM PSEMEK_TOOLS "CMakeLists.txt") + +foreach(tool ${PSEMEK_TOOLS}) + add_subdirectory(${tool}) +endforeach() diff --git a/tools/resource/CMakeLists.txt b/tools/resource/CMakeLists.txt new file mode 100644 index 00000000..026f11d7 --- /dev/null +++ b/tools/resource/CMakeLists.txt @@ -0,0 +1,29 @@ +add_executable(resource-compiler compiler.cpp) + +function(psemek_add_resources TARGET) + get_target_property(OUT_DIR ${TARGET} BINARY_DIR) + get_target_property(INPUT_DIR ${TARGET} SOURCE_DIR) + + target_include_directories(${TARGET} PRIVATE "${OUT_DIR}/resource/include") + + while(ARGN) + list(GET ARGN 0 FILE) + list(GET ARGN 1 NAME) + list(REMOVE_AT ARGN 0 1) + + get_filename_component(NAME_PREFIX ${NAME} DIRECTORY) + + file(MAKE_DIRECTORY "${OUT_DIR}/resource/include/${NAME_PREFIX}") + file(MAKE_DIRECTORY "${OUT_DIR}/resource/source/${NAME_PREFIX}") + + set(OUT_HEADER "${OUT_DIR}/resource/include/${NAME}.hpp") + set(OUT_SOURCE "${OUT_DIR}/resource/source/${NAME}.cpp") + + add_custom_command(OUTPUT "${OUT_HEADER}" "${OUT_SOURCE}" + COMMAND resource-compiler "${FILE}" ${NAME} "${OUT_HEADER}" "${OUT_SOURCE}" + DEPENDS resource-compiler "${FILE}" + WORKING_DIRECTORY "${INPUT_DIR}") + + target_sources(${TARGET} PRIVATE "${OUT_SOURCE}") + endwhile() +endfunction() diff --git a/tools/resource/compiler.cpp b/tools/resource/compiler.cpp new file mode 100644 index 00000000..5ed389a0 --- /dev/null +++ b/tools/resource/compiler.cpp @@ -0,0 +1,102 @@ +#include +#include +#include +#include +#include +#include + +int main(int argc, char * argv[]) +{ + if (argc != 5) + { + std::cerr << "Usage: resource-compiler " << std::endl; + return EXIT_FAILURE; + } + + std::string name = argv[2]; + std::string ns; + + std::size_t const last_slash = name.find_last_of('/'); + + if (last_slash != std::string::npos) + { + ns = name.substr(0, last_slash); + for (std::size_t i = 0;;) + { + i = ns.find('/', i); + if (i == std::string::npos) break; + ns.replace(i, 1, "::"); + } + + name = name.substr(last_slash + 1); + } + + std::string tab; + if (!ns.empty()) tab = "\t"; + + std::ifstream input_file{argv[1], std::ios::binary}; + std::vector input_data{std::istreambuf_iterator{input_file}, {}}; + input_file.close(); + + std::ofstream output_header{argv[3]}; + + output_header + << "#pragma once\n" + << "\n" + << "#include \n" + << "\n"; + + if (!ns.empty()) output_header + << "namespace " << ns << "\n" + << "{\n" + << "\n"; + + output_header << tab << "extern std::string_view " << name << ";\n"; + + if (!ns.empty()) output_header + << "\n" + << "}\n"; + + output_header.close(); + + std::ofstream output_source{argv[4]}; + + output_source + << "#include \n" + << "\n"; + + output_source << "static const char data[" << input_data.size() << "] = {\n"; + + for (std::size_t i = 0; i < input_data.size(); ++i) + { + if ((i % 16) == 0) output_source << "\t"; + + char const c = input_data[i]; + + if constexpr (std::is_signed_v) + { + output_source << (c >= 0 ? ' ' : '-') << "0x" << std::setw(2) << std::setfill('0') << std::hex << std::abs(static_cast(c)) << ", "; + } + else + { + output_source << "0x" << std::setw(2) << std::setfill('0') << std::hex << static_cast(c) << ", "; + } + + + if ((i % 16) == 15) output_source << "\n"; + } + if ((input_data.size() % 16) != 0) output_source << "\n"; + + output_source << "};\n\n"; + + if (!ns.empty()) output_source + << "namespace " << ns << "\n" + << "{\n" + << "\n"; + + output_source << tab << "std::string_view " << name << "{data, sizeof(data)};\n"; + + if (!ns.empty()) output_source + << "\n" + << "}\n"; +}