use strict; # list the directory of a mounted d64 sub listd64($) { my $fname = shift; my ($out, $i, $t, $s, $free, $nameid, $nfiles); my (@size, $sizeref, @name, $nameref, @suffix, $suffixref); # extract data from BAM ($t, $s, $free, $nameid) = parsebam1541(); # load address $out = "\x01\x04"; # title $out .= addtitle(0, "\x12\"$nameid"); print '0 "' . $nameid . "\n"; ($nfiles, $sizeref, $nameref, $suffixref, $_) = parsedir1541($t, $s); @size = @$sizeref; @name = @$nameref; @suffix = @$suffixref; if ($nfiles >= 0) { for $i (0..$nfiles-1) { print substr($size[$i] . " ", 0, 5) . quotename($name[$i]) . $suffix[$i] . "\n"; $out .= addline($size[$i], quotename($name[$i]) . $suffix[$i]); } } else { sendc("?"); goto LID64_DONE; } # add blocks free line $out .= addtitle($free, "BLOCKS FREE."); print "$free BLOCKS FREE.\n"; # end of basic listing $out .= "\x00\x00"; # transmit send_wsize($out); LI64_DONE: } # extract info from the BAM block sub parsebam1541() { my ($b, $t, $s, $free, $nameid, $f, $i, $a); # 18, 0 for the BAM and disk info $b = $::block[&b1541(18, 0)]; # track and sector of directory $t = ord($b); $s = ord(substr($b, 1, 1)); # count number of blocks free $free = 0; for $i (1..35) { $a = substr($b, $i*4, 4); $f = ord($a); # print BAM # printf("%2d %2d: %s\n", $i, $f, unpack("b24", substr($a, 1, 3))); next if ($i == 18); $free += $f; } # name and disk ID $nameid = substr($b, 144, 23); # clean up $a0 $nameid =~ tr/\xa0/\x20/; # insert a " substr($nameid, 16, 1) = '"'; return(($t, $s, $free, $nameid)); } # read a linked list of blocks into a buffer sub readchain1541($$) { my $t = shift; my $s = shift; my ($b, $chain, $count); # read all the blocks into $chain $chain = ""; do { $b = $::block[&b1541($t, $s)]; $chain .= $b; $t = ord($b); $s = ord(substr($b, 1, 1)); $count++; } until ($t == 0 || $count > 684); # in case we get stuck in a loop return(($count, $chain)); } # parse dir sub parsedir1541($$) { # file types, in petscii my @types = ("DEL", "SEQ", "PRG", "USR", "REL"); my $t = shift; my $s = shift; my ($count, $dir, $f, $i); my (@size, @name, @suffix, @ts); ($count, $dir) = readchain1541($t, $s); # now, for every 8 files in every dir block... $f = 0; for $i (0..$count*8-1) { my ($e, $type, $suffix, $size, $name, $ts); # one entry at a time $e = substr($dir, $i*32+2, 30); $ts = substr($e, 1, 2); $ts = ord($ts) + 256*ord(substr($ts,1,1)); $name = substr($e, 3, 16); $size = 256*ord(substr($e, -1, 1)); $size += ord(substr($e, -2, 1)); $type = ord($e); next if ($type == 0); next if (($type & 7) > 4); if ($type < 128) { $suffix = "*"; # unclosed } else { $suffix = " "; # closed } $suffix .= $types[$type & 7]; if ($type & 64) { $suffix .= "<"; } $size[$f] = $size; $name[$f] = $name; $suffix[$f] = $suffix; $ts[$f] = $ts; $f++; } if ($f == 0) { return((-1, ())); } else { return(($f, \@size, \@name, \@suffix, \@ts)); } } # convert a chain of blocks to a plain file sub chaintofile($) { my $chain = shift; my ($file, $b); while (length($chain)) { $b = substr($chain,0,256); $chain = substr($chain,256); if (ord($b) == 0) { $file .= substr($b,2,ord(substr($b,1,1))-1); last; } else { $file .= substr($b,2,254); } } return($file); } # put the name in quotes sub quotename($) { my $name = shift; my $o; $o = index($name, chr(160)); $o = 16 if ($o == -1); substr($name, $o, 1) = '"'; # clean up $a0 $name =~ tr/\xa0/\x20/; return('"' . $name); } # load a file from a mounted d64 sub loadd64($) { my $req = shift; my (@size, $sizeref, @name, $nameref, @suffix, $suffixref, @ts, $tsref); my ($t, $s, $nfiles, $i, $head, $fname, $fpat); ($head, $fname, $_) = splithead($req); if ($fname =~ /^\$/) { listd64($fname); goto LOD64_DONE; } if ($fname eq "..") { $::cd = ""; sendc("."); goto LOD64_DONE; } print "Getting file list\n"; # get start of dir from BAM ($t, $s, $_) = parsebam1541(); # parse the dir data ($nfiles, $sizeref, $nameref, $suffixref, $tsref) = parsedir1541($t, $s); @size = @$sizeref; @name = @$nameref; @suffix = @$suffixref; @ts = @$tsref; # no files? b0rk if ($nfiles < 0) { goto LOD64_FAIL; } # convert filename to a regexp $fpat = $fname; $fpat =~ s/\?/./g; $fpat =~ s/\*/.*/g; print "loading \"$fpat\"\n"; # check the files one after one for $i (0..$nfiles-1) { my ($count, $chain, $file, $o); $o = index($name[$i], chr(0xa0)); if ($o >= 0) { $name[$i] = substr($name[$i], 0, $o); } # print "checking $i: $name[$i]\n"; # print "identical!\n" if ($name[$i] eq $fpat); # next unless ($name[$i] =~ /^$fpat$/); next unless ($name[$i] eq $fname); $t = $ts[$i] & 255; $s = $ts[$i] >>8; ($count, $chain) = readchain1541($t, $s); if ($count > 684) { goto LOD64_FAIL; } $file = chaintofile($chain); send_wsize($file); goto LOD64_DONE; } LOD64_FAIL: sendc("?"); LOD64_DONE: } # buffer a d64 in @block sub opend64($) { my $file = shift; my ($size, $i); return(0) if ((stat($file))[7] != 174848); return(0) unless open(F, $file); binmode(F); for $i (0..682) { die if (read(F, $::block[$i], 256) != 256); } return(1); } # converts track, sector into block offset sub b1541($$) { my $t = shift; my $s = shift; my $b; if ($t < 18) { $b = ($t-1) * 21; } elsif ($t < 25) { $b = ($t-18) * 19 + 17*21; } elsif ($t < 31) { $b = ($t-25) * 18 + 17*21 + 7*19; } else { $b = ($t-31) * 17 + 17*21 + 7*19 + 6*18; } return($b + $s); } return(1);