DOS33-perl/dos33.pl

2373 lines
65 KiB
Perl

#!/usr/bin/perl -w
#
# dos33 version 0.1
# by Vince Weaver <vince@deater.net>
# Perl port 20190225 by Leeland Heins <softwarejanitor@yahoo.com>
#
use strict;
use File::Basename;
# For now hard-coded
# Could be made dynamic if we want to be useful
# On dos3.2 disks, or larger filesystems
my $TRACKS_PER_DISK = 0x23; # 35
my $SECTORS_PER_TRACK = 0x10; # 16
my $BYTES_PER_SECTOR = 0x100; # 256
my $VTOC_TRACK = 0x11; # 17
my $VTOC_SECTOR = 0x00; # 0
# VTOC Values
my $VTOC_CATALOG_T = 0x01; # 1
my $VTOC_CATALOG_S = 0x02; # 2
my $VTOC_DOS_RELEASE = 0x03; # 3
my $VTOC_DISK_VOLUME = 0x06; # 6
my $VTOC_MAX_TS_PAIRS = 0x27; # 39
my $VTOC_LAST_ALLOC_T = 0x30; # 48
my $VTOC_ALLOC_DIRECT = 0x31; # 49
my $VTOC_NUM_TRACKS = 0x34; # 52
my $VTOC_S_PER_TRACK = 0x35; # 53
my $VTOC_BYTES_PER_SL = 0x36; # 54
my $VTOC_BYTES_PER_SH = 0x37; # 55
my $VTOC_FREE_BITMAPS = 0x38; # 56
# CATALOG_VALUES
my $CATALOG_NEXT_T = 0x01; # 1
my $CATALOG_NEXT_S = 0x02; # 2
my $CATALOG_FILE_LIST = 0x0b; # 11
my $CATALOG_ENTRY_SIZE = 0x23; # 35
# CATALOG ENTRY
my $FILE_TS_LIST_T = 0x00; # 0
my $FILE_TS_LIST_S = 0x01; # 1
my $FILE_TYPE = 0x02; # 2
my $FILE_NAME = 0x03; # 3
my $FILE_SIZE_L = 0x21; # 33
my $FILE_SIZE_H = 0x22; # 34
my $FILE_NAME_SIZE = 0x1e; # 30
# TSL
my $TSL_NEXT_TRACK = 0x01; # 1
my $TSL_NEXT_SECTOR = 0x02; # 2
my $TSL_OFFSET_L = 0x05; # 5
my $TSL_OFFSET_H = 0x06; # 6
my $TSL_LIST = 0x0c; # 12
my $TSL_ENTRY_SIZE = 0x02; # 2
my $TSL_MAX_NUMBER = 122; # 0x7a
my $SEEK_SET = 0;
my $VERSION = "0.1";
# Helper Subs
sub TS_TO_INT {
my ($x, $y) = @_;
return (($x << 8) + $y);
}
sub DISK_OFFSET {
my ($track, $sector) = @_;
my $off = ((($track * $SECTORS_PER_TRACK) + $sector) * $BYTES_PER_SECTOR);
return $off;
}
my $sector_buffer;
my %ones_lookup = (
0x00 => 0, # 0x0 = 0000 0
0x01 => 1, # 0x1 = 0001 1
0x02 => 1, # 0x2 = 0010 1
0x03 => 2, # 0x3 = 0011 2
0x04 => 1, # 0x4 = 0100 1
0x05 => 2, # 0x5 = 0101 2
0x06 => 2, # 0x6 = 0110 2
0x07 => 3, # 0x7 = 0111 3
0x08 => 1, # 0x8 = 1000 1
0x09 => 2, # 0x9 = 1001 2
0x0a => 2, # 0xA = 1010 2
0x0b => 3, # 0xB = 1011 3
0x0c => 2, # 0xC = 1100 2
0x0d => 3, # 0xd = 1101 3
0x0e => 3, # 0xe = 1110 3
0x0f => 4, # 0xf = 1111 4
);
sub get_high_byte {
my ($value) = @_;
return ($value >> 8) & 0xff;
}
sub get_low_byte {
my ($value) = @_;
return ($value & 0xff);
}
my $debug = 0;
my $FILE_NORMAL = 0;
my $FILE_DELETED = 1;
sub dos33_file_type {
my ($value) = @_;
my $result = '?';
my $v2 = $value & 0x7f;
if ($v2 == 0x00) {
$result = 'T';
} elsif ($v2 == 0x01) {
$result = 'I';
} elsif ($v2 == 0x02) {
$result = 'A';
} elsif ($v2 == 0x04) {
$result = 'B';
} elsif ($v2 == 0x08) {
$result = 'S';
} elsif ($v2 == 0x10) {
$result = 'R';
} elsif ($v2 == 0x20) {
$result = 'N';
} elsif ($v2 == 0x40) {
$result = 'L';
} else {
$result = '?';
}
return $result;
}
sub dos33_char_to_type {
my ($type, $lock) = @_;
my $result = 0x00;
my $temp_type;
# Covert to upper case
$temp_type = uc($type);
if ($temp_type eq 'T') {
$result = 0x00;
} elsif ($temp_type eq 'I') {
$result = 0x01;
} elsif ($temp_type eq 'A') {
$result = 0x02;
} elsif ($temp_type eq 'B') {
$result = 0x04;
} elsif ($temp_type eq 'S') {
$result = 0x08;
} elsif ($temp_type eq 'R') {
$result = 0x10;
} elsif ($temp_type eq 'N') {
$result = 0x20;
} elsif ($temp_type eq 'L') {
$result = 0x40;
} else {
$result = 0x00;
}
if ($lock) {
$result |= 0x80;
}
return $result;
}
# dos33 filenames have high bit set on ascii chars
# and are padded with spaces
sub dos33_filename_to_ascii {
my ($dest, $src, $len) = @_;
my @srcbytes = unpack "C*", $src;
my @destbytes = ();
my $l = 0;
foreach my $byte (@srcbytes) {
push @destbytes, $byte & 0x7f;
$l++;
last if $l > $len;
}
$dest = pack "C*", @destbytes;
$_[0] = $dest;
return $dest;
}
# Read VTOC into a buffer
sub dos33_read_vtoc {
my ($fd) = @_;
# Seek to VTOC
seek($fd, DISK_OFFSET($VTOC_TRACK, $VTOC_SECTOR), $SEEK_SET);
# read in VTOC
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
if (! defined $result || $result < 0) {
print STDERR "Error on I/O\n";
}
return 0;
}
# Calculate available freespace
sub dos33_free_space {
my ($fd) = @_;
my @bitmap = ();
my $sectors_free = 0;
# Read Vtoc
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @bytes = unpack "C*", $sector_buffer;
for (my $i = 0; $i < $TRACKS_PER_DISK; $i++) {
$bitmap[0] = $bytes[$VTOC_FREE_BITMAPS + ($i * 4)];
$bitmap[1] = $bytes[$VTOC_FREE_BITMAPS + ($i * 4) + 1];
$sectors_free += $ones_lookup{$bitmap[0] & 0x0f};
$sectors_free += $ones_lookup{($bitmap[0] >> 4) & 0x0f};
$sectors_free += $ones_lookup{$bitmap[1] & 0x0f};
$sectors_free += $ones_lookup{($bitmap[1] >> 4) & 0x0f};
}
return $sectors_free * $BYTES_PER_SECTOR;
}
# Get a T/S value from a Catalog Sector
sub dos33_get_catalog_ts {
my ($fd) = @_;
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @bytes = unpack "C*", $sector_buffer;
return TS_TO_INT($bytes[$VTOC_CATALOG_T], $bytes[$VTOC_CATALOG_S]);
}
# returns the next valid catalog entry
# after the one passed in
sub dos33_find_next_file {
my ($fd, $catalog_tsf) = @_;
my $catalog_file = $catalog_tsf >> 16;
my $catalog_track = ($catalog_tsf >> 8) & 0xff;
my $catalog_sector = ($catalog_tsf & 0xff);
catalog_loop:
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
my @bytes = unpack "C*", $sector_buffer;
my $i = $catalog_file;
while ($i < 7) {
my $file_track = $bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)];
# 0xff means file deleted
# 0x0 means empty
if (($file_track != 0xff) && ($file_track != 0x00)) {
return (($i << 16) + ($catalog_track << 8) + $catalog_sector);
}
$i++;
}
$catalog_track = $bytes[$CATALOG_NEXT_T];
$catalog_sector = $bytes[$CATALOG_NEXT_S];
if ($catalog_sector != 0) {
$catalog_file = 0;
goto catalog_loop;
}
if ($result < 0) {
print STDERR "Error on I/O\n";
}
return -1;
}
sub dos33_print_file_info {
my ($fd, $catalog_tsf) = @_;
my $temp_string;
my $catalog_file = $catalog_tsf >> 16;
my $catalog_track = ($catalog_tsf >> 8) & 0xff;
my $catalog_sector = ($catalog_tsf & 0xff);
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
my @bytes = unpack "C*", $sector_buffer;
# Print a * if the file locked
if ($bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE] > 0x7f) {
print "*";
} else {
print " ";
}
# Print the file type
printf("%s", dos33_file_type($bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE]));
print " ";
# Print the file size, stored in LO/HI
printf("%.3i ", $bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE + $FILE_SIZE_L)] +
($bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE + $FILE_SIZE_H)] << 8));
# Print filename.
my $filenamestr = substr($sector_buffer, ($CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE + $FILE_NAME)), 30);
dos33_filename_to_ascii($temp_string, $filenamestr, 30);
print "$temp_string\n";
if ($result < 0) {
print STDERR "Error on I/O\n";
}
return 0;
}
# Checks if "filename" exists
# returns entry/track/sector
sub dos33_check_file_exists {
my ($fd, $filename, $file_deleted) = @_;
$filename =~ s/\s+$//g;
# read the VTOC into buffer
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @vtoc_bytes = unpack "C*", $sector_buffer;
# get the catalog track and sector from the VTOC
my $catalog_track = $vtoc_bytes[$VTOC_CATALOG_T];
my $catalog_sector = $vtoc_bytes[$VTOC_CATALOG_S];
repeat_catalog:
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
my @bytes = unpack "C*", $sector_buffer;
# scan all file entries in catalog sector
for (my $i = 0; $i < 7; $i++) {
my $file_track = $bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)];
# 0xff means file deleted
# 0x0 means empty
if ($file_track != 0x00) {
if ($file_track == 0xff) {
my $filenamestr = substr($sector_buffer, ($CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE + $FILE_NAME)), 29);
my $file_name = '';
dos33_filename_to_ascii($file_name, $filenamestr, 29);
$file_name =~ s/\s+$//g;
if ($file_deleted) {
# return if we found the file
if ($filename eq $file_name) {
return (($i << 16) + ($catalog_track << 8) + $catalog_sector);
}
}
} else {
my $filenamestr = substr($sector_buffer, ($CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE +$FILE_NAME)), 30);
my $file_name = '';
dos33_filename_to_ascii($file_name, $filenamestr, 30);
$file_name =~ s/\s+$//g;
# return if we found the file
if ($filename eq $file_name) {
return (($i << 16) + ($catalog_track << 8) + $catalog_sector);
}
}
}
}
# point to next catalog track/sector
$catalog_track = $bytes[$CATALOG_NEXT_T];
$catalog_sector = $bytes[$CATALOG_NEXT_S];
if ($catalog_sector != 0) {
goto repeat_catalog;
}
if ($result < 0) {
print STDERR "Error on I/O\n";
}
return -1;
}
# could be replaced by "find leading 1" instruction
# if available
sub find_first_one {
my ($byte) = @_;
my $i = 0;
if ($byte == 0) {
return -1;
}
while (($byte & (0x01 << $i)) == 0) {
$i++;
}
return $i;
}
sub dos33_free_sector {
my ($fd, $track, $sector) = @_;
my $vtoc;
# Seek to VTOC
seek($fd, DISK_OFFSET($VTOC_TRACK, $VTOC_SECTOR), $SEEK_SET);
# read in VTOC
my $result = read($fd, $vtoc, $BYTES_PER_SECTOR);
# Unpack VTOC data.
my @bytes = unpack "C*", $vtoc;
# each bitmap is 32 bits. With 16-sector tracks only first 16 used
# 1 indicates free, 0 indicates used
if ($sector < 8) {
$bytes[$VTOC_FREE_BITMAPS + ($track * 4) + 1] |= (0x01 << $sector);
} else {
$bytes[$VTOC_FREE_BITMAPS + ($track * 4)] |= (0x01 << ($sector - 8));
}
# Re-pack VTOC data.
$vtoc = pack "C*", @bytes;
# write modified VTOC back out
seek($fd, DISK_OFFSET($VTOC_TRACK, $VTOC_SECTOR), $SEEK_SET);
print $fd $vtoc;
return 0;
}
sub dos33_allocate_sector {
my ($fd) = @_;
my $found_track = 0;
my $found_sector = 0;
my @bitmap;
my $start_track;
my $track_dir;
my $byte;
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @bytes = unpack "C*", $sector_buffer;
# Originally used to keep things near center of disk for speed
# We can use to avoid fragmentation possibly
$start_track = $bytes[$VTOC_LAST_ALLOC_T] % $TRACKS_PER_DISK;
$track_dir = $bytes[$VTOC_ALLOC_DIRECT];
if ($track_dir == 255) {
$track_dir = -1;
}
if (($track_dir != 1) && ($track_dir != -1)) {
print STDERR sprintf("ERROR! Invalid track dir %i\n", $track_dir);
}
if ((($start_track > $VTOC_TRACK) && ($track_dir != 1)) || (($start_track < $VTOC_TRACK) && ($track_dir != -1))) {
print STDERR sprintf("Warning! Non-optimal values for track dir t=%i d=%i!\n", $start_track, $track_dir);
}
my $i = $start_track;
do {
for ($byte = 1; $byte > -1; $byte--) {
$bitmap[$byte] = $bytes[$VTOC_FREE_BITMAPS + ($i * 4) + $byte];
if ($bitmap[$byte] != 0x00) {
$found_sector = find_first_one($bitmap[$byte]);
$found_track = $i;
# clear bit indicating in use
$bytes[$VTOC_FREE_BITMAPS + ($i * 4) + $byte] &= ~(0x01 << $found_sector);
$found_sector += (8*(1 - $byte));
goto found_one;
}
}
# Move to next track, handling overflows
$i += $track_dir;
if ($i < 0) {
$i = $VTOC_TRACK;
$track_dir = 1;
}
if ($i >= $TRACKS_PER_DISK) {
$i = $VTOC_TRACK;
$track_dir = -1;
}
} while ($i != $start_track);
print STDERR "No room left!\n";
return -1;
found_one:
# store new track/direction info
$bytes[$VTOC_LAST_ALLOC_T] = $found_track;
if ($found_track > $VTOC_TRACK) {
$bytes[$VTOC_ALLOC_DIRECT] = 1;
} else {
$bytes[$VTOC_ALLOC_DIRECT] = -1;
}
# Re-pack VTOC data.
$sector_buffer = pack "C*", @bytes;
# Seek to VTOC
seek($fd, DISK_OFFSET($VTOC_TRACK, $VTOC_SECTOR), $SEEK_SET);
# Write out VTOC
print $fd $sector_buffer;
return (($found_track << 8) + $found_sector);
}
my $track = 0;
my $sector = 0;
# FIXME: currently assume sector is going to be 0
sub dos33_force_allocate_sector {
my ($fd) = @_;
my $found_track = 0;
my $found_sector = 0;
#unsigned char bitmap[4];
my $i;
my $start_track; #, track_dir, byte;
my $result;
my $so;
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @bytes = unpack "C*", $sector_buffer;
# Originally used to keep things near center of disk for speed
# We can use to avoid fragmentation possibly
# $start_track = $bytes[$VTOC_LAST_ALLOC_T] % $TRACKS_PER_DISK;
# $track_dir = $bytes[$VTOC_ALLOC_DIRECT];
$start_track = $track;
$i = $start_track;
$so = !(!$sector / 8);
$found_sector = $sector % 8;
# FIXME: check if free
#$bitmap[$so] = $bytes[$VTOC_FREE_BITMAPS + ($i * 4) + $so];
# clear bit indicating in use
$bytes[$VTOC_FREE_BITMAPS + ($i * 4) + $so] &= ~(0x01 << $found_sector);
$found_sector += (8 * (1 - $so));
$found_track = $i;
printf("VMW: want %d/%d, found %d/%d\n", $track, $sector, $found_track, $found_sector) if $debug;
$sector++;
if ($sector > 15) {
$sector = 0;
$track++;
}
# print STDERR "No room for raw-write!\n";
# return -1;
#found_one:
# store new track/direction info
#$bytes[$VTOC_LAST_ALLOC_T] = $found_track;
#
# $bytes[$VTOC_ALLOC_DIRECT] = 1;
# else $bytes[$VTOC_ALLOC_DIRECT] = -1;
# Re-pack VTOC data.
$sector_buffer = pack "C*", @bytes;
# Seek to VTOC
seek($fd, DISK_OFFSET($VTOC_TRACK, $VTOC_SECTOR), $SEEK_SET);
# Write out VTOC
print $fd $sector_buffer;
printf("raw: T=%d S=%d\n", $found_track, $found_sector) if $debug;
return (($found_track << 8) + $found_sector);
}
my $ERROR_INVALID_FILENAME = 1;
my $ERROR_FILE_NOT_FOUND = 2;
my $ERROR_NO_SPACE = 3;
my $ERROR_IMAGE_NOT_FOUND = 4;
my $ERROR_CATALOG_FULL = 5;
my $ADD_RAW = 0;
my $ADD_BINARY = 1;
# creates file apple_filename on the image from local file filename
# returns ??
sub dos33_add_file {
my ($fd, $dos_type, $file_type, $address, $length, $filename, $apple_filename) = @_;
my $free_space;
my $file_size;
my $needed_sectors;
my $size_in_sectors = 0;
my $initial_ts_list = 0;
my $ts_list = 0;
my $data_ts;
my $bytes_read = 0;
my $old_ts_list;
my $catalog_track;
my $catalog_sector;
my $sectors_used = 0;
my $input_fd;
my $result;
my $first_write = 1;
if ($apple_filename !~ /^[A-Z]/) {
print STDERR "Error! First char of filename must be ASCII 64 or above!\n";
return $ERROR_INVALID_FILENAME;
}
# Check for comma in filename
if ($apple_filename =~ /,/) {
print STDERR "Error! Cannot have , in a filename!\n";
return $ERROR_INVALID_FILENAME;
}
# FIXME
# check type
# and sanity check a/b filesize is set properly
# Determine size of file to upload
my @file_info = stat $filename;
if (! @file_info) {
print STDERR "Error! $filename not found!\n", $filename;
return $ERROR_FILE_NOT_FOUND;
}
$file_size = $file_info[7];
print "Filesize: $file_size\n" if $debug;
if ($file_type == $ADD_BINARY) {
print "Adding 4 bytes for size/offset\n" if $debug;
if ($length == 0) {
$length = $file_size;
}
$file_size += 4;
}
# We need to round up to nearest sector size
# Add an extra sector for the T/S list
# Then add extra sector for a T/S list every 122*256 bytes (~31k)
$needed_sectors = ($file_size / $BYTES_PER_SECTOR) + # round sectors
(($file_size % $BYTES_PER_SECTOR) != 0) + # tail if needed
1 + # first T/S list
($file_size / (122 * $BYTES_PER_SECTOR)); # extra t/s lists
# Get free space on device
$free_space = dos33_free_space($fd);
# Check for free space
if ($needed_sectors * $BYTES_PER_SECTOR > $free_space) {
print STDERR sprintf("Error! Not enough free space on disk image (need %d have %d)\n", ($needed_sectors * $BYTES_PER_SECTOR), $free_space);
return $ERROR_NO_SPACE;
}
# plus one because we need a sector for the tail
$size_in_sectors = ($file_size / $BYTES_PER_SECTOR) + (($file_size % $BYTES_PER_SECTOR) != 0);
printf("Need to allocate %i data sectors\n", $size_in_sectors) if $debug;
printf("Need to allocate %i total sectors\n", $needed_sectors) if $debug;
my $ifh;
# Open the local file
if (!open($input_fd, "<$filename")) {
print STDERR sprintf("Error! could not open %s\n", $filename);
return $ERROR_IMAGE_NOT_FOUND;
}
my $i = 0;
while ($i < $size_in_sectors) {
# Create new T/S list if necessary
if ($i % $TSL_MAX_NUMBER == 0) {
$old_ts_list = $ts_list;
# allocate a sector for the new list
$ts_list = dos33_allocate_sector($fd);
$sectors_used++;
if ($ts_list < 0) {
return -1;
}
# Initialize sector data.
my @bytes = ();
# clear the t/s sector
for (my $x = 0; $x < $BYTES_PER_SECTOR; $x++) {
$bytes[$x] = 0;
}
# Re-pack tslist data.
$sector_buffer = pack "C*", @bytes;
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
print $fd $sector_buffer;
if ($i == 0) {
$initial_ts_list = $ts_list;
} else {
# we aren't the first t/s list so do special stuff
# load in the old t/s list
seek($fd, DISK_OFFSET(get_high_byte($old_ts_list), get_low_byte($old_ts_list)), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
my @bytes = unpack "C*", $sector_buffer;
# point from old ts list to new one we just made
$bytes[$TSL_NEXT_TRACK] = get_high_byte($ts_list);
$bytes[$TSL_NEXT_SECTOR] = get_low_byte($ts_list);
# set offset into file
$bytes[$TSL_OFFSET_H] = get_high_byte(($i - 122) * 256);
$bytes[$TSL_OFFSET_L] = get_low_byte(($i - 122) * 256);
# Re-pack tslist sector data.
$sector_buffer = pack "C*", @bytes;
# write out the old t/s list with updated info
seek($fd, DISK_OFFSET(get_high_byte($old_ts_list), get_low_byte($old_ts_list)), $SEEK_SET);
print $fd $sector_buffer;
}
}
# Allocate a sector
$data_ts = dos33_allocate_sector($fd);
$sectors_used++;
if ($data_ts < 0) {
return -1;
}
# clear sector
my @bytes = ();
for (my $x = 0; $x < $BYTES_PER_SECTOR; $x++) {
$bytes[$x] = 0;
}
# read from input
if (($first_write) && ($file_type == $ADD_BINARY)) {
$first_write = 0;
$bytes[0] = $address & 0xff;
$bytes[1] = ($address >> 8) & 0xff;
$bytes[2] = ($length) & 0xff;
$bytes[3] = (($length) >> 8) & 0xff;
my $buf;
$bytes_read = read($input_fd, $buf, ($BYTES_PER_SECTOR - 4));
$sector_buffer .= $buf;
my @bytes2 = unpack "C*", $buf;
foreach my $byte (@bytes2) {
push @bytes, $byte;
}
$bytes_read += 4;
} else {
$bytes_read = read($input_fd, $sector_buffer, $BYTES_PER_SECTOR);
@bytes = unpack "C*", $sector_buffer;
}
$first_write = 0;
if ($bytes_read < 0) {
print STDERR "Error reading bytes!\n";
}
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
# write to disk image
seek($fd, DISK_OFFSET(($data_ts >> 8) & 0xff, $data_ts & 0xff), $SEEK_SET);
print $fd $sector_buffer;
printf("Writing %i bytes to %i/%i\n", $bytes_read, ($data_ts >> 8) & 0xff, $data_ts & 0xff) if $debug;
# add to T/s table
# read in t/s list
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
my @ts_bytes = unpack "C*", $sector_buffer;
# point to new data sector
$ts_bytes[(($i % $TSL_MAX_NUMBER) * 2) + $TSL_LIST] = ($data_ts >> 8) & 0xff;
$ts_bytes[(($i % $TSL_MAX_NUMBER) * 2) + $TSL_LIST + 1] = ($data_ts & 0xff);
# Re-pack sector data.
$sector_buffer = pack "C*", @ts_bytes;
# write t/s list back out
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
print $fd $sector_buffer;
$i++;
}
# Add new file to Catalog
# read in vtoc
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @vtoc_bytes = unpack "C*", $sector_buffer;
$catalog_track = $vtoc_bytes[$VTOC_CATALOG_T];
$catalog_sector = $vtoc_bytes[$VTOC_CATALOG_S];
continue_parsing_catalog:
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
# Find empty directory entry
$i = 0;
while ($i < 7) {
# for undelete purposes might want to skip 0xff
# (deleted) files first and only use if no room
if (($bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] == 0xff) || ($bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] == 0x00)) {
goto got_a_dentry;
}
$i++;
}
if (($catalog_track == 0x11) && ($catalog_sector == 1)) {
# in theory can only have 105 files
# if full, we have no recourse!
# can we allocate new catalog sectors
# and point to them??
print STDERR "Error! No more room for files!\n";
return $ERROR_CATALOG_FULL;
}
$catalog_track = $bytes[$CATALOG_NEXT_T];
$catalog_sector = $bytes[$CATALOG_NEXT_S];
goto continue_parsing_catalog;
got_a_dentry:
# printf("Adding file at entry %i of catalog 0x%x:0x%x\n", $i, $catalog_track, $catalog_sector);
# Point entry to initial t/s list
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] = ($initial_ts_list >> 8) & 0xff;
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + 1] = ($initial_ts_list & 0xff);
# set file type
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_TYPE] = dos33_char_to_type($dos_type, 0);
# printf("Pointing T/S to %x/%x\n", ($initial_ts_list >> 8) & 0xff, $initial_ts_list & 0xff);
# copy over filename
for (my $x = 0; $x < length($apple_filename); $x++) {
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(substr($apple_filename, $x, 1)) ^ 0x80;
}
# pad out the filename with spaces
for (my $x = length($apple_filename); $x < $FILE_NAME_SIZE; $x++) {
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(' ') ^ 0x80;
}
# fill in filesize in sectors
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_SIZE_L] = $sectors_used & 0xff;
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_SIZE_H] = ($sectors_used >> 8) & 0xff;
# Re-pack catalog sector data.
$sector_buffer = pack "C*", @bytes;
# write out catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
# Create raw file on disk starting at track/sector
# returns ??
sub dos33_raw_file {
my ($fd, $dos_type, $track, $sector, $filename, $apple_filename) = @_;
my $free_space;
my $file_size;
my $needed_sectors;
my $size_in_sectors = 0;
my $initial_ts_list = 0;
my $ts_list = 0;
my $data_ts;
my $bytes_read = 0;
my $old_ts_list;
my $catalog_track;
my $catalog_sector;
my $sectors_used = 0;
my $input_fd;
my $result;
if ($apple_filename !~ /^[A-Z]/) {
print STDERR "Error! First char of filename must be ASCII 64 or above!\n";
return $ERROR_INVALID_FILENAME;
}
# Check for comma in filename
if ($apple_filename =~ /,/) {
print STDERR "Error! Cannot have , in a filename!\n";
return $ERROR_INVALID_FILENAME;
}
# FIXME
# check type
# and sanity check a/b filesize is set properly
# Determine size of file to upload
my @file_info = stat $filename;
if (! @file_info) {
print STDERR "Error! $filename not found!\n";
return $ERROR_FILE_NOT_FOUND;
}
$file_size = $file_info[7];
print "Filesize: $file_size\n" if $debug;
# We need to round up to nearest sector size
# Add an extra sector for the T/S list
# Then add extra sector for a T/S list every 122*256 bytes (~31k)
$needed_sectors = ($file_size / $BYTES_PER_SECTOR) + # round sectors
(($file_size % $BYTES_PER_SECTOR) != 0) + # tail if needed
1 + # first T/S list
($file_size / (122 * $BYTES_PER_SECTOR)); # extra t/s lists
# Get free space on device
$free_space = dos33_free_space($fd);
# Check for free space
if ($needed_sectors * $BYTES_PER_SECTOR > $free_space) {
print STDERR sprintf("Error! Not enough free space on disk image (need %d have %d)\n", ($needed_sectors * $BYTES_PER_SECTOR), $free_space);
return $ERROR_NO_SPACE;
}
# plus one because we need a sector for the tail
$size_in_sectors = ($file_size / $BYTES_PER_SECTOR) + (($file_size % $BYTES_PER_SECTOR) != 0);
print "Need to allocate $size_in_sectors data sectors\n" if $debug;
print "Need to allocate $needed_sectors total sectors\n" if $debug;
# Open the local file
if (!open($input_fd, "<$filename")) {
print STDERR "Error! could not open $filename\n";
return $ERROR_IMAGE_NOT_FOUND;
}
my $i = 0;
while ($i < $size_in_sectors) {
# Create new T/S list if necessary
if ($i % $TSL_MAX_NUMBER == 0) {
$old_ts_list = $ts_list;
# allocate a sector for the new list
$ts_list = dos33_allocate_sector($fd);
$sectors_used++;
if ($ts_list < 0) {
return -1;
}
# clear the t/s sector
my @bytes = ();
for (my $x = 0; $x < $BYTES_PER_SECTOR; $x++) {
$bytes[$x] = 0;
}
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
print $fd $sector_buffer;
if ($i == 0) {
$initial_ts_list = $ts_list;
} else {
# we aren't the first t/s list so do special stuff
# load in the old t/s list
seek($fd, DISK_OFFSET(get_high_byte($old_ts_list), get_low_byte($old_ts_list)), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
my @bytes = unpack "C*", $sector_buffer;
# point from old ts list to new one we just made
$bytes[$TSL_NEXT_TRACK] = get_high_byte($ts_list);
$bytes[$TSL_NEXT_SECTOR] = get_low_byte($ts_list);
# set offset into file
$bytes[$TSL_OFFSET_H] = get_high_byte(($i - 122) * 256);
$bytes[$TSL_OFFSET_L] = get_low_byte(($i - 122) * 256);
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
# write out the old t/s list with updated info
seek($fd, DISK_OFFSET(get_high_byte($old_ts_list), get_low_byte($old_ts_list)), $SEEK_SET);
print $fd $sector_buffer;
}
}
# force-allocate a sector
# VMW
$data_ts = dos33_force_allocate_sector($fd);
$sectors_used++;
if ($data_ts < 0) {
return -1;
}
# clear sector
my @bytes = ();
for (my $x = 0; $x < $BYTES_PER_SECTOR; $x++) {
$bytes[$x] = 0;
}
$bytes_read = read($input_fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
@bytes = unpack "C*", $sector_buffer;
if ($bytes_read < 0) {
print STDERR "Error reading bytes!\n";
}
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
# write to disk image
seek($fd, DISK_OFFSET(($data_ts >> 8) & 0xff, $data_ts & 0xff), $SEEK_SET);
print $fd $sector_buffer;
printf("Writing %i bytes to %i/%i\n", $bytes_read, (($data_ts >> 8) & 0xff), ($data_ts & 0xff)) if $debug;
# add to T/s table
# read in t/s list
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
@bytes = unpack "C*", $sector_buffer;
# point to new data sector
$bytes[(($i % $TSL_MAX_NUMBER) * 2) + $TSL_LIST] = ($data_ts >> 8) & 0xff;
$bytes[(($i % $TSL_MAX_NUMBER) * 2) + $TSL_LIST + 1] = ($data_ts & 0xff);
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
# write t/s list back out
seek($fd, DISK_OFFSET(($ts_list >> 8) & 0xff, $ts_list & 0xff), $SEEK_SET);
print $fd $sector_buffer;
$i++;
}
# Add new file to Catalog
# read in vtoc
dos33_read_vtoc($fd);
# Unpack VTOC data.
my @vtoc_bytes = unpack "C*", $sector_buffer;
$catalog_track = $vtoc_bytes[$VTOC_CATALOG_T];
$catalog_sector = $vtoc_bytes[$VTOC_CATALOG_S];
continue_parsing_catalog:
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
# Find empty directory entry
$i = 0;
while ($i < 7) {
# for undelete purposes might want to skip 0xff
# (deleted) files first and only use if no room
if (($bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] == 0xff) || ($bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] == 0x00)) {
goto got_a_dentry;
}
$i++;
}
if (($catalog_track == 0x11) && ($catalog_sector == 1)) {
# in theory can only have 105 files
# if full, we have no recourse!
# can we allocate new catalog sectors
# and point to them??
print STDERR "Error! No more room for files!\n";
return $ERROR_CATALOG_FULL;
}
$catalog_track = $bytes[$CATALOG_NEXT_T];
$catalog_sector = $bytes[$CATALOG_NEXT_S];
goto continue_parsing_catalog;
got_a_dentry:
# printf("Adding file at entry %i of catalog 0x%x:0x%x\n", $i, $catalog_track, $catalog_sector);
# Point entry to initial t/s list
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE)] = ($initial_ts_list >> 8) & 0xff;
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + 1] = ($initial_ts_list & 0xff);
# set file type
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_TYPE] = dos33_char_to_type($dos_type, 0);
# printf("Pointing T/S to %x/%x\n", ($initial_ts_list >> 8) & 0xff, $initial_ts_list & 0xff);
# copy over filename
for (my $x = 0; $x < length($apple_filename); $x++) {
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(substr($apple_filename, $x, 1)) | 0x80;
}
# pad out the filename with spaces
for (my $x = length($apple_filename); $x < $FILE_NAME_SIZE; $x++) {
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(' ') | 0x80;
}
# fill in filesize in sectors
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_SIZE_L] = $sectors_used & 0xff;
$bytes[$CATALOG_FILE_LIST + ($i * $CATALOG_ENTRY_SIZE) + $FILE_SIZE_H] = ($sectors_used >> 8) & 0xff;
# Re-pack sector data.
$sector_buffer = pack "C*", @bytes;
# write out catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
# load a file. fts=entry/track/sector
sub dos33_load_file {
my ($fd, $fts, $filename) = @_;
#print "filename=$filename\n";
my $output_fd;
my $file_size = -1;
my $data_t;
my $data_s;
my $data_sector;
my $tsl_pointer = 0;
my $output_pointer = 0;
# FIXME!Warn if overwriting file!
if (!open($output_fd, ">$filename")) {
print STDERR "Error! could not open $filename for local save\n";
return -1;
}
chmod 0666, $filename;
my $catalog_file = $fts >> 16;
my $catalog_track = ($fts >> 8) & 0xff;
my $catalog_sector = ($fts & 0xff);
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
my $tsl_track = $bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TS_LIST_T];
my $tsl_sector = $bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TS_LIST_S];
my $file_type = dos33_file_type($bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE]);
if ($file_type eq 'T') {
$file_size = 0;
}
# printf("file_type: %s\n", $file_type);
keep_saving:
# Read in TSL Sector
my $tsl_buf;
seek($fd, DISK_OFFSET($tsl_track, $tsl_sector), $SEEK_SET);
$result = read($fd, $tsl_buf, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @tsl_bytes = unpack "C*", $tsl_buf;
$tsl_pointer = 0;
# check each track/sector pair in the list
while ($tsl_pointer < $TSL_MAX_NUMBER) {
# get the t/s value
#printf("data_t offset = %d\n", ($TSL_LIST + ($tsl_pointer * $TSL_ENTRY_SIZE)));
#printf("data_s offset = %d\n", ($TSL_LIST + ($tsl_pointer * $TSL_ENTRY_SIZE) + 1));
$data_t = $tsl_bytes[$TSL_LIST + ($tsl_pointer * $TSL_ENTRY_SIZE)];
$data_s = $tsl_bytes[$TSL_LIST + ($tsl_pointer * $TSL_ENTRY_SIZE) + 1];
if (($data_s == 0) && ($data_t == 0)) {
# empty
last;
} else {
seek($fd, DISK_OFFSET($data_t, $data_s), $SEEK_SET);
$result = read($fd, $data_sector, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @data_bytes = unpack "C*", $data_sector;
# some file formats have the size in the first sector
# so cheat and get real file size from file itself
if ($output_pointer == 0) {
if ($file_type eq 'A' || $file_type eq 'I') {
$file_size = $data_bytes[0] + ($data_bytes[1] << 8) + 2;
} elsif ($file_type eq 'B') {
$file_size = $data_bytes[2] + ($data_bytes[3] << 8) + 4;
} else {
$file_size = -1;
}
}
# Re-pack sector data.
$data_sector = pack "C*", @data_bytes;
# write the block read in out to the output file
seek($output_fd, $output_pointer * $BYTES_PER_SECTOR, $SEEK_SET);
print $output_fd $data_sector;
if ($file_type eq 'T') {
$file_size += length($data_sector);
}
}
$output_pointer++;
$tsl_pointer++;
}
# finished with TSL sector, see if we have another
$tsl_track = $tsl_bytes[$TSL_NEXT_TRACK];
$tsl_sector = $tsl_bytes[$TSL_NEXT_SECTOR];
# printf("Next track/sector=%d/%d op=%d\n", $tsl_track, $tsl_sector, ($output_pointer * $BYTES_PER_SECTOR));
if (($tsl_track == 0) && ($tsl_sector == 0)) {
} else {
goto keep_saving;
}
# Correct the file size
if ($file_size >= 0) {
# print "Truncating file size to $file_size\n";
$result = truncate $output_fd, $file_size;
}
if ($result < 0) {
print STDERR "Error on I/O\n";
}
return 0;
}
# lock a file. fts=entry/track/sector
sub dos33_lock_file {
my ($fd, $fts, $lock) = @_;
my $catalog_file = $fts >>16;
my $catalog_track = ($fts >> 8) & 0xff;
my $catalog_sector = ($fts & 0xff);
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
my $file_type = $bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE];
if ($lock) {
$file_type |= 0x80;
} else {
$file_type &= 0x7f;
}
$bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE] = $file_type;
# Re-pack catalog sector data.
$sector_buffer = pack "C*", @bytes;
# write back modified catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
# rename a file. fts=entry/track/sector
# FIXME: can we rename a locked file?
# FIXME: validate the new filename is valid
sub dos33_rename_file {
my ($fd, $fts, $new_name) = @_;
my $catalog_file = $fts >> 16;
my $catalog_track = ($fts >> 8) & 0xff;
my $catalog_sector = ($fts & 0xff);
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
# copy over filename
for (my $x = 0; $x < length($new_name); $x++) {
$bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(substr($new_name, $x, 1)) | 0x80;
}
# pad out the filename with spaces
for (my $x = length($new_name); $x < $FILE_NAME_SIZE; $x++) {
$bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_NAME + $x] = ord(' ') | 0x80;
}
# Re-pack catalog sector data.
$sector_buffer = pack "C*", @bytes;
# write back modified catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
# undelete a file. fts=entry/track/sector
# FIXME: validate the new filename is valid
sub dos33_undelete_file {
my ($fd, $fts, $new_name) = @_;
my $catalog_file = $fts >> 16;
my $catalog_track = ($fts>>8) & 0xff;
my $catalog_sector = ($fts & 0xff);
# Read in Catalog Sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
# get the stored track value, and put it back
# FIXME: should walk file to see if T/s valild
# by setting the track value to FF which indicates deleted file
$bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE)] = $bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_NAME + 29];
# restore file name if possible
my $replacement_char = 0xa0;
if (length($new_name) > 29) {
$replacement_char = ord(substr($new_name, 29, 1)) | 0x80;
}
$bytes[$CATALOG_FILE_LIST + ($catalog_file * $CATALOG_ENTRY_SIZE) + $FILE_NAME + 29] = $replacement_char;
# Re-pack catalog sector data.
$sector_buffer = pack "C*", @bytes;
# write back modified catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
sub dos33_delete_file {
my ($fd, $fsl) = @_;
# unpack file/track/sector info
my $catalog_entry = $fsl >> 16;
my $catalog_track = ($fsl >> 8) & 0xff;
my $catalog_sector = ($fsl & 0xff);
# Load in the catalog table for the file
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @bytes = unpack "C*", $sector_buffer;
my $file_type = $bytes[$CATALOG_FILE_LIST + ($catalog_entry * $CATALOG_ENTRY_SIZE) + $FILE_TYPE];
if ($file_type & 0x80) {
print STDERR "File is locked! Unlock before deleting!\n";
exit(1);
}
# get pointer to t/s list
my $ts_track = $bytes[$CATALOG_FILE_LIST + $catalog_entry*$CATALOG_ENTRY_SIZE + $FILE_TS_LIST_T];
my $ts_sector = $bytes[$CATALOG_FILE_LIST + $catalog_entry * $CATALOG_ENTRY_SIZE + $FILE_TS_LIST_S];
keep_deleting:
# load in the t/s list info
seek($fd, DISK_OFFSET($ts_track, $ts_sector), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @ts_bytes = unpack "C*", $sector_buffer;
# Free each sector listed by t/s list
for (my $i = 0; $i < $TSL_MAX_NUMBER; $i++) {
# If t/s = 0/0 then no need to clear
if (($ts_bytes[$TSL_LIST + 2 * $i] == 0) && ($ts_bytes[$TSL_LIST + 2 * $i + 1] == 0)) {
} else {
dos33_free_sector($fd, $ts_bytes[$TSL_LIST + 2 * $i], $ts_bytes[$TSL_LIST + 2 * $i + 1]);
}
}
# free the t/s list
dos33_free_sector($fd, $ts_track, $ts_sector);
# Point to next t/s list
$ts_track = $ts_bytes[$TSL_NEXT_TRACK];
$ts_sector = $ts_bytes[$TSL_NEXT_SECTOR];
# If more tsl lists, keep looping
if (($ts_track == 0x0) && ($ts_sector == 0x0)) {
} else {
goto keep_deleting;
}
# Erase file from catalog entry
# First reload proper catalog sector
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
my @cat_bytes = unpack "C*", $sector_buffer;
# save track as last char of name, for undelete purposes
$cat_bytes[$CATALOG_FILE_LIST + ($catalog_entry * $CATALOG_ENTRY_SIZE) + ($FILE_NAME + $FILE_NAME_SIZE - 1)] = $cat_bytes[$CATALOG_FILE_LIST + ($catalog_entry * $CATALOG_ENTRY_SIZE)];
# Actually delete the file
# by setting the track value to FF which indicates deleted file
$cat_bytes[$CATALOG_FILE_LIST + ($catalog_entry * $CATALOG_ENTRY_SIZE)] = 0xff;
# Re-pack catalog sector data.
$sector_buffer = pack "C*", @cat_bytes;
# Re-seek to catalog position and write out changes
seek($fd, DISK_OFFSET($catalog_track, $catalog_sector), $SEEK_SET);
print $fd $sector_buffer;
return 0;
}
sub dump_sector {
# Unpack sector data.
my @bytes = unpack "C*", $sector_buffer;
for (my $i = 0; $i < 16; $i++) {
printf("\$%02x : ", $i * 16);
for (my $j = 0; $j < 16; $j++) {
printf("%02x ", $bytes[$i * 16 + $j]);
}
print "\n";
}
return 0;
}
sub dos33_dump {
my ($fd) = @_;
my $file;
my $ts_t;
my $ts_s;
my $track;
my $sector;
my $deleted = 0;
my $temp_string;
my $tslist;
# Read Track 1 Sector 9
seek($fd, DISK_OFFSET(1, 9), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack sector data.
my @bytes = unpack "C*", $sector_buffer;
print "Finding name of startup file, Track 1 Sector 9 offset \$75\n";
dump_sector();
print "Startup Filename: ";
for (my $i = 0; $i < 30; $i++) {
printf("%c", $bytes[0x75 + $i] & 0x7f);
}
print "\n";
dos33_read_vtoc($fd);
# Unpack VTOC sector data.
@bytes = unpack "C*", $sector_buffer;
print "\nVTOC Sector:\n";
dump_sector();
print "\n\n";
print "VTOC INFORMATION:\n";
my $catalog_t = $bytes[$VTOC_CATALOG_T];
my $catalog_s = $bytes[$VTOC_CATALOG_S];
printf(" First Catalog = %02x/%02x\n", $catalog_t, $catalog_s);
printf(" DOS RELEASE = 3.%i\n", $bytes[$VTOC_DOS_RELEASE]);
printf(" DISK VOLUME = %i\n", $bytes[$VTOC_DISK_VOLUME]);
my $ts_total = $bytes[$VTOC_MAX_TS_PAIRS];
printf(" T/S pairs that will fit in T/S List = %i\n", $ts_total);
printf(" Last track where sectors were allocated = \$%02x\n", $bytes[$VTOC_LAST_ALLOC_T]);
printf(" Direction of track allocation = %i\n", $bytes[$VTOC_ALLOC_DIRECT]);
my $num_tracks = $bytes[$VTOC_NUM_TRACKS];
printf(" Number of tracks per disk = %i\n", $num_tracks);
printf(" Number of sectors per track = %i\n", $bytes[$VTOC_S_PER_TRACK]);
printf(" Number of bytes per sector = %i\n", ($bytes[$VTOC_BYTES_PER_SH] << 8) + $bytes[$VTOC_BYTES_PER_SL]);
print "\nFree sector bitmap:\n";
print "Track FEDCBA98 76543210\n";
for (my $trk = 0; $trk < $num_tracks; $trk++) {
printf(" \$%02x:", $trk);
for (my $sec = 0; $sec < 8; $sec++) {
if (($bytes[$VTOC_FREE_BITMAPS + ($trk * 4)] << $sec) & 0x80) {
print ".";
} else {
print "U";
}
}
print " ";
for (my $sec = 0; $sec < 8; $sec++) {
if (($bytes[$VTOC_FREE_BITMAPS + ($trk * 4) + 1] << $sec) & 0x80) {
print ".";
} else {
print "U";
}
}
print "\n";
}
repeat_catalog:
printf("\nCatalog Sector \$%02x/\$%02x\n", $catalog_t, $catalog_s);
seek($fd, DISK_OFFSET($catalog_t, $catalog_s), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
@bytes = unpack "C*", $sector_buffer;
dump_sector();
for ($file = 0; $file < 7; $file++) {
#print "\n\n";
print "\n";
$ts_t = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_TS_LIST_T))];
$ts_s = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_TS_LIST_S))];
printf("%i+\$%02x/\$%02x - ", $file, $catalog_t, $catalog_s);
$deleted = 0;
if ($ts_t == 0xff) {
print "**DELETED** ";
$deleted = 1;
$ts_t = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_NAME + 0x1e))];
}
if ($ts_t == 0x00) {
print "UNUSED!";
goto continue_dump;
}
my $filenamestr = substr($sector_buffer, ($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_NAME)), 30);
dos33_filename_to_ascii($temp_string, $filenamestr, 30);
print "$temp_string";
print "\n";
printf(" Locked = %s\n", $bytes[$CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE] > 0x7f ? "YES" : "NO");
printf(" Type = %s\n", dos33_file_type($bytes[$CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE) + $FILE_TYPE]));
printf(" Size in sectors = %i\n", $bytes[$CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_SIZE_L)] + ($bytes[$CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_SIZE_H)] << 8));
repeat_tsl:
printf(" T/S List \$%02x/\$%02x:\n", $ts_t, $ts_s);
if ($deleted) {
goto continue_dump;
}
seek($fd, DISK_OFFSET($ts_t, $ts_s), $SEEK_SET);
$result = read($fd, $tslist, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
my @tslist_bytes = unpack "C*", $tslist;
for (my $i = 0; $i < $ts_total; $i++) {
$track = $tslist_bytes[$TSL_LIST + ($i * $TSL_ENTRY_SIZE)];
$sector = $tslist_bytes[$TSL_LIST + ($i * $TSL_ENTRY_SIZE) + 1];
if (($track == 0) && ($sector == 0)) {
print ".";
} else {
printf("\n %02x/%02x", $track, $sector);
}
}
$ts_t = $tslist_bytes[$TSL_NEXT_TRACK];
$ts_s = $tslist_bytes[$TSL_NEXT_SECTOR];
if (!(($ts_s == 0) && ($ts_t == 0))) {
goto repeat_tsl;
}
continue_dump:;
}
print "\n";
$catalog_t = $bytes[$CATALOG_NEXT_T];
$catalog_s = $bytes[$CATALOG_NEXT_S];
if ($catalog_s != 0) {
$file = 0;
goto repeat_catalog;
}
print "\n";
if ($result < 0) {
print STDERR "Error on I/O\n";
}
return 0;
}
sub dos33_showfree {
my ($fd) = @_;
my $file;
my $ts_t;
my $ts_s;
my $track;
my $sector;
my $deleted = 0;
my $temp_string;
my $tslist;
my $catalog_used;
my $next_letter = ord('A');
my %file_key;
my $num_files = 0;
my @usage;
for (my $i = 0; $i < 35; $i++) {
for (my $j = 0; $j < 16; $j++) {
$usage[$i][$j] = 0;
}
}
# Read Track 1 Sector 9
seek($fd, DISK_OFFSET(1, 9), $SEEK_SET);
my $result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack sector data.
my @bytes = unpack "C*", $sector_buffer;
printf("Finding name of startup file, Track 1 Sector 9 offset \$75\n");
printf("Startup Filename: ");
for (my $i = 0; $i < 30; $i++) {
printf("%c", $bytes[0x75 + $i] & 0x7f);
}
printf("\n");
dos33_read_vtoc($fd);
# Unpack VTOC sector data.
@bytes = unpack "C*", $sector_buffer;
printf("\n");
printf("VTOC INFORMATION:\n");
my $catalog_t = $bytes[$VTOC_CATALOG_T];
my $catalog_s = $bytes[$VTOC_CATALOG_S];
printf(" First Catalog = %02x/%02x\n", $catalog_t, $catalog_s);
printf(" DOS RELEASE = 3.%i\n", $bytes[$VTOC_DOS_RELEASE]);
printf(" DISK VOLUME = %i\n", $bytes[$VTOC_DISK_VOLUME]);
my $ts_total = $bytes[$VTOC_MAX_TS_PAIRS];
printf(" T/S pairs that will fit in T/S List = %i\n", $ts_total);
printf(" Last track where sectors were allocated = \$%02x\n", $bytes[$VTOC_LAST_ALLOC_T]);
printf(" Direction of track allocation = %i\n", $bytes[$VTOC_ALLOC_DIRECT]);
my $num_tracks = $bytes[$VTOC_NUM_TRACKS];
printf(" Number of tracks per disk = %i\n", $num_tracks);
printf(" Number of sectors per track = %i\n", $bytes[$VTOC_S_PER_TRACK]);
my $sectors_per_track = $bytes[$VTOC_S_PER_TRACK];
printf(" Number of bytes per sector = %i\n", ($bytes[$VTOC_BYTES_PER_SH] << 8) + $bytes[$VTOC_BYTES_PER_SL]);
printf("\nFree sector bitmap:\n\n");
printf(" 1111111111111111222\n");
printf(" 0123456789ABCDEF0123456789ABCDEF012\n");
my $disp_sec = 0;
for (my $sec = ($sectors_per_track - 1); $sec >= 0; $sec--) {
printf("\$%01x: ", $disp_sec++);
for (my $trk = 0; $trk < $num_tracks; $trk++) {
if ($sec < 8) {
if (($bytes[$VTOC_FREE_BITMAPS + ($trk * 4)] << $sec) & 0x80) {
printf(".");
} else {
printf("U");
}
} else {
if (($bytes[$VTOC_FREE_BITMAPS + ($trk * 4) + 1] << ($sec - 8)) & 0x80) {
printf(".");
} else {
printf("U");
}
}
}
printf("\n");
}
printf("Key: U=used, .=free\n\n");
# Reserve DOS
for (my $i = 0; $i < 3; $i++) {
for (my $j = 0; $j < 16; $j++) {
$usage[$i][$j] = '$';
}
}
# Reserve CATALOG (not all used?)
my $i = 0x11;
for (my $j = 0; $j < 16; $j++) {
$usage[$i][$j] = '#';
}
repeat_catalog:
$catalog_used = 0;
seek($fd, DISK_OFFSET($catalog_t, $catalog_s), $SEEK_SET);
$result = read($fd, $sector_buffer, $BYTES_PER_SECTOR);
# Unpack catalog sector data.
@bytes = unpack "C*", $sector_buffer;
for ($file = 0; $file < 7; $file++) {
$ts_t = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_TS_LIST_T))];
$ts_s = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_TS_LIST_S))];
$deleted = 0;
if ($ts_t == 0xff) {
printf("**DELETED** ");
$deleted = 1;
$ts_t = $bytes[($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_NAME + 0x1e))];
}
if ($ts_t == 0x00) {
goto continue_dump;
}
my $filenamestr = substr($sector_buffer, ($CATALOG_FILE_LIST + ($file * $CATALOG_ENTRY_SIZE + $FILE_NAME)), 30);
dos33_filename_to_ascii($temp_string, $filenamestr, 30);
printf("%s %s", chr($next_letter), $temp_string);
printf("\n");
if (!$deleted) {
$catalog_used++;
$usage[$catalog_t][$catalog_s] = '@';
}
repeat_tsl:
if ($deleted) {
goto continue_dump;
}
$usage[$ts_t][$ts_s] = chr($next_letter);
$file_key{$temp_string} = chr($next_letter);
$num_files++;
seek($fd, DISK_OFFSET($ts_t, $ts_s), $SEEK_SET);
$result = read($fd, $tslist, $BYTES_PER_SECTOR);
# Unpack tslist sector data.
my @tslist_bytes = unpack "C*", $tslist;
for ($i = 0; $i < $ts_total; $i++) {
$track = $tslist_bytes[$TSL_LIST + ($i * $TSL_ENTRY_SIZE)];
$sector = $tslist_bytes[$TSL_LIST + ($i * $TSL_ENTRY_SIZE) + 1];
if (($track == 0) && ($sector == 0)) {
} else {
$usage[$track][$sector] = chr($next_letter);
}
}
$ts_t = $tslist_bytes[$TSL_NEXT_TRACK];
$ts_s = $tslist_bytes[$TSL_NEXT_SECTOR];
if (!(($ts_s == 0) && ($ts_t == 0))) {
goto repeat_tsl;
}
continue_dump:
if ($next_letter == ord('Z')) {
$next_letter = ord('a');
} elsif ($next_letter == ord('z')) {
$next_letter = ord('0');
} else {
$next_letter++;
}
}
$catalog_t = $bytes[$CATALOG_NEXT_T];
$catalog_s = $bytes[$CATALOG_NEXT_S];
if ($catalog_s != 0) {
$file = 0;
goto repeat_catalog;
}
print "\n";
if ($result < 0) {
print STDERR "Error on I/O\n";
}
print "\nDetailed sector bitmap:\n\n";
print " 1111111111111111222\n";
print " 0123456789ABCDEF0123456789ABCDEF012\n";
for (my $j = 0; $j < $sectors_per_track; $j++) {
printf("\$%01x: ", $j);
for ($i = 0; $i < $num_tracks; $i++) {
if ($usage[$i][$j] eq '0') {
print ".";
} else {
printf("%s", $usage[$i][$j]);
}
}
print "\n";
}
print "Key: \$=DOS, @=catalog used, #=catalog reserved, .=free\n\n";
foreach my $val (sort values %file_key) {
if (defined $val) {
if (defined $file_key{$val}) {
printf(" %s %s\n", $val, $file_key{$val});
}
}
}
return 0;
}
# ???
sub dos33_rename_hello {
my ($fd, $new_name) = @_;
my $buffer;
seek($fd, DISK_OFFSET(1, 9), $SEEK_SET);
read($fd, $buffer, $BYTES_PER_SECTOR);
# Unpack sector data.
my @bytes = unpack "C*", $buffer;
for (my $i = 0; $i < 30; $i++) {
if ($i < length($new_name)) {
$bytes[0x75 + $i] = ord(substr($new_name, $i, 1)) | 0x80;
} else {
$bytes[0x75 + $i] = ord(' ') | 0x80;
}
}
# Re-pack sector data.
$buffer = pack "C*", @bytes;
seek($fd, DISK_OFFSET(1, 9), $SEEK_SET);
print $fd $buffer;
return 0;
}
sub display_help {
my ($name, $version_only) = @_;
printf("\ndos33 version %s\n", $VERSION);
printf("by Vince Weaver <vince\@deater.net>\n");
printf("Perl port by Leeland Heins <softwarejanitor\@yahoo.com>\n");
printf("\n");
if ($version_only) {
return;
}
printf("Usage: %s [-h] [-y] disk_image COMMAND [options]\n", $name);
printf(" -h : this help message\n");
printf(" -y : always answer yes for anying warning questions\n");
printf("\n");
printf("Where disk_image is a valid dos3.3 disk image\nand COMMAND is one of the following:\n");
printf(" CATALOG\n");
printf(" LOAD apple_file <local_file>\n");
printf(" SAVE type local_file <apple_file>\n");
printf(" BSAVE [-a addr] [-l len] local_file <apple_file>\n");
printf(" DELETE apple_file\n");
printf(" LOCK apple_file\n");
printf(" UNLOCK apple_file\n");
printf(" RENAME apple_file_old apple_file_new\n");
printf(" UNDELETE apple_file\n");
printf(" DUMP\n");
printf(" SHOWFREE\n");
printf(" HELLO apple_file\n");
#printf(" INIT\n");
#printf(" COPY\n");
printf("\n");
return;
}
sub truncate_filename {
my ($out) = @_;
my $truncated = 0;
# Truncate filename if too long
if (length($out) > 30) {
$out = substr($out, 0, 30);
print STDERR sprintf("Warning! Truncating %s to 30 chars\n", $out);
$_[0] = $out;
$truncated = 1;
}
return $truncated;
}
## MAIN
my $type = 'b';
my $catalog_entry;
my $temp_string;
my $apple_filename;
my $new_filename;
my $local_filename;
my $always_yes = 0;
my $address = 0;
my $length = 0;
# Process command line arguments.
while (defined $ARGV[0] && $ARGV[0] =~ /^-/) {
# Set base address in decimal.
if ($ARGV[0] eq '-a' && defined $ARGV[1] && $ARGV[1] =~ /^\d+$/) {
$address = $ARGV[1];
shift;
shift;
} elsif ($ARGV[0] eq '-l' && defined $ARGV[1] && $ARGV[1] =~ /^\d+$/) {
$length = $ARGV[1];
shift;
shift;
} elsif ($ARGV[0] eq '-t' && defined $ARGV[1] && $ARGV[1] =~ /^\d+$/) {
$track = $ARGV[1];
shift;
shift;
} elsif ($ARGV[0] eq '-v') {
display_help($ARGV[0], 1);
exit 1;
} elsif ($ARGV[0] eq '-h') {
display_help($ARGV[0], 0);
exit 1;
} else {
die "Invalid argument $ARGV[0]\n";
}
}
# get argument 1, which is image name
my $image = shift;
if (! defined $image) {
print STDERR "ERROR!Must specify disk image!\n\n";
exit 0;
}
my $dos_fd;
if (!open($dos_fd, "+<$image")) {
print STDERR "Error opening disk_image: $image\n";
exit 0;
}
# Grab command
my $command = shift;
if (! defined $command) {
print STDERR "ERROR! Must specify command!\n\n";
exit 0;
}
# Make command be uppercase
$command = uc($command);
# Load a file from disk image to local machine
if ($command eq "LOAD") {
# check and make sure we have apple_filename
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need apple file_name\n";
print STDERR "$0 $image LOAD apple_filename\n";
} else {
print " Apple filename: $apple_filename\n" if $debug;
truncate_filename($apple_filename);
# get output filename
my $local_filename = shift;
if (! defined $local_filename) {
$local_filename = $apple_filename;
print "Using $local_filename for filename\n" if $debug;
} else {
print "Using $apple_filename for filename\n" if $debug;
}
print " Output filename: $local_filename\n" if $debug;
# get the entry/track/sector for file
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
if ($catalog_entry < 0) {
print STDERR "Error! $apple_filename not found!\n";
} else {
dos33_load_file($dos_fd, $catalog_entry, $local_filename);
}
}
} elsif ($command eq "CATALOG") {
# get first catalog
$catalog_entry = dos33_get_catalog_ts($dos_fd);
# Unpack sector data.
my @bytes = unpack "C*", $sector_buffer;
printf("\nDISK VOLUME %i\n\n", $bytes[$VTOC_DISK_VOLUME]);
while ($catalog_entry > 0) {
$catalog_entry = dos33_find_next_file($dos_fd, $catalog_entry);
if ($catalog_entry > 0) {
dos33_print_file_info($dos_fd, $catalog_entry);
# why 1 << 16 ?
$catalog_entry += (1 << 16);
# dos33_find_next_file() handles wrapping issues
}
}
print "\n";
} elsif ($command eq "SAVE") {
# argv3 == type == A, B, T, I, N, L etc
# argv4 == name of local file
# argv5 == optional name of file on disk image
my $type = shift;
if (! defined $type || $type eq '') {
print STDERR "Error! Need type\n";
print STDERR "$0 $image SAVE type file_name apple_filename\n\n";
} else {
print " type=$type\n" if $debug;
if ($type !~ /^[TIABSRNL]$/i) {
print STDERR "Error! Invalied type - must be T, I, A, B, S, R, N or L\n";
} else {
my $local_filename = shift;
if (! defined $local_filename || $local_filename eq '') {
print STDERR "Error! Need file_name\n";
print STDERR "$0 $image SAVE type file_name apple_filename\n\n";
} else {
my $apple_filename = shift;
if (! defined $apple_filename || $apple_filename eq '') {
print STDERR "Error! Need apple_filename\n";
print STDERR "$0 $image SAVE type file_name apple_filename\n\n";
} else {
printf(" Apple filename: %s\n", $apple_filename) if $debug;
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
my $result_string = 'y';
if ($catalog_entry >= 0) {
print STDERR "Warning! $apple_filename exists!\n";
if (!$always_yes) {
printf("Over-write (y/n)?");
$result_string = <STDIN>;
if (($result_string eq '') || ($result_string !~ /^[yY]/)) {
printf("Exiting early...\n");
}
}
if ($result_string =~ /^[Yy]/) {
print STDERR "Deleting previous version...\n";
dos33_delete_file($dos_fd, $catalog_entry);
}
}
if ($result_string =~ /^[Yy]/) {
dos33_add_file($dos_fd, $type, $ADD_RAW, $address, $length, $local_filename, $apple_filename);
}
}
}
}
}
} elsif ($command eq "BSAVE") {
my $local_filename = shift;
if (! defined $local_filename || $local_filename eq '') {
print STDERR "Error! Need file_name\n";
print STDERR "$0 $image BSAVE file_name apple_filename\n\n";
} else {
print " Local filename: $local_filename\n" if $debug;
my $apple_filename = shift;
if (! defined $apple_filename || $apple_filename eq '') {
# apple filename specified
print STDERR "Error! Need apple_filename\n";
print STDERR "$0 $image BSAVE file_name apple_filename\n\n";
} else {
truncate_filename($apple_filename);
# If no filename specified for apple name
# Then use the input name.Note, we strip
# everything up to the last slash so useless
# path info isn't used
$apple_filename = basename($local_filename);
truncate_filename($apple_filename);
}
printf(" Apple filename: %s\n", $apple_filename) if $debug;
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
my $result_string = 'y';
if ($catalog_entry >= 0) {
print STDERR "Warning! $apple_filename exists!\n";
if (!$always_yes) {
printf("Over-write (y/n)?");
$result_string = <STDIN>;
if (($result_string eq '') || ($result_string !~ /^[yY]/)) {
printf("Exiting early...\n");
}
}
if ($result_string =~ /^[Yy]/) {
print STDERR "Deleting previous version...\n";
dos33_delete_file($dos_fd, $catalog_entry);
}
}
if ($result_string =~ /^[Yy]/) {
dos33_add_file($dos_fd, $type, $ADD_BINARY, $address, $length, $local_filename, $apple_filename);
}
}
} elsif ($command eq "RAWWRITE") {
# ???
printf(" type=%s\n", $type) if $debug;
my $local_filename = shift;
if (! defined $local_filename) {
print STDERR "Error! Need file_name\n";
print STDERR "$0 $image RAWWRITE file_name apple_filename\n\n";
} else {
printf(" Local filename: %s\n", $local_filename) if $debug;
my $apple_filename = shift;
if (defined $apple_filename) {
# apple filename specified
truncate_filename($apple_filename);
} else {
$apple_filename = basename($local_filename);
# If no filename specified for apple name
# Then use the input name. Note, we strip
# everything up to the last slash so useless
# path info isn't used
truncate_filename($apple_filename);
}
printf(" Apple filename: %s\n", $apple_filename) if $debug;
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
my $result_string = 'y';
if ($catalog_entry >= 0) {
print STDERR "Warning! $apple_filename exists!\n";
if (!$always_yes) {
print "Over-write (y/n)?";
$result_string = <STDIN>;
if (($result_string eq '') || ($result_string !~ /[yY]/)) {
print "Exiting early...\n";
}
}
if ($result_string =~ /^[Yy]/) {
print STDERR "Deleting previous version...\n";
dos33_delete_file($dos_fd, $catalog_entry);
}
}
if ($result_string =~ /^[Yy]/) {
dos33_raw_file($dos_fd, $type, $track, $sector, $local_filename, $apple_filename);
}
}
} elsif ($command eq "DELETE") {
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need file_name\n";
print STDERR "$0 $image DELETE apple_filename\n";
} else {
truncate_filename($apple_filename);
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
if ($catalog_entry < 0) {
print STDERR "Error! File $apple_filename does not exist\n";
} else {
dos33_delete_file($dos_fd, $catalog_entry);
}
}
} elsif ($command eq "DUMP") {
printf("Dumping %s!\n", $image);
dos33_dump($dos_fd);
} elsif ($command eq "SHOWFREE") {
printf("Showing Free %s!\n", $image);
dos33_showfree($dos_fd);
} elsif ($command eq "LOCK" || $command eq "UNLOCK") {
# check and make sure we have apple_filename
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need apple file_name\n";
print STDERR "$0 $image $command apple_filename\n";
} else {
truncate_filename($apple_filename);
# get the entry/track/sector for file
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
if ($catalog_entry < 0) {
print STDERR "Error! $apple_filename not found!\n";
} else {
dos33_lock_file($dos_fd, $catalog_entry, $command eq "LOCK");
}
}
} elsif ($command eq "RENAME") {
# check and make sure we have apple_filename
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need two filenames\n";
print STDERR "$0 $image LOCK apple_filename_old apple_filename_new\n";
} else {
# Truncate filename if too long
truncate_filename($apple_filename);
my $new_filename = shift;
if (! defined $new_filename) {
print STDERR "Error! Need two filenames\n";
print STDERR "$0 $image LOCK apple_filename_old apple_filename_new\n";
} else {
truncate_filename($new_filename);
# get the entry/track/sector for file
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
if ($catalog_entry < 0) {
print STDERR "Error! $apple_filename not found!\n";
} else {
dos33_rename_file($dos_fd, $catalog_entry, $new_filename);
}
}
}
} elsif ($command eq "UNDELETE") {
# check and make sure we have apple_filename
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need apple file_name\n";
print STDERR "$0 $image UNDELETE apple_filename\n\n";
} else {
truncate_filename($apple_filename);
# get the entry/track/sector for file
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_DELETED);
if ($catalog_entry < 0) {
print STDERR "Error! $apple_filename not found!\n";
} else {
dos33_undelete_file($dos_fd, $catalog_entry, $apple_filename);
}
}
} elsif ($command eq "HELLO") {
my $apple_filename = shift;
if (! defined $apple_filename) {
print STDERR "Error! Need file_name\n";
print "$0 $image HELLO apple_filename\n\n";
} else {
truncate_filename($apple_filename);
$catalog_entry = dos33_check_file_exists($dos_fd, $apple_filename, $FILE_NORMAL);
if ($catalog_entry < 0) {
print STDERR "Warning! File $apple_filename does not exist\n";
}
dos33_rename_hello($dos_fd, $apple_filename);
}
} elsif ($command eq "INIT") {
# use common code from mkdos33fs?
} elsif ($command eq "COPY") {
# use temp file? Walking a sector at a time seems a pain
} else {
print STDERR "ERROR! Unknown command $command\n";
print STDERR " Try \"$0 -h\" for help.\n\n";
}
close($dos_fd);
exit 1;
1;