#!/bin/sh
# shellcheck disable=SC2034,SC2317
# Скрипт преобразования hex в fw.
# В качестве параметра принимает путь к файлу hex.

# https://srecord.sourceforge.net/
# https://manpages.ubuntu.com/manpages/xenial/man1/srec_cat.1.html
# https://manpages.ubuntu.com/manpages/xenial/man1/srec_examples.1.html


# echo -e "\033[0;32m~~~ Hex to FW ~~~\033[0m";

PATH=$(pwd)/$(dirname "$0")/srecord:$PATH
PATH="C:/Program Files/Git/usr/bin/":$PATH

#========================== Functions ==========================
# ByteString - ascii string with bytes separated by spaces like "00 11 22 33"

HexStringToByteString() { printf "%s" "$1" | od -An -t x1 | tr -d '\n' | tr 'a-f' 'A-F' | awk '{$1=$1};1' | tr -d '\n'; }

GetRandomByteString() { openssl rand "$1" | od -An -t x1 | tr -d '\n' | tr 'a-f' 'A-F' | awk '{$1=$1};1' | tr -d '\n'; }

IntelHexFileToByteString()
{
	T=$(srec_cat "$1" -intel -fill 0xFF -over "$1" -intel -address-length=4 -o - -ascii_hex)
	res1=0x$(echo "$T" | head -n 1 | cut -f1 -d , | sed "s/.*\$A//")	# Output: start addr
	T=$(echo "$T" | tr -d '\r' | tr '\n' ' ' | tr '\002\003' ',')
	T=$(echo "$T" | cut -f3 -d , | awk '{$1=$1};1')		# (awk '{$1=$1};1' - remove leading, trailing and extra spaces between fields)
	res2=$(echo "$T" | wc -w)		# Output: size in bytes
	res3=$T							# Output: byte string
}

# parmeters: addr fieldsnum string
GetFieldsFromByteString() { echo "$3" | cut -d ' ' -f "$1"-$(($1 + $2 - 1)); }

# parmeters: addr fieldsnum string
GetHexFromByteString() { printf 0x; GetFieldsFromByteString "$1" "$2" "$3" | awk '{print $4,$3,$2,$1}' | tr -d ' '; }

# parmeters: addr fieldsnum string
GetStringFromByteString() { GetFieldsFromByteString "$1" "$2" "$3" | xxd -r -p | tr -d '\000'; }

ByteStringToAsciiHex()
{
	printf "\002 \$A%08X,\n" "$1"					# Start and Address
	echo "$2" | fold -s -w48 | awk '{$1=$1};1'		# Data (awk '{$1=$1};1' - remove leading, trailing and extra spaces between fields)
	printf "\003"									# The End
}

CRC32_FromByteString()
{
	# CAN прошивка использует кривой MPEG2 CRC32 по историческим причинам
	# В будущем надо исправить на нормальный CRC32 от STM32
	T=$(ByteStringToAsciiHex 0x10000000 "$1")
	T=$(echo "$T" | srec_cat - -ascii_hex -Bit_Reverse 4 -stm32 0 -Bit_Reverse 4 -xor 0xFF -o -ascii_hex)
	echo "$T" | tr -d '\002\003' | head -n 2 | tail -n 1 | cut -d ' ' -f1-4
}

HexToByteString() { echo "$1" | sed 's/^0x//' | fold -w2 | tac | tr '\n' ' ' | sed 's/ $//'; }

#========================== Main code ==========================

IntelHexFileToByteString "$1"
fw_start_addr=$res1
# echo "fw_start_addr $fw_start_addr"
fw_size=$res2
# echo "fw_size $fw_size"
fw_data=$res3
# echo "fw_data: $fw_data"

# выкидываем последние 4 байта и дописываем наше CRC
fw_data=$(GetFieldsFromByteString 1 $((fw_size - 4)) "$fw_data")
crc=$(CRC32_FromByteString "$fw_data")
fw_data="$fw_data $crc"
# echo "fw_data: $fw_data"

struct_addr=$(GetHexFromByteString 1 4 "$fw_data")
# echo "struct_addr $struct_addr"
struct_offset=$(( struct_addr - fw_start_addr ))
# echo "struct_offset $struct_offset"

# // Структура массива информации о CAN-прошивке
# struct TCanFwInfo {
#     TVersion            Version;              // Версия ПО
#     uint32_t            Build;                // Хеш коммита в репозитории git
#     uint32_t const *    pCRC32;               // Адрес, где расположена CRC32
#     uint32_t            reserved2;
#     uint8_t             TextInfo[64];         // Текстовая информация
# } __attribute__((packed, aligned(4)));

# // описание прошивки
# const TCanFwInfo gCanFwInfo =
# {
#     (CAN_FW_VERSION << 24) | BUILD_DATE,        // Version - Версия ПО
#     GIT_BUILD,                                  // Хеш коммита в репозитории git
#     &CanChksum,                                 // *pCRC32 - Адрес, где расположена CRC32
#     0,                                          // reserved2
#     M_NAME CAN_FW_DESCRIPTION "_" GIT_VERSION   // TextInfo[64] - Текстовая информация
# };

index=$(( struct_offset + 1 ))
day=$(GetFieldsFromByteString "$index" 1 "$fw_data")
# echo "day: $day"

index=$(( index + 1 ))
mounth=$(GetFieldsFromByteString "$index" 1 "$fw_data")
# echo "mounth: $mounth"

index=$(( index + 1 ))
year=$(GetFieldsFromByteString "$index" 1 "$fw_data")
# echo "year: $year"

index=$(( index + 1 ))
can_fw_version=$(GetFieldsFromByteString "$index" 1 "$fw_data")
# echo "can_fw_version: $can_fw_version"

index=$(( index + 1 ))
git_build=$(GetFieldsFromByteString "$index" 4 "$fw_data")
# echo "git_build: $git_build"

index=$(( index + 4 ))
CRC_addr=$(GetHexFromByteString "$index" 4 "$fw_data")
# echo "CRC_addr: $CRC_addr"

index=$(( index + 8 ))
text_info=$(GetStringFromByteString "$index" 64 "$fw_data")
# echo "text_info: $text_info"

# Версия git в последнем поле после '_'
git_version=$(echo "$text_info" | awk -F '_' '{print $NF}')
# echo "git_version: $git_version"

# struct TFirmwareHdr {
#     uint32_t  ChckSum;        // вычисляется по всем нижеследующим полям и данным
#     uint32_t  Type;           // тип прошивки
#     uint32_t  Version;        // версия/дата
#     uint32_t  Build;          // Билд прошивки
#     uint32_t  Len;            // длина данных
#     uint32_t  Offset;         // смещение прошивки во flash
#     uint8_t   InitVect[8];    // Случайное число, используется при дешифровании
# };  // sizeof() = 32

# struct TFirmwareFileHdr {
#     uint8_t       Sign[8];    // fixed to 'MOBICAR '
#     uint8_t       Desc[64];   // текстовое описание
#     uint8_t       pad1[24];   // дополнение до 96 байт
#     TFirmwareHdr  bin_hdr;
# };  // sizeof() = 128

# hdr1 - заголовок до CRC
hdr1="MOBICAR version $can_fw_version$year$mounth$day, $day.$mounth.$year, GIT = $git_version"
# echo "hdr1: $hdr1"
length=$(( ${#hdr1} ))
# echo "length: $length"
hdr1=$(HexStringToByteString "$hdr1")

# Добиваем остаток до TFirmwarehdr (8+64+24=96)
for i in $(seq $(( length + 1 )) 96); do
	hdr1="$hdr1 00"
done
# printf "hdr1:\n%s\n" "$hdr1"

# hdr2 - заголовок после CRC
hdr2="02 00 00 00"	# тип прошивки (CAN)
hdr2="$hdr2 $day $mounth $year $can_fw_version"	# версия/дата
hdr2="$hdr2 $git_build"	# Хеш коммита в репозитории git
len=$(printf "%08X" "$fw_size")	# Len (Длина данных)
len=$(HexToByteString "$len")
hdr2="$hdr2 $len"
offset=$(printf "%08X" "$fw_start_addr")	# offset (смещение прошивки во flash)
offset=$(HexToByteString "$offset")
hdr2="$hdr2 $offset"
rand=$(GetRandomByteString 8)
# echo "rand: $rand"
hdr2="$hdr2 $rand"
# printf "hdr2:\n%s\n" "$hdr2"

crc=$(CRC32_FromByteString "$hdr2 $fw_data")
# printf "crc:\n%s\n" "$crc"
fw="$hdr1 $crc $hdr2 $fw_data"
# printf "fw:\n%s\n" "$fw" | fold -w48

fw_path="$(dirname "$1")/$text_info.fw"
printf "Generate \033[0;32m%s\033[0m\n" "$fw_path"
ByteStringToAsciiHex 0 "$fw" | srec_cat - -ascii_hex -o "$fw_path" -binary
