Files
izapple2/storage/blockDiskOverlay.go
Iván Izaguirre 69da610b5a Overlays: no handle kept open, and no work without one
Three things from the review.

The overlay kept the file open from the first write, so it had an open with
no close. It does not any more: read and applyTo open the file and close it,
and openForWriting hands the caller a file to close where it writes.
applyTo opens once for all the units instead of once per unit, and returns
before opening anything when nothing has been saved yet.

Identifying the image, which means reading all of it, happened whenever a
DSK was loaded, even with no overlay in use, which is the usual case. Now
the caller checks for a filename first. openOverlay no longer takes an
empty one, both callers check before asking for it.

blockDiskOverlay moves to its own file.
2026-08-07 08:53:22 +02:00

82 lines
2.1 KiB
Go

package storage
import (
"errors"
"hash/crc64"
)
/*
blockDiskOverlay sends the blocks written to an overlay, leaving the image
untouched, and reads back from it the ones already saved. The blocks are taken
one at a time as ProDOS asks for them, so a 32 Mb hard disk costs nothing to
open however few blocks a game saves.
*/
type blockDiskOverlay struct {
base BlockDisk
overlay *overlay
}
// NewBlockDiskOverlay wraps a block device so that the writes go to the
// overlay kept in overlayFilename instead of to the image. It also makes
// writable a device that was not, as the image is only read. An empty
// overlayFilename returns the device untouched.
func NewBlockDiskOverlay(base BlockDisk, overlayFilename string) (BlockDisk, error) {
if overlayFilename == "" {
return base, nil
}
checksum, err := checksumOfBlockDisk(base)
if err != nil {
return nil, err
}
o, err := openOverlay(overlayFilename, int(ProDosBlockSize),
int(base.GetSizeInBlocks()), checksum)
if err != nil {
return nil, err
}
return &blockDiskOverlay{base: base, overlay: o}, nil
}
// checksumOfBlockDisk identifies the image an overlay was made from. It reads
// the whole device, which for a 32 Mb hard disk is the price of opening it
// once with the overlays in use.
func checksumOfBlockDisk(base BlockDisk) (uint64, error) {
hash := crc64.New(overlayChecksumTable)
for block := range base.GetSizeInBlocks() {
data, err := base.Read(block)
if err != nil {
return 0, err
}
hash.Write(data)
}
return hash.Sum64(), nil
}
func (bd *blockDiskOverlay) GetSizeInBlocks() uint32 {
return bd.base.GetSizeInBlocks()
}
func (bd *blockDiskOverlay) IsReadOnly() bool {
return false
}
func (bd *blockDiskOverlay) Read(block uint32) ([]uint8, error) {
if !bd.overlay.has(int(block)) {
return bd.base.Read(block)
}
buf := make([]uint8, ProDosBlockSize)
if err := bd.overlay.read(int(block), buf); err != nil {
return nil, err
}
return buf, nil
}
func (bd *blockDiskOverlay) Write(block uint32, data []uint8) error {
if block >= bd.base.GetSizeInBlocks() {
return errors.New("disk block number is too big")
}
return bd.overlay.write(int(block), data)
}