mirror of
https://github.com/zellyn/diskii.git
synced 2024-05-28 18:41:27 +00:00
Initial implementation of NakeOS/Super-Mon GetFile
This commit is contained in:
parent
d901a9a0ba
commit
2cf6d2d4a3
|
@ -35,7 +35,8 @@ Current disk operations supported:
|
||||||
|
|
||||||
| Feature | DOS 3.3 | NakedOS/Super-Mon |
|
| Feature | DOS 3.3 | NakedOS/Super-Mon |
|
||||||
| ------------- | ------------------ | ------------------ |
|
| ------------- | ------------------ | ------------------ |
|
||||||
| catalog | :white_check_mark: | :white_check_mark: |
|
| ls | :white_check_mark: | :white_check_mark: |
|
||||||
|
| dump | :x: | :white_check_mark: |
|
||||||
|
|
||||||
### Installing/updating
|
### Installing/updating
|
||||||
Assuming you have Go installed, run `go get -u github.com/zellyn/diskii`
|
Assuming you have Go installed, run `go get -u github.com/zellyn/diskii`
|
||||||
|
|
|
@ -12,12 +12,13 @@ import (
|
||||||
_ "github.com/zellyn/diskii/lib/supermon"
|
_ "github.com/zellyn/diskii/lib/supermon"
|
||||||
)
|
)
|
||||||
|
|
||||||
// catCmd represents the cat command, used to catalog a disk or
|
// catalogCmd represents the cat command, used to catalog a disk or
|
||||||
// directory.
|
// directory.
|
||||||
var catCmd = &cobra.Command{
|
var catalogCmd = &cobra.Command{
|
||||||
Use: "cat",
|
Use: "catalog",
|
||||||
Short: "print a list of files",
|
Aliases: []string{"cat", "ls"},
|
||||||
Long: `Catalog a disk or subdirectory.`,
|
Short: "print a list of files",
|
||||||
|
Long: `Catalog a disk or subdirectory.`,
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
if err := runCat(args); err != nil {
|
if err := runCat(args); err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err.Error())
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
@ -27,7 +28,7 @@ var catCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RootCmd.AddCommand(catCmd)
|
RootCmd.AddCommand(catalogCmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
// runCat performs the actual catalog logic.
|
// runCat performs the actual catalog logic.
|
56
cmd/dump.go
Normal file
56
cmd/dump.go
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
// Copyright © 2016 Zellyn Hunter <zellyn@gmail.com>
|
||||||
|
|
||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/zellyn/diskii/lib/disk"
|
||||||
|
_ "github.com/zellyn/diskii/lib/dos33"
|
||||||
|
_ "github.com/zellyn/diskii/lib/supermon"
|
||||||
|
)
|
||||||
|
|
||||||
|
// dumpCmd represents the dump command, used to dump the raw contents
|
||||||
|
// of a file.
|
||||||
|
var dumpCmd = &cobra.Command{
|
||||||
|
Use: "dump",
|
||||||
|
Short: "dump the raw contents of a file",
|
||||||
|
Long: `Dump the raw contents of a file.
|
||||||
|
|
||||||
|
dump disk-image.dsk HELLO
|
||||||
|
`,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := runDump(args); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err.Error())
|
||||||
|
os.Exit(-1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
RootCmd.AddCommand(dumpCmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
// runDump performs the actual dump logic.
|
||||||
|
func runDump(args []string) error {
|
||||||
|
if len(args) != 2 {
|
||||||
|
return fmt.Errorf("dump expects a disk image filename, and a filename")
|
||||||
|
}
|
||||||
|
sd, err := disk.Open(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
op, err := disk.OperatorFor(sd)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := op.GetFile(args[1])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// TODO(zellyn): allow writing to files
|
||||||
|
os.Stdout.Write(file.Data)
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -31,6 +31,14 @@ type Operator interface {
|
||||||
// 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.
|
||||||
Catalog(subdir string) ([]Descriptor, error)
|
Catalog(subdir string) ([]Descriptor, error)
|
||||||
|
// GetFile retrieves a file by name.
|
||||||
|
GetFile(filename string) (FileInfo, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileInfo represents a file descriptor plus the content.
|
||||||
|
type FileInfo struct {
|
||||||
|
Descriptor Descriptor
|
||||||
|
Data []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// operatorFactory is the type of functions that accept a SectorDisk,
|
// operatorFactory is the type of functions that accept a SectorDisk,
|
||||||
|
|
|
@ -497,6 +497,12 @@ func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
|
||||||
return descs, nil
|
return descs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFile retrieves a file by name.
|
||||||
|
func (o operator) GetFile(filename string) (disk.FileInfo, error) {
|
||||||
|
// TODO(zellyn): Implement GetFile
|
||||||
|
return disk.FileInfo{}, fmt.Errorf("%s does not yet implement `GetFile`", operatorName)
|
||||||
|
}
|
||||||
|
|
||||||
// operatorFactory is the factory that returns dos33 operators given
|
// operatorFactory is the factory that returns dos33 operators given
|
||||||
// disk images.
|
// disk images.
|
||||||
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||||
|
|
|
@ -8,6 +8,7 @@ package supermon
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/zellyn/diskii/lib/disk"
|
"github.com/zellyn/diskii/lib/disk"
|
||||||
|
@ -67,13 +68,14 @@ func (sm SectorMap) Verify() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FileForSector returns the file that owns the given track/sector.
|
// FileForSector returns the file that owns the given track/sector, or
|
||||||
|
// zero if the track or sector is too high.
|
||||||
func (sm SectorMap) FileForSector(track, sector byte) byte {
|
func (sm SectorMap) FileForSector(track, sector byte) byte {
|
||||||
if track >= 35 {
|
if track >= 35 {
|
||||||
panic(fmt.Sprintf("FileForSector called with track=%d > 34", track))
|
return FileIllegal
|
||||||
}
|
}
|
||||||
if sector >= 16 {
|
if sector >= 16 {
|
||||||
panic(fmt.Sprintf("FileForSector called with sector=%d > 15", sector))
|
return FileIllegal
|
||||||
}
|
}
|
||||||
return sm[int(track)*16+int(sector)]
|
return sm[int(track)*16+int(sector)]
|
||||||
}
|
}
|
||||||
|
@ -105,16 +107,16 @@ func (sm SectorMap) SectorsByFile() map[byte][]disk.TrackSector {
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadFile reads the contents of a file.
|
// ReadFile reads the contents of a file.
|
||||||
func (sm SectorMap) ReadFile(sd disk.SectorDisk, file byte) []byte {
|
func (sm SectorMap) ReadFile(sd disk.SectorDisk, file byte) ([]byte, error) {
|
||||||
var result []byte
|
var result []byte
|
||||||
for _, ts := range sm.SectorsForFile(file) {
|
for _, ts := range sm.SectorsForFile(file) {
|
||||||
bytes, err := sd.ReadPhysicalSector(ts.Track, ts.Sector)
|
bytes, err := sd.ReadPhysicalSector(ts.Track, ts.Sector)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err.Error())
|
return nil, err
|
||||||
}
|
}
|
||||||
result = append(result, bytes...)
|
result = append(result, bytes...)
|
||||||
}
|
}
|
||||||
return result
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Symbol represents a single Super-Mon symbol.
|
// Symbol represents a single Super-Mon symbol.
|
||||||
|
@ -159,11 +161,17 @@ type SymbolTable []Symbol
|
||||||
// pointers are problematic), it'll return nil and an error.
|
// pointers are problematic), it'll return nil and an error.
|
||||||
func (sm SectorMap) ReadSymbolTable(sd disk.SectorDisk) (SymbolTable, error) {
|
func (sm SectorMap) ReadSymbolTable(sd disk.SectorDisk) (SymbolTable, error) {
|
||||||
table := make(SymbolTable, 0, 819)
|
table := make(SymbolTable, 0, 819)
|
||||||
symtbl1 := sm.ReadFile(sd, 3)
|
symtbl1, err := sm.ReadFile(sd, 3)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if len(symtbl1) != 0x1000 {
|
if len(symtbl1) != 0x1000 {
|
||||||
return nil, fmt.Errorf("expected file FSYMTBL1(0x3) to be 0x1000 bytes long; got 0x%04X", len(symtbl1))
|
return nil, fmt.Errorf("expected file FSYMTBL1(0x3) to be 0x1000 bytes long; got 0x%04X", len(symtbl1))
|
||||||
}
|
}
|
||||||
symtbl2 := sm.ReadFile(sd, 4)
|
symtbl2, err := sm.ReadFile(sd, 4)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
if len(symtbl2) != 0x1000 {
|
if len(symtbl2) != 0x1000 {
|
||||||
return nil, fmt.Errorf("expected file FSYMTBL1(0x4) to be 0x1000 bytes long; got 0x%04X", len(symtbl2))
|
return nil, fmt.Errorf("expected file FSYMTBL1(0x4) to be 0x1000 bytes long; got 0x%04X", len(symtbl2))
|
||||||
}
|
}
|
||||||
|
@ -213,16 +221,35 @@ func (st SymbolTable) SymbolsByAddress() map[uint16][]Symbol {
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func FilenameString(file byte, symbols []Symbol) string {
|
// NameForFile returns a string representation of a filename:
|
||||||
|
// either DFxx, or a symbol, if one exists for that value.
|
||||||
|
func NameForFile(file byte, symbols []Symbol) string {
|
||||||
if len(symbols) > 0 {
|
if len(symbols) > 0 {
|
||||||
for _, symbol := range symbols {
|
|
||||||
if strings.HasPrefix(symbol.Name, "F") {
|
|
||||||
return symbol.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return symbols[0].Name
|
return symbols[0].Name
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("%02X", file)
|
return fmt.Sprintf("DF%02X", file)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FileForName returns a byte file number for a representation of a
|
||||||
|
// filename: either DFxx, or a symbol, if one exists with the given
|
||||||
|
// name and points to a DFxx address.
|
||||||
|
func (st SymbolTable) FileForName(filename string) (byte, error) {
|
||||||
|
if addr, err := strconv.ParseUint(filename, 16, 16); err == nil {
|
||||||
|
if addr > 0xDF00 && addr < 0xDFFE {
|
||||||
|
return byte(addr - 0xDF00), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, symbol := range st {
|
||||||
|
if strings.EqualFold(symbol.Name, filename) {
|
||||||
|
if symbol.Address > 0xDF00 && symbol.Address < 0xDFFE {
|
||||||
|
return byte(symbol.Address - 0xDF00), nil
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, fmt.Errorf("invalid filename: %q", filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
// operator is a disk.Operator - an interface for performing
|
// operator is a disk.Operator - an interface for performing
|
||||||
|
@ -261,9 +288,9 @@ func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
|
||||||
if l == 0 {
|
if l == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
fileAddr := uint16(0xDF00) + uint16(file)
|
fileAddr := 0xDF00 + uint16(file)
|
||||||
descs = append(descs, disk.Descriptor{
|
descs = append(descs, disk.Descriptor{
|
||||||
Name: FilenameString(file, o.symbols[fileAddr]),
|
Name: NameForFile(file, o.symbols[fileAddr]),
|
||||||
Sectors: l,
|
Sectors: l,
|
||||||
Length: l * 256,
|
Length: l * 256,
|
||||||
Locked: false,
|
Locked: false,
|
||||||
|
@ -272,6 +299,31 @@ func (o operator) Catalog(subdir string) ([]disk.Descriptor, error) {
|
||||||
return descs, nil
|
return descs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFile retrieves a file by name.
|
||||||
|
func (o operator) GetFile(filename string) (disk.FileInfo, error) {
|
||||||
|
file, err := o.st.FileForName(filename)
|
||||||
|
if err != nil {
|
||||||
|
return disk.FileInfo{}, err
|
||||||
|
}
|
||||||
|
data, err := o.sm.ReadFile(o.sd, file)
|
||||||
|
if err != nil {
|
||||||
|
return disk.FileInfo{}, fmt.Errorf("error reading file DF%02x: %v", file, err)
|
||||||
|
}
|
||||||
|
if len(data) == 0 {
|
||||||
|
return disk.FileInfo{}, fmt.Errorf("file DF%02x not fount", file)
|
||||||
|
}
|
||||||
|
desc := disk.Descriptor{
|
||||||
|
Name: NameForFile(file, o.symbols[0xDF00+uint16(file)]),
|
||||||
|
Sectors: len(data) / 256,
|
||||||
|
Length: len(data),
|
||||||
|
Locked: false,
|
||||||
|
}
|
||||||
|
return disk.FileInfo{
|
||||||
|
Descriptor: desc,
|
||||||
|
Data: data,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
// operatorFactory is the factory that returns dos33 operators given
|
// operatorFactory is the factory that returns dos33 operators given
|
||||||
// disk images.
|
// disk images.
|
||||||
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
func operatorFactory(sd disk.SectorDisk) (disk.Operator, error) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user