|
| 1 | +#!/bin/bash |
| 2 | +# |
| 3 | +# Compile a NAM controller _F_A_S_T_ (experimental). |
| 4 | +# |
| 5 | +# Requires bash perl sed tail dd iconv xargs. |
| 6 | +set -e |
| 7 | +print_usage() { |
| 8 | +cat << EOM |
| 9 | +Compile a NAM controller _F_A_S_T_ (experimental). Usage: |
| 10 | +
|
| 11 | + ./nam-compiler-cheetah.sh [--lhd] [--low-ram] [ <input> [<output>] ] |
| 12 | +
|
| 13 | +where <input> Controller directory (defaults to ./Controller/) |
| 14 | + <output> directory or .dat file (defaults to ./NetworkAddonMod_Controller.dat) |
| 15 | + --lhd compile a LHD controller (defaults to RHD) |
| 16 | + --low-ram compile a smaller controller excluding RHW |
| 17 | + -h, --help display this help |
| 18 | +
|
| 19 | +Not for productive use as the resulting controller file can differ from the output of the ordinary compiler. |
| 20 | +EOM |
| 21 | +} |
| 22 | +######################################## |
| 23 | + |
| 24 | +# handling of input arguments |
| 25 | +for arg in "$@"; do |
| 26 | + shift |
| 27 | + case "$arg" in |
| 28 | + '-h') print_usage; exit 0 ;; |
| 29 | + '--help') print_usage; exit 0 ;; |
| 30 | + '--low-ram') lowram="1" ;; |
| 31 | + '--lhd') lhd="1" ;; |
| 32 | + '--rhd') ;; |
| 33 | + *) set -- "$@" "$arg" ;; |
| 34 | + esac |
| 35 | +done |
| 36 | +INDIR="${1:-./Controller}" |
| 37 | +INDIR="${INDIR%/}" # removes trailing slash |
| 38 | +OUT="${2:-.}" |
| 39 | +if [[ "$OUT" != *.dat ]]; then |
| 40 | + # assumes that OUT was a directory |
| 41 | + OUT="${OUT%/}/NetworkAddonMod_Controller.dat" |
| 42 | +fi |
| 43 | + |
| 44 | +if [ -z ${lhd+x} ]; then driveside='RHD'; else driveside='LHD'; fi |
| 45 | +if [ -z ${lhd+x} ]; then drivesideignore='*LHD.*'; else drivesideignore='*RHD.*'; fi |
| 46 | + |
| 47 | +directories=("RUL0" "RUL1" "RUL2" "INI" "LText") |
| 48 | +tgis=("0x0A5BCF4B" "0xAA5BCF57" "0x10000000" |
| 49 | + "0x0A5BCF4B" "0xAA5BCF57" "0x10000001" |
| 50 | + "0x0A5BCF4B" "0xAA5BCF57" "0x10000002" |
| 51 | + "0x00000000" "0x8A5971C5" "0x8A5993B9" |
| 52 | + "0x2026960B" "0x123006AA" "0x6A47FFFF") |
| 53 | + |
| 54 | +for d in "${directories[@]}" |
| 55 | +do |
| 56 | + # error out before doing anything harmful |
| 57 | + if [[ ! -d "$INDIR/$d" && "$d" != "${directories[-1]}" ]]; then |
| 58 | + echo "Error: Directory ‘$INDIR/$d’ does not exist."; exit 1; |
| 59 | + fi |
| 60 | +done |
| 61 | + |
| 62 | +sorted_input_files() { |
| 63 | + # If lowram, this skips all paths containing 'RHW' |
| 64 | + find -L "$INDIR/$1" -type f \( -name '*.txt' -o -name '*.rul' -o -name '*.ini' \) \ |
| 65 | + \! -ipath "$drivesideignore" ${lowram:+ \! -ipath '*RHW*'} -print0 | sort -z |
| 66 | +} |
| 67 | + |
| 68 | +concat_before_separator() { |
| 69 | + # `tail -v` adds a header line starting with `==>` which demarcates the |
| 70 | + # start of each file, so this command includes everything between pairs of |
| 71 | + # ==> and separator (and finally deletes lines starting with ==>). |
| 72 | + xargs -0 tail -v -n +1 - | sed -ne '/^==>/,/^\s*;###separator###/p' - | sed -e '/^==>/d' - |
| 73 | +} |
| 74 | + |
| 75 | +concat_after_separator() { |
| 76 | + # Deletes everything from ==> to separator, repeatedly. |
| 77 | + xargs -0 tail -v -n +1 - | sed -e '/^==>/,/^\s*;###separator###/d' - |
| 78 | +} |
| 79 | + |
| 80 | +strip_drive_side() { |
| 81 | + # Uncomments the RHD code (and leaves LHD code commented out, or vice versa). |
| 82 | + # If an argument is passed, this uses xargs to read file names from stdin, |
| 83 | + # else stdin is read as input directly. |
| 84 | + ${1:+ xargs -0} sed -e "s/;###${driveside}###//g" |
| 85 | +} |
| 86 | +# Note that we used sed and tail for concatenation which handle missing |
| 87 | +# newlines at the end of input files gracefully (unlike cat). |
| 88 | + |
| 89 | +strip_comments() { |
| 90 | + # Not matching whitespace for speed |
| 91 | + sed -e '/^;/d' - |
| 92 | +} |
| 93 | + |
| 94 | +uint32_to_bytes() { |
| 95 | + # little endian |
| 96 | + perl -e "print pack('V', $1)" |
| 97 | +} |
| 98 | + |
| 99 | +ltext() { |
| 100 | + uint32_to_bytes $(("${#1}" | "0x10000000")) |
| 101 | + printf "$1" | iconv -f utf-8 -t utf-16le |
| 102 | +} |
| 103 | + |
| 104 | +# Header |
| 105 | +rm -f "$OUT" |
| 106 | +printf 'DBPF' >> "$OUT" |
| 107 | +printf '\x01\x00\x00\x00\x00\x00\x00\x00' >> "$OUT" # major.minor = 1.0 |
| 108 | +for i in {1..21}; do printf '\x00\x00\x00\x00' >> "$OUT"; done # placeholder |
| 109 | + |
| 110 | +declare -a lengths=() |
| 111 | +declare -a offsets=($(wc --bytes < "$OUT")) |
| 112 | + |
| 113 | +for d in "${directories[@]}" |
| 114 | +do |
| 115 | + printf "$d..." |
| 116 | + if [ "$d" = "${directories[-1]}" ]; then |
| 117 | + # LText file |
| 118 | + ltext "Cheetah variant $driveside (not for production) compiled in $SECONDS seconds on $(date)" >> "$OUT" |
| 119 | + elif [ "$d" = "${directories[0]}" ]; then |
| 120 | + # RUL0 directory |
| 121 | + sorted_input_files "$d" | concat_before_separator | strip_drive_side >> "$OUT" |
| 122 | + sorted_input_files "$d" | concat_after_separator | strip_drive_side >> "$OUT" |
| 123 | + else |
| 124 | + # other directories |
| 125 | + sorted_input_files "$d" | strip_drive_side -0 | strip_comments >> "$OUT" |
| 126 | + fi |
| 127 | + offsets+=($(wc --bytes < "$OUT")) |
| 128 | + lengths+=($(("${offsets[-1]}" - "${offsets[-2]}"))) |
| 129 | +done |
| 130 | + |
| 131 | +# TGI Index |
| 132 | +for((i=0, k=0, grp=3; i < "${#tgis[@]}"; i+=grp, k+=1)) |
| 133 | +do |
| 134 | + tgi=("${tgis[@]:i:grp}") |
| 135 | + for id in "${tgi[@]}" |
| 136 | + do |
| 137 | + uint32_to_bytes "$id" >> "$OUT" |
| 138 | + done |
| 139 | + uint32_to_bytes "${offsets[$k]}" >> "$OUT" |
| 140 | + uint32_to_bytes "${lengths[$k]}" >> "$OUT" |
| 141 | +done |
| 142 | + |
| 143 | +# update Header |
| 144 | +header=("$(date +%s)" # creation date |
| 145 | + "$(date +%s)" # modification date |
| 146 | + "7" # index version |
| 147 | + "$k" # number of entries |
| 148 | + "${offsets[-1]}" # index location |
| 149 | + "$(($k * 20))") # index length |
| 150 | +(for h in "${header[@]}"; do uint32_to_bytes "$h"; done) \ |
| 151 | + | dd conv=notrunc status=none bs=4 seek=6 count="${#header[@]}" of="$OUT" |
| 152 | + |
| 153 | +echo "Done." |
0 commit comments