mirror of
https://github.com/zellyn/diskii.git
synced 2024-11-24 12:31:32 +00:00
Add (NOP) prodos factory and DiskBlockDevice
The prodos operator factory functions just return errors for now, until Catalog is implemented.
This commit is contained in:
parent
118944b512
commit
df80529449
67
lib/disk/dev.go
Normal file
67
lib/disk/dev.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright © 2017 Zellyn Hunter <zellyn@gmail.com>
|
||||
|
||||
// dev.go contains logic for reading ".po" disk images.
|
||||
|
||||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
)
|
||||
|
||||
// Dev represents a .po disk image.
|
||||
type Dev struct {
|
||||
data []byte // The actual data in the file
|
||||
blocks uint16 // Number of blocks
|
||||
}
|
||||
|
||||
var _ BlockDevice = (*Dev)(nil)
|
||||
|
||||
// LoadDev loads a .po image from a file.
|
||||
func LoadDev(filename string) (Dev, error) {
|
||||
bb, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return Dev{}, err
|
||||
}
|
||||
if len(bb)%512 != 0 {
|
||||
return Dev{}, fmt.Errorf("expected file %q to contain a multiple of 512 bytes, but got %d", filename, len(bb))
|
||||
}
|
||||
return Dev{
|
||||
data: bb,
|
||||
blocks: uint16(len(bb) / 512),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Empty creates a .po image that is all zeros.
|
||||
func EmptyDev(blocks uint16) Dev {
|
||||
return Dev{
|
||||
data: make([]byte, 512*int(blocks)),
|
||||
blocks: blocks,
|
||||
}
|
||||
}
|
||||
|
||||
// ReadBlock reads a single block from the device. It always returns
|
||||
// 512 byes.
|
||||
func (d Dev) ReadBlock(index uint16) (Block, error) {
|
||||
var b Block
|
||||
copy(b[:], d.data[int(index)*512:int(index+1)*512])
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteBlock writes a single block to a device. It expects exactly
|
||||
// 512 bytes.
|
||||
func (d Dev) WriteBlock(index uint16, data Block) error {
|
||||
copy(d.data[int(index)*512:int(index+1)*512], data[:])
|
||||
return nil
|
||||
}
|
||||
|
||||
// Blocks returns the number of blocks in the device.
|
||||
func (d Dev) Blocks() uint16 {
|
||||
return d.blocks
|
||||
}
|
||||
|
||||
// Write writes the device contents to the given file.
|
||||
func (d Dev) Write(w io.Writer) (n int, err error) {
|
||||
return w.Write(d.data)
|
||||
}
|
122
lib/disk/disk.go
122
lib/disk/disk.go
@ -166,15 +166,125 @@ func OpenDisk(filename string) (SectorDisk, error) {
|
||||
return nil, fmt.Errorf("Unimplemented/unknown disk file extension %q", ext)
|
||||
}
|
||||
|
||||
// OpenDev opens a device image by filename.
|
||||
func OpenDev(filename string) (BlockDevice, error) {
|
||||
ext := strings.ToLower(path.Ext(filename))
|
||||
switch ext {
|
||||
case ".po":
|
||||
return LoadDev(filename)
|
||||
}
|
||||
return nil, fmt.Errorf("Unimplemented/unknown device file extension %q", ext)
|
||||
}
|
||||
|
||||
// Open opens a disk image by filename, returning an Operator.
|
||||
func Open(filename string) (Operator, error) {
|
||||
sd, err := OpenDisk(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
if err == nil {
|
||||
var op Operator
|
||||
op, err = OperatorForDisk(sd)
|
||||
if err == nil {
|
||||
return op, nil
|
||||
}
|
||||
}
|
||||
op, err := OperatorForDisk(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
dev, err2 := OpenDev(filename)
|
||||
if err2 == nil {
|
||||
var op Operator
|
||||
op, err2 = OperatorForDevice(dev)
|
||||
if err2 != nil {
|
||||
return nil, err2
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
return op, nil
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
type DiskBlockDevice struct {
|
||||
lsd LogicalSectorDisk
|
||||
blocks uint16
|
||||
}
|
||||
|
||||
// BlockDeviceFromSectorDisk creates a ProDOS block device from a
|
||||
// SectorDisk. It reads maps ProDOS to physical sectors.
|
||||
func BlockDeviceFromSectorDisk(sd SectorDisk) (BlockDevice, error) {
|
||||
lsd, err := NewMappedDisk(sd, ProDOSLogicalToPhysicalSectorMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return DiskBlockDevice{
|
||||
lsd: lsd,
|
||||
blocks: uint16(lsd.Tracks()) / 2 * uint16(lsd.Sectors()),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// ReadBlock reads a single block from the device. It always returns
|
||||
// 512 byes.
|
||||
func (dbv DiskBlockDevice) ReadBlock(index uint16) (Block, error) {
|
||||
var b Block
|
||||
if index >= dbv.blocks {
|
||||
return b, fmt.Errorf("device has %d blocks; tried to read block %d (index=%d)", dbv.blocks, index+1, index)
|
||||
}
|
||||
i := int(index) * 2
|
||||
sectors := int(dbv.lsd.Sectors())
|
||||
|
||||
track0 := i / sectors
|
||||
sector0 := i % sectors
|
||||
sector1 := sector0 + 1
|
||||
track1 := track0
|
||||
if sector1 == sectors {
|
||||
sector1 = 0
|
||||
track1++
|
||||
}
|
||||
|
||||
b0, err := dbv.lsd.ReadLogicalSector(byte(track0), byte(sector0))
|
||||
if err != nil {
|
||||
return b, fmt.Errorf("error reading first half of block %d (t:%d s:%d): %v", index, track0, sector0, err)
|
||||
}
|
||||
b1, err := dbv.lsd.ReadLogicalSector(byte(track1), byte(sector1))
|
||||
if err != nil {
|
||||
return b, fmt.Errorf("error reading second half of block %d (t:%d s:%d): %v", index, track1, sector1, err)
|
||||
}
|
||||
copy(b[:256], b0)
|
||||
copy(b[256:], b1)
|
||||
return b, nil
|
||||
}
|
||||
|
||||
// WriteBlock writes a single block to a device. It expects exactly
|
||||
// 512 bytes.
|
||||
func (dbv DiskBlockDevice) WriteBlock(index uint16, data Block) error {
|
||||
if index >= dbv.blocks {
|
||||
return fmt.Errorf("device has %d blocks; tried to read block %d (index=%d)", dbv.blocks, index+1, index)
|
||||
}
|
||||
i := int(index) * 2
|
||||
sectors := int(dbv.lsd.Sectors())
|
||||
|
||||
track0 := i / sectors
|
||||
sector0 := i % sectors
|
||||
sector1 := sector0 + 1
|
||||
track1 := track0
|
||||
if sector1 == sectors {
|
||||
sector1 = 0
|
||||
track1++
|
||||
}
|
||||
|
||||
if err := dbv.lsd.WriteLogicalSector(byte(track0), byte(sector0), data[:256]); err != nil {
|
||||
return fmt.Errorf("error writing first half of block %d (t:%d s:%d): %v", index, track0, sector0, err)
|
||||
}
|
||||
if err := dbv.lsd.WriteLogicalSector(byte(track1), byte(sector1), data[256:]); err != nil {
|
||||
return fmt.Errorf("error writing second half of block %d (t:%d s:%d): %v", index, track1, sector1, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Blocks returns the number of blocks on the device.
|
||||
func (dbv DiskBlockDevice) Blocks() uint16 {
|
||||
return dbv.blocks
|
||||
}
|
||||
|
||||
// Write writes the device contents to the given Writer.
|
||||
func (dbv DiskBlockDevice) Write(w io.Writer) (int, error) {
|
||||
return dbv.lsd.Write(w)
|
||||
}
|
||||
|
@ -91,3 +91,41 @@ func OperatorForDisk(sd SectorDisk) (Operator, error) {
|
||||
sort.Strings(names)
|
||||
return nil, fmt.Errorf("Cannot find a disk operator matching the given disk image (tried %s)", strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
// deviceOperatorFactory is the type of functions that accept a BlockDevice,
|
||||
// and may return an Operator interface to operate on it.
|
||||
type deviceOperatorFactory func(BlockDevice) (Operator, error)
|
||||
|
||||
// deviceOperatorFactories is the map of currently-registered device
|
||||
// operator factories.
|
||||
var deviceOperatorFactories map[string]deviceOperatorFactory
|
||||
|
||||
func init() {
|
||||
deviceOperatorFactories = make(map[string]deviceOperatorFactory)
|
||||
}
|
||||
|
||||
// RegisterDeviceOperatorFactory registers a device operator factory with
|
||||
// the given name: a function that accepts a BlockDevice, and may
|
||||
// return an Operator. It doesn't lock deviceOperatorFactories: it is
|
||||
// expected to be called only from package `init` functions.
|
||||
func RegisterDeviceOperatorFactory(name string, factory deviceOperatorFactory) {
|
||||
deviceOperatorFactories[name] = factory
|
||||
}
|
||||
|
||||
// OperatorForDevice returns an Operator for the given BlockDevice, if possible.
|
||||
func OperatorForDevice(sd BlockDevice) (Operator, error) {
|
||||
if len(deviceOperatorFactories) == 0 {
|
||||
return nil, errors.New("Cannot find an operator matching the given device image (none registered)")
|
||||
}
|
||||
for _, factory := range deviceOperatorFactories {
|
||||
if operator, err := factory(sd); err == nil {
|
||||
return operator, nil
|
||||
}
|
||||
}
|
||||
names := make([]string, 0, len(deviceOperatorFactories))
|
||||
for name := range deviceOperatorFactories {
|
||||
names = append(names, `"`+name+`"`)
|
||||
}
|
||||
sort.Strings(names)
|
||||
return nil, fmt.Errorf("Cannot find a device operator matching the given device image (tried %s)", strings.Join(names, ", "))
|
||||
}
|
||||
|
@ -590,3 +590,29 @@ func (o operator) PutFile(fileInfo disk.FileInfo, overwrite bool) (existed bool,
|
||||
func (o operator) Write(w io.Writer) (int, error) {
|
||||
return o.dev.Write(w)
|
||||
}
|
||||
|
||||
// deviceOperatorFactory is the factory that returns prodos operators
|
||||
// given device images.
|
||||
func deviceOperatorFactory(bd disk.BlockDevice) (disk.Operator, error) {
|
||||
op := operator{dev: bd}
|
||||
_, err := op.Catalog("")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Cannot read catalog. Underlying error: %v", err)
|
||||
}
|
||||
return op, nil
|
||||
}
|
||||
|
||||
// diskOperatorFactory is the factory that returns dos3 operators
|
||||
// given disk images.
|
||||
func diskOperatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||
bd, err := disk.BlockDeviceFromSectorDisk(sd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return deviceOperatorFactory(bd)
|
||||
}
|
||||
|
||||
func init() {
|
||||
disk.RegisterDeviceOperatorFactory(operatorName, deviceOperatorFactory)
|
||||
disk.RegisterDiskOperatorFactory(operatorName, diskOperatorFactory)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user