Compare commits

...

2 Commits

Author SHA1 Message Date
Zorchenhimer bf2afab07d
Moved --extract to its own utility
fdsextract now handles all extraction stuff.  This utility also has more
functionality.  There are options to extract a single side, a single
file on a single side, and an address range from a single file from a
single side.  Files and sides are 0-indexed.
2025-12-12 23:38:23 -05:00
Zorchenhimer c2a6fc7fa3
Add the 0x05 block type; Remove --extract from fdslist
- Block type 0x05 is some weird test pattern thing???
- Removed --extract from the fdslist utility
2025-12-12 23:37:13 -05:00
5 changed files with 224 additions and 27 deletions

View File

@ -1,6 +1,7 @@
CMDS= \
bin/fdslist
bin/fdslist \
bin/fdsextract
all: bin $(CMDS)

View File

@ -2,6 +2,7 @@ package fds
import (
"fmt"
"strings"
)
type BlockType byte
@ -11,6 +12,7 @@ const (
BtFileAmount BlockType = 0x02
BtFileHeader BlockType = 0x03
BtFileData BlockType = 0x04
BtTest BlockType = 0x05
)
func (bt BlockType) String() string {
@ -192,3 +194,20 @@ func (b *BlockFileData) Type() BlockType {
func (b *BlockFileData) String() string {
return "BlockFileData"
}
type BlockTest struct {
Raw []byte
}
func (b *BlockTest) Type() BlockType {
return BtTest
}
func (b *BlockTest) String() string {
bytes := []string{}
for _, byt := range b.Raw {
bytes = append(bytes, fmt.Sprintf("$%02X", byt))
}
return fmt.Sprintf("BlockTest Raw:[%s]", strings.Join(bytes, " "))
}

155
cmd/fdsextract.go Normal file
View File

@ -0,0 +1,155 @@
package main
import (
"fmt"
"os"
"path/filepath"
"strings"
"strconv"
"github.com/alexflint/go-arg"
"git.zorchenhimer.com/zorchenhimer/go-fds"
)
type Arguments struct {
Input string `arg:"positional,required"`
// for getting a specific file
Side int `arg:"--side" default:"-1"`
FileNum int `arg:"--file-num" default:"-1"`
Range string `arg:"--range"`
}
func main() {
args := &Arguments{}
arg.MustParse(args)
err := run(args)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run(args *Arguments) error {
rom, err := fds.ReadRomFile(args.Input)
if err != nil {
return err
}
outdir := strings.TrimSuffix(args.Input, filepath.Ext(args.Input))
err = os.MkdirAll(outdir, 0775)
if err != nil {
return err
}
if args.Side >= 0 && args.FileNum == -1 {
if args.Side >= len(rom.Sides) {
return fmt.Errorf("--side out of range")
}
num := 0
for _, s := range rom.Sides[:args.Side] {
num += len(s.Files)
}
side := rom.Sides[args.Side]
sideName := fmt.Sprintf("Side%d", side.Header.PhysicalSide)
for _, file := range side.Files {
fileName := filepath.Join(outdir, fmt.Sprintf("%03d_%s_%02d_%02X", num, sideName, file.Number, file.Id))
err := os.WriteFile(fileName, file.Data, 0664)
if err != nil {
return err
}
num++
}
return nil
} else if args.Side >= 0 && args.FileNum >= 0 {
if args.Side >= len(rom.Sides) {
return fmt.Errorf("--side out of range")
}
side := rom.Sides[args.Side]
if len(side.Files) <= args.FileNum {
return fmt.Errorf("--file-num out of range; %d", len(side.Files))
}
sideName := fmt.Sprintf("Side%d", side.Header.PhysicalSide)
file := side.Files[args.FileNum]
var start int
var end int
if args.Range != "" {
start, end, err = parseRange(args.Range)
if err != nil {
return err
}
fmt.Printf("start: %04X\nend: %04X\n", start, end)
start = start - int(file.Address)
end = end - int(file.Address)
fmt.Printf("start: %04X\nend: %04X\n", start, end)
} else {
start = 0
end = file.Size
}
fileName := filepath.Join(outdir, fmt.Sprintf("%03d_%s_%02d_%02X", args.FileNum, sideName, file.Number, file.Id))
fmt.Println("fileName:", fileName)
err := os.WriteFile(fileName, file.Data[start:end], 0664)
if err != nil {
return err
}
return nil
} else if args.Range != "" && (args.Side < 0 || args.FileNum < 0) {
return fmt.Errorf("--range cannot be given alone")
}
num := 0
for _, side := range rom.Sides {
sideName := fmt.Sprintf("Side%d", side.Header.PhysicalSide)
for _, file := range side.Files {
fileName := filepath.Join(outdir, fmt.Sprintf("%03d_%s_%02d_%02X", args.FileNum, sideName, file.Number, file.Id))
err := os.WriteFile(fileName, file.Data, 0664)
if err != nil {
return err
}
num++
}
}
return nil
}
// $6000-$5000
func parseRange(input string) (int, int, error) {
splits := strings.Split(input, "-")
if len(splits) != 2 {
return 0, 0, fmt.Errorf("invalid range: %q", input)
}
fmt.Println("splits:", splits)
splits[0] = strings.Replace(splits[0], "$", "0x", 1)
splits[1] = strings.Replace(splits[1], "$", "0x", 1)
fmt.Println("splits:", splits)
start, err := strconv.ParseInt(splits[0], 0, 64)
if err != nil {
return 0, 0, fmt.Errorf("invalid range: %q", input)
}
end, err := strconv.ParseInt(splits[1], 0, 64)
if err != nil {
return 0, 0, fmt.Errorf("invalid range: %q", input)
}
return int(start), int(end), nil
}

View File

@ -3,10 +3,6 @@ package main
import (
"fmt"
"os"
//"encoding/binary"
//"bufio"
//"bytes"
//"io"
"path/filepath"
"github.com/alexflint/go-arg"
@ -16,7 +12,8 @@ import (
type Arguments struct {
Input string `arg:"positional,required"`
Extract string `arg:"--extract" help:"Extract files to the given directory"`
Blocks bool `arg:"--blocks"`
}
@ -32,6 +29,25 @@ func main() {
}
func run(args *Arguments) error {
if args.Blocks {
file, err := os.Open(args.Input)
if err != nil {
return err
}
defer file.Close()
blocks, err := fds.ReadRomBlocks(file, filepath.Ext(args.Input) == ".fds")
if err != nil {
return err
}
for _, block := range blocks {
fmt.Println(block.String())
}
return nil
}
rom, err := fds.ReadRomFile(args.Input)
if err != nil {
return err
@ -39,26 +55,6 @@ func run(args *Arguments) error {
fmt.Println(rom.Info())
if args.Extract != "" {
err = os.MkdirAll(args.Extract, 0775)
if err != nil {
return err
}
num := 0
for _, side := range rom.Sides {
sideName := fmt.Sprintf("Side%d", side.Header.PhysicalSide)
for _, file := range side.Files {
fileName := filepath.Join(args.Extract, fmt.Sprintf("%03d_%s_%02d_%02X", num, sideName, file.Number, file.Id))
err := os.WriteFile(fileName, file.Data, 0664)
if err != nil {
return err
}
num++
}
}
}
return nil
}

View File

@ -52,6 +52,7 @@ func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
reader := bufio.NewReader(r)
outerLoop:
for {
raw, err := reader.Peek(1)
if err != nil {
@ -79,6 +80,31 @@ func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
// variable length
readLen = dataSize+3
case 0x05: // some weird test block??
reader.Discard(1)
bt := &BlockTest{}
for {
b, err := reader.ReadByte()
if err != nil {
if err == io.EOF {
blocks = append(blocks, bt)
break outerLoop
}
return blocks, fmt.Errorf("error reading 0x05 block type: %w", err)
}
switch b {
case 0x6D, 0xB6, 0xDB:
bt.Raw = append(bt.Raw, b)
continue
case 0x00:
blocks = append(blocks, bt)
continue outerLoop
}
return blocks, fmt.Errorf("Read unknown byte in 0x05 block: 0x%02X", b)
}
case 0x00:
reader.Discard(1)
continue
@ -144,9 +170,9 @@ func ReadBlocks(r io.Reader, IsFds bool) ([]DiskBlock, error) {
db.Data = buf[1:readLen]
//fmt.Println("len(db.Data)", len(db.Data))
block = db
//fmt.Printf("Block:%#v\n", block)
}
//fmt.Printf("Block:%s\n", block.String())
blocks = append(blocks, block)
}