mirror of
https://github.com/zellyn/diskii.git
synced 2025-02-20 05:28:56 +00:00
prodos: basic catalog now works
This commit is contained in:
parent
c244ec5257
commit
05fdbcd0ba
@ -46,7 +46,7 @@ Current disk operations supported:
|
|||||||
| Feature | DOS 3.3 | ProDOS | NakedOS/Super-Mon |
|
| Feature | DOS 3.3 | ProDOS | NakedOS/Super-Mon |
|
||||||
| ---------------- | -------- | ------ | ------------------ |
|
| ---------------- | -------- | ------ | ------------------ |
|
||||||
| basic structures | ✓ | ✓ | ✓ |
|
| basic structures | ✓ | ✓ | ✓ |
|
||||||
| ls | ✓ | ✗ | ✓ |
|
| ls | ✓ | ✓ | ✓ |
|
||||||
| dump | ✓ | ✗ | ✓ |
|
| dump | ✓ | ✗ | ✓ |
|
||||||
| put | ✗ | ✗ | ✓ |
|
| put | ✗ | ✗ | ✓ |
|
||||||
| dumptext | ✗ | ✗ | ✗ |
|
| dumptext | ✗ | ✗ | ✗ |
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
// Copyright © 2017 Zellyn Hunter <zellyn@gmail.com>
|
// Copyright © 2017 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
// Package prodos contains routines for working with the on-disk
|
// Package prodos contains routines for working with the on-device
|
||||||
// structures of Apple ProDOS.
|
// structures of Apple ProDOS.
|
||||||
package prodos
|
package prodos
|
||||||
|
|
||||||
@ -12,6 +12,17 @@ import (
|
|||||||
"github.com/zellyn/diskii/lib/disk"
|
"github.com/zellyn/diskii/lib/disk"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Storage types.
|
||||||
|
const (
|
||||||
|
TypeDeleted = 0
|
||||||
|
TypeSeedling = 0x1
|
||||||
|
TypeSapling = 0x2
|
||||||
|
TypeTree = 0x3
|
||||||
|
TypeSubdirectory = 0xD
|
||||||
|
TypeSubdirectoryHeader = 0xE
|
||||||
|
TypeVolumeDirectoryHeader = 0xF
|
||||||
|
)
|
||||||
|
|
||||||
// blockBase represents a 512-byte block of data.
|
// blockBase represents a 512-byte block of data.
|
||||||
type blockBase struct {
|
type blockBase struct {
|
||||||
block uint16 // Block index this data was loaded from.
|
block uint16 // Block index this data was loaded from.
|
||||||
@ -91,9 +102,9 @@ func (vbm VolumeBitMap) IsFree(block uint16) bool {
|
|||||||
return vbm[blockIndex].data[blockByteIndex]&bit > 0
|
return vbm[blockIndex].data[blockByteIndex]&bit > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadVolumeBitMap reads the entire volume bitmap from a block
|
// readVolumeBitMap reads the entire volume bitmap from a block
|
||||||
// device.
|
// device.
|
||||||
func ReadVolumeBitMap(bd disk.BlockDevice, startBlock uint16) (VolumeBitMap, error) {
|
func readVolumeBitMap(bd disk.BlockDevice, startBlock uint16) (VolumeBitMap, error) {
|
||||||
blocks := bd.Blocks() / 4096
|
blocks := bd.Blocks() / 4096
|
||||||
vbm := NewVolumeBitMap(startBlock, blocks)
|
vbm := NewVolumeBitMap(startBlock, blocks)
|
||||||
for i := 0; i < len(vbm); i++ {
|
for i := 0; i < len(vbm); i++ {
|
||||||
@ -357,6 +368,11 @@ func (fd FileDescriptor) Name() string {
|
|||||||
return string(fd.FileName[0 : fd.TypeAndNameLength&0xf])
|
return string(fd.FileName[0 : fd.TypeAndNameLength&0xf])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Type returns the type of a file descriptor.
|
||||||
|
func (fd FileDescriptor) Type() byte {
|
||||||
|
return fd.TypeAndNameLength >> 4
|
||||||
|
}
|
||||||
|
|
||||||
// toBytes converts a FileDescriptor to a slice of bytes.
|
// toBytes converts a FileDescriptor to a slice of bytes.
|
||||||
func (fd FileDescriptor) toBytes() []byte {
|
func (fd FileDescriptor) toBytes() []byte {
|
||||||
buf := make([]byte, 0x27)
|
buf := make([]byte, 0x27)
|
||||||
@ -598,7 +614,8 @@ type Volume struct {
|
|||||||
keyBlock *VolumeDirectoryKeyBlock
|
keyBlock *VolumeDirectoryKeyBlock
|
||||||
blocks []*VolumeDirectoryBlock
|
blocks []*VolumeDirectoryBlock
|
||||||
bitmap *VolumeBitMap
|
bitmap *VolumeBitMap
|
||||||
subdirs map[uint16]*Subdirectory
|
subdirsByBlock map[uint16]*Subdirectory
|
||||||
|
subdirsByName map[string]*Subdirectory
|
||||||
}
|
}
|
||||||
|
|
||||||
// Subdirectory is the in-memory representation of a single
|
// Subdirectory is the in-memory representation of a single
|
||||||
@ -608,19 +625,45 @@ type Subdirectory struct {
|
|||||||
blocks []*SubdirectoryBlock
|
blocks []*SubdirectoryBlock
|
||||||
}
|
}
|
||||||
|
|
||||||
// readVolume reads the entire volume and subdirectories frmo a disk
|
// descriptors returns a slice of all top-level file descriptors in a
|
||||||
|
// volume, deleted or not.
|
||||||
|
func (v Volume) descriptors() []FileDescriptor {
|
||||||
|
var descs []FileDescriptor
|
||||||
|
|
||||||
|
descs = append(descs, v.keyBlock.Descriptors[:]...)
|
||||||
|
for _, block := range v.blocks {
|
||||||
|
descs = append(descs, block.Descriptors[:]...)
|
||||||
|
}
|
||||||
|
return descs
|
||||||
|
}
|
||||||
|
|
||||||
|
// subdirDescriptors returns a slice of all top-level file descriptors
|
||||||
|
// in a volume that are subdirectories.
|
||||||
|
func (v Volume) subdirDescriptors() []FileDescriptor {
|
||||||
|
var descs []FileDescriptor
|
||||||
|
|
||||||
|
for _, desc := range v.descriptors() {
|
||||||
|
if desc.Type() == TypeSubdirectory {
|
||||||
|
descs = append(descs, desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descs
|
||||||
|
}
|
||||||
|
|
||||||
|
// readVolume reads the entire volume and subdirectories from a device
|
||||||
// into memory.
|
// into memory.
|
||||||
func readVolume(bd disk.BlockDevice) (Volume, error) {
|
func readVolume(bd disk.BlockDevice, keyBlock uint16) (Volume, error) {
|
||||||
v := Volume{
|
v := Volume{
|
||||||
keyBlock: &VolumeDirectoryKeyBlock{},
|
keyBlock: &VolumeDirectoryKeyBlock{},
|
||||||
subdirs: make(map[uint16]*Subdirectory),
|
subdirsByBlock: make(map[uint16]*Subdirectory),
|
||||||
|
subdirsByName: make(map[string]*Subdirectory),
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := disk.UnmarshalBlock(bd, v.keyBlock, 2); err != nil {
|
if err := disk.UnmarshalBlock(bd, v.keyBlock, keyBlock); err != nil {
|
||||||
return v, fmt.Errorf("cannot read first block of volume directory (block 2): %v", err)
|
return v, fmt.Errorf("cannot read first block of volume directory (block %d): %v", keyBlock, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if vbm, err := ReadVolumeBitMap(bd, v.keyBlock.Header.BitMapPointer); err != nil {
|
if vbm, err := readVolumeBitMap(bd, v.keyBlock.Header.BitMapPointer); err != nil {
|
||||||
return v, err
|
return v, err
|
||||||
} else {
|
} else {
|
||||||
v.bitmap = &vbm
|
v.bitmap = &vbm
|
||||||
@ -634,9 +677,100 @@ func readVolume(bd disk.BlockDevice) (Volume, error) {
|
|||||||
v.blocks = append(v.blocks, &vdb)
|
v.blocks = append(v.blocks, &vdb)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sdds := v.subdirDescriptors()
|
||||||
|
|
||||||
|
for i := 0; i < len(sdds); i++ {
|
||||||
|
sdd := sdds[i]
|
||||||
|
sub, err := readSubdirectory(bd, sdd)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
v.subdirsByBlock[sdd.KeyPointer] = &sub
|
||||||
|
sdds = append(sdds, sub.subdirDescriptors()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sd := range v.subdirsByBlock {
|
||||||
|
name := sd.keyBlock.Header.Name()
|
||||||
|
parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, v.subdirsByBlock)
|
||||||
|
if err != nil {
|
||||||
|
return v, err
|
||||||
|
}
|
||||||
|
if parentName != "" {
|
||||||
|
name = parentName + "/" + name
|
||||||
|
}
|
||||||
|
|
||||||
|
v.subdirsByName[name] = sd
|
||||||
|
}
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// descriptors returns a slice of all top-level file descriptors in a
|
||||||
|
// subdirectory, deleted or not.
|
||||||
|
func (s Subdirectory) descriptors() []FileDescriptor {
|
||||||
|
var descs []FileDescriptor
|
||||||
|
|
||||||
|
descs = append(descs, s.keyBlock.Descriptors[:]...)
|
||||||
|
for _, block := range s.blocks {
|
||||||
|
descs = append(descs, block.Descriptors[:]...)
|
||||||
|
}
|
||||||
|
return descs
|
||||||
|
}
|
||||||
|
|
||||||
|
// subdirDescriptors returns a slice of all top-level file descriptors
|
||||||
|
// in a subdirectory that are subdirectories.
|
||||||
|
func (s Subdirectory) subdirDescriptors() []FileDescriptor {
|
||||||
|
var descs []FileDescriptor
|
||||||
|
|
||||||
|
for _, desc := range s.descriptors() {
|
||||||
|
if desc.Type() == TypeSubdirectory {
|
||||||
|
descs = append(descs, desc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return descs
|
||||||
|
}
|
||||||
|
|
||||||
|
// fullDirName returns the full recursive directory name of the given parent directory.
|
||||||
|
func parentDirName(parentDirectoryBlock uint16, keyBlock uint16, subdirMap map[uint16]*Subdirectory) (string, error) {
|
||||||
|
if parentDirectoryBlock == keyBlock {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
sd := subdirMap[parentDirectoryBlock]
|
||||||
|
if sd == nil {
|
||||||
|
return "", fmt.Errorf("Unable to find subdirectory for block %d", parentDirectoryBlock)
|
||||||
|
}
|
||||||
|
parentName, err := parentDirName(sd.keyBlock.Header.ParentPointer, keyBlock, subdirMap)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if parentName == "" {
|
||||||
|
return sd.keyBlock.Header.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return parentName + "/" + sd.keyBlock.Header.Name(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// readSubdirectory reads a single subdirectory from a device into
|
||||||
|
// memory.
|
||||||
|
func readSubdirectory(bd disk.BlockDevice, fd FileDescriptor) (Subdirectory, error) {
|
||||||
|
s := Subdirectory{
|
||||||
|
keyBlock: &SubdirectoryKeyBlock{},
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := disk.UnmarshalBlock(bd, s.keyBlock, fd.KeyPointer); err != nil {
|
||||||
|
return s, fmt.Errorf("cannot read first block of subdirectory %q (block %d): %v", fd.Name(), fd.KeyPointer, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for block := s.keyBlock.Next; block != 0; block = s.blocks[len(s.blocks)-1].Next {
|
||||||
|
sdb := SubdirectoryBlock{}
|
||||||
|
if err := disk.UnmarshalBlock(bd, &sdb, block); err != nil {
|
||||||
|
return s, err
|
||||||
|
}
|
||||||
|
s.blocks = append(s.blocks, &sdb)
|
||||||
|
}
|
||||||
|
|
||||||
|
return s, nil
|
||||||
|
}
|
||||||
|
|
||||||
// copyBytes is just like the builtin copy, but just for byte slices,
|
// copyBytes is just like the builtin copy, but just for byte slices,
|
||||||
// and it checks that dst and src have the same length.
|
// and it checks that dst and src have the same length.
|
||||||
func copyBytes(dst, src []byte) int {
|
func copyBytes(dst, src []byte) int {
|
||||||
@ -672,18 +806,34 @@ func (o operator) HasSubdirs() bool {
|
|||||||
// Catalog returns a catalog of disk entries. subdir should be empty
|
// Catalog returns a catalog of disk entries. subdir should be empty
|
||||||
// for operating systems that do not support subdirectories.
|
// for operating systems that do not support subdirectories.
|
||||||
func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
|
func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
|
||||||
return nil, fmt.Errorf("%s doesn't implement Catalog yet", operatorName)
|
|
||||||
/*
|
vol, err := readVolume(o.dev, 2)
|
||||||
fds, _, err := ReadCatalog(o.dev)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
descs := make([]disk.Descriptor, 0, len(fds))
|
|
||||||
for _, fd := range fds {
|
var result []disk.Descriptor
|
||||||
descs = append(descs, fd.descriptor())
|
|
||||||
|
if subdir == "" {
|
||||||
|
for _, desc := range vol.descriptors() {
|
||||||
|
if desc.Type() != TypeDeleted {
|
||||||
|
result = append(result, desc.descriptor())
|
||||||
}
|
}
|
||||||
return descs, nil
|
}
|
||||||
*/
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
sd, ok := vol.subdirsByName[subdir]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("subdirectory %q not found", subdir)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, desc := range sd.descriptors() {
|
||||||
|
if desc.Type() != TypeDeleted {
|
||||||
|
result = append(result, desc.descriptor())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetFile retrieves a file by name.
|
// GetFile retrieves a file by name.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user