Clean up the disk image creation script.
[monolithium.git] / tests / mkfat.sh
1 #!/bin/bash
2 #
3 # mkfat.sh
4 #
5 # Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU Affero General Public License as
9 # published by the Free Software Foundation, either version 3 of the
10 # License, or (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 # GNU Affero General Public License for more details.
16 #
17 # You should have received a copy of the GNU Affero General Public License
18 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20
21 OUTPUT_FILE=floppy.img
22 ROOT_DIR_ENTRIES=224
23 NUMBER_OF_FATS=2
24 TOTAL_SECTORS=2880
25 BYTES_PER_SECTOR=512
26
27 SECTORS_PER_FAT=$((((TOTAL_SECTORS * 3 / 2) + BYTES_PER_SECTOR - 1) / BYTES_PER_SECTOR))
28 FIRST_ROOT_SECTOR=$((1 + NUMBER_OF_FATS * SECTORS_PER_FAT))
29 ROOT_DIR_OFFSET=$((FIRST_ROOT_SECTOR * BYTES_PER_SECTOR))
30 FIRST_DATA_SECTOR=$((FIRST_ROOT_SECTOR + (ROOT_DIR_ENTRIES * 32) / BYTES_PER_SECTOR))
31
32 function get_free_cluster()
33 {
34     local cluster=2
35
36     while [[ "$cluster" -lt $TOTAL_SECTORS ]]
37     do
38         local cluster_pair=`dd if=$OUTPUT_FILE skip=$((BYTES_PER_SECTOR + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p`
39
40         if [[ "${cluster_pair:3:1}${cluster_pair:0:2}" == "000" ]] && [[ "$cluster" -ne 2 ]]
41         then
42             echo "Found a free cluster: $cluster" > /dev/stderr
43             echo $cluster
44             return
45         elif [[ "${cluster_pair:4:2}${cluster_pair:2:1}" == "000" ]]
46         then
47             echo "Found a free cluster: $((cluster + 1))" > /dev/stderr
48             echo $((cluster + 1))
49             return
50         fi
51
52         cluster=$((cluster + 2))
53     done
54
55     echo 'Ran out of the free clusters.' > /dev/stderr
56     rm $OUTPUT_FILE
57     exit 1
58 }
59
60 function set_next_cluster()
61 {
62     echo "Setting the next cluster of $1 to $2" > /dev/stderr
63
64     local cluster=$1
65     local value=$(printf "%03X" $2)
66     local cluster_pair=`dd if=$OUTPUT_FILE skip=$((BYTES_PER_SECTOR + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p`
67
68     if [[ "$((cluster % 2))" -eq 0 ]]
69     then
70         cluster_pair="${value:1:2}${cluster_pair:2:1}${value:0:1}${cluster_pair:4:2}"
71     else
72         cluster_pair="${cluster_pair:0:2}${value:2:1}${cluster_pair:3:1}${value:0:2}"
73     fi
74
75     for fat in `seq 0 $((NUMBER_OF_FATS - 1))`
76     do
77         echo $cluster_pair | xxd -r -p | dd conv=notrunc \
78                                             iflag=fullblock \
79                                             oflag=seek_bytes \
80                                             of=$OUTPUT_FILE \
81                                             seek=$((BYTES_PER_SECTOR + fat * SECTORS_PER_FAT + 3 * (cluster / 2))) \
82                                             bs=3 \
83                                             count=1 \
84                                             2>/dev/null
85     done
86 }
87
88 dd if=/dev/zero of=$OUTPUT_FILE bs=$BYTES_PER_SECTOR count=$TOTAL_SECTORS 2>/dev/null
89 /sbin/mkfs.vfat -r $ROOT_DIR_ENTRIES -f $NUMBER_OF_FATS -s 1 -S $BYTES_PER_SECTOR $OUTPUT_FILE >/dev/null 2>&1
90
91 echo "Created empty FAT12 disk image $OUTPUT_FILE" > /dev/stderr
92
93 for file in *.prg
94 do
95     [[ -f $file ]] || break
96     echo "Copying $file to the disk image..." > /dev/stderr
97
98     size=`stat --printf '%s' $file`
99     clusters=$(((size + BYTES_PER_SECTOR - 1) / BYTES_PER_SECTOR))
100     dosname=$(printf '%-8s%-3s' `echo ${file%%.*} | tr 'a-z' 'A-Z'` `echo ${file##*.} | tr 'a-z' 'A-Z'`)
101
102     if [[ ${#dosname} -ne 11 ]]
103     then
104         echo "Cannot easily create a DOS name for $file" > /dev/stderr
105         continue
106     fi
107
108     for dirent in `seq 0 $((ROOT_DIR_ENTRIES - 1))`
109     do
110         if [[ `dd if=$OUTPUT_FILE skip=$((dirent * 32 + ROOT_DIR_OFFSET)) bs=1 count=1 2>/dev/null | xxd -p` == "00" ]]
111         then
112             break
113         fi
114     done
115
116     if [[ $dirent -ge $ROOT_DIR_ENTRIES ]]
117     then
118         echo 'Too many files, sorry.' > /dev/stderr
119         exit 1
120     fi
121
122     echo "$file will be placed in directory entry number $dirent" > /dev/stderr
123
124     if [[ $clusters -ne 0 ]]
125     then
126         first_cluster=`get_free_cluster`
127         if [[ $? -ne 0 ]]
128         then
129             echo 'No more free clusters...'
130             exit 1
131         fi
132     else
133         first_cluster=0
134     fi
135
136     (
137         echo -en "${dosname}\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
138         printf '%02X%02X' $((first_cluster & 0xFF)) $((first_cluster >> 8)) | xxd -r -p | tr -d '\n'
139         printf "%02X%02X%02X%02X" \
140                $((size & 0xFF)) \
141                $(((size >> 8) & 0xFF)) \
142                $(((size >> 16) & 0xFF)) \
143                $(((size >> 24) & 0xFF)) | xxd -r -p | tr -d '\n'
144     ) | dd conv=notrunc \
145            iflag=fullblock \
146            of=$OUTPUT_FILE \
147            seek=$(((ROOT_DIR_OFFSET / 32) + dirent)) \
148            bs=32 \
149            count=1 \
150            2>/dev/null
151
152     current_cluster=$first_cluster
153
154     for ((i = 0; i < $clusters; i++))
155     do
156         echo "Writing cluster #$i of $file to cluster $current_cluster" > /dev/stderr
157         dd conv=notrunc \
158            if=$file \
159            of=$OUTPUT_FILE \
160            skip=$i \
161            seek=$((FIRST_DATA_SECTOR + current_cluster)) \
162            bs=$BYTES_PER_SECTOR count=1 \
163            2>/dev/null
164
165         set_next_cluster $current_cluster 4095
166
167         if [[ "$i" -ne "$((clusters - 1))" ]]
168         then
169             next_cluster=`get_free_cluster`
170             set_next_cluster $current_cluster $next_cluster
171             current_cluster=$next_cluster
172         fi
173     done
174 done
175
176 echo "All done!" > /dev/stderr