c1891f4bb75fe5a27532ffae3b056ced347ecbe9
[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
23 function get_free_cluster()
24 {
25     local cluster=2
26
27     while [[ "$cluster" -lt 3072 ]]
28     do
29         local cluster_pair=`dd if=$OUTPUT_FILE skip=$((0x200 + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p`
30
31         if [[ "${cluster_pair:3:1}${cluster_pair:0:2}" == "000" ]] && [[ "$cluster" -ne 2 ]]
32         then
33             echo "Found a free cluster: $cluster" > /dev/stderr
34             echo $cluster
35             return
36         elif [[ "${cluster_pair:4:2}${cluster_pair:2:1}" == "000" ]]
37         then
38             echo "Found a free cluster: $((cluster + 1))" > /dev/stderr
39             echo $((cluster + 1))
40             return
41         fi
42
43         cluster=$((cluster + 2))
44     done
45
46     echo 'Ran out of the free clusters.' > /dev/stderr
47     rm $OUTPUT_FILE
48     exit 1
49 }
50
51 function set_next_cluster()
52 {
53     echo "Setting the next cluster of $1 to $2" > /dev/stderr
54
55     local cluster=$1
56     local value=$(printf "%03X" $2)
57     local cluster_pair=`dd if=$OUTPUT_FILE skip=$((0x200 + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p`
58
59     if [[ "$((cluster % 2))" -eq 0 ]]
60     then
61         cluster_pair="${value:1:2}${cluster_pair:2:1}${value:0:1}${cluster_pair:4:2}"
62     else
63         cluster_pair="${cluster_pair:0:2}${value:2:1}${cluster_pair:3:1}${value:0:2}"
64     fi
65
66     echo $cluster_pair | xxd -r -p | dd conv=notrunc \
67                                         iflag=fullblock \
68                                         oflag=seek_bytes \
69                                         of=$OUTPUT_FILE \
70                                         seek=$((0x200 + 3 * (cluster / 2))) \
71                                         bs=3 \
72                                         count=1 \
73                                         2>/dev/null
74     echo $cluster_pair | xxd -r -p | dd conv=notrunc \
75                                         iflag=fullblock \
76                                         oflag=seek_bytes \
77                                         of=$OUTPUT_FILE \
78                                         seek=$((0x1400 + 3 * (cluster / 2))) \
79                                         bs=3 \
80                                         count=1 \
81                                         2>/dev/null
82 }
83
84 dd if=/dev/zero of=$OUTPUT_FILE bs=512 count=2880 2>/dev/null
85 /sbin/mkfs.vfat $OUTPUT_FILE 2>&1 >/dev/null
86
87 echo "Created empty FAT12 disk image $OUTPUT_FILE" > /dev/stderr
88
89 for file in *.prg
90 do
91     [[ -f $file ]] || break
92     echo "Copying $file to the disk image..." > /dev/stderr
93
94     size=`stat --printf '%s' $file`
95     clusters=$(((size + 511) / 512))
96     dosname=$(printf '%-8s%-3s' `echo ${file%%.*} | tr 'a-z' 'A-Z'` `echo ${file##*.} | tr 'a-z' 'A-Z'`)
97
98     for ((dirent = 0; dirent < 16; dirent++))
99     do
100         if [[ `dd if=$OUTPUT_FILE skip=$((dirent * 32 + 0x2600)) bs=1 count=1 2>/dev/null | xxd -p` == "00" ]]
101         then
102             break
103         fi
104     done
105
106     if [[ $dirent -ge 224 ]]
107     then
108         echo 'Too many files, sorry.' > /dev/stderr
109         exit 1
110     fi
111
112     echo "$file will be placed in directory entry number $dirent" > /dev/stderr
113
114     if [[ $clusters -ne 0 ]]
115     then
116         first_cluster=`get_free_cluster`
117         if [[ $? -ne 0 ]]
118         then
119             echo 'No more free clusters...'
120             exit 1
121         fi
122     else
123         first_cluster=0
124     fi
125
126     (
127         echo -en "${dosname}\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
128         printf '%02X%02X' $((first_cluster % 256)) $((first_cluster / 256)) | xxd -r -p | tr -d '\n'
129         printf "%02X%02X%02X%02X" \
130                $((size % 256)) \
131                $(((size / 256) % 256)) \
132                $(((size / 65536) % 256)) \
133                $(((size / 16777216) % 256)) | xxd -r -p | tr -d '\n'
134     ) | dd conv=notrunc iflag=fullblock of=$OUTPUT_FILE seek=$((304 + dirent)) bs=32 count=1 2>/dev/null
135
136     current_cluster=$first_cluster
137
138     for ((i = 0; i < $clusters; i++))
139     do
140         echo "Writing cluster #$i of $file to cluster $current_cluster" > /dev/stderr
141         dd conv=notrunc if=$file of=$OUTPUT_FILE skip=$i seek=$((31 + current_cluster)) bs=512 count=1 2>/dev/null
142
143         set_next_cluster $current_cluster 4095
144
145         if [[ "$i" -ne "$((clusters - 1))" ]]
146         then
147             next_cluster=`get_free_cluster`
148             set_next_cluster $current_cluster $next_cluster
149             current_cluster=$next_cluster
150         fi
151     done
152 done
153
154 echo "All done!" > /dev/stderr