mirror of
https://github.com/zellyn/diskii.git
synced 2025-02-19 13:30:48 +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)
|
||||||
|
}
|
120
lib/disk/disk.go
120
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)
|
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.
|
// Open opens a disk image by filename, returning an Operator.
|
||||||
func Open(filename string) (Operator, error) {
|
func Open(filename string) (Operator, error) {
|
||||||
sd, err := OpenDisk(filename)
|
sd, err := OpenDisk(filename)
|
||||||
if err != nil {
|
if err == nil {
|
||||||
return nil, err
|
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)
|
sort.Strings(names)
|
||||||
return nil, fmt.Errorf("Cannot find a disk operator matching the given disk image (tried %s)", strings.Join(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) {
|
func (o operator) Write(w io.Writer) (int, error) {
|
||||||
return o.dev.Write(w)
|
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)
|
||||||
|
}
|
||||||
|
1
main.go
1
main.go
@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
// Import disk operator factories for DOS3 and Super-Mon
|
// Import disk operator factories for DOS3 and Super-Mon
|
||||||
_ "github.com/zellyn/diskii/lib/dos3"
|
_ "github.com/zellyn/diskii/lib/dos3"
|
||||||
|
_ "github.com/zellyn/diskii/lib/prodos"
|
||||||
_ "github.com/zellyn/diskii/lib/supermon"
|
_ "github.com/zellyn/diskii/lib/supermon"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user