package storage import ( "errors" "fmt" "os" ) /* Valid for ProDOS disks with 512 bytes blocks. Can be diskettes or hard disks */ const ( // ProDosBlockSize is the size of the blocks on the ProDOS devices ProDosBlockSize = uint32(512) proDosMaxBlocks = uint32(65536) ) // BlockDisk is any block device with 512 bytes blocks type BlockDisk struct { file *os.File readOnly bool dataOffset uint32 blocks uint32 } // OpenBlockDisk creates a new block device links to a file func OpenBlockDisk(filename string) (*BlockDisk, error) { var bd BlockDisk bd.readOnly = false file, err := os.OpenFile(filename, os.O_RDWR, 0) if os.IsPermission(err) { // Retry in read-only mode bd.readOnly = true file, err = os.OpenFile(filename, os.O_RDONLY, 0) } if err != nil { return nil, err } bd.file = file err2mg := parse2mg(&bd) if err2mg == nil { // It's a 2mg file, ready to use return &bd, nil } // Let's try to load as raw ProDOS Blocks fileInfo, err := bd.file.Stat() if err != nil { return nil, err } if fileInfo.Size() > int64(ProDosBlockSize*proDosMaxBlocks) { return nil, fmt.Errorf("File is too big OR %s", err2mg.Error()) } size := uint32(fileInfo.Size()) if size%ProDosBlockSize != 0 { return nil, fmt.Errorf("File size os invalid OR %s", err2mg.Error()) } // It's a valid raw file bd.blocks = size / ProDosBlockSize bd.dataOffset = 0 return &bd, nil } // GetSizeInBlocks returns the number of blocks of the device func (bd *BlockDisk) GetSizeInBlocks() uint32 { return bd.blocks } // IsReadOnly returns true if the device is read only func (bd *BlockDisk) IsReadOnly() bool { return bd.readOnly } func (bd *BlockDisk) Read(block uint32) ([]uint8, error) { if block >= bd.blocks { return nil, errors.New("disk block number is too big") } buf := make([]uint8, ProDosBlockSize) offset := int64(bd.dataOffset + block*ProDosBlockSize) _, err := bd.file.ReadAt(buf, offset) if err != nil { return nil, err } return buf, nil } func (bd *BlockDisk) Write(block uint32, data []uint8) error { if bd.readOnly { return errors.New("can't write in a readonly disk") } if block >= bd.blocks { return errors.New("disk block number is too big") } offset := int64(bd.dataOffset + block*ProDosBlockSize) _, err := bd.file.WriteAt(data, offset) if err != nil { return err } return nil }