# Filename: ConcatenateToCXX.cmake
#
# Description: When run, creates a single C++ file which includes a const char[]
#   containing the bytes from one or more files.
#
#   There is a {SYMBOL_NAME}_size symbol defined as well, storing the total
#   number of bytes in the concatenated input files.
#
#   A single null terminator byte is added for the benefit of programs that
#   simply treat the data array as a string.
#
# Usage:
#   This script is invoked via add_custom_target, like this:
#   cmake -D OUTPUT_FILE="out.cxx" -D SYMBOL_NAME=data -D INPUT_FILES="a.bin b.bin" -P ConcatenateToCXX.cmake
#

if(NOT CMAKE_SCRIPT_MODE_FILE)
  message(FATAL_ERROR "ConcatenateToCXX.cmake should not be included but run in script mode.")
  return()
endif()

if(NOT DEFINED OUTPUT_FILE)
  message(FATAL_ERROR "OUTPUT_FILE should be defined when running ConcatenateToCXX.cmake!")
  return()
endif()

if(NOT DEFINED SYMBOL_NAME)
  set(SYMBOL_NAME "data")
endif()

file(WRITE "${OUTPUT_FILE}" "/* Generated by CMake.  DO NOT EDIT. */

extern const char ${SYMBOL_NAME}[];
extern const int ${SYMBOL_NAME}_size;

const char ${SYMBOL_NAME}[] = {\n")

set(byte_count 0)
separate_arguments(INPUT_FILES)
foreach(infile ${INPUT_FILES})
  file(APPEND "${OUTPUT_FILE}" "  /* ${infile} */\n")

  set(offset 0)
  while(1)
    # Read up to 1024 bytes from the input file
    file(READ "${infile}" data LIMIT 1024 OFFSET ${offset} HEX)
    math(EXPR offset "${offset} + 1024")

    # If 'data' is empty, we're done
    if(NOT data)
      break()
    endif()

    # Count the bytes we're adding
    string(LENGTH "${data}" strlen)
    math(EXPR byte_count "${byte_count} + (${strlen} / 2)")

    # Format runs of up to 32 hex chars by indenting and giving a newline
    string(REGEX REPLACE
      "(...?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?.?)" " \\1\n"
      data "${data}")
    # Format each byte (2 hex chars) in each line with 0x prefix and comma suffix
    string(REGEX REPLACE "([0-9a-fA-F][0-9a-fA-F])" " 0x\\1," data "${data}")
    file(APPEND "${OUTPUT_FILE}" "${data}")
  endwhile()
endforeach(infile)

file(APPEND "${OUTPUT_FILE}" "  0\n};

extern const int ${SYMBOL_NAME}_size = ${byte_count};\n")
