IC-CS-X/ic_f300_patcher.sh
2023-08-10 17:49:55 +03:00

260 lines
5.8 KiB
Bash
Executable File

#!/usr/bin/env bash
# pure sh has no implementation of hash tables, bash is present on x6100
MODE="undef"
NO_ARGS=0
E_OPTERROR=85
# TODO: unify keys in constants
# ============================== Option descriptions for help output
declare -A description
description["first_ch"]="icom offset for freqs"
CH_STEP=0x29 # 40 bytes
# ============================== Channel address pointers
declare -A addrs
addrs["1"]=0000050b
addrs["2"]=00000537
addrs["3"]=00000563
addrs["4"]=0000058f
addrs["5"]=000005bb
addrs["6"]=000005e7
addrs["7"]=00000613
addrs["8"]=0000063f
addrs["9"]=00000673
addrs["10"]=0000069f
addrs["11"]=000006cb
addrs["12"]=000006f7
addrs["13"]=00000723
addrs["14"]=0000074f
addrs["15"]=0000077b
addrs["16"]=000007b5
addrs["17"]=000007db
addrs["18"]=00000807
addrs["19"]=00000833
addrs["20"]=0000085f
addrs["21"]=0000088b
addrs["22"]=000008b7
addrs["23"]=000008e3
addrs["24"]=0000091d
addrs["25"]=00000943
addrs["26"]=0000096f
addrs["27"]=0000099b
addrs["28"]=000009c7
addrs["29"]=000009f3
addrs["30"]=00000a1f
# ============================== Power address pointers
declare -A pwr
pwr["l1"]=1
pwr["l2"]=2
pwr["h"]=3
power_offset=0x13
# ============================== Modulation address pointers
declare -A mod
mod["n"]=8
mod["w"]=0
modulation_offset=0x18
help()
{
echo "Script, that patches ICOM IC-F320 firmware"
echo
echo "Keys:"
echo "-s %ch%=%frequency_mhz%"
echo "-h see this help"
echo
echo "Syntax: $0 -s %ch%=%frequency_mhz% [-f %path/to/ICF_FILE%"]
echo "takes one %ch%=%frequency_mhz% pair at once for now"
echo
echo "Filename would be created based on date and time,"
echo "if not provided"
echo
echo "Available options:"
for k in "${!description[@]}"
do
printf " - %s\n" "$k: ${description[$k]}"
done
echo
exit 1
}
# ============================== Argparse
if [ $# -eq "$NO_ARGS" ] # Script invoked with no command-line args?
then
echo "Arguments required"
echo
help
exit $E_OPTERROR
fi
ARG_REGEX="(.+)=(.+)"
while getopts "s:g:p:m:f:h" arg; do
case $arg in
h)
help
;;
s)
# TODO: populate a hashtable here and then cycle throug them in a main cycle
MODE="set"
if [[ $OPTARG =~ $ARG_REGEX ]]
then
key="${BASH_REMATCH[1]}"
value="${BASH_REMATCH[2]}"
else
echo "Syntax error at -s parametr"
exit $E_OPTERROR
fi
;;
g)
# TODO get values
# hexdump -s0x50b -n44 $filename
# and parse
MODE="get"
;;
p)
#TODO power
# l1 l2 h (default)
power="${OPTARG:-l1}"
power=$(echo "$power" | tr '[:upper:]' '[:lower:]')
;;
m)
#todo modulation
# n w (n to default?)
modulation="${OPTARG:-n}"
modulation=$(echo "$modulation" | tr '[:upper:]' '[:lower:]')
;;
f)
file="$OPTARG"
;;
esac
done
# ============================== Main code part
generate_filename() {
# is just date
# cho $(date +'%Y-%m-%d_%H-%M').ICF
echo $(date +'%Y-%m').ICF
}
hexify_string() {
# arg1 - value to prepare for inserting into binary
# returns - hex ascii code
local value="$1"
echo "$value" | xxd -ps | head -c-3
}
calculate_offset() {
# ar1 - initial offset
# arg2 - additional offset
local initial=$1
local additional=$2
echo $(printf "%X" $(( 0x$(echo $initial | sed 's/^0*//') + $additional )))
}
compose_patchstring() {
# arg1 - option offset
# arg2 - value in ASCII
# returns - <addr: hex value> formatted string to use in xxd
local addr="$1"
local val="$2"
echo "<$addr: $(hexify_string $val)>"
}
patch_bin() {
# arg1 - prepared binary patch string
# arg2 - path to the file to parch
# returns - void
local patchstring="$1"
local target="$2"
echo "$patchstring" | xxd -r - "$target"
}
patch_value() {
# arg1 - prepared binary patch string
# arg2 - path to the file to parch
# returns - void
local offset="$1"
local value="$2"
patchstring=$(compose_patchstring $offset $value)
# here we might want to convert csv to number of patchstrings
echo "writing :: $patchstring into $filename"
patch_bin "$patchstring" "$filename"
}
prepare_file(){
# arg1 - prepared binary patch string
local filepath="$1"
if [ -z "$filepath" ]; then
local filename=$(generate_filename)
cp ./EMPTY.ICF "./$filename"
echo "./$filename"
else
if [ ! -f "$filepath" ]; then
cp ./EMPTY.ICF "$filepath"
fi
echo "$filepath"
fi
}
main(){
local ch=$1
local freq=$2
# if no $3, then copy EMPTY.ICF to %date%.icf and patch it
local file=$3
local power=$power
local modulation=$modulation
local chan_offset=${addrs["$ch"]}
# TODO IF MODE
filename=$(prepare_file $file)
patch_value $chan_offset $freq
if [ -n "$power" ]; then
offset=$(calculate_offset $chan_offset $power_offset)
echo $offset
patch_value $offset ${pwr["$power"]}
fi
if [ -n "$modulation" ]; then
offset=$(calculate_offset $chan_offset $modulation_offset)
patch_value $offset ${mod["$modulation"]}
fi
}
# ============================== Sanity checks
# TODO optimize
# if [ -z ${file+x} ]; then echo "No file provided"; exit $E_OPTERROR; fi
# if [ ! -f "$file" ]; then
# echo "File $file does not exist."
# exit $E_OPTERROR
# fi
if [ -z ${key+x} ]; then echo "No channel provided"; exit $E_OPTERROR; fi
if [ -z ${value+x} ]; then echo "No frequency provided"; exit $E_OPTERROR; fi
if [ -z ${addrs["$key"]+x} ]; then
echo "Wrong key: $key"
exit $E_OPTERROR
fi
if [ -n "${power+x}" ] && [ -z "${pwr["$power"]+x}" ]; then
echo "Wrong power setting: $power"
exit $E_OPTERROR
fi
if [ -n "${modulation+x}" ] && [ -z ${mod["$modulation"]+x} ]; then
echo "Wrong moudlation: $modulation"
exit $E_OPTERROR
fi
# we should review arguments string
main $key $value $file #$power $modulation