mrc-1.3.15/0000755000175000017500000000000014746144532012234 5ustar maartenmaartenmrc-1.3.15/.gitattributes0000644000175000017500000000005314746144532015125 0ustar maartenmaartenrsrc/*.txt eol=lf rsrc/subdir/*.txt eol=lf mrc-1.3.15/.github/0000755000175000017500000000000014746144532013574 5ustar maartenmaartenmrc-1.3.15/.github/workflows/0000755000175000017500000000000014746144532015631 5ustar maartenmaartenmrc-1.3.15/.github/workflows/cmake-multi-platform.yml0000644000175000017500000000665114746144532022416 0ustar maartenmaarten# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. # See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml name: multi platform test on: push: branches: [ "trunk", "develop" ] pull_request: branches: [ "trunk" ] jobs: build: runs-on: ${{ matrix.os }} strategy: # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. fail-fast: false # Set up a matrix to run the following 3 configurations: # 1. # 2. # 3. # # To add more build types (Release, Debug, RelWithDebInfo, etc.) customize the build_type list. matrix: os: [ubuntu-latest, windows-latest] build_type: [Release] # compilation error with clang/catch2 forced me to comment clang out # c_compiler: [gcc, clang, cl] c_compiler: [gcc, cl] include: - os: windows-latest c_compiler: cl cpp_compiler: cl - os: ubuntu-latest c_compiler: gcc cpp_compiler: g++ # - os: ubuntu-latest # c_compiler: clang # cpp_compiler: clang++ exclude: - os: windows-latest c_compiler: gcc # - os: windows-latest # c_compiler: clang - os: ubuntu-latest c_compiler: cl steps: - uses: actions/checkout@v3 - name: Set reusable strings # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. id: strings shell: bash run: | echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" - name: Configure CMake # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type run: > cmake -B ${{ steps.strings.outputs.build-output-dir }} -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -S ${{ github.workspace }} - name: Build # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} - name: Test working-directory: ${{ steps.strings.outputs.build-output-dir }} # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail run: ctest --build-config ${{ matrix.build_type }} --rerun-failed --output-on-failure mrc-1.3.15/.gitignore0000644000175000017500000000010214746144532014215 0ustar maartenmaarten.vscode/ **/build/ src/revision.hpp src/version.hpp **/sdkconfig* mrc-1.3.15/.gitmodules0000644000175000017500000000012014746144532014402 0ustar maartenmaarten[submodule "libmcfp"] path = libmcfp url = git@github.com:mhekkel/libmcfp.git mrc-1.3.15/CMakeLists.txt0000644000175000017500000001054214746144532014776 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.15) include(CTest) set(CXX_EXTENSIONS OFF) set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) 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(mcfp 1.3.5 QUIET) if(NOT mcfp_FOUND) include(FetchContent) FetchContent_Declare( mcfp GIT_REPOSITORY https://github.com/mhekkel/libmcfp GIT_TAG v1.3.5 ) FetchContent_MakeAvailable(mcfp) 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 mcfp::mcfp) target_link_libraries(mrc-bootstrap mcfp::mcfp) if(BUILD_TESTING) find_package(Catch2 3 QUIET) if(NOT Catch2_FOUND) Include(FetchContent) FetchContent_Declare( Catch2 GIT_REPOSITORY https://github.com/catchorg/Catch2.git GIT_TAG v3.8.0 # or a later release ) FetchContent_MakeAvailable(Catch2) endif() 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) if(TARGET Catch2::Catch2WithMain) target_link_libraries(mrc-unit-test Catch2::Catch2WithMain) add_test(NAME unit-test COMMAND $ WORKING_DIRECTORY .) else() message(WARNING "No Catch2::Catch2WithMain target, something is wrong") endif() 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 examples DESTINATION share/doc/mrc/) # add_subdirectory(examples/simple) mrc-1.3.15/LICENSE0000644000175000017500000000244614746144532013247 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.15/README.md0000644000175000017500000000615214746144532013517 0ustar maartenmaarten[![github CI](https://github.com/mhekkel/mrc/actions/workflows/cmake-multi-platform.yml/badge.svg)](https://github.com/mhekkel/mrc/actions) Maartens Resource Compiler ========================== Abstract -------- A long, long time ago there was 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. No need to use installers or install scripts that put dozens of files at the most obscure locations on your disk. One of the technical features to make this possible was called resources: a 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 generating object files in 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 can then be accessed 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: ```console 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) { std::string s(hello.data(), hello.size()); std::cout << s << '\n'; } return 0; } ``` To create a resource file: ```console 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): ```console git clone https://github.com/mhekkel/mrc.git cd mrc cmake -S . -B build cmake --build build ctest --test-dir build cmake --install build ``` mrc-1.3.15/changelog0000644000175000017500000000434714746144532014116 0ustar maartenmaartenVersion 1.3.15 - Yet another fix for the cmake config file Version 1.3.14 - Fixed the cmake code to generate dep files Version 1.3.13 - Readlink does not write a null character, so we have to add it ourselves. Version 1.3.12 - Only use FetchContent when required Version 1.3.11 - Added option --elf-template that can use an object file or executable in ELF format as template to find the correct --elf-* flags to use. This option is used automatically by mrc when you use it via the cmake plugin. - Remove libmcfp as submodule - Moved from Boost.Test to Catch2 for unit testing - Using FetchContent for dependencies - Dropped FindFilesystem, assuming std::filesystem is standard by now. Version 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.15/cmake/0000755000175000017500000000000014746144532013314 5ustar maartenmaartenmrc-1.3.15/cmake/VersionString.cmake0000644000175000017500000003150114746144532017132 0ustar maartenmaarten# SPDX-License-Identifier: BSD-2-Clause # Copyright (c) 2021-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. # 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 # from https://github.com/rpavlik/cmake-modules #[=======================================================================[.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. #]=======================================================================] # Record the location of this module now, not at the time the CMakeLists.txt # is being processed get_filename_component(_current_cmake_module_dir ${CMAKE_CURRENT_LIST_FILE} PATH) # 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 ${_start_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() 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}_") set(BOOL_IS_MAIN "false") else() set(VAR_PREFIX "") set(IDENT_PREFIX "") set(BOOL_IS_MAIN "true") endif() # # And finally, write out the header file # 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 # namespace version_info_v1 # { # class version_info_base # { # public: # static void write(std::ostream &os, bool verbose) # { # auto &s_head = head(); # if (s_head != nullptr) # write(s_head, os, verbose); # } # protected: # version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) # : m_name(name) # , m_version(version) # , m_build_number(build_number) # , m_git_tag(git_tag) # , m_revision_date(revision_date) # { # auto &s_head = head(); # m_next = s_head; # s_head = this; # } # static void write(const version_info_base *inst, std::ostream &os, bool verbose) # { # if (inst->m_next) # { # 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_number != 0) # { # os << "build: " << inst->m_build_number << ' ' << inst->m_revision_date << std::endl; # if (inst->m_git_tag[0] != 0) # os << "git tag: " << inst->m_git_tag << std::endl; # } # } # } # using version_info_ptr = version_info_base *; # static version_info_ptr &head() # { # static version_info_ptr s_head = nullptr; # return s_head; # } # const char *m_name; # const char *m_version; # int m_build_number; # const char *m_git_tag; # const char *m_revision_date; # version_info_base *m_next = nullptr; # }; # template # class version_info : public version_info_base # { # public: # using implementation_type = T; # version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) # : version_info_base(name, version, build_number, git_tag, revision_date) # { # } # struct register_object # { # register_object() # { # static implementation_type s_instance; # } # }; # template struct reference_object; # static register_object s_registered_object; # static reference_object s_referenced_object; # }; # template typename version_info::register_object version_info::s_registered_object; # } # inline void write_version_string(std::ostream &os, bool verbose) # { # version_info_v1::version_info_base::write(os, verbose); # } # #endif # class version_info_@IDENT_PREFIX@impl : public version_info_v1::version_info # { # public: # version_info_@IDENT_PREFIX@impl() # : version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate) # { # } # }; # ]]) # configure_file("${VERSION_STRING_DATA}/${file_name}.in" "${dir}/${file_name}" @ONLY) configure_file("${_current_cmake_module_dir}/revision.hpp.in" "${dir}/${file_name}" @ONLY) endfunction() mrc-1.3.15/cmake/mrc-config.cmake0000644000175000017500000002040114746144532016337 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) # internal, create an ELF template file based on the current compiler flags and all function(_mrc_create_elf_template _target) try_compile(BUILD_OK SOURCE_FROM_CONTENT my_code.cpp [[ extern "C" int my_global_int = 42; int main() { return 0; } ]] NO_CACHE NO_LOG COPY_FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc_template.exe" ) if(NOT BUILD_OK) message(FATAL_ERROR "Failed to create template executable") endif() endfunction() #[=======================================================================[.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. ``CREATE_ELF_TEMPLATE`` Create a small executable to be used as template for the ELF object file generation. This is executable is built using the same compiler settings as the final project and thus contains the correct ELF header from which mrc can copy the necessary information. ``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. ``CREATE_ELF_TEMPLATE`` Create a small executable to be used as template for the ELF object file generation. This is executable is built using the same compiler settings as the final project and thus contains the correct ELF header from which mrc can copy the necessary information. #]=======================================================================] function(mrc_target_resources _target) set(flags VERBOSE CREATE_ELF_TEMPLATE) 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() set(_dir "${CMAKE_CURRENT_BINARY_DIR}/mrc-${_target}.dir") file(MAKE_DIRECTORY ${_dir}) if(MRC_OPTION_RSRC_FILE) set(RSRC_FILE ${MRC_OPTION_RSRC_FILE}) else() set(RSRC_FILE "${_dir}/rsrc.obj") endif() if(MRC_OPTION_RSRC_DEP_FILE) set(RSRC_DEP_FILE "${MRC_OPTION_DEPENDS_FILE}") else() set(RSRC_DEP_FILE "${_dir}/rsrc.d") endif() if(CMAKE_HOST_WIN32) if(MRC_OPTION_COFF_TYPE) list(APPEND OPTIONS "--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() list(APPEND OPTIONS "--coff=${COFF_TYPE}") endif() endif() if(${MRC_OPTION_VERBOSE}) list(APPEND OPTIONS "--verbose") endif() if(CMAKE_CROSSCOMPILING OR ${MRC_OPTION_CREATE_ELF_TEMPLATE}) set(TEMPLATE "${_dir}/template.exe") _mrc_create_elf_template(${TEMPLATE}) list(APPEND OPTIONS "--elf-template=${TEMPLATE}") endif() if(CMAKE_CROSSCOMPILING OR ${MRC_OPTION_CREATE_ELF_TEMPLATE}) _mrc_create_elf_template(${_target}) list(APPEND MRC_OPTION_RESOURCES "--elf-template=${CMAKE_CURRENT_BINARY_DIR}/${_target}_rsrc_template.exe") endif() cmake_policy(SET CMP0116 NEW) # If we can use DEPFILE, use it. if(${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.21") add_custom_target( "generate_${_target}_depends_file" BYPRODUCTS ${RSRC_DEP_FILE} COMMENT "Generating depends file for ${_target} resources" COMMAND ${MRC_EXECUTABLE} -d ${RSRC_DEP_FILE} -o ${RSRC_FILE} ${MRC_OPTION_RESOURCES} VERBATIM) add_custom_command(OUTPUT ${RSRC_FILE} DEPFILE ${RSRC_DEP_FILE} DEPENDS "generate_${_target}_depends_file" COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${OPTIONS} ${MRC_OPTION_RESOURCES} VERBATIM) else() message(STATUS "Not using dependency file since cmake version is too old") add_custom_command(OUTPUT ${RSRC_FILE} COMMAND ${MRC_EXECUTABLE} -o ${RSRC_FILE} ${OPTIONS} ${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.15/cmake/revision.hpp.in0000644000175000017500000000637714746144532016305 0ustar maartenmaarten// 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 namespace version_info_v1_1 { class version_info_base { public: static void write_version_string(std::ostream &os, bool verbose) { auto s_main = registered_main(); if (s_main != nullptr) s_main->write(os, verbose); if (verbose) { for (auto lib = registered_libraries(); lib != nullptr; lib = lib->m_next) { os << '-' << std::endl; lib->write(os, verbose); } } } protected: version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date, bool is_main) : m_name(name) , m_version(version) , m_build_number(build_number) , m_git_tag(git_tag) , m_revision_date(revision_date) { if (is_main) registered_main() = this; else { auto &s_head = registered_libraries(); m_next = s_head; s_head = this; } } void write(std::ostream &os, bool verbose) { os << m_name << " version " << m_version << std::endl; if (verbose) { if (m_build_number != 0) { os << "build: " << m_build_number << ' ' << m_revision_date << std::endl; if (m_git_tag[0] != 0) os << "git tag: " << m_git_tag << std::endl; } } } using version_info_ptr = version_info_base *; static version_info_ptr ®istered_main() { static version_info_ptr s_main = nullptr; return s_main; } static version_info_ptr ®istered_libraries() { static version_info_ptr s_head = nullptr; return s_head; } const char *m_name; const char *m_version; int m_build_number; const char *m_git_tag; const char *m_revision_date; version_info_base *m_next = nullptr; }; template class version_info : public version_info_base { public: using implementation_type = T; version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date, bool is_main) : version_info_base(name, version, build_number, git_tag, revision_date, is_main) { } struct register_object { register_object() { static implementation_type s_instance; } }; template struct reference_object; static register_object s_registered_object; static reference_object s_referenced_object; }; template typename version_info::register_object version_info::s_registered_object; } // namespace version_info_v1_1 inline void write_version_string(std::ostream &os, bool verbose) { version_info_v1_1::version_info_base::write_version_string(os, verbose); } #endif class version_info_@IDENT_PREFIX@impl : public version_info_v1_1::version_info { public: version_info_@IDENT_PREFIX@impl() : version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate, @BOOL_IS_MAIN@) { } };mrc-1.3.15/doc/0000755000175000017500000000000014746144532013001 5ustar maartenmaartenmrc-1.3.15/doc/mrc-manual.pdf0000644000175000017500000006405614746144532015543 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.15/doc/mrc.10000644000175000017500000001512314746144532013646 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.15/examples/0000755000175000017500000000000014746144532014052 5ustar maartenmaartenmrc-1.3.15/examples/esp-idf-rsrc/0000755000175000017500000000000014746144532016350 5ustar maartenmaartenmrc-1.3.15/examples/esp-idf-rsrc/CMakeLists.txt0000644000175000017500000000035414746144532021112 0ustar maartenmaarten# The following lines of boilerplate have to be in your project's # CMakeLists in this exact order for cmake to work correctly cmake_minimum_required(VERSION 3.16) include($ENV{IDF_PATH}/tools/cmake/project.cmake) project(my-rsrc-app) mrc-1.3.15/examples/esp-idf-rsrc/README.md0000644000175000017500000000745314746144532017640 0ustar maartenmaartenUsing resources in ESP-IDF ========================== It is very easy to add resources to esp-idf projects as is shown in this example. Here we create a single resource called `hello.txt` and its content comes from the file `hello.txt`. The header file is created and resources are added to the main component using the cmake snippet: ```cmake # locate and include the mrc macro's find_package(Mrc) # write a header file mrc_write_header(${CMAKE_CURRENT_SOURCE_DIR}/mrsrc.hpp) # Add one resource to the executable mrc_target_resources(${COMPONENT_LIB} CREATE_ELF_TEMPLATE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) ``` Now you can simply use that resource: ```c++ #include "esp_log.h" #include "mrsrc.hpp" const char TAG[] = "esp-rsrc"; extern "C" void app_main(void) { mrsrc::rsrc data("hello.txt"); if (data) { std::string s(data.data(), data.size()); ESP_LOGI(TAG, "Resource found: '%s'!", s.c_str()); } else ESP_LOGI(TAG, "The resource was not found"); } ``` Use `idf.py build flash monitor` and the output will be: ```console ESP-ROM:esp32h2-20221101 Build:Nov 1 2022 rst:0x1 (POWERON),boot:0xc (SPI_FAST_FLASH_BOOT) SPIWP:0xee mode:DIO, clock div:1 load:0x408460e0,len:0x17ac load:0x4083cfd0,len:0xe88 load:0x4083efd0,len:0x2c94 entry 0x4083cfda I (23) boot: ESP-IDF v5.3-dev-1353-gb3f7e2c8a4 2nd stage bootloader I (24) boot: compile time Feb 1 2024 15:04:08 I (25) boot: chip revision: v0.1 I (28) boot.esp32h2: SPI Speed : 64MHz I (33) boot.esp32h2: SPI Mode : DIO I (38) boot.esp32h2: SPI Flash Size : 2MB I (42) boot: Enabling RNG early entropy source... I (48) boot: Partition Table: I (51) boot: ## Label Usage Type ST Offset Length I (59) boot: 0 nvs WiFi data 01 02 00009000 00006000 I (66) boot: 1 phy_init RF data 01 01 0000f000 00001000 I (74) boot: 2 factory factory app 00 00 00010000 00100000 I (81) boot: End of partition table I (85) esp_image: segment 0: paddr=00010020 vaddr=42018020 size=0a0e8h ( 41192) map I (107) esp_image: segment 1: paddr=0001a110 vaddr=40800000 size=05f08h ( 24328) load I (116) esp_image: segment 2: paddr=00020020 vaddr=42000020 size=1734ch ( 95052) map I (146) esp_image: segment 3: paddr=00037374 vaddr=40805f08 size=03acch ( 15052) load I (152) esp_image: segment 4: paddr=0003ae48 vaddr=408099e0 size=011f4h ( 4596) load I (157) boot: Loaded app from partition at offset 0x10000 I (158) boot: Disabling RNG early entropy source... I (174) cpu_start: Unicore app W (183) clk: esp_perip_clk_init() has not been implemented yet I (190) cpu_start: Pro cpu start user code I (190) cpu_start: cpu freq: 96000000 Hz I (191) heap_init: Initializing. RAM available for dynamic allocation: I (195) heap_init: At 4080BDD0 len 000415B0 (261 KiB): RAM I (201) heap_init: At 4084D380 len 00002B60 (10 KiB): RAM I (209) spi_flash: detected chip: generic I (212) spi_flash: flash io: dio W (216) spi_flash: Detected size(4096k) larger than the size in the binary image header(2048k). Using the size in the binary image header. I (229) cpu_start: Application information: I (234) cpu_start: Project name: my-rsrc-app I (239) cpu_start: App version: v1.3.10-23-gaff8ce4-dirty I (246) cpu_start: Compile time: Feb 1 2024 15:04:01 I (252) cpu_start: ELF file SHA256: 30e7d4161... I (257) cpu_start: ESP-IDF: v5.3-dev-1353-gb3f7e2c8a4 I (264) cpu_start: Min chip rev: v0.0 I (269) cpu_start: Max chip rev: v0.99 I (273) cpu_start: Chip rev: v0.1 I (279) sleep: Configure to isolate all GPIO pins in sleep state I (285) sleep: Enable automatic switching of GPIO sleep configuration I (292) main_task: Started on CPU0 I (292) main_task: Calling app_main() I (292) esp-rsrc: Resource found: 'Hello, world! '! I (302) main_task: Returned from app_main() ```mrc-1.3.15/examples/esp-idf-rsrc/main/0000755000175000017500000000000014746144532017274 5ustar maartenmaartenmrc-1.3.15/examples/esp-idf-rsrc/main/CMakeLists.txt0000644000175000017500000000041714746144532022036 0ustar maartenmaartenfind_package(Mrc) idf_component_register(SRCS esp-rsrc-app.cpp INCLUDE_DIRS ".") mrc_write_header(${CMAKE_CURRENT_SOURCE_DIR}/mrsrc.hpp) mrc_target_resources(${COMPONENT_LIB} CREATE_ELF_TEMPLATE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) mrc-1.3.15/examples/esp-idf-rsrc/main/esp-rsrc-app.cpp0000644000175000017500000000326214746144532022317 0ustar maartenmaarten/*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2024 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. */ #include "esp_log.h" #include "mrsrc.hpp" const char TAG[] = "esp-rsrc"; extern "C" void app_main(void) { mrsrc::rsrc data("hello.txt"); if (data) { std::string s(data.data(), data.size()); ESP_LOGI(TAG, "Resource found: '%s'!", s.c_str()); } else ESP_LOGI(TAG, "The resource was not found"); } mrc-1.3.15/examples/esp-idf-rsrc/main/hello.txt0000644000175000017500000000001614746144532021135 0ustar maartenmaartenHello, world! mrc-1.3.15/examples/esp-idf-rsrc/main/mrsrc.hpp0000644000175000017500000002306714746144532021143 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.15/examples/simple/0000755000175000017500000000000014746144532015343 5ustar maartenmaartenmrc-1.3.15/examples/simple/CMakeLists.txt0000644000175000017500000000143714746144532020110 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 CREATE_ELF_TEMPLATE VERBOSE RESOURCES ${CMAKE_CURRENT_SOURCE_DIR}/hello.txt) mrc-1.3.15/examples/simple/hello.txt0000644000175000017500000000001514746144532017203 0ustar maartenmaartenHello, world!mrc-1.3.15/examples/simple/mrc-user.cpp0000644000175000017500000000070214746144532017603 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.15/rsrc/0000755000175000017500000000000014746144532013205 5ustar maartenmaartenmrc-1.3.15/rsrc/resource-1.txt0000644000175000017500000000006214746144532015731 0ustar maartenmaartenThis is the first line And this is the second linemrc-1.3.15/rsrc/resource-2.txt0000644000175000017500000000014614746144532015735 0ustar maartenmaartenThis is the first line And this is the second linemrc-1.3.15/rsrc/subdir/0000755000175000017500000000000014746144532014475 5ustar maartenmaartenmrc-1.3.15/rsrc/subdir/resource-3.txt0000644000175000017500000000002114746144532017216 0ustar maartenmaartenDit is resource 3mrc-1.3.15/rsrc/subdir/subsubdir/0000755000175000017500000000000014746144532016477 5ustar maartenmaartenmrc-1.3.15/rsrc/subdir/subsubdir/resource-4.txt0000644000175000017500000000002114746144532021221 0ustar maartenmaartenDit is resource 4mrc-1.3.15/src/0000755000175000017500000000000014746144532013023 5ustar maartenmaartenmrc-1.3.15/src/dummy.cpp0000644000175000017500000000031714746144532014663 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.15/src/mrc-unit-test.cpp0000644000175000017500000000512014746144532016240 0ustar maartenmaarten#define CATCH_CONFIG_MAIN #include #include "mrsrc.h" #include #include #include TEST_CASE("test_1", "test_1") { mrsrc::rsrc r1("resource-1.txt"); REQUIRE((bool)r1); REQUIRE(r1.data() != nullptr); REQUIRE(r1.size() == 50); int r = std::memcmp(r1.data(), R"(This is the first line And this is the second line)", r1.size()); REQUIRE(r == 0); } TEST_CASE("test_2", "test_2") { mrsrc::rsrc r2("resource-2.txt"); REQUIRE((bool)r2); REQUIRE(r2.data() != nullptr); REQUIRE(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()); REQUIRE(r == 0); */ } TEST_CASE("test_3", "test_3") { mrsrc::streambuf buf("resource-1.txt"); std::istream is(&buf); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "This is the first line"); REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "And this is the second line"); REQUIRE(not std::getline(is, line)); } TEST_CASE("test_4", "test_4") { mrsrc::istream is("resource-1.txt"); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "This is the first line"); REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "And this is the second line"); REQUIRE(not std::getline(is, line)); } TEST_CASE("test_10", "test_10") { mrsrc::rsrc r0(""); REQUIRE(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" }; REQUIRE(found == kTest); if (found != kTest) { for (auto &f : found) std::cout << f << std::endl; } } TEST_CASE("test_11", "test_11") { mrsrc::rsrc r0("subdir/resource-3.txt"); REQUIRE((bool)r0); mrsrc::istream is(r0); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "Dit is resource 3"); } TEST_CASE("test_12", "test_12") { mrsrc::rsrc r0("subdir/subsubdir/resource-4.txt"); REQUIRE((bool)r0); mrsrc::istream is(r0); std::string line; REQUIRE((bool)std::getline(is, line)); REQUIRE(line == "Dit is resource 4"); } TEST_CASE("test_13", "test_13") { mrsrc::istream ri("subdir/resource-3.txt"); REQUIRE((bool)ri); REQUIRE(ri.eof() == false); std::string line; REQUIRE((bool)std::getline(ri, line)); REQUIRE(line == "Dit is resource 3"); REQUIRE(ri.eof() == true); } TEST_CASE("test_14", "test_14") { mrsrc::istream ri("resource-5.txt"); REQUIRE((bool)ri == false); REQUIRE(ri.bad() == true); } mrc-1.3.15/src/mrc.cpp0000644000175000017500000010137614746144532014320 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 elf_config { int elf_machine; int elf_class; int elf_data; int elf_flags; int elf_abi; bool operator==(const elf_config &) const = default; }; // -------------------------------------------------------------------- struct MObjectFileImp { fs::path mFile; uint32_t mTextSize; uint32_t mDataSize; virtual ~MObjectFileImp() {} virtual void Write(std::ofstream &inFile) = 0; static MObjectFileImp *Create(elf_config elfc); 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, kNoteGNU_stack, 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"); (void)AddNameToNameTable(shstrtab, ".note.GNU-stack"); 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 }, { // kStrtabSection // sh_name AddNameToNameTable(shstrtab, ".note.GNU-stack"), SHT_STRTAB, // sh_type // sh_flags 0, 0, // sh_addr 0, // sh_offset 0, // 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(elf_config elfc) { MObjectFileImp *result = nullptr; if (elfc.elf_class == ELFCLASS32 and elfc.elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS32 and elfc.elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS64 and elfc.elf_data == ELFDATA2LSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else if (elfc.elf_class == ELFCLASS64 and elfc.elf_data == ELFDATA2MSB) result = new MELFObjectFileImp(elfc.elf_machine, elfc.elf_abi, elfc.elf_flags); else { std::cerr << "Unsupported ELF class and/or data " << elfc.elf_class << ", " << elfc.elf_data << std::endl; exit(1); } return result; } #endif // -------------------------------------------------------------------- class MObjectFile { public: #if __has_include() MObjectFile(elf_config elfc) : mImpl(MObjectFileImp::Create(elfc)) { } #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())); } // -------------------------------------------------------------------- #if __has_include() elf_config get_elf_options(std::filesystem::path object_file) { elf_config elfc{}; int fd = open(object_file.c_str(), O_RDONLY); if (fd < 0) { std::cerr << "Error opening template file " << std::quoted(object_file.string()) << '\n'; exit(1); } 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! elfc.elf_class = e_ident[EI_CLASS]; elfc.elf_data = e_ident[EI_DATA]; if (e_ident[EI_ABIVERSION]) elfc.elf_abi = e_ident[EI_ABIVERSION]; lseek(fd, 0, SEEK_SET); switch (elfc.elf_class) { case ELFCLASS32: { Elf32_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf32_Ehdr)) { elfc.elf_machine = hdr.e_machine; elfc.elf_flags = hdr.e_flags; } break; } case ELFCLASS64: { Elf64_Ehdr hdr; if (read(fd, &hdr, sizeof(hdr)) == sizeof(Elf64_Ehdr)) { elfc.elf_machine = hdr.e_machine; elfc.elf_flags = hdr.e_flags; } break; } default: std::cerr << "Unknown ELF class" << std::endl; } } return elfc; } #endif // -------------------------------------------------------------------- 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-template", "Use the specified file to extract the required ELF flags and properties"), 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 required ELF format. elf_config elfc{}; #if not defined(_WIN32) // Use a template, if specified if (config.has("elf-template")) elfc = get_elf_options(config.get("elf-template")); // use ourselves otherwise else { char exePath[PATH_MAX + 1]; # if __linux or __linux__ elfc.elf_abi = ELFOSABI_LINUX; int r = readlink("/proc/self/exe", exePath, PATH_MAX); # elif __FreeBSD__ elfc.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; // The NULL is not written by readlink elfc = get_elf_options(exePath); } } #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")); } if (config.has("elf-machine")) elfc.elf_machine = config.get("elf-machine"); if (config.has("elf-class")) elfc.elf_class = config.get("elf-class"); if (config.has("elf-data")) elfc.elf_data = config.get("elf-data"); if (config.has("elf-abi")) elfc.elf_abi = config.get("elf-abi"); if (config.has("elf-flags")) elfc.elf_flags = config.get("elf-flags"); if (win_machine and elfc == elf_config{}) { MObjectFile obj(win_machine); rsrcFile.Write(obj); obj.Write(file); } else #if __has_include() { MObjectFile obj(elfc); 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.15/src/mrsrc.h0000644000175000017500000002306614746144532014331 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.15/src/rsrc-test.cpp0000644000175000017500000000060314746144532015454 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; }