mrc-1.3.10/0000775000175000017500000000000014466617054012234 5ustar maartenmaartenmrc-1.3.10/.gitignore0000664000175000017500000000012114466617054014216 0ustar maartenmaarten.vscode/ mrc mrc-unit-test mrc-bootstrap build/ src/revision.hpp src/version.hpp mrc-1.3.10/.gitmodules0000664000175000017500000000012014466617054014402 0ustar maartenmaarten[submodule "libmcfp"] path = libmcfp url = git@github.com:mhekkel/libmcfp.git mrc-1.3.10/CMakeLists.txt0000664000175000017500000000767614466617054015014 0ustar maartenmaarten# Copyright (c) 2021 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. cmake_minimum_required(VERSION 3.10) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # set the project name project(mrc VERSION 1.3.10) include(Dart) include(FindFilesystem) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(Filesystem REQUIRED) if(MSVC) # make msvc standards compliant... add_compile_options(/permissive-) endif() if(WIN32) # Windows is always little endian, right? add_compile_definitions(LITTLE_ENDIAN) # And we certainly don't need those pesky min/max macro's add_compile_definitions(NOMINMAX) # Find out the processor type for the target if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") set(COFF_TYPE "x64") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") set(COFF_TYPE "x86") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64") set(COFF_TYPE "arm64") else() message(FATAL_ERROR "Unsupported or unknown processor type ${CMAKE_SYSTEM_PROCESSOR}") endif() set(COFF_SPEC "--coff=${COFF_TYPE}") message(STATUS "Using COFF spec: ${COFF_SPEC}") endif() find_package(libmcfp 1.2.4 QUIET) if(NOT libmcfp_FOUND) add_subdirectory(libmcfp EXCLUDE_FROM_ALL) endif() include(VersionString) write_version_header(${PROJECT_SOURCE_DIR}/src/) add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/mrc-rsrc.obj COMMAND $ -o ${CMAKE_BINARY_DIR}/mrc-rsrc.obj ${PROJECT_SOURCE_DIR}/src/mrsrc.h ${COFF_SPEC} DEPENDS ${PROJECT_SOURCE_DIR}/src/mrsrc.h mrc-bootstrap ) add_executable(mrc ${PROJECT_SOURCE_DIR}/src/mrc.cpp ${CMAKE_BINARY_DIR}/mrc-rsrc.obj) add_executable(mrc-bootstrap ${PROJECT_SOURCE_DIR}/src/mrc.cpp ${PROJECT_SOURCE_DIR}/src/dummy.cpp) target_link_libraries(mrc std::filesystem libmcfp::libmcfp) target_link_libraries(mrc-bootstrap std::filesystem libmcfp::libmcfp) if(ENABLE_TESTING) enable_testing() find_package(Boost REQUIRED) file(GLOB UNIT_TEST_RSRC LIST_DIRECTORIES true ${PROJECT_SOURCE_DIR}/rsrc/*) add_custom_command(OUTPUT mrc-unit-test-rsrc.obj COMMAND $ -o mrc-unit-test-rsrc.obj ${UNIT_TEST_RSRC} ${COFF_SPEC} DEPENDS ${UNIT_TEST_RSRC} mrc-bootstrap ) add_executable(mrc-unit-test ${PROJECT_SOURCE_DIR}/src/mrc-unit-test.cpp ${CMAKE_BINARY_DIR}/mrc-unit-test-rsrc.obj) target_link_libraries(mrc-unit-test Boost::boost) add_test(NAME unit-test COMMAND $ WORKING_DIRECTORY .) endif() install(TARGETS mrc) if(WIN32) install(FILES cmake/mrc-config.cmake DESTINATION cmake) install(FILES doc/mrc-manual.pdf DESTINATION doc) else() install(FILES cmake/mrc-config.cmake DESTINATION share/mrc/cmake) install(FILES doc/mrc.1 DESTINATION share/man/man1) endif() install(DIRECTORY example DESTINATION share/doc/mrc/)mrc-1.3.10/LICENSE0000664000175000017500000000244614466617054013247 0ustar maartenmaartenBSD-2-Clause Copyright (c) 2020 Maarten L. Hekkelman All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mrc-1.3.10/README.md0000664000175000017500000000624414466617054013521 0ustar maartenmaartenMaartens Resource Compiler ========================== Abstract -------- A long, long time ago there existed an operating system that did things differently. One of the cool features of this OS was that applications were self contained. A single file was everything you needed to copy in order to install a new application instead of having to use installers or install scripts that put dozens of files at the most obscure locations on your disk. One of the technical features of this OS to make this possible was what they called resources. In fact, resources were stored as some kind of database in one of the two forks of a file, the other fork being the data fork. Resources were mildly popular, other OS'es copied the concept in one way or another. The company that invented them abandoned the whole concept though, they thought they had something better.... Anyway, I still like being able to provide a single executable to the users of my software. And given the usefulness of resources I decided to create a compiler for them that works with the ELF executable format. Since using resource forks is not an option I decided to store the data in the static data section of an executable. The data is then available through global variables. New in version 1.3.4 is support for writing COFF files, so you can now use this type of resources in Windows as well. Synopsis -------- First, create the mrsrc.h file that contains C++ classes to access the resources. This file can be generated by executing: mrc --header -o mrsrc.h Then include this file and use it: ```c++ #include "mrsrc.h" int main() { mrsrc::rsrc hello("texts/greeting"); if (hello) { string s(hello.data(), hello.size()); cout << s << endl; } return 0; } ``` To create a resource file: ```bash echo "Hello, world!" > greeting mrc -o my-rsrc.o --root texts greeting c++ -o my-app foo.cpp my-rsrc.o ``` CMake integration ----------------- mrc comes with a mrc-config.cmake file installed at a suitable location. This means you can include mrc using a `find_package` call. The previous example might have a _CMakeLists.txt_ file containing the following: ```cmake project(hello VERSION 1.0.0 LANGUAGES CXX) # Include the mrc package file find_package(Mrc) # The MRC_FOUND variable is set if MRC was found if(NOT MRC_FOUND) message(FATAL_ERROR "mrc not found") endif() # The executable to create add_executable(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/src/mrc-user.cpp) # Write out the mrc header file and add the directory to the include paths mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) target_include_directories(mrc-user ${CMAKE_CURRENT_BINARY_DIR}) # Add the file hello.txt in the directory rsrc as resource 'hello.txt' mrc_target_resources(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/rsrc/hello.txt) ``` Building mrc ------------ To build mrc, you should use [cmake](https://cmake.org), e.g.: The `--recurse-submodules` option below is optional, if you have [libmcfp](https://github.com/mhekkel/libmcfp.git) already installed, you can leave it out. ```bash git clone https://github.com/mhekkel/mrc.git --recurse-submodules cd mrc mkdir build cd build cmake .. cmake --build . cmake --install . ``` mrc-1.3.10/changelog0000664000175000017500000000304714466617054014112 0ustar maartenmaartenVersion 1.3.10 - Check cmake version before trying to write dependency file commands - New version string code Version 1.3.9 - Updated cmake config file: added option to specify coff type in WIN32 manually. - Fix 32 bit version for Windows Version 1.3.8 - Avoid potential crash on uninitialised resources Version 1.3.7 - Added -d/--depends option to write out a depends file - Updated the cmake config to use aforementioned depends file Version 1.3.6 - Properly report files that cannot be read as input - Removed dependency on boost::program_options Version 1.3.5 - Properly set the badbit in case a resources is not found while constructing an istream. Avoids a crash. Version 1.3.4 - Fix the Windows version again. Version 1.3.3 - Add the ABI flag to specify ABI OS version in ELF headers Version 1.3.2 - Fix install rules in CMakeLists.txt - Added example code Version 1.3.1 - Fix writing header to specified output instead of stdout - Added a cmake config file, for easy configuration Version 1.3.0 - Use cmake as new build system instead of autoconf - Write COFF files (for MS Windows) Version 1.2.3 - Should now work on all architectures. - replaced cpu and eabi flags with more generic --elf-xxx options. Version 1.2.2 - Fixed error caused by switching to std::filesystem usage. Version 1.2.0 - Added istream class Version 1.2.0 - Added streambuf class - Added unit tests Version 1.1.0 - New interface file, now no longer throws exception when a resource is not found. So you should check the validity using e.g. operator bool. Version 1.0.0 mrc-1.3.10/cmake/0000775000175000017500000000000014466617054013314 5ustar maartenmaartenmrc-1.3.10/cmake/FindFilesystem.cmake0000664000175000017500000000432714466617054017251 0ustar maartenmaarten# Simplistic reimplementation of https://github.com/vector-of-bool/CMakeCM/blob/master/modules/FindFilesystem.cmake if(TARGET std::filesystem) return() endif() cmake_minimum_required(VERSION 3.10) include(CMakePushCheckState) include(CheckIncludeFileCXX) include(CheckCXXSourceCompiles) cmake_push_check_state() set(CMAKE_CXX_STANDARD 17) check_include_file_cxx("filesystem" _CXX_FILESYSTEM_HAVE_HEADER) mark_as_advanced(_CXX_FILESYSTEM_HAVE_HEADER) set(code [[ #include #include int main() { auto cwd = std::filesystem::current_path(); return EXIT_SUCCESS; } ]]) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS_EQUAL 8.4.0) # >> https://stackoverflow.com/questions/63902528/program-crashes-when-filesystempath-is-destroyed set(CXX_FILESYSTEM_NO_LINK_NEEDED 0) else() # Check a simple filesystem program without any linker flags check_cxx_source_compiles("${code}" CXX_FILESYSTEM_NO_LINK_NEEDED) endif() if(CXX_FILESYSTEM_NO_LINK_NEEDED) set(_found 1) else() set(prev_libraries ${CMAKE_REQUIRED_LIBRARIES}) # Add the libstdc++ flag set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lstdc++fs) check_cxx_source_compiles("${code}" CXX_FILESYSTEM_STDCPPFS_NEEDED) set(_found ${CXX_FILESYSTEM_STDCPPFS_NEEDED}) if(NOT CXX_FILESYSTEM_STDCPPFS_NEEDED) # Try the libc++ flag set(CMAKE_REQUIRED_LIBRARIES ${prev_libraries} -lc++fs) check_cxx_source_compiles("${code}" CXX_FILESYSTEM_CPPFS_NEEDED) set(_found ${CXX_FILESYSTEM_CPPFS_NEEDED}) endif() endif() if(_found) add_library(std::filesystem INTERFACE IMPORTED) set_property(TARGET std::filesystem APPEND PROPERTY INTERFACE_COMPILE_FEATURES cxx_std_17) if(CXX_FILESYSTEM_NO_LINK_NEEDED) # Nothing to add... elseif(CXX_FILESYSTEM_STDCPPFS_NEEDED) set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME stdc++fs) elseif(CXX_FILESYSTEM_CPPFS_NEEDED) set_target_properties(std::filesystem PROPERTIES IMPORTED_LIBNAME c++fs) endif() endif() cmake_pop_check_state() set(Filesystem_FOUND ${_found} CACHE BOOL "TRUE if we can run a program using std::filesystem" FORCE) if(Filesystem_FIND_REQUIRED AND NOT Filesystem_FOUND) message(FATAL_ERROR "Cannot run simple program using std::filesystem") endif() mrc-1.3.10/cmake/VersionString.cmake0000664000175000017500000002772414466617054017146 0ustar maartenmaarten# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2021-2023 NKI/AVL, Netherlands Cancer Institute # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # This cmake extension writes out a revision.hpp file in a specified directory. # The file will contain a C++ inline function that can be used to write out # version information. cmake_minimum_required(VERSION 3.15) # We want the revision.hpp file to be updated whenever the status of the # git repository changes. Use the same technique as in GetGitRevisionDescription.cmake #[=======================================================================[.rst: .. command:: write_version_header Write a file named revision.hpp containing version info:: write_version_header( [FILE_NAME ] [LIB_NAME ] ) This command will generate the code to write a file name revision.hpp in the directory ````. ``FILE_NAME`` Specify the name of the file to create, default is ``revision.hpp``. ``LIB_NAME`` Specify the library name which will be used as a prefix part for the variables contained in the revision file. #]=======================================================================] # First locate a .git file or directory. function(_get_git_dir _start_dir _variable) set(cur_dir "${_start_dir}") set(git_dir "${_start_dir}/.git") while(NOT EXISTS "${git_dir}") # .git dir not found, search parent directories set(prev_dir "${cur_dir}") get_filename_component(cur_dir "${cur_dir}" DIRECTORY) if(cur_dir STREQUAL prev_dir OR cur_dir STREQUAL ${_start_dir}) # we are not in git since we either hit root or # the CMAKE_SOURCE_DIR which should be the top set(${_variable} "" PARENT_SCOPE) return() endif() set(git_dir "${cur_dir}/.git") endwhile() set(${_variable} "${git_dir}" PARENT_SCOPE) endfunction() # Locate the git refspec hash and load the hash # This code locates the file containing the git refspec/hash # and loads it. Doing it this way assures that each time the git # repository changes the revision.hpp file gets out of date. function(_get_git_hash _data_dir _variable) # Be pessimistic set(_variable "" PARENT_SCOPE) # Load git package if needed if(NOT GIT_FOUND) find_package(Git QUIET) endif() # And fail if not found if(NOT GIT_FOUND) return() endif() # Locate the nearest .git file or directory _get_git_dir(${CMAKE_CURRENT_SOURCE_DIR} GIT_DIR) # And fail if not found if("${GIT_DIR}" STREQUAL "") return() endif() # Check if the current source dir is a git submodule or a worktree. # In both cases .git is a file instead of a directory. # if(IS_DIRECTORY ${GIT_DIR}) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # The following git command will return a non empty string that # points to the super project working tree if the current # source dir is inside a git submodule. # Otherwise the command will return an empty string. # execute_process( COMMAND "${GIT_EXECUTABLE}" rev-parse --show-superproject-working-tree WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT "${out}" STREQUAL "") # If out is not empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule file(READ ${GIT_DIR} submodule) string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE ${submodule}) string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") else() # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree file(READ ${GIT_DIR} worktree_ref) # The .git directory contains a path to the worktree information directory # inside the parent git repo of the worktree. # string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir ${worktree_ref}) string(STRIP ${git_worktree_dir} git_worktree_dir) _get_git_dir("${git_worktree_dir}" GIT_DIR) set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") endif() endif() # Fail if the 'head' file was not found if(NOT EXISTS "${HEAD_SOURCE_FILE}") return() endif() # Make a copy of the head file set(HEAD_FILE "${_data_dir}/HEAD") configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) # Now we create a cmake file that will read the contents of this # head file in the appropriate way file(WRITE "${_data_dir}/grab-ref.cmake.in" [[ set(HEAD_HASH) file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) if(HEAD_CONTENTS MATCHES "ref") # named branch string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") if(EXISTS "@GIT_DIR@/${HEAD_REF}") configure_file("@GIT_DIR@/${HEAD_REF}" "@VERSION_STRING_DATA@/head-ref" COPYONLY) else() configure_file("@GIT_DIR@/packed-refs" "@VERSION_STRING_DATA@/packed-refs" COPYONLY) file(READ "@VERSION_STRING_DATA@/packed-refs" PACKED_REFS) if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") set(HEAD_HASH "${CMAKE_MATCH_1}") endif() endif() else() # detached HEAD configure_file("@GIT_DIR@/HEAD" "@VERSION_STRING_DATA@/head-ref" COPYONLY) endif() if(NOT HEAD_HASH) file(READ "@VERSION_STRING_DATA@/head-ref" HEAD_HASH LIMIT 1024) string(STRIP "${HEAD_HASH}" HEAD_HASH) endif() ]]) configure_file("${VERSION_STRING_DATA}/grab-ref.cmake.in" "${VERSION_STRING_DATA}/grab-ref.cmake" @ONLY) # Include the aforementioned file, this will define # the HEAD_HASH variable we're looking for include("${VERSION_STRING_DATA}/grab-ref.cmake") set(${_variable} "${HEAD_HASH}" PARENT_SCOPE) endfunction() # Create a revision file, containing the current git version info, if any function(write_version_header dir) set(flags ) set(options LIB_NAME FILE_NAME) set(sources ) cmake_parse_arguments(VERSION_STRING_OPTION "${flags}" "${options}" "${sources}" ${ARGN}) # parameter check if(NOT IS_DIRECTORY ${dir}) message(FATAL_ERROR "First parameter to write_version_header should be a directory where the final revision.hpp file will be placed") endif() if(VERSION_STRING_OPTION_FILE_NAME) set(file_name "${VERSION_STRING_OPTION_FILE_NAME}") else() set(file_name "revision.hpp") endif() # Where to store intermediate files set(VERSION_STRING_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/VersionString") if(NOT EXISTS "${VERSION_STRING_DATA}") file(MAKE_DIRECTORY "${VERSION_STRING_DATA}") endif() # Load the git hash using the wizzard-like code above. _get_git_hash("${VERSION_STRING_DATA}" GIT_HASH) # If git was found, fetch the git description string if(GIT_HASH) execute_process( COMMAND "${GIT_EXECUTABLE}" describe --dirty --match=build WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" RESULT_VARIABLE res OUTPUT_VARIABLE out ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if(res EQUAL 0) set(REVISION_STRING "${out}") endif() endif() # Check the revision string, if it matches we fill in the required info if(REVISION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?") set(BUILD_NUMBER ${CMAKE_MATCH_1}) if(CMAKE_MATCH_3) set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}*") else() set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}") endif() string(TIMESTAMP REVISION_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC) else() message(STATUS "no git info available, cannot update version string") set(REVISION_GIT_TAGREF "") set(BUILD_NUMBER 0) set(REVISION_DATE_TIME "") endif() if(VERSION_STRING_OPTION_LIB_NAME) set(VAR_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}") set(IDENT_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}_") else() set(VAR_PREFIX "") set(IDENT_PREFIX "") endif() file(WRITE "${VERSION_STRING_DATA}/${file_name}.in" [[// This file was generated by VersionString.cmake #pragma once #include constexpr const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@"; constexpr const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@"; constexpr int k@VAR_PREFIX@BuildNumber = @BUILD_NUMBER@; constexpr const char k@VAR_PREFIX@RevisionGitTag[] = "@REVISION_GIT_TAGREF@"; constexpr const char k@VAR_PREFIX@RevisionDate[] = "@REVISION_DATE_TIME@"; #ifndef VERSION_INFO_DEFINED #define VERSION_INFO_DEFINED 1 class version_info_base { public: virtual ~version_info_base() = default; static void write(std::ostream &os, bool verbose) { auto s_head = head(); if (s_head != nullptr) write(s_head, os, verbose); } protected: struct instance { const char *m_name; const char *m_version; int m_build; const char *m_git_tag; const char *m_revision_date; instance *m_next = nullptr; }; static void write(const instance *inst, std::ostream &os, bool verbose) { if (inst->m_next != nullptr) { write(inst->m_next, os, verbose); if (not verbose) return; os << '-' << std::endl; } os << inst->m_name << " version " << inst->m_version << std::endl; if (verbose) { if (inst->m_build != 0) { os << "build: " << inst->m_build << ' ' << inst->m_revision_date << std::endl; if (inst->m_git_tag[0] != 0) os << "git tag: " << inst->m_git_tag << std::endl; } else os << "No revision information available" << std::endl; } } using instance_ptr = instance *; static instance_ptr &head() { static instance_ptr s_head = nullptr; return s_head; } }; template class version_info : public version_info_base { public: using implementation_type = T; protected: version_info() { auto &s_head = head(); static instance s_next{ implementation_type::name(), implementation_type::version(), implementation_type::build_number(), implementation_type::git_tag(), implementation_type::revision_date(), s_head }; s_head = &s_next; } }; inline void write_version_string(std::ostream &os, bool verbose) { version_info_base::write(os, verbose); } #endif class version_info_@IDENT_PREFIX@impl : public version_info { public: static constexpr const char *name() { return k@VAR_PREFIX@ProjectName; } static constexpr const char *version() { return k@VAR_PREFIX@VersionNumber; } static constexpr int build_number() { return k@VAR_PREFIX@BuildNumber; } static constexpr const char *git_tag() { return k@VAR_PREFIX@RevisionGitTag; } static constexpr const char *revision_date() { return k@VAR_PREFIX@RevisionDate; } } s_@IDENT_PREFIX@instance; ]]) configure_file("${VERSION_STRING_DATA}/${file_name}.in" "${dir}/${file_name}" @ONLY) endfunction() mrc-1.3.10/cmake/mrc-config.cmake0000664000175000017500000001526514466617054016353 0ustar maartenmaarten# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2021 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # 1. Redistributions of source code must retain the above copyright notice, this # list of conditions and the following disclaimer # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR # ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #[=======================================================================[.rst: FindMrc ------- The module defines the following variables: ``MRC_EXECUTABLE`` Path to mrc executable. ``mrc_FOUND``, ``MRC_FOUND`` True if the mrc executable was found. ``MRC_VERSION_STRING`` The version of mrc found. Additionally, the following commands will be added: :command:`mrc_write_header` :command:`mrc_target_resources` Example usage: .. code-block:: cmake find_package(Mrc) if(mrc_FOUND) message("mrc found: ${MRC_EXECUTABLE}") mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) mrc_target_resources(my-app RESOURCES rsrc/hello-world.txt) endif() #]=======================================================================] find_program(MRC_EXECUTABLE NAMES mrc DOC "mrc executable" ) if(CMAKE_HOST_WIN32) find_program(MRC_EXECUTABLE NAMES mrc PATHS $ENV{LOCALAPPDATA} PATH_SUFFIXES mrc/bin DOC "mrc executable" ) endif() mark_as_advanced(MRC_EXECUTABLE) # Retrieve the version number execute_process(COMMAND ${MRC_EXECUTABLE} --version OUTPUT_VARIABLE mrc_version ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) if (mrc_version MATCHES "^mrc version [0-9]") string(REPLACE "mrc version " "" MRC_VERSION_STRING "${mrc_version}") endif() unset(mrc_version) find_package(PackageHandleStandardArgs REQUIRED) find_package_handle_standard_args(Mrc REQUIRED_VARS MRC_EXECUTABLE VERSION_VAR MRC_VERSION_STRING) #[=======================================================================[.rst: .. command:: mrc_target_resources Add resources to a target:: mrc_target_resources( [RSRC_FILE ] [DEPENDS_FILE ] [VERBOSE] [COFF_TYPE ] RESOURCES ... ) This command will specify that resources should be added to the target ````. ``RSRC_FILE`` Specify the resource file (object file) to create, default is based on ````. ``DEPENDS_FILE`` Specify the depends file to create, default is based on ````. ``VERBOSE`` Pass the --verbose to mrc so it will print out the list of files added as resource and the path to be used to access them inside the executable. ``RESOURCES`` The files to pack into the resources of the executable. ```` here can be a file or a directory. Files will be added to the root with their filename. Directories will be added recursively, if the directory name ends with a slash character, the directory part of the name will be stripped. #]=======================================================================] function(mrc_target_resources _target) set(flags VERBOSE) set(options COFF_TYPE RSRC_FILE DEPENDS_FILE) set(sources RESOURCES) cmake_parse_arguments(MRC_OPTION "${flags}" "${options}" "${sources}" ${ARGN}) if(NOT _target) message(FATAL_ERROR "TARGET option is missing") endif() if("${MRC_OPTION_RESOURCES}" STREQUAL "") if("${ARGN}" STREQUAL "") message(FATAL_ERROR "no RESOURCES specified") else() message(DEBUG "no RESOURCES specified, falling back to use ARGN") set(MRC_OPTION_RESOURCES ${ARGN}) endif() endif() if(MRC_OPTION_RSRC_FILE) set(RSRC_FILE ${MRC_OPTION_RSRC_FILE}) else() set(RSRC_FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc.obj") endif() if(MRC_OTPION_RSRC_DEP_FILE) set(RSRC_DEP_FILE "${MRC_OPTION_DEPENDS_FILE}") else() set(RSRC_DEP_FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc.d") endif() if(CMAKE_HOST_WIN32) if(MRC_OPTION_COFF_TYPE) set(COFF_SPEC "--coff=${MRC_OPTION_COFF_TYPE}") else() # Find out the processor type for the target if(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "AMD64") set(COFF_TYPE "x64") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "i386") set(COFF_TYPE "x86") elseif(${CMAKE_SYSTEM_PROCESSOR} STREQUAL "ARM64") set(COFF_TYPE "arm64") else() message(FATAL_ERROR "Unsupported or unknown processor type ${CMAKE_SYSTEM_PROCESSOR}") endif() set(COFF_SPEC "--coff=${COFF_TYPE}") endif() endif() if(${MRC_OPTION_VERBOSE}) list(APPEND MRC_OPTION_RESOURCES "--verbose") endif() # If we can use DEPFILE, use it. if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21") add_custom_target("mrc-depends-file_${_target}" ALL BYPRODUCTS ${RSRC_DEP_FILE} COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} -d ${RSRC_DEP_FILE} ${MRC_OPTION_RESOURCES} VERBATIM) add_custom_command(OUTPUT ${RSRC_FILE} DEPFILE ${RSRC_DEP_FILE} COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${COFF_SPEC} ${MRC_OPTION_RESOURCES} VERBATIM) else() message(STATUS "Not using dependency file since cmake version is too old") add_custom_command(OUTPUT ${RSRC_FILE} DEPFILE ${RSRC_DEP_FILE} COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${COFF_SPEC} ${MRC_OPTION_RESOURCES} VERBATIM) endif() target_sources(${_target} PRIVATE ${RSRC_FILE}) endfunction() #[=======================================================================[.rst: .. command:: mrc_write_header Write out the header file needed to use resources in a C++ application. The argument specifies the file to create. #]=======================================================================] function(mrc_write_header _header) execute_process( COMMAND ${MRC_EXECUTABLE} --header --output ${_header} ) endfunction() mrc-1.3.10/doc/0000775000175000017500000000000014466617054013001 5ustar maartenmaartenmrc-1.3.10/doc/mrc-manual.pdf0000664000175000017500000006405614466617054015543 0ustar maartenmaarten%PDF-1.4 %쏢 %%Invocation: path/gs -P- -dSAFER -dCompatibilityLevel=1.4 -q -P- -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sstdout=? -sOutputFile=? -P- -dSAFER -dCompatibilityLevel=1.4 ? 5 0 obj <> stream xWnG]vؙSRT@JP5^ㅽDm߀>J߱ߙٵNIBQ$9;g3 7+g; -d%{=| K>OR(g"x")gsk+C{"teUO3R@E ),4$#~g΋_)Ë|vx=r`Yۺo2Ͳ<2W'\sE/_<;9`DvtO<1xևw ]/umlA,qyΫ#Sho"'N]w&E<GQe%E(7D> H}-{|r2nOTxyB∧=X6y󉬉"$`'+TZ |ir ~cg:udFؤ`&2c f2?Iow{#1 =c@ZH(ܧ,AWk֑Kۦo4mTT>.;/ios8ՐnXQnw<]"NZ\Z&t.:묪u_eFꦸG!8+n::LphͩzFZbĩ'}G]z\B`лbOȪ1U3DY|9Cن*Vm"I6kG2v C0NTv:[Yt(vkKUe@Q."EpJyDu?6nVY.-KpBk2E5M]q/-9$IvÑZܿ44ס/eL݃7ϛn}7>)8fVۑZ*8R5C5~`>!2+$ "f`WuoX;`\Tt`N [RjZ* _Ofo6?L PwێGfv,lS>a gͅo!#$ lA,;jsgP@` ꁌ*5l>>xw*GG%zk@AM1yQ/U.2nSU{~hC_/Kҧ]d8cT (45q!bۗpzp|"SAc"xOߦc{,XcD#(ر2єHԷ7M`x 1sC,_,`z;$ء.=)"  P%͏endstream endobj 6 0 obj 1772 endobj 15 0 obj <> stream xXn}W4bN {HI4Ixb&dw[_[٬\zcS}<*o}m3M\?".]rx[P'{ qWyOPU^mK$˜l%Ew®>3fq/<ܘr楁~]ݐRZ!svs𨷿ˬkIAhg(b*(hG֏STKh#.h2\lj?~> oo!tK| 6 L䊡A<#+4BG$]w(Zum NY_W xAȜ'RT&}}Y8rA4DiԷӷ薇s\yL(MHHdh̃qq78:QoYuD4١ D}# =ϹsbNSMQ$o|snETvQSNb u̹ i&A"6Cۮn$ ]|Cdֿ*˜`dCN4J$d ҙL;H/lN!dqb x7Q”ˠhꄶ_1lڢqeˁ|/D ջ<x&lu'Qjd>uh=ɬ&K"O޿'JϳFjSNANc/86Gs0C4yxBcEu-, Ccݫ97cXy~%U"ˠ*di IΣȶ8CqfoHֲ `;&ȁ,kj;D|l\)e5X<}6&.\K! lV.0o0E;~.+Dcp8}}%ge$4viuTf~a,F[-ͻǖkR{<:Q?,QOT?zuUgiɲ3a[_ ^'m3k8qBM.O;ɵ(JG?U ٬!GpTME}.- jG"<'%gY\CqHa|}> eʧQם1L]y8ۊb_e̤B˲\T{HN_Y Y6 L|kTb\20^4Ķt4d̾u5A6ŒкG7 %||4S'HfbHASFcIx]l2LN3x(8nU/h\JҖ 0k8VA1\ M0P^/&#Ī}B՜J-Rzn&?x߀FLS5{Qxyb2l_ywF(ĨmE9e㛢UݷbKh^\ !Mܚ<К݁/ əUMR0hvA#¦K,z0. % [VEʣa2m9[rZJJ->BOSa Kㅂ@Ŭ"\ xF0~⸿?`)'\r#:{XˎZyZhF>V41%7MoDghmSYd[#L0`h)fP 9+$ ݿցms|-͜㰑}Q%1c_̜CN*@sHDa7kUb$rj)F%oBA@h4~hyNaV{aZ{q v< d5 xbL 74T/&$`p3LbmLe9 aqaú |uc^=ZwbNb:ovp*aܭYdf aPk1 l0!M:=@av:瀣 I}|/endstream endobj 16 0 obj 2273 endobj 19 0 obj <> stream xXێ}W`^wxl0H`[~0ցAQ)\&Sl̎< ]}ԩjL8,Y)~~.-,Q,4O3>,d"xn)8Oi~L\Hp!ێ,":fOo>i_~X1`JBrBwzo2Y(X/;VEuږrל-^Xxz!x&ٶlqHӖU<βuXkDsAۋZ"Sp_.T]aW ZWiUttJG{7U\b#\(s;=@E2X6o}Yˋt#)HIڰ۸ %O1MHA[hԆffO- ΰENے ^G" 3 d-LQȢ萍Y.5_r3"k+hB 5MgAon\E6"c3.,^E[=AY! l[Բx xLw+1;aR\ Aa>k#7Vx$F石O0D?|Cp6frALg޷'`{O(611Z[$ d%Q `5$fQ#5!:Wgw  ~ʛIFW\ՕL< =uё̑vL$!rn* C5ND lBWW${\"@@APAC>F8N8uE )v.lvo&5bM'xJ U\}js5\ JdJ3V<ȤD89I_&v=VMsic^\%Cœt4hZ9!.Z Z>>B4{`/}۷_ύRt=zMsm,vpH\'6+t@0Q@n@I`ay Tuai S7c!Iѱ?3H Rc7[?tHQDAް24B E@4P:Xt-~-XTx6Y;FcIލofs "|1(N 4c\Jӭ@W']ط5F1(OF$My ţXpLpRZ#?`j@%sḽ$7G MT'#.]M٪l0A_Ŵ*F-у2[`fi=E;^iL5lM[1 KEvhșHL©1MhhR>$]z΋Y\l,KaA? /P5D أ fB̨1E #934D T1C8k^ ʓtq}UY% x$ /qdƑm4v=Ѱ.b]wX[2f b\oP` bL}VҝPפY1T%Oc5~ .-h8ɫlgW<EAm0Fk lK鬹q:&1\ ;i"uy> /Contents 5 0 R >> endobj 14 0 obj <> /Contents 15 0 R >> endobj 18 0 obj <> /Contents 19 0 R >> endobj 3 0 obj << /Type /Pages /Kids [ 4 0 R 14 0 R 18 0 R ] /Count 3 >> endobj 1 0 obj <> endobj 13 0 obj <> endobj 17 0 obj <> endobj 21 0 obj <> endobj 11 0 obj <> endobj 25 0 obj <> endobj 9 0 obj <> endobj 26 0 obj <> endobj 7 0 obj <> endobj 27 0 obj <> endobj 12 0 obj <> endobj 22 0 obj <>stream xypW XXq봄@@i )6-|Ⱥ[+,$ˇlcp$t(&Ni:[LЙy;rSRX҂A*̡_B˼lF 2dIDȋlwHglw376uT?es =h'z6mɴ:IQqb3Iݙ#4aFIPs0'E/ Vvif\]&MHpj" AQAI*=o? O\AL#Qf*gM/uF:Ï`lBq=!,0jw @'/fjVn2(T$hۛ77NC# P8.ᛷrGקgڎHJqg e+6ߚ- UK{a8w|~lDg"CX߈És9'T1y@mfآ-d07*ţ Ok ^C[Xŭ7nm9yGeGK 'fƒ1Sk]n.QC$e%½[6._ĜAZE2QeD}PEڤB\}xwXv^؞ \# aEEyb"+QkG;vl-64e[e< bDB&Yv$+UN+Fjcl#$ƬWv9(oao|TKbE'.CO ' sCzAH~w)bꊢ A0Y gBUTE4p̆2 NiV7 X=((^Dj+k;)8*"v픁;㙙h<W YQz_?<{jhj; n38 v}G,¼Τ0%ox4QOx(P9u$\,8nc^z蘴k/3YʬdRwz筯n_cdpiK)^T"ּDYY1vTs.S˟Te"syAJjaW5x hstv+eVZ]{4~{ovb_1&-P퓦cu=̡[0B9zp vA Pt*|[FPo 秳}/>x ~Ŝ]XncU+hA[]Mn7drSlX:PɆ^fuΑ:ꂭ!mO' CbcWK+bߞ/:|s+"q?󵾡(4 :ѡ̼EAg}[v.Wf"-2ەCpQS'/zl XE.6~ ?d.jVVY5[j<6r $~U& I6Td)o%:IKm%d@zGzÔJBRRtEJB@MG閴Wc硘tg_I XK!(q {IpYˑs>>&Eg2 Wp̡7dI- :ue6,, vњ I-06FOkF>(W.Ѽ9kSk!iP*e6JTRs>حꁈZ0\/zۀe VDrPMJ R%j o ~а&P5}U'~n?} /}B_x*Ef34V6`wٽ6MP$E&3sy3nBiuu:{K3{C1Om{D {6kThn6/O-ݚĪMnĖj2:E QWDULI"64qCOǥB\X2tfk7.[}IqE+TTElIЫNSj€3mJOc$- 07SF&~t碍3f r2 endstream endobj 10 0 obj <> endobj 23 0 obj <>stream xWiXS׺1&{ *Vu֡uEEf3 !dXa  A@uhjuxzڞV=Vz~.=}AY8x"c%d1?fY|Y2C)GfuΜTh )Do,\hfY|<2<"-w Nu-REdx|!Y#%zF')b~E-wx-[I;C }`Ln>\n)o;5Kޢ>j5PA6CmP%zF-SQ;2ʃZN}@PKMx+5EI(G*rRxj"N}KN0?8Q8Q1sB'\A;uyך_A9p;v\lR>r;Y28ٔK.:8g7wq(ST%|0dgmV2T\dB,;']I?* 5bk'.$l[SP:+kTӍcz` Mmqd0aeզn ]ZO& /$ 2 ȱj3dB2FtZ0&[siT'5F[m]|znI.G)"mxEm1 TWQґG>L (;-p8!6L>Co=RHj[Fu} P&"+:~A2H!HH.CplW4.:r,at`Dka-S[3`z{~w5b<,ת1cJRoaSBѓ o0xM3M쨪}y:pɜ(8qnm  =X.o,gⷞ=Ǭ^=WwR|y|Liao%Xks\EYP::!*}1Pޠ@L}uIfC9fhTZ"a/ 22?&FTYfa`7 p/L98Z+\%V. ?!nvG,/O'#WYFM>yJ x1a;87^9 T{~ Bc `p`c t8ˎK!ϷC WtoF1#:<\LWqB xᔴbNrK}+ NUm%r𜶫nqnxBkF ѧkZUQXqn鋲Qs8.BXι6N Ƴ`t-.:덹5=FUw뇰?!ȪnUV2}*bbѓOmM4v? KMDg+ Q/j1w}>W1MWϞc]Ǜ)lZavAv9v3pmZ=oDHy\R'tEɧ5UVZ"%G\⣲e&+'Cj ]JO.F՟_TR\zBV\^R*Q"-Ҩsss 9+Ԍ紥Y`fE O Y?"@>D!|=[knEpN%vŷ ԥQ\!FMN]Op1thi8F-Z]#^79WsKdž%#f0kCjmF0 !d:Rԅ3; fXh!LU/[^\3e]_J.yyw,r1\$ɭIGd2֬;FX1\ cMB()+ ՚Dz;fU Yi~LҸ1sB_f1w9'r}DN`"}ke+Y ]Y6q5 ó~狭 V8Lzyp@ԊuJbrFb?7mjkBZeT^+}>HI ח0|HX^TV(%w-h F -F5^ij;0 >BQ܉V1BaΟT]g|-NSVx7`[ސjXhUGa>SvA7kt;ð;^$#, 'I_.c I8{bf0x1׀$D X 6Qi-V[Nٚ\}NN8KDLLft_ϊ>xqr6}24:R ~"aC'~`-Ֆ1I S[A_-}?0,$#V̌y=I:=4d0r`j*Zm n4!QHނ^z|u1ŹER㌰vp"FNdE!3ʝ#*2IV=\љڨ7D3|`zg=L+LB\Cn|xwMԭ9%kfD׍b?iL;̯A"Zbj3~Xjӧ':%^ziq|FOCa)|І(21l|A̿B&@H66 3t|ZkhnBp.Z)9 6>';M~25~E3Uhnp/0Țn;TߙzK&¢ƒ3r}}}$X8xw?NA]_; E,5^&"{(+ 2 Jdݐ_ v`N 'P'50S 1+mXҮvGaG:ϭKF{v)Ãq'DruYF .`0z]t}Yy whgUk/!=dQp~jQ,&GǑqj/#%++5ɺd?4qaqn K2c܊I$m# 4U8'*:sgn U Wk+kJtU+ƗUaE|=3X`~ ޚ.zF~D{x{b\t̽|2iX*)\ 1Rn> endobj 24 0 obj <>stream xxyXS׺!{o nL&vZcJcE 2 <BBHJHqlZ:P;TN{tVwp}k}~8 KYq8!.8>^ƺr؅3J+M\0ۡfዟ8~ 0}.l8W˗3!139*"2uk- \̢]a)Q^&$ƅŧ IKY4uai?Anߑ3q=)gC3Oy8+;'7o[^v{M,ٲ@c+VZFV&ALGBxob5‹XBx>/#DXA!v+@b7XM%ěĻ:p'"CF!K+4!"fM,b3G3fgy9/10ȹUϽ7/h=َNNNV'w_4?Xy"ts.`˂Q>g|mZtyeK/8g"ؠ)%g7*K#GAI<+P+#V*0XQX.3yBZ^Xd,bXBMfRRҨ76|:wqa 0fnvRԖu|}~ ʳRj*tiItyXɁ''RIhқl!=Pds&VvyP368P5Әmۖ-w~E=kz:mg90(ikJ,{rvzPk>\EۄG7`H-~rL #2S;@ -(N Z?xNQ6k9-ŠoȞ`Ђ5ˑ-a t?EG4_][^D}BE Y_nNEpbs'&1(ALrA PɕR J磥0YQfV @)0i+S_Kp͜y?׬ZCY\ o|X{@},d1Nx:=Kr(-I]< :poyxQjOzdDEEtgh2 v:WmL[L?qdΣ[~u%![ {HH:n^Z7gmc!N7zШNL>Q r6 0m% +ϩ QxO:@ca!맠q.#|I _:^щ+FԘ\^FAi*)[&ߠJL[  ]Zr-@WxbMYa2lNl\h<:CgȪ^a~BsI4 -2M9-U鶨)+-B敉[ 2RZ?]CR bɔbDM^U rMvo]xɃ/&Wj4( SKR SN_FweTXAPLTaZ]N͠L[g1{R:!⃄*i=hjkN8){c\X2A,5B qQJm$_5j%rCk{;洤4 6g4S6qYmE2}&G5mVփRZzz.U]I/f<6cpFC1JtgiZ+n)AHdĴXyCc:O:EHBj9Oz*T|B0)႕t3*v ˾0b./I[ vd6v&a"lϷ(()U-QCW~YlIv(%eeMTfA1(4UC!+І+z_an3\#tbP(0i֧uzz#֌[mPW,Қt%nj?#E鍠`d%;8wKyFd}v77(5ڽ`'6 b3FD( (a/D#J=_-l6^ dh@%MPk#b<€}})[f4R fP`,&o23Bϒ B;{ebOzV֥þ]̂2|}gAPh,r@C!4ìK3)(d =<$b?hVb]W =&e'V"z]{mg;E|d_gJ%W*$Ix@o΅G^:wWdG0R z"8 N_Uko%=XG3â$n4_<Bw7@}B)ff Iv $ B f<}եâ߄~UD\%?&rhCr@G8 KGVII$FF6grxϧ}kG=-6^n͜xӎpfm-WW +ʔUXiTWu5xN^ |p/q>0,ISo!1Fc<e'B`_$}[j,W닲XRY'3ᒒ)IKhhuOp+?_97:ϧ9Vƭ g#[r=@qzR &ojWOANM |R>Ggi̮q>;l 6ͺt"^ƨ1 JF599jP,2k V@_~K*?bqrBFy d˲q>n RAzNE7?b\Yֆ;F w^bƒaxث%v#g3U= ?څyzdB?s[ A>aiye\i8Ct7*JuiGlVbbe֗i/7d*l8fɑj%HYDyZ2Z`[Pr UL2`-M-$Lfv<1a 1a!sAwqN,]07 Tuz63B=Txy=ByYwT@{Y@_N(BCTE *S> [<<}W|qR|Q _QQ)+P(WwMv7`rQS CL@|ć?KA}]%lbꊮ4sSBUyS/.E d>vOd7ux)@.|:l6_:wl-pJg^zΤ'D(gqA*OG7Y30|eOlcGD59t)&sO̠ U .M4(1_߉?#7}~.4,6SoL01V ʇ렅W3S%k Re C3{4:X 8@W33znk͹6$FNWz_6 "F d(rSd>wzM`nWg֣͢i@.1YBڠ ﷑ed+<$qmSX`7A#NFXU Гɰs!k858z|\ b⒓c[[ےD9ն_rN^b>#{GE k=z})Pzג"unLBO}8%spmrö̤~?'Xʁ{ftOw<2%5*V[\d?Ӣ"ixۇv5Sa3U ^C ĞxjGʹ\xGÏZYSi1eMGS(g/n4lh&2ԟ^=3W]8?9r.֯]Խ)y[>6x9-PvIV7L8_?yLhXOEeܞMfs~9}6p:㶱뙻!ݯ{0/%b½.`Isj@Y ުObC u=}g@37DZRY7дegґQ\"\ރfH9FЩ&H9ߑ2}-,]FMƠo1ۤvJ?xPVfz(]4>c| &>{2_8#jr]5@iNG Eɮим7]$yaݳV'N :`(wr;&FL\CON]TA(:b{u>XʸC-#E={]/p0F3bCR'SrfoeZ! 7S/ ۫GF}Xv6Sh.־D=nwfdnz1Nx(a$f9{r hjV=b֘1"_k%vws{ۿK=uS._d =:%fǂ_~_paL4FF{ ڰV Ouq$|m5̝XVkeM.'6Ǻhawn[Wg6p7v4}o~w~$>pu9R{-2ÅՒb VRYz@ڽOO=pYC mVmʀ IL0.A%?zo8!LN᩽)Y[ Wx8{!o9 endstream endobj 28 0 obj <>stream 2022-12-03T11:16:57+01:00 2022-12-03T11:16:57+01:00 groff version 1.22.4 Untitled endstream endobj 2 0 obj <>endobj xref 0 29 0000000000 65535 f 0000007062 00000 n 0000025780 00000 n 0000006989 00000 n 0000006559 00000 n 0000000182 00000 n 0000002024 00000 n 0000008286 00000 n 0000017198 00000 n 0000007730 00000 n 0000011917 00000 n 0000007266 00000 n 0000008976 00000 n 0000007127 00000 n 0000006701 00000 n 0000002044 00000 n 0000004389 00000 n 0000007177 00000 n 0000006845 00000 n 0000004410 00000 n 0000006538 00000 n 0000007227 00000 n 0000009265 00000 n 0000012287 00000 n 0000017722 00000 n 0000007645 00000 n 0000008198 00000 n 0000008864 00000 n 0000024519 00000 n trailer << /Size 29 /Root 1 0 R /Info 2 0 R /ID [] >> startxref 25936 %%EOF mrc-1.3.10/doc/mrc.10000664000175000017500000001512314466617054013646 0ustar maartenmaarten.TH mrc 1 "2022-12-03" "version 1.3.6" "User Commands" .if n .ad l .nh .SH NAME mrc \- A resource compiler .SH SYNOPSIS mrc [\fB-o\fR|\fB--output\fR outputfile] [\fB--root\fR arg] [\fB--resource-prefix\fR arg] [\fB--elf-machine\fR arg] [\fB--elf-class\fR arg] [\fB--elf-data\fR arg] [\fB--elf-abi\fR arg] [\fB--elf-flags\fR arg] file1 [file2...] .sp mrc [\fB-h\fR|\fB--help\fR] .sp mrc [\fB-v\fR|\fB--version\fR] .sp mrc [\fB--header\fR] > mrsrc.h .sp #include "mrsrc.h" void foo() { mrsrc::rsrc data("/alerts/text.txt"); if (data) { mrsrc::istream is(data); ... } } .SH DESCRIPTION Many applications come with supplementary data. This data is usually stored on disk as regular files. The disadvantage of this procedure is that an application cannot simply be copied to another location or computer and expected to function properly. .sp Resources are a way to overcome this problem by including all data inside the executable file. The mrc resource compiler can create object files containing both the data and an index. This data can then be accessed from within an application using .BR C++ classes. .SH OPTIONS .TP [\fB-o\fR|\fB--output\fR] file Specify the output file, the resulting file will be an object file you can link together with the rest of your object files into an executable file. .TP \fB--root\fR The resources are accessed using a path. You can specify the root part of the path using this parameter. .TP \fB--resource-prefix\fR name Use this option to specify another name for the global variables in the data section. .TP \fB--elf-machine\fR arg By default mrc assumes you want to create a resource file for the machine it runs on. But using this option you can create files for other architectures, useful when cross compiling. .sp The machine flag is used to specify the value of the \fIe_machine\fR field in the ELF header. .TP \fB--elf-class\fR number The ELF class to use, should be either \fI1\fR for 32-bit objects or \fI2\fR for 64-bit objects. .TP \fB--elf-data\fR number The ELF data endianness to use, should be either \fI1\fR for little-endian (=LSB) objects or \fI2\fR for big-endian (=MSB) objects. .TP \fB--elf-abi\fR number The ELF OS ABI flag to use, the exact value for this flag should be looked up in \fIelf.h\fR. Default is to use the value for the current architecture. (Value of 3 is for Linux, 9 is for FreeBSD). .TP \fB--elf-flags\fR number A value to store in the \fIe_flags\fR field of the ELF header. This can contain the EABI version for ARM e.g. .TP \fB--coff\fR type When this option is specified, a COFF file is created for use on Windows. The argument \fItype\fR should be one of \fBx64\fR, \fBx86\fR or \fBarm64\fR. .TP \fB--header\fR This option will print a \fBmrsrc.h\fR file to \fBstdout\fR which you can write to disk and use to access resources. Use with the \fB--output\fR option to write to a file instead. .TP [\fB-v\fR|\fB--verbose\fR] Print debug output, useful to track where all data ends up in the resource. .TP \fB--version\fR Print version number and exit. .TP [\fB-h\fR|\fB--help\fR] Print simple help summary and exit. .TP file [file...] One or more files to include in the resource file. Directory names can be used as well. All regular files end up in the root of the resource tree, files found in directories end up in directies in the resource tree. The following rules apply: .sp Regular files are added in the root of the resource tree using their proper file name. .sp If the file name refers to a directory, the directory is traversed recursively and all files are added. If the file name ends with a forward slash (/) files are added to the root. If the file does not end with a slash, the name of the directory will be placed in the root and all contained files will be placed beneath this. .sp .SH EXAMPLES .PP Here's a list of usage cases. .TP mrc -o x.o my-resource.txt my-image.png Will create a resource file containing two resources accessible using the path "/my-resource.txt" and "/my-image.png" respectively. .TP mrc -o x.o img/my-image.png Will create a resource file containing a single resource accessible using the path "/my-image.png". .TP mrc -o x.o img/ Assuming there are two images in the directory img called my-image-1.png and my-image-2.png, the resource file will contain them both accessible under the name "/my-image-1.png" and "/my-image-1.png". .sp mrc -o x.o img Same as the previous, but note there's no trailing slash, the resource file will contain both images but they are now accessible under the name "/img/my-image-1.png" and "/img/my-image-1.png". .PP Use the verbose flag (\fB--verbose\fR) to track what ends up where. .SH DETAILS .sp The way this works is that mrc first collects all data from the files specified, including the files found in specified directories. An simple index is created to allow hierarchical access to the data. The data is then flattened into three data structures and these are written to the \fB.data\fR section of the object file. The three data blobs are then made available as globals in your application with the names \fBgResourceIndex\fR, \fBgResourceName\fR and \fBgResourceData\fR. You can specify the prefix part of this variable with the -fB--resource-prefix\fR option. .sp The index entries have the following format: struct rsrc_imp { unsigned int m_next; // index of the next sibling entry unsigned int m_child; // index of the first child entry unsigned int m_name; // offset of the name for this entry unsigned int m_size; // data size for this entry unsigned int m_data; // offset of the data for this entry }; .sp The classes in the \fBmrsrc.h\fR file are contained in the namespace \fBmrsrc\fR. The available classes are .TP \fBmrsrc::rsrc\fR This is the basic class to access data. It has a constructor that takes a path to a resource. Data can be accessed using the \fBdata\fR method and the size of the data is available via the \fBsize\fR method. If the resource was not found, \fBdata\fR will return \fBnullptr\fR and \fBsize\fR will return zero. You can also use \fBoperator bool\fR to check for valid data. .TP \fBmrsrc::streambuf\fR This class is derived from \fBstd::streambuf\fR. It can take both a \fBmrsrc::rsrc\fR or a path as constructor parameter. .sp .TP \fBmrsrc::istream\fR This class is derived from \fBstd::istream\fR. It can take both a \fBmrsrc::rsrc\fR or a path as constructor parameter. .SH BUGS This application can only generate ELF formatted object files on machines that have an \fB\fR header file installed. .sp Only a single resource entry can be generated and there's no way to merge or manipulate resource files yet. mrc-1.3.10/example/0000775000175000017500000000000014466617054013667 5ustar maartenmaartenmrc-1.3.10/example/CMakeLists.txt0000664000175000017500000000140214466617054016424 0ustar maartenmaartencmake_minimum_required(VERSION 3.16) # set the project name project(mrc-user VERSION 1.0.0 LANGUAGES CXX) # Locate the mrc executable and load the mrc_* functions find_package(Mrc) # resources depend on C++17 features, like std::filesystem set(CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) # Write out an mrsrc.hpp file and make sure the compiler finds it mrc_write_header(${CMAKE_CURRENT_BINARY_DIR}/mrsrc.hpp) include_directories(${CMAKE_CURRENT_BINARY_DIR}) # The executable to create add_executable(mrc-user ${CMAKE_CURRENT_SOURCE_DIR}/mrc-user.cpp) # Add the hello.txt file as a resource, more files and/or directories # can be specified here mrc_target_resources(mrc-user RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) mrc-1.3.10/example/hello.txt0000664000175000017500000000001514466617054015527 0ustar maartenmaartenHello, world!mrc-1.3.10/example/mrc-user.cpp0000664000175000017500000000070214466617054016127 0ustar maartenmaarten/* Example of the use of resources created with mrc in a C++ application */ #include // Include the generated header file #include "mrsrc.hpp" int main() { try { mrsrc::rsrc res("hello.txt"); if (not res) throw std::runtime_error("Resource not found"); std::cout.write(res.data(), res.size()); std::cout << std::endl; } catch(const std::exception& e) { std::cerr << e.what() << '\n'; exit(1); } return 0; } mrc-1.3.10/libmcfp/0000775000175000017500000000000014466617054013650 5ustar maartenmaartenmrc-1.3.10/rsrc/0000775000175000017500000000000014466617054013205 5ustar maartenmaartenmrc-1.3.10/rsrc/resource-1.txt0000664000175000017500000000006214466617054015731 0ustar maartenmaartenThis is the first line And this is the second linemrc-1.3.10/rsrc/resource-2.txt0000664000175000017500000000014614466617054015735 0ustar maartenmaartenThis is the first line And this is the second linemrc-1.3.10/rsrc/subdir/0000775000175000017500000000000014466617054014475 5ustar maartenmaartenmrc-1.3.10/rsrc/subdir/resource-3.txt0000664000175000017500000000002114466617054017216 0ustar maartenmaartenDit is resource 3mrc-1.3.10/rsrc/subdir/subsubdir/0000775000175000017500000000000014466617054016477 5ustar maartenmaartenmrc-1.3.10/rsrc/subdir/subsubdir/resource-4.txt0000664000175000017500000000002114466617054021221 0ustar maartenmaartenDit is resource 4mrc-1.3.10/src/0000775000175000017500000000000014466617054013023 5ustar maartenmaartenmrc-1.3.10/src/dummy.cpp0000664000175000017500000000031714466617054014663 0ustar maartenmaarten// Dummy file for creating a first mrc-mini application #include "mrsrc.h" const mrsrc::rsrc_imp gResourceIndex[1] = {}; const char gResourceData[] = "\0\0\0\0"; const char gResourceName[] = "\0\0\0\0"; mrc-1.3.10/src/mrc-unit-test.cpp0000664000175000017500000000516714466617054016253 0ustar maartenmaarten#define BOOST_TEST_MODULE MRC_Test #include #include "mrsrc.h" BOOST_AUTO_TEST_CASE(test_1) { mrsrc::rsrc r1("resource-1.txt"); BOOST_ASSERT((bool)r1); BOOST_TEST(r1.data() != nullptr); BOOST_TEST(r1.size() == 50); int r = std::memcmp(r1.data(), R"(This is the first line And this is the second line)", r1.size()); BOOST_TEST(r == 0); } BOOST_AUTO_TEST_CASE(test_2) { mrsrc::rsrc r2("resource-2.txt"); BOOST_ASSERT((bool)r2); BOOST_TEST(r2.data() != nullptr); BOOST_TEST(r2.size() == 102); /* const char16_t* t = u"\xfeffThis is the first line\ And this is the second line"; // t[0] = 0xfeff; int r = std::memcmp(r2.data(), (char*)t, r2.size()); BOOST_TEST(r == 0); */ } BOOST_AUTO_TEST_CASE(test_3) { mrsrc::streambuf buf("resource-1.txt"); std::istream is(&buf); std::string line; BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "This is the first line"); BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "And this is the second line"); BOOST_TEST(not std::getline(is, line)); } BOOST_AUTO_TEST_CASE(test_4) { mrsrc::istream is("resource-1.txt"); std::string line; BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "This is the first line"); BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "And this is the second line"); BOOST_TEST(not std::getline(is, line)); } BOOST_AUTO_TEST_CASE(test_10) { mrsrc::rsrc r0(""); BOOST_TEST(std::distance(r0.begin(), r0.end()) == 3); std::set found; for (auto& r1: r0) found.insert(r1.name()); std::set kTest{"resource-1.txt", "resource-2.txt", "subdir"}; BOOST_TEST(found == kTest); if (found != kTest) { for (auto& f: found) std::cout << f << std::endl; } } BOOST_AUTO_TEST_CASE(test_11) { mrsrc::rsrc r0("subdir/resource-3.txt"); BOOST_TEST((bool)r0); mrsrc::istream is(r0); std::string line; BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "Dit is resource 3"); } BOOST_AUTO_TEST_CASE(test_12) { mrsrc::rsrc r0("subdir/subsubdir/resource-4.txt"); BOOST_TEST((bool)r0); mrsrc::istream is(r0); std::string line; BOOST_TEST((bool)std::getline(is, line)); BOOST_TEST(line == "Dit is resource 4"); } BOOST_AUTO_TEST_CASE(test_13) { mrsrc::istream ri("subdir/resource-3.txt"); BOOST_TEST((bool)ri); BOOST_CHECK_EQUAL(ri.eof(), false); std::string line; BOOST_TEST((bool)std::getline(ri, line)); BOOST_TEST(line == "Dit is resource 3"); BOOST_CHECK_EQUAL(ri.eof(), true); } BOOST_AUTO_TEST_CASE(test_14) { mrsrc::istream ri("resource-5.txt"); BOOST_CHECK_EQUAL((bool)ri, false); BOOST_CHECK_EQUAL(ri.bad(), true); } mrc-1.3.10/src/mrc.cpp0000664000175000017500000007744114466617054014325 0ustar maartenmaarten/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2017-2021 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ // // mrc, A simple resource compiler. // // Use this program to make data available to your program without // having to distribute this data in separate files. // // Example usage: mrc -o myrsrc.o rsrc/ // // This will create an object file called myrsrs.o containing the data for all file found in the rsrc/ directory. #include #include #include #if __has_include() #include #elif not defined(EM_NONE) #define EM_NONE 0 #endif #include #include #include #include #include "mrsrc.h" #include "revision.hpp" #ifndef PATH_MAX #define PATH_MAX 1024 #endif namespace fs = std::filesystem; int VERBOSE = 0; // -------------------------------------------------------------------- uint32_t AddNameToNameTable(std::string &ioNameTable, const std::string &inName) { uint32_t result = 0; const char *p = ioNameTable.c_str(); const char *e = p + ioNameTable.length(); while (p < e) { if (inName == p) { result = static_cast(p - ioNameTable.c_str()); break; } p += strlen(p) + 1; } if (p >= e) { result = static_cast(ioNameTable.length()); ioNameTable.append(inName); ioNameTable.append("\0", 1); } return result; } // -------------------------------------------------------------------- struct MObjectFileImp { fs::path mFile; uint32_t mTextSize; uint32_t mDataSize; virtual ~MObjectFileImp() {} virtual void Write(std::ofstream &inFile) = 0; static MObjectFileImp *Create(int machine, int elf_class, int elf_data, int elf_abi, int flags); protected: friend class MObjectFile; struct MGlobal { std::string name; std::string data; }; typedef std::vector MGlobals; MGlobals mGlobals; }; // -------------------------------------------------------------------- // byte swapping namespace Swap { struct no_swapper { template T operator()(T inValue) const { return inValue; } }; struct swapper { template T operator()(T inValue) const { return inValue; } }; template <> inline int16_t swapper::operator()(int16_t inValue) const { return static_cast( ((inValue & 0xFF00UL) >> 8) | ((inValue & 0x00FFUL) << 8)); } template <> inline uint16_t swapper::operator()(uint16_t inValue) const { return static_cast( ((inValue & 0xFF00UL) >> 8) | ((inValue & 0x00FFUL) << 8)); } template <> inline int32_t swapper::operator()(int32_t inValue) const { return static_cast( ((inValue & 0xFF000000UL) >> 24) | ((inValue & 0x00FF0000UL) >> 8) | ((inValue & 0x0000FF00UL) << 8) | ((inValue & 0x000000FFUL) << 24)); } template <> inline uint32_t swapper::operator()(uint32_t inValue) const { return static_cast( ((inValue & 0xFF000000UL) >> 24) | ((inValue & 0x00FF0000UL) >> 8) | ((inValue & 0x0000FF00UL) << 8) | ((inValue & 0x000000FFUL) << 24)); } template <> inline int64_t swapper::operator()(int64_t inValue) const { return static_cast( (((static_cast(inValue)) << 56) & 0xFF00000000000000ULL) | (((static_cast(inValue)) << 40) & 0x00FF000000000000ULL) | (((static_cast(inValue)) << 24) & 0x0000FF0000000000ULL) | (((static_cast(inValue)) << 8) & 0x000000FF00000000ULL) | (((static_cast(inValue)) >> 8) & 0x00000000FF000000ULL) | (((static_cast(inValue)) >> 24) & 0x0000000000FF0000ULL) | (((static_cast(inValue)) >> 40) & 0x000000000000FF00ULL) | (((static_cast(inValue)) >> 56) & 0x00000000000000FFULL)); } template <> inline uint64_t swapper::operator()(uint64_t inValue) const { return static_cast( ((((uint64_t)inValue) << 56) & 0xFF00000000000000ULL) | ((((uint64_t)inValue) << 40) & 0x00FF000000000000ULL) | ((((uint64_t)inValue) << 24) & 0x0000FF0000000000ULL) | ((((uint64_t)inValue) << 8) & 0x000000FF00000000ULL) | ((((uint64_t)inValue) >> 8) & 0x00000000FF000000ULL) | ((((uint64_t)inValue) >> 24) & 0x0000000000FF0000ULL) | ((((uint64_t)inValue) >> 40) & 0x000000000000FF00ULL) | ((((uint64_t)inValue) >> 56) & 0x00000000000000FFULL)); } #if defined(LITTLE_ENDIAN) typedef no_swapper lsb_swapper; typedef swapper msb_swapper; #elif defined(BIG_ENDIAN) typedef swapper lsb_swapper; typedef no_swapper msb_swapper; #else #error Undefined endianness #endif } // namespace Swap // -------------------------------------------------------------------- // Write data aligned to some alignment value uint32_t WriteDataAligned(std::ofstream &inStream, const void *inData, uint32_t inSize, uint32_t inAlignment = 1) { inStream.write(reinterpret_cast(inData), inSize); if (inAlignment > 1) { while ((inStream.tellp() % inAlignment) != 0) inStream.put('\0'); } return static_cast(inStream.tellp()); } // -------------------------------------------------------------------- // // Implementation for ELF #if __has_include() template struct ElfClass; template <> struct ElfClass { using Elf_Ehdr = Elf32_Ehdr; using Elf_Shdr = Elf32_Shdr; using Elf_Sym = Elf32_Sym; using Elf_Word = Elf64_Word; using Elf_Half = Elf64_Half; }; template <> struct ElfClass { using Elf_Ehdr = Elf64_Ehdr; using Elf_Shdr = Elf64_Shdr; using Elf_Sym = Elf64_Sym; using Elf_Word = Elf64_Word; using Elf_Half = Elf64_Half; }; template struct ElfData; template <> struct ElfData { using swapper = Swap::msb_swapper; }; template <> struct ElfData { using swapper = Swap::lsb_swapper; }; template struct MELFObjectFileImp : public MObjectFileImp { using swapper = typename ElfData::swapper; using Elf_Ehdr = typename ElfClass::Elf_Ehdr; using Elf_Shdr = typename ElfClass::Elf_Shdr; using Elf_Sym = typename ElfClass::Elf_Sym; using Elf_Word = typename ElfClass::Elf_Word; using Elf_Half = typename ElfClass::Elf_Half; virtual void Write(std::ofstream &inFile) override; MELFObjectFileImp(int machine, uint8_t elf_abi, int flags) : MObjectFileImp() , mMachine(machine) , mABI(elf_abi) , mFlags(flags) { } Elf_Half mMachine; uint8_t mABI; Elf_Word mFlags; }; enum { kNullSection, kTextSection, kDataSection, kBssSection, kShStrtabSection, kSymtabSection, kStrtabSection, kSectionCount }; enum { kNullSymbol, kTextSectionSymbol, kDataSectionSymbol, kBssSectionSymbol, kGlobalSymbol, kSymbolCount }; template void MELFObjectFileImp::Write(std::ofstream &f) { Elf_Ehdr eh = { // e_ident { ELFMAG0, ELFMAG1, ELFMAG2, ELFMAG3, ELF_CLASSXX, ELF_DATAXX, EV_CURRENT, mABI }, ET_REL, // e_type mMachine, // e_machine EV_CURRENT, // e_version 0, // e_entry 0, // e_phoff 0, // e_shoff mFlags, // e_flags sizeof(Elf_Ehdr), // e_ehsize 0, // e_phentsize 0, // e_phnum sizeof(Elf_Shdr), // e_shentsize kSectionCount, // e_shnum kShStrtabSection // e_shstrndx }; uint32_t data_offset = WriteDataAligned(f, &eh, sizeof(eh), 16); std::string strtab; AddNameToNameTable(strtab, ""); // null name Elf_Sym sym = {}; std::vector syms; // kNullSymbol syms.push_back(sym); // text section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kTextSection; syms.push_back(sym); // data section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kDataSection; syms.push_back(sym); // bss section symbol sym.st_info = ELF32_ST_INFO(STB_LOCAL, STT_SECTION); sym.st_shndx = kBssSection; syms.push_back(sym); uint32_t sym_offset = data_offset; for (const auto &[name, data] : mGlobals) { sym.st_name = AddNameToNameTable(strtab, name); sym.st_value = sym_offset - data_offset; sym.st_size = data.length(); sym.st_info = ELF32_ST_INFO(STB_GLOBAL, STT_OBJECT); sym.st_shndx = kDataSection; syms.push_back(sym); sym_offset = WriteDataAligned(f, data.c_str(), data.length(), 8); } uint32_t data_size = sym_offset; uint32_t symtab_off = sym_offset; assert((sizeof(Elf_Sym) % 8) == 0); uint32_t symtab_size = syms.size() * sizeof(sym); uint32_t strtab_off = WriteDataAligned(f, syms.data(), symtab_size, 8); uint32_t shstrtab_off = WriteDataAligned(f, strtab.c_str(), strtab.length(), 8); std::string shstrtab; (void)AddNameToNameTable(shstrtab, ""); // null name (void)AddNameToNameTable(shstrtab, ".text"); (void)AddNameToNameTable(shstrtab, ".rsrc_data"); (void)AddNameToNameTable(shstrtab, ".bss"); (void)AddNameToNameTable(shstrtab, ".shstrtab"); (void)AddNameToNameTable(shstrtab, ".symtab"); (void)AddNameToNameTable(shstrtab, ".strtab"); eh.e_shoff = WriteDataAligned(f, shstrtab.c_str(), shstrtab.length(), 16); Elf_Shdr sh[kSectionCount] = { { // kNullSection }, { // kTextSection // sh_name AddNameToNameTable(shstrtab, ".text"), SHT_PROGBITS, // sh_type // sh_flags SHF_ALLOC | SHF_EXECINSTR, 0, // sh_addr data_offset, // sh_offset 0, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kDataSection // sh_name AddNameToNameTable(shstrtab, ".rsrc_data"), SHT_PROGBITS, // sh_type // sh_flags SHF_ALLOC | SHF_WRITE, 0, // sh_addr data_offset, // sh_offset data_size, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kBssSection // sh_name AddNameToNameTable(shstrtab, ".bss"), SHT_NOBITS, // sh_type // sh_flags SHF_ALLOC | SHF_WRITE, 0, // sh_addr eh.e_shoff, // sh_offset 0, // sh_size 0, // sh_link 0, // sh_info 4, // sh_addralign 0 // sh_entsize }, { // kShStrtabSection // sh_name AddNameToNameTable(shstrtab, ".shstrtab"), SHT_STRTAB, // sh_type 0, // sh_flags 0, // sh_addr shstrtab_off, // sh_offset Elf32_Word(shstrtab.length()), // sh_size 0, // sh_link 0, // sh_info 1, // sh_addralign 0 // sh_entsize }, { // kSymtabSection // sh_name AddNameToNameTable(shstrtab, ".symtab"), SHT_SYMTAB, // sh_type // sh_flags 0, 0, // sh_addr symtab_off, // sh_offset symtab_size, // sh_size 6, // sh_link kGlobalSymbol, // sh_info 8, // sh_addralign sizeof(Elf_Sym) // sh_entsize }, { // kStrtabSection // sh_name AddNameToNameTable(shstrtab, ".strtab"), SHT_STRTAB, // sh_type // sh_flags 0, 0, // sh_addr strtab_off, // sh_offset Elf32_Word(strtab.length()), // sh_size 0, // sh_link 0, // sh_info 1, // sh_addralign 0 // sh_entsize }, }; WriteDataAligned(f, sh, sizeof(sh), 1); f.flush(); f.seekp(0); WriteDataAligned(f, &eh, sizeof(eh)); } #endif // -------------------------------------------------------------------- // PE/COFF #ifndef IMAGE_FILE_MACHINE_AMD64 enum COFF_Machine : uint16_t { IMAGE_FILE_MACHINE_AMD64 = 0x8664, IMAGE_FILE_MACHINE_I386 = 0x14c, IMAGE_FILE_MACHINE_ARM64 = 0xaa64 }; enum COFF_HeaderCharacteristics : uint16_t { IMAGE_FILE_RELOCS_STRIPPED = 0x0001, IMAGE_FILE_LINE_NUMS_STRIPPED = 0x0004 }; enum COFF_SectionHeaderCharacteristics : uint32_t { IMAGE_SCN_CNT_CODE = 0x00000020, IMAGE_SCN_LNK_INFO = 0x00000200, IMAGE_SCN_LNK_REMOVE = 0x00000800, IMAGE_SCN_CNT_INITIALIZED_DATA = 0x00000040, IMAGE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080, IMAGE_SCN_ALIGN_1BYTES = 0x00100000, IMAGE_SCN_ALIGN_8BYTES = 0x00400000, IMAGE_SCN_ALIGN_16BYTES = 0x00500000, IMAGE_SCN_MEM_EXECUTE = 0x20000000, IMAGE_SCN_MEM_READ = 0x40000000, IMAGE_SCN_MEM_WRITE = 0x80000000 }; enum COFF_StorageClass : uint8_t { IMAGE_SYM_CLASS_EXTERNAL = 0x02, IMAGE_SYM_CLASS_STATIC = 0x03, IMAGE_SYM_CLASS_FILE = 0x67 }; #endif struct COFF_Header { uint16_t machine = IMAGE_FILE_MACHINE_AMD64; uint16_t numberOfSections; uint32_t timeDateStamp = 0; uint32_t pointerToSymbolTable; uint32_t numberOfSymbols; uint16_t sizeOfOptionalHeader; uint16_t characteristics = 0; }; union COFF_Name { char str[8] = ""; struct { uint32_t _filler_; uint32_t offset; }; }; static_assert(sizeof(COFF_Header) == 20, "COFF_Header size should be 20 bytes"); struct COFF_SectionHeader { COFF_Name name; uint32_t virtualSize; uint32_t virtualAddress; uint32_t sizeOfRawData; uint32_t pointerToRawData; uint32_t pointerToRelocations; uint32_t pointerToLineNumbers; uint16_t numberOfRelocations; uint16_t numberOfLineNumbers; uint32_t characteristics; }; static_assert(sizeof(COFF_SectionHeader) == 40, "Section headers should be 40 bytes"); /// \brief COFF symbol table entry, should be 18 bytes... right... struct COFF_Symbol { COFF_Name name; uint32_t value; int16_t sectionNumber; uint16_t type; uint8_t storageClass; uint8_t numberOfAuxSymbols; uint16_t _filler_; }; static_assert(sizeof(COFF_Symbol) == 20, "Symbols should be 18 bytes, plus that filler of 2 bytes which is not written..."); struct COFF_Relocation { uint32_t virtualAddress; uint32_t symbolTableIndex; uint16_t type; }; // -------------------------------------------------------------------- struct MCOFFObjectFileImp : public MObjectFileImp { using swapper = Swap::lsb_swapper; virtual void Write(std::ofstream &inFile) override; MCOFFObjectFileImp(uint16_t machine) : MObjectFileImp() , mMachine(machine) { } uint16_t mMachine; }; void MCOFFObjectFileImp::Write(std::ofstream &f) { // Start by allocating a header COFF_Header header = { mMachine, // machine 0, // numberOfSections 0, // timeDateStamp 0, // pointerToSymbolTable 0, // numberOfSymbols 0, // sizeOfOptionalHeader 0, // characteristics }; std::string strtab; auto addName = [&strtab](const std::string &name) { COFF_Name result{}; if (name.length() <= 8) name.copy(result.str, name.length()); else result.offset = 4 + AddNameToNameTable(strtab, name); return result; }; auto sectionHeaderStart = WriteDataAligned(f, &header, sizeof(header), 1); COFF_SectionHeader sectionHeaders[] = { { addName(".rdata"), // name 0, // virtualSize 0, // virtualAddress 0, // sizeOfRawData 0, // pointerToRawData 0, // pointerToRelocations 0, // pointerToLineNumbers 0, // numberOfRelocations 0, // numberOfLineNumbers IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_8BYTES | IMAGE_SCN_MEM_READ // characteristics }, { addName(".rdata$z"), // name 0, // virtualSize 0, // virtualAddress 0, // sizeOfRawData 0, // pointerToRawData 0, // pointerToRelocations 0, // pointerToLineNumbers 0, // numberOfRelocations 0, // numberOfLineNumbers IMAGE_SCN_CNT_INITIALIZED_DATA | IMAGE_SCN_ALIGN_1BYTES | IMAGE_SCN_MEM_READ // characteristics } }; std::vector symbols{}; auto rawDataOffset = WriteDataAligned(f, sectionHeaders, sizeof(sectionHeaders)); auto offset = rawDataOffset; for (const auto &[name, data] : mGlobals) { symbols.emplace_back( COFF_Symbol{ addName(mMachine == IMAGE_FILE_MACHINE_I386 ? '_' + name : name), offset - rawDataOffset, 1, 0, IMAGE_SYM_CLASS_EXTERNAL }); offset = WriteDataAligned(f, data.data(), static_cast(data.size())); } auto dataSize = offset - rawDataOffset; auto padding = 8 - dataSize % 8; if (padding == 8) padding = 0; auto rawDataSize = dataSize + padding; auto rawData2Offset = WriteDataAligned(f, std::string('\0', 8).data(), padding); using namespace std::literals; std::string package_string = kProjectName + " "s + kVersionNumber; offset = WriteDataAligned(f, package_string.data(), static_cast(package_string.length())); auto rawData2Size = offset - rawData2Offset; auto symbolTableOffset = offset; // We can now fill in the blanks sectionHeaders[0].pointerToRawData = rawDataOffset; sectionHeaders[0].sizeOfRawData = rawDataSize; sectionHeaders[1].pointerToRawData = rawData2Offset; sectionHeaders[1].sizeOfRawData = rawData2Size; // create the rest of the symbols symbols.emplace_back( COFF_Symbol{ addName(".rdata"), 0, 1, 0, IMAGE_SYM_CLASS_STATIC, 1 }); COFF_Name n1{}; n1._filler_ = dataSize; symbols.emplace_back(COFF_Symbol{ n1 }); symbols.emplace_back( COFF_Symbol{ addName(".rdata$z"), 0, 2, 0, IMAGE_SYM_CLASS_STATIC, 1 }); COFF_Name n2{}; n2._filler_ = dataSize; symbols.emplace_back(COFF_Symbol{ n2 }); for (auto &sym : symbols) WriteDataAligned(f, &sym, 18); uint32_t strTabSize = static_cast(strtab.size() + 4); WriteDataAligned(f, &strTabSize, sizeof(strTabSize)); WriteDataAligned(f, strtab.data(), static_cast(strtab.size() + 1)); // write section headers f.seekp(sectionHeaderStart); WriteDataAligned(f, sectionHeaders, sizeof(sectionHeaders)); // Write header f.seekp(0); header.numberOfSections = sizeof(sectionHeaders) / sizeof(COFF_SectionHeader); header.pointerToSymbolTable = symbolTableOffset; header.numberOfSymbols = static_cast(symbols.size()); WriteDataAligned(f, &header, sizeof(header)); } // -------------------------------------------------------------------- #if __has_include() MObjectFileImp *MObjectFileImp::Create(int machine, int elf_class, int elf_data, int elf_abi, int flags) { MObjectFileImp *result = nullptr; if (elf_class == ELFCLASS32 and elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(machine, elf_abi, flags); else if (elf_class == ELFCLASS32 and elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(machine, elf_abi, flags); else if (elf_class == ELFCLASS64 and elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(machine, elf_abi, flags); else if (elf_class == ELFCLASS64 and elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(machine, elf_abi, flags); else { std::cerr << "Unsupported ELF class and/or data " << elf_class << ", " << elf_data << std::endl; exit(1); } return result; } #endif // -------------------------------------------------------------------- class MObjectFile { public: #if __has_include() MObjectFile(int machine, int elf_class, int elf_data, int elf_abi, int flags) : mImpl(MObjectFileImp::Create(machine, elf_class, elf_data, elf_abi, flags)) { } #endif MObjectFile(uint16_t machine) : mImpl(new MCOFFObjectFileImp(machine)) { } ~MObjectFile(); void AddGlobal(const std::string &inName, const void *inData, uint32_t inSize); void Write(std::ofstream &inFile); private: MObjectFileImp *mImpl; }; MObjectFile::~MObjectFile() { delete mImpl; } void MObjectFile::AddGlobal(const std::string &inName, const void *inData, uint32_t inSize) { MObjectFileImp::MGlobal g; g.name = inName; g.data.assign(reinterpret_cast(inData), inSize); mImpl->mGlobals.push_back(g); } void MObjectFile::Write(std::ofstream &inFile) { if (mImpl == nullptr) throw std::runtime_error("nullptr error"); mImpl->Write(inFile); } // -------------------------------------------------------------------- class MResourceFile { public: MResourceFile(const std::string &prefix) : mPrefix(prefix) { mIndex.push_back({}); mName.push_back(0); } void Write(MObjectFile &objFile); void Add(const fs::path &inPath, const fs::path &inFile); private: void AddEntry(fs::path inPath, const char *inData, uint32_t inSize); std::vector mIndex; std::vector mData, mName; std::string mPrefix; }; void MResourceFile::AddEntry(fs::path inPath, const char *inData, uint32_t inSize) { uint32_t node = 0; // start at root for (fs::path::iterator p = inPath.begin(); p != inPath.end(); ++p) { if (*p == ".") // flatten continue; // no such child? Add it and continue if (mIndex[node].m_child == 0) { mrsrc::rsrc_imp child = {}; child.m_name = static_cast(mName.size()); std::string n = p->string(); copy(n.begin(), n.end(), std::back_inserter(mName)); mName.push_back(0); mIndex[node].m_child = static_cast(mIndex.size()); mIndex.push_back(child); node = mIndex[node].m_child; continue; } // lookup the path element in the current directory uint32_t next = mIndex[node].m_child; for (;;) { const char *name = mName.data() + mIndex[next].m_name; // if this is the one we're looking for, break out of the loop if (*p == name) { node = next; break; } // if there is a next element, loop if (mIndex[next].m_next != 0) { next = mIndex[next].m_next; continue; } // not found, create it mrsrc::rsrc_imp n = {}; n.m_name = static_cast(mName.size()); std::string s = p->string(); copy(s.begin(), s.end(), back_inserter(mName)); mName.push_back(0); node = static_cast(mIndex.size()); mIndex[next].m_next = node; mIndex.push_back(n); break; } } assert(node != 0); assert(node < mIndex.size()); mIndex[node].m_size = inSize; mIndex[node].m_data = static_cast(mData.size()); copy(inData, inData + inSize, back_inserter(mData)); while ((mData.size() % 8) != 0) mData.push_back('\0'); } void MResourceFile::Add(const fs::path &inPath, const fs::path &inFile) { if (fs::is_directory(inFile)) { fs::path ns = inPath / inFile.filename(); fs::path cwd = fs::current_path(); fs::current_path(inFile); for (auto i = fs::directory_iterator(fs::current_path()); i != fs::directory_iterator(); ++i) Add(ns, i->path().filename()); fs::current_path(cwd); } else { if (VERBOSE > 0) std::cerr << "adding " << inFile << " as " << inPath / inFile.filename() << std::endl; std::ifstream f(inFile, std::ios::binary); if (not f.is_open()) throw std::runtime_error("Could not open data file \'" + inFile.string() + '\''); std::filebuf *b = f.rdbuf(); uint32_t size = static_cast(b->pubseekoff(0, std::ios::end, std::ios::in)); b->pubseekoff(0, std::ios::beg, std::ios::in); std::vector text(size); b->sgetn(text.data(), size); f.close(); AddEntry(inPath / inFile.filename(), text.data(), size); } } void MResourceFile::Write(MObjectFile &obj) { obj.AddGlobal(mPrefix + "Index", mIndex.data(), static_cast(mIndex.size() * sizeof(mrsrc::rsrc_imp))); obj.AddGlobal(mPrefix + "Data", mData.data(), static_cast(mData.size())); obj.AddGlobal(mPrefix + "Name", mName.data(), static_cast(mName.size())); } // -------------------------------------------------------------------- int main(int argc, char *argv[]) { auto &config = mcfp::config::instance(); config.init( "usage: mrc [options] -o output file1 [file2...]", mcfp::make_option("help,h", "Display help message"), mcfp::make_option("version", "Print version"), mcfp::make_option("output,o", "Output file, this file is in the default object file format for this OS."), mcfp::make_option("header", "This will print out the header file you need to include in your program to access your resources"), mcfp::make_option("root", "Root path for the stored data (in the final resource data structure"), mcfp::make_option("resource-prefix", "gResource", "Prefix for the name of the global variables, default is gResource"), mcfp::make_option("depends,d", "Print a list of files the resource depends upon to the file specified"), #if __has_include() mcfp::make_option("elf-machine", "The ELF machine type to use, default is same as this machine. Use one of the values from elf.h"), mcfp::make_option("elf-class", "ELF class, default is same as this machine. Acceptable values are 1 (32bit) and 2 (64bit)."), mcfp::make_option("elf-data", "ELF endianness, default is same as this machine. Acceptable values are 1 (little-endian, LSB) and 2 (big-endian, MSB)."), mcfp::make_option("elf-abi", "ELF OS ABI value, see file elf.h for values (linux = 3, freebsd = 9)"), mcfp::make_option("elf-flags", "Processor specific flags in the ELF header, e.g. the EABI version for ARM"), #endif mcfp::make_option("coff", "Write a PE/COFF file for Windows, values should be one of x64, x86 or arm64"), mcfp::make_option("verbose,v", "Verbose output") ); std::error_code ec; config.parse(argc, argv, ec); if (ec) { std::cerr << ec.message() << std::endl; exit(1); } if (config.has("version")) { write_version_string(std::cout, config.has("verbose")); exit(0); } if (config.has("header")) { mrsrc::rsrc data("mrsrc.h"); std::string text(data.data(), data.size()); if (config.has("output")) { std::ofstream file(config.get("output"), std::ios::binary); if (not file.is_open()) throw std::runtime_error("Could not open output file for writing"); file << text << std::endl; } else std::cout << text << std::endl; exit(0); } if (config.has("help") or config.operands().empty() or not config.has("output")) { std::cout << config << std::endl; exit(config.has("help") ? 0 : 1); } VERBOSE = config.count("verbose"); if (config.has("depends")) { std::vector files; for (auto p : config.operands()) { if (fs::is_directory(p)) { for (auto i = fs::recursive_directory_iterator(p); i != fs::recursive_directory_iterator(); ++i) files.emplace_back(i->path().string()); } else files.emplace_back(p); } std::ofstream depends(config.get("depends")); if (not depends.is_open()) throw std::runtime_error("Could not open depends file for output"); depends << config.get("output") << ": "; for (size_t i = 0; i < files.size(); ++i) { auto &file = files[i]; for (char c : { ' ', '$', '#' }) { for (auto s = file.find(c); s != std::string::npos; s = file.find(c, s)) { file.insert(file.begin() + s, '\\'); s += 2; } } depends << files[i]; if (i + 1 < files.size()) depends << " \\\n\t"; } depends << "\n"; depends.close(); exit(0); } // -------------------------------------------------------------------- // find out the native format. Simply look at how we were assembled ourselves int elf_machine = EM_NONE, elf_class = 0, elf_data = 0, elf_flags = 0, elf_abi = 0; #if not defined(_WIN32) char exePath[PATH_MAX + 1]; #if __linux or __linux__ elf_abi = ELFOSABI_LINUX; int r = readlink("/proc/self/exe", exePath, PATH_MAX); #elif __FreeBSD__ elf_abi = ELFOSABI_FREEBSD; int r = strlen(argv[0]); strcpy(exePath, argv[0]); #else #error "Unsupported OS, sorry..." #endif if (r > 0) { exePath[r] = 0; int fd = open(exePath, O_RDONLY); if (fd >= 0) { unsigned char e_ident[16]; if (read(fd, e_ident, sizeof(e_ident)) == sizeof(e_ident) and e_ident[EI_MAG0] == ELFMAG0 and e_ident[EI_MAG1] == ELFMAG1 and e_ident[EI_MAG2] == ELFMAG2 and e_ident[EI_MAG3] == ELFMAG3) { // Yes, we're an ELF! elf_class = e_ident[EI_CLASS]; elf_data = e_ident[EI_DATA]; if (e_ident[EI_ABIVERSION]) elf_abi = e_ident[EI_ABIVERSION]; lseek(fd, 0, SEEK_SET); switch (elf_class) { case ELFCLASS32: { Elf32_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf32_Ehdr)) { elf_machine = hdr.e_machine; elf_flags = hdr.e_flags; } break; } case ELFCLASS64: { Elf64_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf64_Ehdr)) { elf_machine = hdr.e_machine; elf_flags = hdr.e_flags; } break; } default: std::cerr << "Unknown ELF class" << std::endl; } } } } #endif std::string ns; if (config.has("root")) ns = config.get("root"); std::string prefix = config.get("resource-prefix"); try { MResourceFile rsrcFile(prefix); for (fs::path i : config.operands()) rsrcFile.Add(ns, i); if (not config.has("output")) return 0; std::ofstream file(config.get("output"), std::ios::binary); if (not file.is_open()) throw std::runtime_error("Could not open output file for writing"); uint16_t win_machine = {}; #if defined(_WIN32) #if defined(_M_AMD64) win_machine = IMAGE_FILE_MACHINE_AMD64; #elif defined(_M_ARM64) win_machine = IMAGE_FILE_MACHINE_ARM64; #elif defined(_M_IX86) win_machine = IMAGE_FILE_MACHINE_I386; #endif #endif if (config.has("coff")) { if (config.get("coff") == "x64") win_machine = IMAGE_FILE_MACHINE_AMD64; else if (config.get("coff") == "arm64") win_machine = IMAGE_FILE_MACHINE_ARM64; else if (config.get("coff") == "x86") win_machine = IMAGE_FILE_MACHINE_I386; else throw std::runtime_error("Unsupported machine for COFF: " + config.get("coff")); } bool target_elf = config.has("elf-machine") and config.has("elf-class") and config.has("elf-data") and config.has("elf-flags"); if (config.has("elf-machine")) elf_machine = config.get("elf-machine"); if (config.has("elf-class")) elf_class = config.get("elf-class"); if (config.has("elf-data")) elf_data = config.get("elf-data"); if (config.has("elf-abi")) elf_abi = config.get("elf-abi"); if (config.has("elf-flags")) elf_flags = config.get("elf-flags"); if (win_machine and not target_elf) { MObjectFile obj(win_machine); rsrcFile.Write(obj); obj.Write(file); } else #if __has_include() { MObjectFile obj(elf_machine, elf_class, elf_data, elf_abi, elf_flags); rsrcFile.Write(obj); obj.Write(file); } #else throw std::runtime_error("Could not create resource file, probably you're trying to create a ELF resource file on Windows?"); #endif } catch (const std::exception &ex) { std::cerr << "Error executing mrc: " << ex.what() << std::endl; exit(1); } return 0; } mrc-1.3.10/src/mrsrc.h0000664000175000017500000002306614466617054014331 0ustar maartenmaarten/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2006-2023 Maarten L. Hekkelman * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #pragma once #include #include #include #include #include #include /* Resources are data sources for the application. They are retrieved by name. Basic usage: mrsrc::rsrc rsrc("dialogs/my-cool-dialog.glade"); if (rsrc) { GladeXML* glade = glade_xml_new_from_buffer(rsrc.data(), rsrc.size(), NULL, "japi"); ... } Alternative, to loop over all resources: mrsrc rsrc; // <- the root resource for (rsrc child: rsrc) std::cout << child.name() << std::endl; ------------------------------------------------- Stream interface: mrsrc::streambuf rb("data/my-text.txt"); std::istream is(&rb); std::string line; while (std::gettline(is, line)) std::cout << line << std::endl; */ namespace mrsrc { /// \brief Internal data structure as generated by mrc struct rsrc_imp { unsigned int m_next; unsigned int m_child; unsigned int m_name; unsigned int m_size; unsigned int m_data; }; } // namespace mrsrc // The following three variables are generated by mrc: extern "C" const mrsrc::rsrc_imp gResourceIndex[]; extern "C" const char gResourceData[]; extern "C" const char gResourceName[]; namespace mrsrc { /// \brief Class mrsrc::rsrc contains a pointer to the data in the /// resource, as well as offering an iterator interface to its /// children. class rsrc { public: rsrc() : m_impl(gResourceIndex) { } rsrc(const rsrc &other) : m_impl(other.m_impl) { } rsrc &operator=(const rsrc &other) { m_impl = other.m_impl; return *this; } rsrc(std::filesystem::path path); std::string name() const { return m_impl ? gResourceName + m_impl->m_name : ""; } const char *data() const { return m_impl ? gResourceData + m_impl->m_data : nullptr; } unsigned long size() const { return m_impl ? m_impl->m_size : 0; } explicit operator bool() const { return m_impl != nullptr and m_impl->m_size > 0; } template class iterator_t { public: using iterator_category = std::input_iterator_tag; using value_type = RSRC; using difference_type = std::ptrdiff_t; using pointer = value_type *; using reference = value_type &; iterator_t(const rsrc_imp *cur) : m_cur(cur) { } iterator_t(const iterator_t &i) : m_cur(i.m_cur) { } iterator_t &operator=(const iterator_t &i) { m_cur = i.m_cur; return *this; } reference operator*() { return m_cur; } pointer operator->() { return &m_cur; } iterator_t &operator++() { if (m_cur.m_impl->m_next) m_cur.m_impl = gResourceIndex + m_cur.m_impl->m_next; else m_cur.m_impl = nullptr; return *this; } iterator_t operator++(int) { auto tmp(*this); this->operator++(); return tmp; } bool operator==(const iterator_t &rhs) const { return m_cur.m_impl == rhs.m_cur.m_impl; } bool operator!=(const iterator_t &rhs) const { return m_cur.m_impl != rhs.m_cur.m_impl; } private: value_type m_cur; }; using iterator = iterator_t; iterator begin() const { const rsrc_imp *impl = nullptr; if (m_impl and m_impl->m_child) impl = gResourceIndex + m_impl->m_child; return iterator(impl); } iterator end() const { return iterator(nullptr); } private: rsrc(const rsrc_imp *imp) : m_impl(imp) { } const rsrc_imp *m_impl; }; inline rsrc::rsrc(std::filesystem::path p) { m_impl = gResourceIndex; // using std::filesytem::path would have been natural here of course... auto pb = p.begin(); auto pe = p.end(); while (m_impl != nullptr and pb != pe) { auto name = *pb++; const rsrc_imp *impl = nullptr; for (rsrc child : *this) { if (child.name() == name) { impl = child.m_impl; break; } } m_impl = impl; } if (pb != pe) // not found m_impl = nullptr; } // -------------------------------------------------------------------- template class basic_streambuf : public std::basic_streambuf { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; /// \brief constructor taking a \a path to the resource in memory basic_streambuf(const std::string &path) : m_rsrc(path) { init(); } /// \brief constructor taking a \a rsrc basic_streambuf(const rsrc &rsrc) : m_rsrc(rsrc) { init(); } basic_streambuf(const basic_streambuf &) = delete; basic_streambuf(basic_streambuf &&rhs) : basic_streambuf(rhs.m_rsrc) { } basic_streambuf &operator=(const basic_streambuf &) = delete; basic_streambuf &operator=(basic_streambuf &&rhs) { swap(rhs); return *this; } void swap(basic_streambuf &rhs) { std::swap(m_begin, rhs.m_begin); std::swap(m_end, rhs.m_end); std::swap(m_current, rhs.m_current); } /// \brief Analogous to is_open of an ifstream_buffer, return true if the resource is valid bool is_valid() const { return static_cast(m_rsrc); } private: void init() { if (m_rsrc) { m_begin = reinterpret_cast(m_rsrc.data()); m_end = reinterpret_cast(m_rsrc.data() + m_rsrc.size()); m_current = m_begin; } } int_type underflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current); } int_type uflow() { if (m_current == m_end) return traits_type::eof(); return traits_type::to_int_type(*m_current++); } int_type pbackfail(int_type ch) { if (m_current == m_begin or (ch != traits_type::eof() and ch != m_current[-1])) return traits_type::eof(); return traits_type::to_int_type(*--m_current); } std::streamsize showmanyc() { assert(std::less_equal()(m_current, m_end)); return m_end - m_current; } pos_type seekoff(off_type off, std::ios_base::seekdir dir, std::ios_base::openmode /*which*/) { switch (dir) { case std::ios_base::beg: m_current = m_begin + off; break; case std::ios_base::end: m_current = m_end + off; break; case std::ios_base::cur: m_current += off; break; default: break; } if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } pos_type seekpos(pos_type pos, std::ios_base::openmode /*which*/) { m_current = m_begin + pos; if (m_current < m_begin) m_current = m_begin; if (m_current > m_end) m_current = m_end; return m_current - m_begin; } private: rsrc m_rsrc; const char_type *m_begin = nullptr; const char_type *m_end = nullptr; const char_type *m_current = nullptr; }; using streambuf = basic_streambuf>; // -------------------------------------------------------------------- // class mrsrc::istream template class basic_istream : public std::basic_istream { public: typedef CharT char_type; typedef Traits traits_type; typedef typename traits_type::int_type int_type; typedef typename traits_type::pos_type pos_type; typedef typename traits_type::off_type off_type; private: using __streambuf_type = basic_streambuf; using __istream_type = std::basic_istream; __streambuf_type m_buffer; public: basic_istream(const std::string &path) : basic_istream(rsrc(path)) { } basic_istream(const rsrc &resource) : __istream_type(&m_buffer) , m_buffer(resource) { if (resource) this->init(&m_buffer); else __istream_type::setstate(std::ios_base::badbit); } basic_istream(const basic_istream &) = delete; basic_istream(basic_istream &&rhs) : __istream_type(std::move(rhs)) , m_buffer(std::move(rhs.m_buffer)) { __istream_type::set_rdbuf(&m_buffer); } basic_istream &operator=(const basic_istream &) = delete; basic_istream &operator=(basic_istream &&rhs) { __istream_type::operator=(std::move(rhs)); m_buffer = std::move(rhs.m_buffer); return *this; } void swap(basic_istream &rhs) { __istream_type::swap(rhs); m_buffer.swap(rhs.m_buffer); } __streambuf_type *rdbuf() const { return const_cast<__streambuf_type *>(&m_buffer); } }; using istream = basic_istream>; } // namespace mrsrc mrc-1.3.10/src/rsrc-test.cpp0000664000175000017500000000060314466617054015454 0ustar maartenmaarten#include #include #include "mrsrc.h" int main() { mrsrc::rsrc eerste("eerste"); if (eerste) std::cout << std::string(eerste.data(), eerste.size()) << std::endl; else std::cout << "not found" << std::endl; mrsrc::rsrc error_rsrc("invalid"); assert(not error_rsrc); assert(error_rsrc.data() == nullptr); assert(error_rsrc.size() == 0); return 0; }