#!/bin/bash # # mkfat.sh # # Copyright (C) 2017 Aleksandar Andrejevic # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as # published by the Free Software Foundation, either version 3 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . # OUTPUT_FILE=floppy.img ROOT_DIR_ENTRIES=224 NUMBER_OF_FATS=2 TOTAL_SECTORS=2880 BYTES_PER_SECTOR=512 SECTORS_PER_FAT=$((((TOTAL_SECTORS * 3 / 2) + BYTES_PER_SECTOR - 1) / BYTES_PER_SECTOR)) FIRST_ROOT_SECTOR=$((1 + NUMBER_OF_FATS * SECTORS_PER_FAT)) ROOT_DIR_OFFSET=$((FIRST_ROOT_SECTOR * BYTES_PER_SECTOR)) FIRST_DATA_SECTOR=$((FIRST_ROOT_SECTOR + (ROOT_DIR_ENTRIES * 32) / BYTES_PER_SECTOR)) function get_free_cluster() { local cluster=2 while [[ "$cluster" -lt $TOTAL_SECTORS ]] do local cluster_pair=`dd if=$OUTPUT_FILE skip=$((BYTES_PER_SECTOR + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p` if [[ "${cluster_pair:3:1}${cluster_pair:0:2}" == "000" ]] && [[ "$cluster" -ne 2 ]] then echo "Found a free cluster: $cluster" > /dev/stderr echo $cluster return elif [[ "${cluster_pair:4:2}${cluster_pair:2:1}" == "000" ]] then echo "Found a free cluster: $((cluster + 1))" > /dev/stderr echo $((cluster + 1)) return fi cluster=$((cluster + 2)) done echo 'Ran out of the free clusters.' > /dev/stderr rm $OUTPUT_FILE exit 1 } function set_next_cluster() { echo "Setting the next cluster of $1 to $2" > /dev/stderr local cluster=$1 local value=$(printf "%03X" $2) local cluster_pair=`dd if=$OUTPUT_FILE skip=$((BYTES_PER_SECTOR + 3 * (cluster / 2))) bs=1 count=3 2>/dev/null | xxd -p` if [[ "$((cluster % 2))" -eq 0 ]] then cluster_pair="${value:1:2}${cluster_pair:2:1}${value:0:1}${cluster_pair:4:2}" else cluster_pair="${cluster_pair:0:2}${value:2:1}${cluster_pair:3:1}${value:0:2}" fi for fat in `seq 0 $((NUMBER_OF_FATS - 1))` do echo $cluster_pair | xxd -r -p | dd conv=notrunc \ iflag=fullblock \ oflag=seek_bytes \ of=$OUTPUT_FILE \ seek=$((BYTES_PER_SECTOR * (1 + fat * SECTORS_PER_FAT) + 3 * (cluster / 2))) \ bs=3 \ count=1 \ 2>/dev/null done } dd if=/dev/zero of=$OUTPUT_FILE bs=$BYTES_PER_SECTOR count=$TOTAL_SECTORS 2>/dev/null /sbin/mkfs.vfat -r $ROOT_DIR_ENTRIES -f $NUMBER_OF_FATS -s 1 -S $BYTES_PER_SECTOR $OUTPUT_FILE >/dev/null 2>&1 echo "Created empty FAT12 disk image $OUTPUT_FILE" > /dev/stderr for file in *.prg do [[ -f $file ]] || break echo "Copying $file to the disk image..." > /dev/stderr size=`stat --printf '%s' $file` clusters=$(((size + BYTES_PER_SECTOR - 1) / BYTES_PER_SECTOR)) dosname=$(printf '%-8s%-3s' `echo ${file%%.*} | tr 'a-z' 'A-Z'` `echo ${file##*.} | tr 'a-z' 'A-Z'`) if [[ ${#dosname} -ne 11 ]] then echo "Cannot easily create a DOS name for $file" > /dev/stderr continue fi for dirent in `seq 0 $((ROOT_DIR_ENTRIES - 1))` do if [[ `dd if=$OUTPUT_FILE skip=$((dirent * 32 + ROOT_DIR_OFFSET)) bs=1 count=1 2>/dev/null | xxd -p` == "00" ]] then break fi done if [[ $dirent -ge $ROOT_DIR_ENTRIES ]] then echo 'Too many files, sorry.' > /dev/stderr exit 1 fi echo "$file will be placed in directory entry number $dirent" > /dev/stderr if [[ $clusters -ne 0 ]] then first_cluster=`get_free_cluster` if [[ $? -ne 0 ]] then echo 'No more free clusters...' exit 1 fi else first_cluster=0 fi ( echo -en "${dosname}\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" printf '%02X%02X' $((first_cluster & 0xFF)) $((first_cluster >> 8)) | xxd -r -p printf '%02X%02X%02X%02X' \ $((size & 0xFF)) \ $(((size >> 8) & 0xFF)) \ $(((size >> 16) & 0xFF)) \ $(((size >> 24) & 0xFF)) | xxd -r -p ) | dd conv=notrunc \ iflag=fullblock \ of=$OUTPUT_FILE \ seek=$(((ROOT_DIR_OFFSET / 32) + dirent)) \ bs=32 \ count=1 \ 2>/dev/null current_cluster=$first_cluster for ((i = 0; i < $clusters; i++)) do echo "Writing cluster #$i of $file to cluster $current_cluster" > /dev/stderr dd conv=notrunc \ if=$file \ of=$OUTPUT_FILE \ skip=$i \ seek=$((FIRST_DATA_SECTOR + current_cluster - 2)) \ bs=$BYTES_PER_SECTOR \ count=1 \ 2>/dev/null set_next_cluster $current_cluster 4095 if [[ "$i" -ne "$((clusters - 1))" ]] then next_cluster=`get_free_cluster` set_next_cluster $current_cluster $next_cluster current_cluster=$next_cluster fi done done echo "All done!" > /dev/stderr