commit ee0ceba35e1c68d6369fd08970a41a561d9454ae Author: Mykola Korobko Date: Thu Aug 10 17:44:34 2023 +0300 version 0.1 lol diff --git a/EMPTY.ICF b/EMPTY.ICF new file mode 100644 index 0000000..29b40e6 --- /dev/null +++ b/EMPTY.ICF @@ -0,0 +1,130 @@ +20550200 +# +0000102055020000000000A69C718800C0B63E +00101083105000000000000A143232642D3C00 +0020108C051903000000000000000000000000 +00301000000000000000000000000000000000 +00401000000000000000000000000000000000 +00501000000000000000000000000000000000 +006010123415935700081F04062C0E01022829 +0070100000000000090000FF1F1FF8F80A0E0F +008010000D100C808064321E50505050460A0A +0090101E321E1010140A0A1E14051E003C3C0A +00A0100A0A0A000000000000001A480D240000 +00B01020202020202020202020202020202020 +00C01020202020202020202020202020202020 +00D01000000000000000000000000000000000 +00E01000000000000000000000000000000000 +00F010065A0B680B0C0A35096409180850080D +010010074D071205440E2405750D3306262306 +011010240D1402141715091607161217101805 +012010190619111A121B0A1C0C1D051E091F04 +01301020092104220A2A0B2C091013110A2802 +014010290A1115120D25092704646446464628 +01501028642110100A0A0A0606100611110C0C +0160100C070711060095009500950095009500 +01701000000000000000000000000000000000 +01801000000000000000000000000000000000 +01901000000000000000000000000000000000 +01A01000000000000000000000000000000000 +01B01080080080080080080080080080080080 +01C01008008008008008008008000000000000 +01D0100000FFFFFFFF81000000000081000000 +01E01000008100000000008100000000008100 +01F010FF000000000080080003023000010000 +02001000FFFF00000000008008000302300001 +021010000000FFFF0000000000800800030230 +0220100001000000FFFF000000000080080003 +02301002300001000000FFFF00000000008008 +024010000302300001000000FFFF0000000000 +0250108008000302300001000000FFFF000000 +02601000008008000302300001000000FFFF00 +027010000000008008000302300001000000FF +028010FF000000000080080003023000010000 +02901000FFFF00000000008008000302300001 +02A010000000FFFF0000000000800800030230 +02B0100001000000FFFF000000000080080003 +02C01002300001000000FFFF00000000008008 +02D010000302300001000000FFFF0000000000 +02E0108008000302300001000000FFFF000000 +02F01000008008000302300001000000FFFF00 +030010000000008008000302300001000000FF +031010FF000000000080080003023000010000 +03201000FFFF00000000008008000302300001 +033010000000FFFF0000000000800800030230 +0340100001000000FFFF000000000080080003 +03501002300001000000FFFF00000000008008 +036010000302300001000000FFFF0000000000 +0370108008000302300001000000FFFF000000 +03801000008008000302300001000000FFFF00 +039010000000008008000302300001000000FF +03A010FF000000000080080003023000010000 +03B01000FFFF00000000008008000302300001 +03C010000000FFFF0000000000800800030230 +03D0100001000000FFFF000000000080080003 +03E01002300001000000FFFF00000000008008 +03F010000302300001000000FFFF0000000000 +0400108008000302300001000000FFFF000000 +04101000008008000302300001000000FFFF00 +042010000000008008000302300001000000FF +04301011111FFF190022222FFF190033333FFF +044010190044444FFF190055555FFF19006666 +0450106FFF190077777FFF190088888FFF1900 +04601099999FFF190000000FFF190000000FFF +047010190000000FFF190000000FFF19000000 +0480100FFF190000000FFF190000000FFF1900 +04901000000FFF190000000FFF190000000FFF +04A010190000000FFF190000000FFF19000000 +04B0100FFF190000000FFF190000000FFF1900 +04C01000000FFF190000000FFF190000000FFF +04D010190000000FFF190000000FFF00300000 +04E0100FFF003000000FFF003000000FFF0030 +04F010000082070011111FFF00008306002222 +0500102FFF000083060033333FFF0000830600 +05101044444FFF000083060055555FFF000083 +052010060066666FFF000083060077777FFF00 +0530100083060088888FFF0000830600000000 +05401020202020202020002020202020202000 +05501020202020202020002020202020202000 +05601020202020202020002020202020202000 +05701020202020202020002020202020202000 +05801020202020202020002020202020202000 +05901020202020202020002020202020202000 +05A01020202020202020002020202020202000 +05B01020202020202020002020202020202000 +05C01020202020202020002020202020202000 +05D01020202020202020002020202020202000 +05E01020202020202020002020202020202000 +05F01020202020202020002020202020202000 +06001020202020202020002020202020202000 +06101020202020202020002020202020202000 +06201020202020202020002020202020202000 +06301020202020202020002020202020202000 +06401020202020202020002020202020202000 +06501020202020202020002020202020202000 +06601020202020202020002020202020202000 +06701020202020202020002020202020202000 +06801020202020202020002020202020202000 +06901020202020202020002020202020202000 +06A01020202020202020002020202020202000 +06B01020202020202020002020202020202000 +06C01020202020202020002020202020202000 +06D01020202020202020002020202020202000 +06E01020202020202020002020202020202000 +06F01020202020202020002020202020202000 +07001020202020202020002020202020202000 +07101020202020202020002020202020202000 +072010202043414C204200202043414C204100 +07301020205245532032002020524553203100 +074010202047524F555000202043414C4C3100 +075010202043414C4C3200202043414C4C3300 +076010202043414C4C3400202043414C4C3500 +077010202043414C4C3600202043414C4C3700 +078010202043414C4C38000000000000000000 +07901000000000000000000000000000000000 +07A01001010101010101010101010101010101 +07B01001010101010101010101010101010101 +07C01020205343414E310020205343414E3200 +07D01020205343414E330020205343414E3400 +07E01020205343414E35002020202020202000 +07F010000000000000000049434F4D20496E63 diff --git a/README.org b/README.org new file mode 100644 index 0000000..0cd7f1e --- /dev/null +++ b/README.org @@ -0,0 +1,149 @@ +* ICEX patcher +** Disclaimer +THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, YOU ARE USING THIS AT YOUR OWN RISK. PATCHING THE FIRMWARE MIGHT VOID YOUR TRANSEIVER WARRANTY. ENSURE MAKING A BACKUPS AND THINK OF WHAT ACTIONS YOU ARE PERFORMING. NONE OF THE AUTHORS, CONTRIBUTORS, ADMINISTRATORS, OR ANYONE ELSE CONNECTED WITH THIS REPOSITORY, IN ANY WAY WHATSOEVER, CAN BE RESPONSIBLE FOR YOUR USE OF THE SOFTWARE BELOW. + +** Motivation +Icom CS software for IC-F0 model is back from 1997, requires DOSbox and not more usefull than =xxb=. That's how we get IC-CS-X, or ICEX +** TODO Usage +=Syntax: ./ic_f300_patcher.sh -s %channel_numeber%=%valuein kHz% -f %path/to/x6110_app%-p %power_setting% -m %modulation_depth%= + +File will be created from EMPTY.ICF template, if not provided. + +See =./ic_f300_patcher.sh -h= to see the list of supported values for changing. + +Help and fallback is not yet stable, so follow the available values: +| %channel_numeber% | 1-30 | +| %power_setting% | l1, l2, h (default) | +| %modulation_depth%= | w (default) | + +Example: +#+NAME: example +#+BEGIN_SRC sh :eval never +./ic_f300_patcher.sh -s 1=172867 -p l1 -m n +#+END_SRC + +As soon as this script substitutes the existing values inside the binary, provided values shoud have the same bit-length as default. Otherwise it may lead to the segmentation fault. Remember to backup the firmware. + + +** Theory behind +Well, it needs a bit of a hex-magic. But valuables are easy find-and-replaceable in hexeditor. Here are some offsets to remember: + +Start offset of the Rx freq of the first Channel of the first bank - =0000050b= + +Length of channel record is 44 bytes? +#+NAME: theoretical chan mapping +#+BEGIN_SRC bash +#!/bin/bash +result=$(( 0x50b - 0x2c )) +for i in {0..30}; do + result=$(($result + 0x2c)) + + if [ "$i" -eq 9 ]; then + result=$(( $result + 0x8 )) + elif [ "$i" -eq 14 ]; then + result=$(( $result + 0x8)) + elif [ "$i" -eq 23 ]; then + result=$(( $result + 0x8)) + fi + + + printf "%d, 0x%X\n" "$((i + 1))" "$result" +done + +#+END_SRC + +Here is the comparison of calculated channel offsetts and the real one, obtained from the incremental changes diffing +#+RESULTS: theoretical chan mapping +| ch | offset calculated | real offset | +| 1 | 0x50B | 0x50b | +| 2 | 0x537 | 0x537 | +| 3 | 0x563 | 0x563 | +| 4 | 0x58F | 0x58f | +| 5 | 0x5BB | 0x5bb | +| 6 | 0x5E7 | 0x5e7 | +| 7 | 0x613 | 0x613 | +| 8 | 0x63F | 0x63f | +| 9 | 0x66B | 0x673 | +| 10 | 0x69F | 0x69f | +| 11 | 0x6CB | 0x6cb | +| 12 | 0x6F7 | 0x6f7 | +| 13 | 0x723 | 0x723 | +| 14 | 0x74F | 0x74f | +| 15 | 0x783 | 0x77b | +| 16 | 0x7AF | 0x7b5 | +| 17 | 0x7DB | 0x7db | +| 18 | 0x807 | 0x807 | +| 19 | 0x833 | 0x833 | +| 20 | 0x85F | 0x85f | +| 21 | 0x88B | 0x88b | +| 22 | 0x8B7 | 0x8b7 | +| 23 | 0x8E3 | 0x8e3 | +| 24 | 0x917 | 0x91d | +| 25 | 0x943 | 0x943 | +| 26 | 0x96F | 0x96f | +| 27 | 0x99B | 0x99b | +| 28 | 0x9C7 | 0x9c7 | +| 29 | 0x9F3 | 0x9f3 | +| 30 | 0xA1F | 0xa1f | +| 31 | 0xA4B | | + + ++ Ch8 is 8 bytes longer ++ Ch15 is 8 bytes longer ++ Ch23 is 8 bytes longer + +#+NAME: let's try +#+BEGIN_SRC shell :results output +hexdump -s0x50b -n44 -C FIRST_AN.ICF +#+END_SRC + +#+RESULTS: let's try +: 0000050b 31 34 35 35 30 30 31 34 36 36 30 30 30 30 43 30 |14550014660000C0| +: 0000051b 30 43 30 33 30 36 33 30 30 30 30 31 30 30 30 30 |0C03063000010000| +: 0000052b 0d 0a 30 32 30 30 31 30 30 30 46 46 |..02001000FF| +: 00000537 + + +Watch the =14550014600= - Rx and Tx freqs of the first channel + +Last channel is 31, let's try and pick it directly +#+BEGIN_SRC shell :results output +hexdump -s$(printf "0x%X" $((0x50b + 29 * 0x2c + 3*0x8))) -n44 -C FIRST_AN.ICF +#+END_SRC + +#+RESULTS: +: 00000a1f 31 35 35 36 30 30 31 34 34 37 30 30 0d 0a 30 34 |155600144700..04| +: 00000a2f 30 30 31 30 38 30 30 38 30 30 30 33 30 32 33 30 |0010800800030230| +: 00000a3f 30 30 30 31 30 30 30 30 30 30 46 46 |0001000000FF| +: 00000a4b +*** Channel in details +Rx/Tx frequencies are going one by one. Power bit for ch1 is 0x51e. 19th position, offset 0x13 +#+NAME: power bit +#+BEGIN_SRC sh :eval once :results output +cd ~/src/radio/IC-F300 +binwalk -i -t -W L1N14480.ICF L2N14480.ICF N144800.ICF +#+END_SRC + +#+RESULTS: power bit +: +: OFFSET L1N14480.ICF L2N14480.ICF N144800.ICF +: -------------------------------------------------------------------------------- +: * +: 0x00000510 30 31 34 34 38 30 30 38 30 30 38 30 30 30 31 30 |0144800800800010| \ 30 31 34 34 38 30 30 38 30 30 38 30 30 30 32 30 |0144800800800020| \ 30 31 34 34 38 30 30 38 30 30 38 30 30 30 33 30 |0144800800800030| +: * +: + +Modulation width for ch1 is 0x523. 24th position, offset 0x18 +#+NAME: narrow/wide +#+BEGIN_SRC sh :eval once :results output +binwalk -i -W 144800.ICF N144800.ICF +#+END_SRC + +#+RESULTS: narrow/wide +: +: OFFSET 144800.ICF N144800.ICF +: -------------------------------------------------------------------------------- +: * +: 0x00000520 32 33 30 30 30 30 31 30 30 30 30 0D 0A 30 32 30 |23000010000..020| \ 32 33 30 38 30 30 31 30 30 30 30 0D 0A 30 32 30 |23080010000..020| +: * +: diff --git a/ic_f300_patcher.sh b/ic_f300_patcher.sh new file mode 100755 index 0000000..503e92a --- /dev/null +++ b/ic_f300_patcher.sh @@ -0,0 +1,259 @@ +#!/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 - 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