#!/bin/bash

set -x

# Written by Steve Palm - n9yty@n9yty.com
# If this breaks your V-Accordion, I am not responsible. I have used
# it only on my FR-4xb and have not had a chance to test it with any
# other accordion. I think the basic principles here could apply to
# the FR-8x as well, but I have no way to test that.
#
# Use with care
#
#
# bash ./FR_extract_sets_from_set_all.sh
# The file it processes is defined as "ALL_SET" below

DATE=`date +"%Y%m%d%H%M%S"`
OUTDIR=FR4X_SETS_$DATE
mkdir -p $OUTDIR

ALL_SET=SET_ALL.FR4
ALL_SET_DATA=SET_ALL.DATA

SET_BINARY_HEADER="\\xEF\\xBB\\xBF\\x00\\x00\\x03\\x28"

# echo "Binary header for single set: $SET_BINARY_HEADER"

# This is the XML that has to get inserted at the top of each set file
#
# NOTE: TABS ARE EMBEDDED HERE!
#
read -r -d '' SET_XML_HEADER <<'END_OF_XML'
<?xml version="1.0" encoding="utf-8"?>
<Roland_Fr_Set>
	<SET size="26038" number="1" offset="0">
		<SC size="128" number="1" offset="0"/>
		<O_R size="86" number="26" offset="128"/>
		<OB_R size="66" number="7" offset="2364"/>
		<OBC_R size="72" number="8" offset="2826"/>
		<OFB_R size="72" number="8" offset="3402"/>
		<TR size="304" number="16" offset="3978"/>
		<BR size="84" number="16" offset="8842"/>
		<BCR size="96" number="16" offset="10186"/>
		<ORR size="64" number="26" offset="11722"/>
		<OBR size="42" number="7" offset="13386"/>
		<OBCR size="124" number="8" offset="13680"/>
		<OFBR size="118" number="8" offset="14672"/>
		<ORCH1 size="210" number="28" offset="15616"/>
		<ORCH2 size="152" number="28" offset="21496"/>
		<SC2 size="286" number="1" offset="25752"/>
	</SET>
</Roland_Fr_Set>
END_OF_XML

XML_MATCH_POS=`strings -a -t d $ALL_SET | grep '<?xml' | awk '{print $1}'`
XML_MATCH_STR=`strings -a -t d $ALL_SET | grep '<?xml' | awk '{print $2}'`
# Since there may be a printable character in front of the <?xml, we have to handle that
XML_SUBPOS=`awk -v a="$XML_MATCH_STR" -v b="<?xml" 'BEGIN{print index(a,b)}'`
let XML_START="$XML_MATCH_POS + $XML_SUBPOS - 1"

XML_END=`strings -a -t d $ALL_SET | grep '</Roland_Fr>' |awk '{print $1}'`
# Length of search string plus 0x0a
let XML_END+=13
let XML_SIZE="$XML_END - $XML_START"

echo "$XML_START to $XML_END is $XML_SIZE"

read a

XML=`dd skip=$XML_START bs=1 count=$XML_SIZE if=$ALL_SET 2>/dev/null`

echo "XML:\n$XML\n"

# Get size, count and offset for each section from the XML

read a

VR_SIZE=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VR/@size)' -`
VR_COUNT=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VR/@number)' -`
VR_SKIP=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VR/@offset)' -`

NEGP_SIZE=`echo $XML | xmllint --xpath 'string(/Roland_Fr/NEGP/@size)' -`
NEGP_COUNT=`echo $XML | xmllint --xpath 'string(/Roland_Fr/NEGP/@number)' -`
NEGP_SKIP=`echo $XML | xmllint --xpath 'string(/Roland_Fr/NEGP/@offset)' -`

EGP_SIZE=`echo $XML | xmllint --xpath 'string(/Roland_Fr/EGP/@size)' -`
EGP_COUNT=`echo $XML | xmllint --xpath 'string(/Roland_Fr/EGP/@number)' -`
EGP_SKIP=`echo $XML | xmllint --xpath 'string(/Roland_Fr/EGP/@offset)' -`

VTW_SIZE=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VTW_PRESET/@size)' -`
VTW_COUNT=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VTW_PRESET/@number)' -`
VTW_SKIP=`echo $XML | xmllint --xpath 'string(/Roland_Fr/VTW_PRESET/@offset)' -`

SET_SIZE=`echo $XML | xmllint --xpath 'string(/Roland_Fr/SET/@size)' -`
SET_COUNT=`echo $XML | xmllint --xpath 'string(/Roland_Fr/SET/@number)' -`
SET_SKIP=`echo $XML | xmllint --xpath 'string(/Roland_Fr/SET/@offset)' -`

echo "$VR_COUNT VR pieces, each $VR_SIZE bytes, starting at position $VR_SKIP"
echo "$NEGP_COUNT NEGP pieces, each $NEGP_SIZE bytes, starting at position $NEGP_SKIP"
echo "$EGP_COUNT EGP pieces, each $EGP_SIZE bytes, starting at position $EGP_SKIP"
echo "$VTW_COUNT VTW_PRESET pieces, each $VTW_SIZE bytes, starting at position $VTW_SKIP"
echo "$SET_COUNT sets, each $SET_SIZE bytes, starting at position $SET_SKIP"
echo

function dump_section() {
  COUNT=$1
  SIZE=$2
  SKIP=$3
  FNSUFF=$4
  let size="$COUNT * $SIZE"
  let offset="$XML_END + $SKIP"
  echo "Dumping out $FNSUFF data ($size bytes)"
  dd bs=1 skip=$offset count=$size if=$ALL_SET of=$OUTDIR/SET_ALL_$FNSUFF.DATA 2>/dev/null
} 

dump_section $VR_COUNT $VR_SIZE $VR_SKIP VR
dump_section $NEGP_COUNT $NEGP_SIZE $NEGP_SKIP NEGP
dump_section $EGP_COUNT $EGP_SIZE $EGP_SKIP EGP
dump_section $VTW_COUNT $VTW_SIZE $VTW_SKIP VTW
dump_section $SET_COUNT $SET_SIZE $SET_SKIP SET 


set=1
let ALL_SET_DATA_SIZE="$SET_COUNT * $SET_SIZE"
for (( pos=0; pos<$ALL_SET_DATA_SIZE; pos+=$SET_SIZE ))
do
  SET_NUM=$(printf "%03d" $set)
  OUT_TMP=$OUTDIR/SET_${SET_NUM}.DATA
  OUT_FN=$OUTDIR/SET_${SET_NUM}.ST4
  echo "Working on set $SET_NUM at pos $pos"
  dd bs=1 skip=$pos count=$SET_SIZE if=$OUTDIR/SET_ALL_SET.DATA of=$OUT_TMP 2>/dev/null
  # No newline
  echo -n -e "$SET_BINARY_HEADER" >$OUT_FN
  # Keep the newline
  echo -e "$SET_XML_HEADER" >>$OUT_FN
  cat $OUT_TMP >>$OUT_FN
  # Remove our working data file
  rm $OUT_TMP
  let set++
done

# Looking at the original file, not a data subset, so we have to account
# for the XML header and all the other parts, then offset to where we left
# off with the SET processing
#
echo "Dumping out ending data"
let offset="$XML_END + $VR_SIZE * $VR_COUNT + $NEGP_SIZE * $VR_COUNT + $EGP_SIZE * $EGP_COUNT + $VTW_SIZE * $VTW_COUNT + $SET_SIZE * $SET_COUNT"
dd bs=1 skip=$offset if=$ALL_SET of=$OUTDIR/SET_ALL_ENDING.DATA 2>/dev/null

# rm SET_ALL_SET.DATA

# When comparing the dumped set here from the block data dumped out of an individual set export
# The only difference was one byte at offset 0x3d47 (15687) in the data section, which is set
# to 00 in the ALL file, but was set to 01 in the individual file.. Could it be "active", as
# I probably had that set as the active set when I did the export...
#
# There was also this data after the SET data, not sure what it is. The pattern repeats a lot,
# but at the end there are some varitions:
# 00000000  00 08 00 08 00 08 00 00  00 00 00 00 00 00 00 00  |................|
# 00000010  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
# 00000020  00 00 00 81 02 04 08 10  20 40 81 02 04 08 10 20  |........ @..... |
# 00000030  40 81 02 04 08 10 20 40  81 02 04 08 10 20 40 81  |@..... @..... @.|
# 00000040  02 04 08 10 20 40 81 02  04 08 10 20 40 81 02 04  |.... @..... @...|
# 00000050  08 10 20 40 81 02 04 08  10 20 40 81 02 04 08 10  |.. @..... @.....|
# 00000060  20 40 81 02 04 08 10 20  40 81 02 04 08 10 20 40  | @..... @..... @|
# 00000070  81 02 04 08 10 20 40 81  02 04 08 10 20 40 81 02  |..... @..... @..|
# 00000080  04 08 10 20 40 81 02 04  08 10 20 40 81 02 04 08  |... @..... @....|
# 00000090  10 20 40 81 02 04 08 10  20 40 81 02 04 08 10 20  |. @..... @..... |
# 000000a0  40 81 02 04 08 10 20 40  81 02 04 08 10 20 40 81  |@..... @..... @.|
# 000000b0  02 04 08 10 20 40 81 02  04 08 10 20 40 81 02 04  |.... @..... @...|
# 000000c0  08 10 20 40 81 02 04 08  10 20 40 81 02 04 08 10  |.. @..... @.....|
# 000000d0  20 40 81 02 04 08 10 20  40 81 02 04 08 10 20 40  | @..... @..... @|
# 000000e0  81 02 04 08 10 20 40 81  02 04 08 10 20 40 81 02  |..... @..... @..|
# 000000f0  04 08 10 20 40 81 02 04  08 10 20 40 81 02 04 08  |... @..... @....|
# 00000100  10 20 c9 02 04 08 10 20  40 81 02 04 08 00 80 00  |. ..... @.......|
# 00000110  06 40 1f e0 1f e0 1f e0  1f e0 1f e0 1f e0 1f e0  |.@..............|
# 00000120  1f e0 1f e0 1f e0 1f e0  1f e0 00 00 0e 40 00 00  |.............@..|
# 00000130  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  |................|
# *
# 00000740  00 00 00 00 00 00 00 00  00 00 00 00 00 00        |..............|
# 0000074e
# 
