NAME

D64::Disk::Image - Perl interface to Per Olofsson's "diskimage.c", an ANSI C library for manipulating Commodore disk images

SYNOPSIS

use D64::Disk::Image qw(:all);

# Create an empty image:
my $d64 = D64::Disk::Image->create_image('image.d64');

# Format the image:
my $rawname = $d64->rawname_from_name('title');
my $rawid = $d64->rawname_from_name('id');
$d64->format($rawname, $rawid);

# Write the image to disk:
$d64->free_image();

# Load an image from disk:
my $d64 = D64::Disk::Image->load_image('image.d64');

# Open a file for writing:
my $rawname = $d64->rawname_from_name('filename');
my $prg = $d64->open($rawname, T_PRG, F_WRITE);

# Write data to file:
my $counter = $prg->write($buffer);

# Close a file:
$prg->close();

# Open a file for reading:
my $rawname = $d64->rawname_from_name('filename');
my $prg = $d64->open($rawname, T_PRG, F_READ);

# Read data from file:
my ($counter, $buffer) = $prg->read();

# Close a file:
$prg->close();

# Free an image in memory:
$d64->free_image();

DESCRIPTION

Per Olofsson's "diskimage.c" is an ANSI C library for manipulating Commodore disk images. In Perl the following operations are implemented via D64::Disk::Image package:

  • Open file ('$' reads directory)

  • Delete file

  • Rename file

  • Format disk

  • Allocate sector

  • Deallocate sector

Additionally, the following operations are implemented via accompanying D64::Disk::Image::File package:

  • Read file

  • Write file

  • Close file

The following formats are supported:

  • D64 (single-sided 1541 disk image, with optional error info, which is currently ignored)

  • D71 (double-sided 1571 disk image)

  • D81 (3,5" 1581 disk image, however only root directory)

METHODS

new / load_image

Create new D64::Disk::Image object and load existing D64/D71/D81 image file from disk:

my $d64DiskImageObj = D64::Disk::Image->new($name);
my $d64DiskImageObj = D64::Disk::Image->load_image($name);

new / create_image

Create new D64::Disk::Image object and create new D64/D71/D81 image file on disk:

my $d64DiskImageObj = D64::Disk::Image->new($name, $imageType);
my $d64DiskImageObj = D64::Disk::Image->create_image($name, $imageType);

The following image type constants are available: D64, D71, D81 (image type D64 is used by default when executed as "create_image"). Each disk created needs to be formatted first before it can be used.

free_image

Free an image in memory (each opened disk needs to be subsequently freed to avoid memory leaks):

$d64DiskImageObj->free_image();

If the image has been modified, the changes will be written to disk.

sync

Write the image to disk:

$d64DiskImageObj->sync();

status

Get the drive status:

my ($numstatus, $status) = $d64DiskImageObj->status();

Numerical status is returned first, textual content of a status message is copied to the second return value.

open

Open a file for reading or writing:

my $imageFileObj = $d64DiskImageObj->open($rawname, $fileType, $mode);

The following file type constants are available: T_DEL, T_SEQ, T_PRG, T_USR, T_REL, T_CBM, T_DIR (by default file type T_PRG is used)

There are two open modes available: F_READ for reading, F_WRITE for writing (by default file is opened in F_READ mode)

Opening, reading, writing, and closing files is described in detail in D64::Disk::Image::File

format

Format the image:

my $numstatus = $d64DiskImageObj->format($rawname, $rawid);

If $rawid is given, a full format is performed.

my $numstatus = $d64DiskImageObj->format($rawname);

If no $rawid is given, a quick format is performed.

delete

Delete files matching the pattern:

my $numstatus = $d64DiskImageObj->delete($rawPattern, $fileType);

rename

Rename a file:

my $numstatus = $d64DiskImageObj->rename($oldRawName, $newRawName, $fileType);

sectors_per_track

Get the number of sectors in a given track:

my $sectors = D64::Disk::Image->sectors_per_track($imageType, $track);
my $sectors = $d64DiskImageObj->sectors_per_track($imageType, $track);

tracks

Get the number of tracks in the image:

my $tracks = D64::Disk::Image->tracks($imageType);
my $tracks = $d64DiskImageObj->tracks($imageType);

title

Get the disk title and id in the BAM:

my ($title, $id) = $d64DiskImageObj->title();

track_blocks_free

Get the number of free sectors in a given track:

my $track_blocks_free = $d64DiskImageObj->track_blocks_free($track);

is_ts_free

Get non-zero if the given track and sector is free, and zero if it's allocated:

my $is_ts_free = $d64DiskImageObj->is_ts_free($track, $sector);

alloc_ts

Allocate a given track and sector:

$d64DiskImageObj->alloc_ts($track, $sector);

free_ts

Free a given track and sector:

$d64DiskImageObj->free_ts($track, $sector);

rawname_from_name

Convert a NULL-terminated string to 16-byte 0xA0 padding:

my $rawname = D64::Disk::Image->rawname_from_name($name);
my $rawname = $d64DiskImageObj->rawname_from_name($name);

name_from_rawname

Converts a 0xA0 padded string to a NULL-terminated string:

my $name = D64::Disk::Image->name_from_rawname($rawname);
my $name = $d64DiskImageObj->name_from_rawname($rawname);

blocksfree

Get number of blocks free:

my $blocksFree = $d64DiskImageObj->blocksfree();

type

Get image type:

my $imageType = $d64DiskImageObj->type();

ascii_to_petscii

Convert an ASCII string to a PETSCII string:

my $petscii_string = D64::Disk::Image->ascii_to_petscii($ascii_string);
my $petscii_string = $d64DiskImageObj->ascii_to_petscii($ascii_string);

petscii_to_ascii

Convert a PETSCII string to an ASCII string:

my $ascii_string = D64::Disk::Image->petscii_to_ascii($petscii_string);
my $ascii_string = $d64DiskImageObj->petscii_to_ascii($petscii_string);

EXAMPLES

Print out the BAM:

# Load image into RAM:
my $d64 = D64::Disk::Image->load_image('image.d64');

# Get image type:
my $imageType = $d64->type();

# Print BAM:
print "TRK  FREE  MAP\n";
for (my $track = 1; $track <= $d64->tracks($imageType); $track++) {
  my $sectors = $d64->sectors_per_track($imageType, $track);
  printf "%3d: %2d/%d ", $track, $d64->track_blocks_free($track), $sectors;
  for (my $sector = 0; $sector < $sectors; $sector++) {
    printf "%d", $d64->is_ts_free($track, $sector);
  }
  print "\n";
}
print "\n";

# Print number of blocks free:
my $blocksFree = $d64->blocksfree();
printf "%d blocks free\n", $blocksFree;

# Release image:
$d64->free_image();

List the directory:

my @file_types = qw/del seq prg usr rel cbm dir ???/;

# Load image into RAM:
my $d64 = D64::Disk::Image->load_image('image.d64');

# Open directory for reading:
my $dir = $d64->open('$', T_PRG, F_READ);

# Convert title to ASCII:
my ($title, $id) = $d64->title();
$title = $d64->name_from_rawname($title);
$title = $d64->petscii_to_ascii($title);

# Convert ID to ASCII:
$id = $d64->name_from_rawname($id);
$id = $d64->petscii_to_ascii($id);

# Print title and disk ID:
printf "0 \"%-16s\" %s\n", $title, $id;

# Read first block into buffer:
my ($counter, $buffer) = $dir->read(254);
die 'BAM read failed' if $counter != 254;

# Read directory blocks:
while (1) {
  my ($counter, $buffer) = $dir->read(254);
  last unless $counter == 254;

  for (my $offset = -2; $offset < 254; $offset += 32) {

    # If file type != 0:
    my $file_type = ord (substr $buffer, $offset + 2, 1);
    if ($file_type != 0) {

      my $rawname = substr $buffer, $offset + 5;
      my $name = $d64->name_from_rawname($rawname);
      my $type = $file_type & 7;
      my $closed = $file_type & 0x80;
      my $locked = $file_type & 0x40;
      my $size = ord (substr $buffer, $offset + 31, 1) << 8 | ord (substr $buffer, $offset + 30, 1);

      # Convert to ASCII and add quotes:
      $name = $d64->petscii_to_ascii($name);
      my $quotename = sprintf "\"%s\"", $name;

      # Print directory entry:
      printf "%-4d  %-18s%c%s%c\n", $size, $quotename, $closed ? ord ' ' : ord '*', $file_types[$type], $locked ? ord '<' : ord ' ';
    }
  }
}

# Print number of blocks free:
my $blocksFree = $d64->blocksfree();
printf "%d blocks free\n", $blocksFree;

# Close directory:
$dir->close();

# Release image:
$d64->free_image();

Copy a file from a disk image:

# Load image into RAM:
my $d64 = D64::Disk::Image->load_image('image.d64');

# Convert filename:
my $name = 'filename';
my $rawname = $d64->rawname_from_name($d64->ascii_to_petscii($name));

# Open file for reading:
my $prg = $d64->open($rawname, T_PRG, F_READ);

# Open file for writing:
die "$name file already exists" if -e $name;
open PRG, '>:bytes', $name or die "Couldn't open $name file for writing";

# Read data from file:
my ($size, $buffer) = $prg->read();
print PRG $buffer;
printf "Read %d bytes from %s\n", $size, $disk;

# Close files:
close PRG;
$prg->close();

# Release image:
$d64->free_image();

Copy a file to a disk image:

# Load image into RAM:
my $d64 = D64::Disk::Image->load_image('image.d64');

# Convert filename:
my $name = 'filename';
my $rawname = $d64->rawname_from_name($d64->ascii_to_petscii($name));

# Open file for writing:
my $prg = $d64->open($rawname, T_PRG, F_WRITE);

# Open file for reading:
die "$name file does not exist" unless -e $name;
open PRG, '<:bytes', $name or die "Couldn't open $name file for reading";

# Write data to file:
my $buffer;
my $filesize = (stat($name))[7];
sysread PRG, $buffer, $filesize;
my $size = $prg->write($buffer);
printf "Wrote %d bytes to %s\n", $size, $disk_3;

# Close files:
close PRG;
$prg->close();

# Release image:
$d64->free_image();

Create an empty disk image:

# Create an empty image:
my $d64 = D64::Disk::Image->create_image('image.d64', D64);

# Convert title:
my $name = 'title';
my $rawname = $d64->rawname_from_name($d64->ascii_to_petscii($name));

# Convert ID:
my $id = 'id';
my $rawid = $d64->rawname_from_name($d64->ascii_to_petscii($id));

# Format the image:
$d64->format($rawname, $rawid);

# Release image:
$d64->free_image();

BUGS

There are no known bugs at the moment. Please report any bugs or feature requests.

EXPORT

D64::Disk::Image exports nothing by default.

You may request the import of image type constants (D64, D71, and D81), and file type constants (T_DEL, T_SEQ, T_PRG, T_USR, T_REL, T_CBM, and T_DIR). All of these constants can be explicitly imported from D64::Disk::Image by using it with ":types" tag. You may also request the import of open mode constants (F_READ, and F_WRITE). Both these constants can be explicitly imported from D64::Disk::Image by using it with ":modes" tag. All constants can be explicitly imported from D64::Disk::Image by using it with ":all" tag.

SEE ALSO

D64::Disk::Image::File

AUTHOR

Pawel Krol, <pawelkrol@cpan.org>.

VERSION

Version 0.05 (2018-12-01)

COPYRIGHT AND LICENSE

diskimage.c is released under a slightly modified BSD license.

Copyright (c) 2003-2006, Per Olofsson All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

diskimage.c website: https://paradroid.automac.se/diskimage/