mirror of
https://github.com/paleotronic/diskm8.git
synced 2024-06-02 04:41:27 +00:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
a00aeed763 | ||
|
f46d7ff6ee | ||
|
6b8517efec | ||
|
583624a6f8 | ||
|
d147f7e61b | ||
|
5135679d0b | ||
|
7d13e7f8d8 | ||
|
9df9fb6c89 | ||
|
613b5f1cd1 | ||
|
824eac4ce5 | ||
|
8f395e8360 | ||
|
2a5efddea2 | ||
|
6d952cd8a1 | ||
|
6d12d71867 | ||
|
b605401357 | ||
|
086a2da973 | ||
|
2ca1798d95 | ||
|
16c8a2100d | ||
|
a6d47bbafa | ||
|
e38cb58d37 | ||
|
358c50662f | ||
|
0a4cc590d8 | ||
|
4479f21f0b | ||
|
76dfc34fd2 | ||
|
c72cc80e5b | ||
|
185caf6c4f |
36
README.md
36
README.md
|
@ -5,7 +5,7 @@ Download from: https://github.com/paleotronic/diskm8/releases
|
|||
Features include:
|
||||
|
||||
- Read from ProDOS, DOS 3.X, RDOS and Pascal disk images;
|
||||
- ProDOS or DOS ordered; 2MG and NIB; 113-800K
|
||||
- ProDOS or DOS ordered; DSK, PO, 2MG and NIB; 113-800K
|
||||
- Write to Prodos and DOS 3.3 disk images;
|
||||
- Extract and convert binary, text and detokenize BASIC files (Integer and Applesoft);
|
||||
- Write binary, text and retokenized BASIC (Applesoft) files back to disk images;
|
||||
|
@ -42,11 +42,13 @@ ls List local files
|
|||
mkdir Create a directory on disk
|
||||
mount Mount a disk image
|
||||
move Move files from one volume to another
|
||||
put Copy local file to disk
|
||||
prefix Change volume path
|
||||
put Copy local file to disk (with optional target dir)
|
||||
quarantine Like report, but allow moving dupes to a backup folder
|
||||
quit Leave this place
|
||||
rename Rename a file on the disk
|
||||
report Run a report
|
||||
search Run a search
|
||||
target Select mounted volume as default
|
||||
unlock Unlock file on the disk
|
||||
unmount unmount disk image
|
||||
|
@ -81,7 +83,7 @@ Command-line flags:
|
|||
-csv
|
||||
Output data to CSV format
|
||||
-datastore string
|
||||
Database of disk fingerprints for checking (default "/Users/nnnnn/DiskM8/fingerprints")
|
||||
Database of disk fingerprints for checking (default "/home/myname/DiskM8/fingerprints")
|
||||
-dir
|
||||
Directory specified disk (needs -disk)
|
||||
-dir-create string
|
||||
|
@ -142,26 +144,36 @@ Command-line flags:
|
|||
Run whole disk dupe report
|
||||
-with-disk string
|
||||
Perform disk operation (-file-extract,-file-put,-file-delete)
|
||||
-with-path string
|
||||
Target path for disk operation (-file-extract,-file-put,-file-delete)
|
||||
```
|
||||
Getting Started
|
||||
|
||||
## Getting Started
|
||||
|
||||
Ingest your disk collection, so diskm8 can report on them:
|
||||
|
||||
diskm8 -ingest "C:\Users\myname\LotsOfDisks"
|
||||
Simple Reports
|
||||
```diskm8 -ingest "C:\Users\myname\LotsOfDisks"```
|
||||
|
||||
### Simple Reports
|
||||
|
||||
Find Whole Disk duplicates:
|
||||
|
||||
diskm8 -whole-dupes
|
||||
```diskm8 -whole-dupes```
|
||||
|
||||
Find Active Sectors duplicates (inactive sectors can be different):
|
||||
|
||||
diskm8 -as-dupes
|
||||
```diskm8 -as-dupes```
|
||||
|
||||
Find Duplicate files across disks:
|
||||
|
||||
diskm8 -file-dupes
|
||||
Limiting reports to subdirectories
|
||||
```diskm8 -file-dupes```
|
||||
|
||||
### Limiting reports to subdirectories
|
||||
|
||||
Find Active Sector duplicates but only under a folder:
|
||||
|
||||
diskm8 -as-dupes -select "C:\Users\myname\LotsOfDisks\Operating Systems"
|
||||
```
|
||||
```diskm8 -as-dupes -select "C:\Users\myname\LotsOfDisks\Operating Systems"```
|
||||
|
||||
### Putting a file onto a disk in a particular path
|
||||
|
||||
```diskm8 -with-disk prodos_basic.dsk -with-path practice -file-put start#0x0801.BAS```
|
||||
|
|
12
USAGE.md
12
USAGE.md
|
@ -1,32 +1,32 @@
|
|||
## Usage examples
|
||||
|
||||
Ingest your disk collection, so dskalyzer can report on them:
|
||||
Ingest your disk collection, so diskm8 can report on them:
|
||||
|
||||
```
|
||||
dskalyzer -ingest C:\Users\myname\LotsOfDisks
|
||||
diskm8 -ingest C:\Users\myname\LotsOfDisks
|
||||
```
|
||||
|
||||
Find Whole Disk duplicates:
|
||||
|
||||
```
|
||||
dskalyzer -whole-dupes
|
||||
diskm8 -whole-dupes
|
||||
```
|
||||
|
||||
Find Active Sectors duplicates (inactive sectors can be different):
|
||||
|
||||
```
|
||||
dskalyzer -as-dupes
|
||||
diskm8 -as-dupes
|
||||
```
|
||||
|
||||
Find Duplicate files across disks:
|
||||
|
||||
```
|
||||
dskalyzer -file-dupes
|
||||
diskm8 -file-dupes
|
||||
```
|
||||
|
||||
Find Active Sector duplicates but only under a folder:
|
||||
|
||||
```
|
||||
dskalyzer -as-dupes -select "C:\Users\myname\LotsOfDisks\Operating Systems"
|
||||
diskm8 -as-dupes -select "C:\Users\myname\LotsOfDisks\Operating Systems"
|
||||
```
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@ func banner() {
|
|||
t, _ := base64.StdEncoding.DecodeString(text)
|
||||
|
||||
os.Stderr.WriteString(string(t) + "\r\n")
|
||||
os.Stderr.WriteString("(c) 2015 - 2018 Paleotronic.com\n\n")
|
||||
os.Stderr.WriteString("(c) 2015 - 2024 Paleotronic.com\n\n")
|
||||
os.Stderr.WriteString("type 'help' to see commands\n\n")
|
||||
|
||||
}
|
||||
|
|
17
data.go
17
data.go
|
@ -1,21 +1,16 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/md5"
|
||||
"encoding/gob"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"time"
|
||||
|
||||
"crypto/md5"
|
||||
"encoding/hex"
|
||||
|
||||
"os"
|
||||
|
||||
"strings"
|
||||
|
||||
"encoding/gob"
|
||||
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/paleotronic/diskm8/disk"
|
||||
"github.com/paleotronic/diskm8/loggy"
|
||||
|
|
166
disk/atokens.go
166
disk/atokens.go
|
@ -120,6 +120,14 @@ var ApplesoftTokens = map[int]string{
|
|||
0xEA: "MID$",
|
||||
}
|
||||
|
||||
var ASPartials = map[string]bool{
|
||||
"COLOR": true,
|
||||
"HCOLOR": true,
|
||||
"SPEED": true,
|
||||
"SCALE": true,
|
||||
"-": true,
|
||||
}
|
||||
|
||||
var ApplesoftReverse map[string]int
|
||||
var IntegerReverse map[string]int
|
||||
|
||||
|
@ -128,6 +136,9 @@ func init() {
|
|||
IntegerReverse = make(map[string]int)
|
||||
for k, v := range ApplesoftTokens {
|
||||
ApplesoftReverse[v] = k
|
||||
if v == "PRINT" {
|
||||
ApplesoftReverse["?"] = k
|
||||
}
|
||||
}
|
||||
for k, v := range IntegerTokens {
|
||||
IntegerReverse[v] = k
|
||||
|
@ -334,6 +345,8 @@ func ApplesoftDetoks(data []byte) []byte {
|
|||
break
|
||||
}
|
||||
|
||||
// var lineStart = len(out)
|
||||
|
||||
nextAddr = Read16(&srcptr, &length, data)
|
||||
|
||||
if nextAddr == 0 {
|
||||
|
@ -349,7 +362,7 @@ func ApplesoftDetoks(data []byte) []byte {
|
|||
lineNum = Read16(&srcptr, &length, data)
|
||||
ln := fmt.Sprintf("%d", lineNum)
|
||||
|
||||
out = append(out, []byte(" "+ln+" ")...)
|
||||
out = append(out, []byte(ln+" ")...)
|
||||
|
||||
if length == 0 {
|
||||
break
|
||||
|
@ -393,7 +406,9 @@ func ApplesoftDetoks(data []byte) []byte {
|
|||
t = Read8(&srcptr, &length, data)
|
||||
}
|
||||
|
||||
out = append(out, []byte("\r\n")...)
|
||||
out = append(out, []byte("\n")...)
|
||||
|
||||
// log.Printf("Line bytes: %+v", out[lineStart:])
|
||||
|
||||
inQuote, inRem = false, false
|
||||
|
||||
|
@ -428,6 +443,8 @@ func IntegerDetoks(data []byte) []byte {
|
|||
var trailingSpace bool
|
||||
var newTrailingSpace bool = false
|
||||
|
||||
// var lineStart = len(out)
|
||||
|
||||
// read the line length
|
||||
lineLen = Read8(&srcptr, &length, data)
|
||||
|
||||
|
@ -548,7 +565,7 @@ func ApplesoftTokenize(lines []string) []byte {
|
|||
|
||||
for _, l := range lines {
|
||||
|
||||
l = strings.Trim(l, "\r")
|
||||
l = strings.Trim(l, " \r\n\t")
|
||||
if l == "" {
|
||||
continue
|
||||
}
|
||||
|
@ -556,8 +573,16 @@ func ApplesoftTokenize(lines []string) []byte {
|
|||
chunk := ""
|
||||
inqq := false
|
||||
tmp := strings.SplitN(l, " ", 2)
|
||||
ln, _ := strconv.Atoi(tmp[0])
|
||||
rest := strings.Trim(tmp[1], " ")
|
||||
var rest string
|
||||
var ln int
|
||||
if len(tmp) == 1 {
|
||||
ln = 0
|
||||
rest = ""
|
||||
} else {
|
||||
ln, _ = strconv.Atoi(tmp[0])
|
||||
rest = strings.Trim(tmp[1], " ")
|
||||
}
|
||||
// lastIsTok := false
|
||||
|
||||
linebuffer := make([]byte, 4)
|
||||
|
||||
|
@ -566,27 +591,116 @@ func ApplesoftTokenize(lines []string) []byte {
|
|||
linebuffer[0x03] = byte(ln / 0x100)
|
||||
|
||||
// PROCESS LINE
|
||||
var lastKeyword string
|
||||
var inREM bool
|
||||
|
||||
for _, ch := range rest {
|
||||
|
||||
switch {
|
||||
case inqq && ch != '"':
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
continue
|
||||
case ch == '"':
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
inqq = !inqq
|
||||
continue
|
||||
case !inqq && breakingChar(ch):
|
||||
linebuffer = append(linebuffer, []byte(chunk)...)
|
||||
// case for a single character token, not in string
|
||||
if codech, ok := ApplesoftReverse[strings.ToUpper(string(ch))]; ok && !inREM && !inqq {
|
||||
|
||||
// log.Printf("'%s' (%.2x) is a token... (lastKW=%s, chunk=%s)", string(ch), ch, lastKeyword, chunk)
|
||||
|
||||
// 1st - is chunk + string a token?
|
||||
if chunk != "" {
|
||||
code, ok := ApplesoftReverse[strings.ToUpper(chunk+string(ch))]
|
||||
if ok {
|
||||
if strings.ToUpper(chunk+string(ch)) == "REM" || strings.ToUpper(chunk+string(ch)) == "DATA" {
|
||||
inREM = true
|
||||
}
|
||||
linebuffer = append(linebuffer, byte(code))
|
||||
lastKeyword = chunk + string(ch)
|
||||
chunk = ""
|
||||
// lastIsTok = true
|
||||
continue // we absorbed it ... eg. COLOR=
|
||||
} else {
|
||||
// chunk wasn't so treat as a string
|
||||
// log.Printf("output (%s) by itself...", chunk)
|
||||
linebuffer = append(linebuffer, []byte(chunk)...)
|
||||
// lastIsTok = false
|
||||
// lastKeyword = chunk
|
||||
chunk = ""
|
||||
}
|
||||
}
|
||||
|
||||
// just the symbol
|
||||
linebuffer = append(linebuffer, byte(codech))
|
||||
lastKeyword = string(ch)
|
||||
chunk = ""
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
// lastIsTok = true
|
||||
continue
|
||||
}
|
||||
|
||||
chunk += string(ch)
|
||||
switch {
|
||||
case ch < 32 || ch > 127:
|
||||
continue
|
||||
case inREM && ch != ':':
|
||||
chunk += string(ch)
|
||||
continue
|
||||
case inREM && ch == ':':
|
||||
if chunk != "" {
|
||||
linebuffer = append(linebuffer, []byte(chunk)...)
|
||||
}
|
||||
chunk = ""
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
inREM = false
|
||||
continue
|
||||
case inqq && ch != '"':
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
lastKeyword = ""
|
||||
// lastIsTok = false
|
||||
continue
|
||||
case ch == '"':
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
lastKeyword = ""
|
||||
inqq = !inqq
|
||||
// lastIsTok = false
|
||||
continue
|
||||
case !inqq && breakingChar(ch) && !ASPartials[chunk]:
|
||||
|
||||
if chunk != "" {
|
||||
code, ok := ApplesoftReverse[strings.ToUpper(chunk+string(ch))]
|
||||
if ok {
|
||||
linebuffer = append(linebuffer, byte(code))
|
||||
lastKeyword = strings.ToUpper(chunk + string(ch))
|
||||
chunk = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
linebuffer = append(linebuffer, []byte(chunk)...)
|
||||
chunk = ""
|
||||
if ch != ' ' {
|
||||
linebuffer = append(linebuffer, byte(ch))
|
||||
}
|
||||
lastKeyword = ""
|
||||
continue
|
||||
}
|
||||
|
||||
if ch != ' ' {
|
||||
chunk += string(ch)
|
||||
}
|
||||
|
||||
if lastKeyword != "" {
|
||||
code, ok := ApplesoftReverse[strings.ToUpper(lastKeyword+chunk)]
|
||||
if ok {
|
||||
if strings.ToUpper(lastKeyword+chunk) == "REM" || strings.ToUpper(lastKeyword+chunk) == "DATA" {
|
||||
inREM = true
|
||||
}
|
||||
linebuffer[len(linebuffer)-1] = byte(code)
|
||||
lastKeyword = lastKeyword + chunk
|
||||
chunk = ""
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
code, ok := ApplesoftReverse[strings.ToUpper(chunk)]
|
||||
if ok {
|
||||
if strings.ToUpper(lastKeyword+chunk) == "REM" || strings.ToUpper(lastKeyword+chunk) == "DATA" {
|
||||
inREM = true
|
||||
}
|
||||
linebuffer = append(linebuffer, byte(code))
|
||||
lastKeyword = chunk
|
||||
chunk = ""
|
||||
}
|
||||
}
|
||||
|
@ -594,6 +708,24 @@ func ApplesoftTokenize(lines []string) []byte {
|
|||
linebuffer = append(linebuffer, []byte(chunk)...)
|
||||
}
|
||||
|
||||
//~ for i := 5; i < len(linebuffer)-1; i++ {
|
||||
//~ if linebuffer[i] == 0xc9 {
|
||||
//~ log.Printf("Found 0xC9 (-) in linebuffer...")
|
||||
//~ // minus token...
|
||||
//~ before := rune(linebuffer[i-1])
|
||||
//~ after := rune(linebuffer[i+1])
|
||||
//~ log.Printf("Before = %s, After = %s", string(before), string(after))
|
||||
|
||||
//~ if after == '.' || (after >= '0' && after <= '9') {
|
||||
//~ // number part
|
||||
//~ if before > 128 || before == ',' || before == 0xD0 {
|
||||
//~ log.Printf("changing - token at %d to symbol", i)
|
||||
//~ linebuffer[i] = byte('-')
|
||||
//~ }
|
||||
//~ }
|
||||
//~ }
|
||||
//~ }
|
||||
|
||||
// ENDING ZERO BYTE
|
||||
linebuffer = append(linebuffer, 0x00)
|
||||
|
||||
|
|
44
disk/atokens_test.go
Normal file
44
disk/atokens_test.go
Normal file
|
@ -0,0 +1,44 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHGR2Tokenise(t *testing.T) {
|
||||
|
||||
lines := []string{
|
||||
"10 HGR2 : REM SOMETHING",
|
||||
"20 REM SOMETHING ELSE",
|
||||
}
|
||||
|
||||
a := ApplesoftTokenize(lines)
|
||||
|
||||
s := string(ApplesoftDetoks(a))
|
||||
|
||||
t.Logf("code: %s", s)
|
||||
|
||||
if !strings.Contains(s, "HGR2 ") {
|
||||
t.Fatalf("Expected HGR2")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestHGRTokenise(t *testing.T) {
|
||||
|
||||
lines := []string{
|
||||
"10 HGR : REM SOMETHING",
|
||||
"20 REM SOMETHING ELSE",
|
||||
}
|
||||
|
||||
a := ApplesoftTokenize(lines)
|
||||
|
||||
s := string(ApplesoftDetoks(a))
|
||||
|
||||
t.Logf("code: %s", s)
|
||||
|
||||
if !strings.Contains(s, "HGR ") {
|
||||
t.Fatalf("Expected HGR")
|
||||
}
|
||||
|
||||
}
|
|
@ -1,31 +0,0 @@
|
|||
package disk
|
||||
|
||||
import (
|
||||
//"strings"
|
||||
"testing"
|
||||
)
|
||||
import "fmt"
|
||||
|
||||
//import "os"
|
||||
|
||||
func TestDisk(t *testing.T) {
|
||||
|
||||
if STD_DISK_BYTES != 143360 {
|
||||
t.Error(fmt.Sprintf("Wrong size got %d", STD_DISK_BYTES))
|
||||
}
|
||||
|
||||
dsk, e := NewDSKWrapper("g19.dsk")
|
||||
if e != nil {
|
||||
t.Error(e)
|
||||
}
|
||||
|
||||
fmt.Printf("Disk format is %d\n", dsk.Format)
|
||||
|
||||
_, fdlist, e := dsk.GetCatalogProDOSPathed(2, "GAMES", "")
|
||||
for _, fd := range fdlist {
|
||||
fmt.Printf("[%s]\n", fd.Name())
|
||||
}
|
||||
|
||||
t.Fail()
|
||||
|
||||
}
|
|
@ -2,20 +2,16 @@ package disk
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//import "math/rand"
|
||||
|
||||
const STD_BYTES_PER_SECTOR = 256
|
||||
const STD_TRACKS_PER_DISK = 35
|
||||
const STD_SECTORS_PER_TRACK = 16
|
||||
|
@ -69,6 +65,11 @@ var LINEAR_SECTOR_ORDER = []int{
|
|||
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
|
||||
}
|
||||
|
||||
var DIVERSE_SECTOR_ORDER = []int{
|
||||
0x00, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
|
||||
0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x0f,
|
||||
}
|
||||
|
||||
var NIBBLE_62 = []byte{
|
||||
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
||||
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
||||
|
@ -98,6 +99,7 @@ const (
|
|||
SectorOrderDOS33Alt
|
||||
SectorOrderProDOS
|
||||
SectorOrderProDOSLinear
|
||||
SectorOrderDiversiDOS
|
||||
)
|
||||
|
||||
func (so SectorOrder) String() string {
|
||||
|
@ -112,6 +114,8 @@ func (so SectorOrder) String() string {
|
|||
return "ProDOS"
|
||||
case SectorOrderProDOSLinear:
|
||||
return "Linear"
|
||||
case SectorOrderDiversiDOS:
|
||||
return "DiversiDOS"
|
||||
}
|
||||
|
||||
return "Linear"
|
||||
|
@ -141,6 +145,15 @@ type DiskFormat struct {
|
|||
tpd int
|
||||
}
|
||||
|
||||
func (df DiskFormat) IsOneOf(args ...DiskFormatID) bool {
|
||||
for _, f := range args {
|
||||
if f == df.ID {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func GetDiskFormat(id DiskFormatID) DiskFormat {
|
||||
return DiskFormat{ID: id}
|
||||
}
|
||||
|
@ -176,7 +189,7 @@ func (f DiskFormat) String() string {
|
|||
case DF_RDOS_32:
|
||||
return "SSI RDOS 32 (13/13/Physical)"
|
||||
case DF_RDOS_33:
|
||||
return "SSI RDOS 32 (16/16/PD)"
|
||||
return "SSI RDOS 33 (16/16/PD)"
|
||||
case DF_PRODOS_CUSTOM:
|
||||
return fmt.Sprintf("ProDOS Custom (%d SPT, %d TPD)", f.SPT(), f.TPD())
|
||||
}
|
||||
|
@ -304,8 +317,15 @@ type DSKWrapper struct {
|
|||
//Nibbles []byte
|
||||
Nibbles Nibbler
|
||||
CurrentSectorOrder []int
|
||||
CatalogSectorOrder []int // usually nil but for badly behaved OS: looking at YOU, DiversiDOS... :(
|
||||
WriteProtected bool
|
||||
NibblesChanged bool
|
||||
DOSVolumeID int
|
||||
}
|
||||
|
||||
// SectoreMapperDOS33 handles the interleaving for dos sectors
|
||||
func SectorMapperLinear(wanted int) int {
|
||||
return wanted
|
||||
}
|
||||
|
||||
// SectoreMapperDOS33 handles the interleaving for dos sectors
|
||||
|
@ -313,39 +333,42 @@ func SectorMapperDOS33(wanted int) int {
|
|||
|
||||
return wanted
|
||||
|
||||
// 0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
|
||||
// 0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F,
|
||||
|
||||
switch wanted {
|
||||
case 0:
|
||||
return 0
|
||||
case 13:
|
||||
return 1
|
||||
case 11:
|
||||
return 2
|
||||
case 9:
|
||||
return 3
|
||||
case 7:
|
||||
return 4
|
||||
case 5:
|
||||
return 5
|
||||
case 3:
|
||||
return 6
|
||||
case 1:
|
||||
return 7
|
||||
case 14:
|
||||
return 8
|
||||
case 12:
|
||||
return 9
|
||||
case 10:
|
||||
return 10
|
||||
case 8:
|
||||
return 11
|
||||
case 6:
|
||||
return 12
|
||||
case 4:
|
||||
return 13
|
||||
case 2:
|
||||
return 14
|
||||
case 15:
|
||||
return 15
|
||||
case 0x00:
|
||||
return 0x00
|
||||
case 0x07:
|
||||
return 0x01
|
||||
case 0x0e:
|
||||
return 0x02
|
||||
case 0x06:
|
||||
return 0x03
|
||||
case 0x0d:
|
||||
return 0x04
|
||||
case 0x05:
|
||||
return 0x05
|
||||
case 0x0c:
|
||||
return 0x06
|
||||
case 0x04:
|
||||
return 0x07
|
||||
case 0x0b:
|
||||
return 0x08
|
||||
case 0x03:
|
||||
return 0x09
|
||||
case 0x0a:
|
||||
return 0x0a
|
||||
case 0x02:
|
||||
return 0x0b
|
||||
case 0x09:
|
||||
return 0x0c
|
||||
case 0x01:
|
||||
return 0x0d
|
||||
case 0x08:
|
||||
return 0x0e
|
||||
case 0x0f:
|
||||
return 0x0f
|
||||
}
|
||||
|
||||
return -1 // invalid sector
|
||||
|
@ -473,6 +496,46 @@ func SectorMapperProDOS(wanted int) int {
|
|||
return -1 // invalid sector
|
||||
}
|
||||
|
||||
// SectoreMapperProDOS handles the interleaving for dos sectors
|
||||
func SectorMapperDiversiDOS(wanted int) int {
|
||||
switch wanted {
|
||||
case 0x00:
|
||||
return 0x00
|
||||
case 0x01:
|
||||
return 0x0e
|
||||
case 0x02:
|
||||
return 0x0d
|
||||
case 0x03:
|
||||
return 0x0c
|
||||
case 0x04:
|
||||
return 0x0b
|
||||
case 0x05:
|
||||
return 0x0a
|
||||
case 0x06:
|
||||
return 0x09
|
||||
case 0x07:
|
||||
return 0x08
|
||||
case 0x08:
|
||||
return 0x07
|
||||
case 0x09:
|
||||
return 0x06
|
||||
case 0x0a:
|
||||
return 0x05
|
||||
case 0x0b:
|
||||
return 0x04
|
||||
case 0x0c:
|
||||
return 0x03
|
||||
case 0x0d:
|
||||
return 0x02
|
||||
case 0x0e:
|
||||
return 0x01
|
||||
case 0x0f:
|
||||
return 0x0f
|
||||
}
|
||||
|
||||
return -1 // invalid sector
|
||||
}
|
||||
|
||||
func (d *DSKWrapper) SetTrack(t int) error {
|
||||
|
||||
if t >= 0 && t < d.Format.TPD() {
|
||||
|
@ -522,6 +585,10 @@ func (d *DSKWrapper) SetSectorPointer() {
|
|||
isector = SectorMapperDOS33(sector)
|
||||
case SectorOrderProDOS:
|
||||
isector = SectorMapperProDOS(sector)
|
||||
case SectorOrderProDOSLinear:
|
||||
isector = SectorMapperLinear(sector)
|
||||
case SectorOrderDiversiDOS:
|
||||
isector = SectorMapperDiversiDOS(sector)
|
||||
}
|
||||
|
||||
d.SectorPointer = (track * d.Format.SPT() * STD_BYTES_PER_SECTOR) + (STD_BYTES_PER_SECTOR * isector)
|
||||
|
@ -551,12 +618,10 @@ func (d *DSKWrapper) IsChanged() bool {
|
|||
|
||||
// Read is a simple function to return the current pointed to sector
|
||||
func (d *DSKWrapper) Read() []byte {
|
||||
////fmt.Printf("---> Reading track %d, sector %d\n", d.CurrentTrack, d.CurrentSector)
|
||||
return d.Data[d.SectorPointer : d.SectorPointer+256]
|
||||
}
|
||||
|
||||
func (d *DSKWrapper) Write(data []byte) {
|
||||
////fmt.Printf("---> Reading track %d, sector %d\n", d.CurrentTrack, d.CurrentSector)
|
||||
l := len(data)
|
||||
if l > STD_BYTES_PER_SECTOR {
|
||||
l = STD_BYTES_PER_SECTOR
|
||||
|
@ -605,6 +670,7 @@ func NewDSKWrapper(nibbler Nibbler, filename string) (*DSKWrapper, error) {
|
|||
}
|
||||
|
||||
w, e := NewDSKWrapperBin(nibbler, data, filename)
|
||||
|
||||
return w, e
|
||||
|
||||
}
|
||||
|
@ -618,7 +684,8 @@ func NewDSKWrapperBin(nibbler Nibbler, data []byte, filename string) (*DSKWrappe
|
|||
len(data) != PRODOS_400KB_DISK_BYTES+64 &&
|
||||
len(data) != PRODOS_800KB_DISK_BYTES &&
|
||||
len(data) != PRODOS_800KB_DISK_BYTES+64 &&
|
||||
len(data) != STD_DISK_BYTES+64 {
|
||||
len(data) != STD_DISK_BYTES+64 &&
|
||||
len(data) < STD_DISK_BYTES {
|
||||
return nil, errors.New("Incorrect disk bytes")
|
||||
}
|
||||
|
||||
|
@ -657,26 +724,30 @@ func (dsk *DSKWrapper) SetNibbles(data []byte) {
|
|||
}
|
||||
}
|
||||
|
||||
func layoutWithHints(l SectorOrder, hint SectorOrder) SectorOrder {
|
||||
if hint != -1 {
|
||||
return hint
|
||||
}
|
||||
return l
|
||||
}
|
||||
|
||||
func (dsk *DSKWrapper) Identify() {
|
||||
|
||||
dsk.Format = GetDiskFormat(DF_NONE)
|
||||
|
||||
var hint DiskFormat
|
||||
var hint SectorOrder = -1
|
||||
|
||||
dsk.Filename = strings.ToLower(dsk.Filename)
|
||||
lowerFilename := strings.ToLower(dsk.Filename)
|
||||
|
||||
switch {
|
||||
case strings.HasSuffix(dsk.Filename, ".po"):
|
||||
hint = GetDiskFormat(DF_PRODOS)
|
||||
case strings.HasSuffix(dsk.Filename, ".do"):
|
||||
hint = GetDiskFormat(DF_DOS_SECTORS_16)
|
||||
default:
|
||||
hint = GetDiskFormat(DF_DOS_SECTORS_16)
|
||||
case strings.HasSuffix(lowerFilename, ".po"):
|
||||
hint = SectorOrderProDOS
|
||||
case strings.HasSuffix(lowerFilename, ".do"):
|
||||
hint = SectorOrderDOS33
|
||||
}
|
||||
|
||||
is2MG, Format, Layout, zdsk := dsk.Is2MG()
|
||||
if is2MG {
|
||||
////fmt.Println("repacked", len(zdsk.Data))
|
||||
dsk.SetData(zdsk.Data)
|
||||
dsk.Layout = Layout
|
||||
dsk.Format = Format
|
||||
|
@ -685,9 +756,12 @@ func (dsk *DSKWrapper) Identify() {
|
|||
|
||||
isPD, Format, Layout := dsk.IsProDOS()
|
||||
if isPD {
|
||||
if Format == GetDiskFormat(DF_PRODOS) {
|
||||
if Format == GetDiskFormat(DF_PRODOS_CUSTOM) {
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS)
|
||||
dsk.Layout = Layout
|
||||
} else if Format == GetDiskFormat(DF_PRODOS) {
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS)
|
||||
dsk.Layout = layoutWithHints(Layout, hint)
|
||||
switch dsk.Layout {
|
||||
case SectorOrderProDOS:
|
||||
dsk.CurrentSectorOrder = PRODOS_SECTOR_ORDER
|
||||
|
@ -739,25 +813,21 @@ func (dsk *DSKWrapper) Identify() {
|
|||
|
||||
isAppleDOS, Format, Layout := dsk.IsAppleDOS()
|
||||
if isAppleDOS {
|
||||
////fmt.Printf("Format: %s\n", Format.String())
|
||||
dsk.Format = Format
|
||||
dsk.Layout = Layout
|
||||
switch Layout {
|
||||
dsk.Layout = layoutWithHints(Layout, hint)
|
||||
switch dsk.Layout {
|
||||
case SectorOrderProDOS:
|
||||
////fmt.Println("Sector Order: ProDOS")
|
||||
dsk.CurrentSectorOrder = PRODOS_SECTOR_ORDER
|
||||
case SectorOrderProDOSLinear:
|
||||
////fmt.Println("Sector Order: Linear")
|
||||
dsk.CurrentSectorOrder = LINEAR_SECTOR_ORDER
|
||||
case SectorOrderDOS33:
|
||||
////fmt.Println("Sector Order: DOS33")
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
case SectorOrderDOS32:
|
||||
////fmt.Println("Sector Order: DOS32")
|
||||
//dsk.CurrentSectorOrder = DOS_32_SECTOR_ORDER
|
||||
dsk.CurrentSectorOrder = DOS_32_SECTOR_ORDER
|
||||
case SectorOrderDOS33Alt:
|
||||
////fmt.Println("Sector Order: Alt Linear")
|
||||
dsk.CurrentSectorOrder = LINEAR_SECTOR_ORDER
|
||||
case SectorOrderDiversiDOS:
|
||||
dsk.CurrentSectorOrder = DIVERSE_SECTOR_ORDER
|
||||
}
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
return
|
||||
|
@ -766,14 +836,10 @@ func (dsk *DSKWrapper) Identify() {
|
|||
fp := hex.EncodeToString(dsk.Data[:32])
|
||||
if dfmt, ok := identity[fp]; ok {
|
||||
dsk.Format = dfmt
|
||||
//fmt.Println(dsk.Format.String())
|
||||
}
|
||||
|
||||
//fmt.Printf("Disk name: %s\n", dsk.Filename)
|
||||
|
||||
// 1. NIB
|
||||
if len(dsk.Data) == 232960 {
|
||||
//fmt.Println("THIS IS A NIB")
|
||||
dsk.Format = GetDiskFormat(DF_DOS_SECTORS_16)
|
||||
dsk.SetNibbles(dsk.Data)
|
||||
return
|
||||
|
@ -781,7 +847,6 @@ func (dsk *DSKWrapper) Identify() {
|
|||
|
||||
// 2. Wrong size
|
||||
if len(dsk.Data) != STD_DISK_BYTES && len(dsk.Data) != STD_DISK_BYTES_OLD && len(dsk.Data) != PRODOS_800KB_DISK_BYTES {
|
||||
//fmt.Println("NOT STANDARD DISK")
|
||||
dsk.Format = GetDiskFormat(DF_NONE)
|
||||
dsk.SetNibbles(make([]byte, 232960))
|
||||
return
|
||||
|
@ -789,14 +854,10 @@ func (dsk *DSKWrapper) Identify() {
|
|||
|
||||
// 3. DOS 3x Disk
|
||||
vtoc, e := dsk.AppleDOSGetVTOC()
|
||||
//fmt.Println(vtoc.GetTracks(), vtoc.GetSectors())
|
||||
if e == nil && vtoc.GetTracks() == 35 {
|
||||
//bps := vtoc.BytesPerSector()
|
||||
t := vtoc.GetTracks()
|
||||
s := vtoc.GetSectors()
|
||||
|
||||
//fmt.Printf("DOS Tracks = %d, Sectors = %d\n", t, s)
|
||||
|
||||
if t == 35 && s == 16 {
|
||||
dsk.Layout = SectorOrderDOS33
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
|
@ -806,13 +867,11 @@ func (dsk *DSKWrapper) Identify() {
|
|||
dsk.Layout = SectorOrderDOS32
|
||||
dsk.CurrentSectorOrder = DOS_32_SECTOR_ORDER
|
||||
dsk.Format = GetDiskFormat(DF_DOS_SECTORS_13)
|
||||
dsk.SetNibbles(make([]byte, 232960))
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
//fmt.Println("Trying prodos / pascal")
|
||||
|
||||
isPAS, volName := dsk.IsPascal()
|
||||
if isPAS && volName != "" {
|
||||
dsk.Format = GetDiskFormat(DF_PASCAL)
|
||||
|
@ -828,28 +887,22 @@ func (dsk *DSKWrapper) Identify() {
|
|||
dsk.Format = GetDiskFormat(DF_PRODOS_800KB)
|
||||
}
|
||||
|
||||
//fmt.Println(dsk.Format.String())
|
||||
|
||||
switch dsk.Format.ID {
|
||||
|
||||
case DF_PRODOS:
|
||||
|
||||
vdh, e := dsk.PRODOSGetVDH(2)
|
||||
////fmt.Printf("Blocks = %d\n", vdh.GetTotalBlocks())
|
||||
if e == nil && vdh.GetStorageType() == 0xf && vdh.GetTotalBlocks() == 280 {
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS)
|
||||
dsk.Layout = SectorOrderDOS33
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
|
||||
//fmt.Println("THIS IS A PRODOS DISKETTE, DOS Ordered")
|
||||
return
|
||||
} else {
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS)
|
||||
dsk.Layout = SectorOrderDOS33Alt
|
||||
|
||||
////fmt.Println("Try again")
|
||||
|
||||
vdh, e = dsk.PRODOSGetVDH(2)
|
||||
|
||||
if e == nil && vdh.GetStorageType() == 0xf && vdh.GetTotalBlocks() == 280 {
|
||||
|
@ -857,7 +910,6 @@ func (dsk *DSKWrapper) Identify() {
|
|||
dsk.CurrentSectorOrder = PRODOS_SECTOR_ORDER
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
|
||||
//fmt.Println("THIS IS A PRODOS DISKETTE, ProDOS Ordered")
|
||||
return
|
||||
|
||||
}
|
||||
|
@ -866,41 +918,39 @@ func (dsk *DSKWrapper) Identify() {
|
|||
case DF_PRODOS_800KB:
|
||||
|
||||
vdh, e := dsk.PRODOS800GetVDH(2)
|
||||
//fmt.Printf("Blocks = %d\n", vdh.GetTotalBlocks())
|
||||
if e == nil && vdh.GetStorageType() == 0xf && vdh.GetTotalBlocks() == 1600 {
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS_800KB)
|
||||
dsk.Layout = SectorOrderDOS33
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
//dsk.SetNibbles(dsk.nibblize())
|
||||
dsk.SetNibbles(make([]byte, 232960))
|
||||
|
||||
//fmt.Println("THIS IS A PRODOS DISKETTE, DOS Ordered")
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch hint.ID {
|
||||
case DF_PRODOS:
|
||||
switch hint {
|
||||
case SectorOrderProDOS:
|
||||
dsk.Format = GetDiskFormat(DF_PRODOS)
|
||||
dsk.Layout = SectorOrderProDOS
|
||||
dsk.CurrentSectorOrder = PRODOS_SECTOR_ORDER
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
//fmt.Println("VTOC read failed, will nibblize anyway...")
|
||||
case DF_DOS_SECTORS_16:
|
||||
//fmt.Println("VTOC read failed, will nibblize anyway...")
|
||||
dsk.Layout = SectorOrderProDOS
|
||||
return
|
||||
case SectorOrderDOS33:
|
||||
dsk.Layout = SectorOrderDOS33
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
dsk.Format = GetDiskFormat(DF_DOS_SECTORS_16)
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
return
|
||||
}
|
||||
|
||||
dsk.Format = GetDiskFormat(DF_NONE)
|
||||
dsk.Layout = SectorOrderDOS33
|
||||
dsk.CurrentSectorOrder = DOS_33_SECTOR_ORDER
|
||||
dsk.SetNibbles(dsk.Nibblize())
|
||||
|
||||
}
|
||||
|
||||
//func (d *DSKWrapper) ReadFileSectorsProDOS(fd ProDOSFileDescriptor) ([]byte, error) {
|
||||
|
||||
//}
|
||||
|
||||
func Dump(bytes []byte) {
|
||||
perline := 0xC
|
||||
base := 0
|
||||
|
|
|
@ -205,8 +205,10 @@ func (fd *FileDescriptor) TotalSectors() int {
|
|||
}
|
||||
|
||||
func (fd *FileDescriptor) SetTotalSectors(v int) {
|
||||
fmt.Printf("Call to set sector count to %d\n", v)
|
||||
fd.Data[0x21] = byte(v & 0xff)
|
||||
fd.Data[0x22] = byte(v / 0x100)
|
||||
fmt.Printf("Sector count bytes are %d, %d\n", fd.Data[0x21], fd.Data[0x22])
|
||||
}
|
||||
|
||||
func (fd *FileDescriptor) SetTrackSectorListStart(t, s int) {
|
||||
|
@ -255,7 +257,11 @@ func (fd *VTOC) GetTrackOrder() int {
|
|||
}
|
||||
|
||||
func (fd *VTOC) BytesPerSector() int {
|
||||
return int(fd.Data[0x36]) + 256*int(fd.Data[0x37])
|
||||
size := int(fd.Data[0x36]) + 256*int(fd.Data[0x37])
|
||||
if size < 256 {
|
||||
size = 256
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
func (fd *VTOC) IsTSFree(t, s int) bool {
|
||||
|
@ -901,25 +907,33 @@ func (dsk *DSKWrapper) AppleDOSWriteFile(name string, kind FileType, data []byte
|
|||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("Writing file with type %s\n", kind.Ext())
|
||||
|
||||
if kind == FileTypeBIN {
|
||||
l := len(data)
|
||||
fmt.Printf("Length is %d\n", l)
|
||||
header := []byte{byte(l % 256), byte(l / 256)}
|
||||
data = append(header, data...)
|
||||
}
|
||||
|
||||
if kind != FileTypeTXT {
|
||||
header := []byte{byte(loadAddr % 256), byte(loadAddr / 256)}
|
||||
data = append(header, data...)
|
||||
}
|
||||
|
||||
// 1st: check we have sufficient space...
|
||||
tsBlocks, dataBlocks, err := dsk.AppleDOSGetFreeSectors(len(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// try get catalog entry - delete existing if match found
|
||||
fd, err := dsk.AppleDOSNamedCatalogEntry(name)
|
||||
//fmt.Println("FD=", fd)
|
||||
if err == nil {
|
||||
if kind != fd.Type() {
|
||||
return errors.New("File type mismatch")
|
||||
} else {
|
||||
// need to delete this file...
|
||||
err = dsk.AppleDOSDeleteFile(name)
|
||||
err = dsk.AppleDOSRemoveFile(fd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// reread vtoc here
|
||||
vtoc, err = dsk.AppleDOSGetVTOC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -931,7 +945,14 @@ func (dsk *DSKWrapper) AppleDOSWriteFile(name string, kind FileType, data []byte
|
|||
}
|
||||
}
|
||||
|
||||
// 1st: check we have sufficient space...
|
||||
tsBlocks, dataBlocks, err := dsk.AppleDOSGetFreeSectors(len(data))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2nd: check we can get a free catalog entry
|
||||
sectorCount := len(dataBlocks)
|
||||
|
||||
// 3rd: Write the datablocks
|
||||
var block int = 0
|
||||
|
@ -1010,7 +1031,8 @@ func (dsk *DSKWrapper) AppleDOSWriteFile(name string, kind FileType, data []byte
|
|||
fd.SetName(name)
|
||||
fd.SetTrackSectorListStart(tsBlocks[0][0], tsBlocks[0][1])
|
||||
fd.SetType(kind)
|
||||
fd.SetTotalSectors(len(dataBlocks))
|
||||
fd.SetTotalSectors(sectorCount)
|
||||
fd.Publish(dsk)
|
||||
|
||||
return nil
|
||||
|
||||
|
@ -1045,6 +1067,8 @@ func (d *DSKWrapper) AppleDOSRemoveFile(fd *FileDescriptor) error {
|
|||
vtoc.SetTSFree(pair[0], pair[1], true)
|
||||
}
|
||||
|
||||
vtoc.Publish(d)
|
||||
|
||||
fd.Data[0x00] = 0xff
|
||||
fd.SetName("")
|
||||
return fd.Publish(d)
|
||||
|
|
|
@ -137,7 +137,9 @@ func (pvh *PascalVolumeHeader) GetNameLength() int {
|
|||
|
||||
func (pvh *PascalVolumeHeader) GetName() string {
|
||||
l := pvh.GetNameLength()
|
||||
return string(pvh.data[0x07 : 0x07+l])
|
||||
s := strings.Trim(string(pvh.data[0x07:0x07+l]), " ")
|
||||
s += "." + PascalFileType(pvh.GetType()).Ext()
|
||||
return s
|
||||
}
|
||||
|
||||
func (pvh *PascalVolumeHeader) GetTotalBlocks() int {
|
||||
|
@ -182,7 +184,7 @@ func (pvh *PascalFileEntry) GetNameLength() int {
|
|||
|
||||
func (pvh *PascalFileEntry) GetName() string {
|
||||
l := pvh.GetNameLength()
|
||||
return string(pvh.data[0x07 : 0x07+l])
|
||||
return strings.Trim(string(pvh.data[0x07:0x07+l]), "")
|
||||
}
|
||||
|
||||
func (pvh *PascalFileEntry) GetBytesRemaining() int {
|
||||
|
|
|
@ -2,10 +2,10 @@ package disk
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type VDH struct {
|
||||
|
@ -75,6 +75,10 @@ func (fd *VDH) GetNameLength() int {
|
|||
return int(fd.Data[0] & 0xf)
|
||||
}
|
||||
|
||||
func (fd *VDH) SetNameLength(l int) {
|
||||
fd.Data[0] = (fd.Data[0] & 0xf0) | byte(l&0x0f)
|
||||
}
|
||||
|
||||
func (fd *VDH) GetStorageType() ProDOSStorageType {
|
||||
return ProDOSStorageType((fd.Data[0]) >> 4)
|
||||
}
|
||||
|
@ -83,6 +87,21 @@ func (fd *VDH) GetDirName() string {
|
|||
return fd.GetVolumeName()
|
||||
}
|
||||
|
||||
func (fd *VDH) SetVolumeName(s string) {
|
||||
if len(s) > 15 {
|
||||
s = s[:15]
|
||||
}
|
||||
l := len(s)
|
||||
for i := 0; i < 15; i++ {
|
||||
if i < l {
|
||||
fd.Data[1+i] = byte(s[i])
|
||||
} else {
|
||||
fd.Data[1+i] = 0x00
|
||||
}
|
||||
}
|
||||
fd.SetNameLength(l)
|
||||
}
|
||||
|
||||
func (fd *VDH) GetVolumeName() string {
|
||||
|
||||
l := fd.GetNameLength()
|
||||
|
@ -193,9 +212,9 @@ func (fd *VDH) Publish(dsk *DSKWrapper) error {
|
|||
for i, v := range fd.Data {
|
||||
bd[fd.blockoffset+i] = v
|
||||
}
|
||||
fmt.Printf("Writing dir header at block %d\n", fd.blockid)
|
||||
//fmt.Printf("Writing dir header at block %d\n", fd.blockid)
|
||||
|
||||
fmt.Printf("Data=%v\n", bd)
|
||||
//fmt.Printf("Data=%v\n", bd)
|
||||
|
||||
return dsk.PRODOSWrite(fd.blockid, bd)
|
||||
}
|
||||
|
@ -246,6 +265,28 @@ func (dsk *DSKWrapper) IsProDOS() (bool, DiskFormat, SectorOrder) {
|
|||
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
fmt.Println("Trying alternative format identification")
|
||||
|
||||
layouts := []SectorOrder{SectorOrderDOS33, SectorOrderDOS33Alt, SectorOrderProDOS, SectorOrderProDOSLinear}
|
||||
|
||||
for _, l := range layouts {
|
||||
|
||||
dsk.Layout = l
|
||||
vdh, err := dsk.PRODOSGetVDH(2)
|
||||
if err != nil {
|
||||
return false, oldFormat, oldLayout
|
||||
}
|
||||
|
||||
fmt.Printf("Blocks = %d, Size/512 = %d, Storage Type = %d\n", vdh.GetTotalBlocks(), len(dsk.Data)/512, vdh.GetStorageType())
|
||||
|
||||
if vdh.GetStorageType() == 0xf && vdh.GetTotalBlocks() == len(dsk.Data)/512 {
|
||||
return true, GetDiskFormat(DF_PRODOS_CUSTOM), l
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return false, oldFormat, oldLayout
|
||||
|
@ -324,6 +365,9 @@ func (vb ProDOSVolumeBitmap) IsBlockFree(b int) bool {
|
|||
}
|
||||
|
||||
func (vb ProDOSVolumeBitmap) SetBlockFree(b int, free bool) {
|
||||
|
||||
//log.Printf("Freeing block %d", b)
|
||||
|
||||
bidx := b / 8
|
||||
bit := 7 - (b % 8)
|
||||
setmask := byte(1 << uint(bit))
|
||||
|
@ -813,6 +857,15 @@ func (fd *ProDOSFileDescriptor) HeaderPointer() int {
|
|||
|
||||
func (d *DSKWrapper) PRODOS800GetBlock(block int) ([]byte, error) {
|
||||
|
||||
if len(d.Data) > 819200 {
|
||||
maxblocks := len(d.Data) / 512
|
||||
if block < maxblocks {
|
||||
offset := 512 * block
|
||||
return d.Data[offset : offset+512], nil
|
||||
}
|
||||
return nil, errors.New("Invalid block")
|
||||
}
|
||||
|
||||
t, s1, s2 := d.PRODOS800GetBlockSectors(block)
|
||||
|
||||
e := d.Seek(t, s1)
|
||||
|
@ -881,6 +934,20 @@ func (d *DSKWrapper) PRODOSGetVDH(b int) (*VDH, error) {
|
|||
|
||||
}
|
||||
|
||||
func (d *DSKWrapper) PRODOSSetVDH(b int, vdh *VDH) error {
|
||||
|
||||
data, e := d.PRODOSGetBlock(b)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
||||
for i, v := range vdh.Data {
|
||||
data[4+i] = v
|
||||
}
|
||||
|
||||
return d.PRODOSWrite(b, data)
|
||||
}
|
||||
|
||||
func (d *DSKWrapper) PRODOSGetCatalogPathed(start int, path string, pattern string) (*VDH, []ProDOSFileDescriptor, error) {
|
||||
|
||||
path = strings.Trim(path, "/")
|
||||
|
@ -942,9 +1009,13 @@ func (d *DSKWrapper) PRODOSGetCatalog(startblock int, pattern string) (*VDH, []P
|
|||
var e error
|
||||
var vtoc *VDH
|
||||
|
||||
if d.Format.ID == DF_PRODOS_800KB {
|
||||
fmt.Printf("FormatID = %s", d.Format.String())
|
||||
|
||||
if d.Format.ID == DF_PRODOS_800KB || d.Format.ID == DF_PRODOS_CUSTOM {
|
||||
fmt.Println("HC VDH")
|
||||
vtoc, e = d.PRODOS800GetVDH(startblock)
|
||||
} else {
|
||||
fmt.Println("LC VDH")
|
||||
vtoc, e = d.PRODOSGetVDH(startblock)
|
||||
}
|
||||
if e != nil {
|
||||
|
@ -958,7 +1029,7 @@ func (d *DSKWrapper) PRODOSGetCatalog(startblock int, pattern string) (*VDH, []P
|
|||
refnum := startblock
|
||||
|
||||
var data []byte
|
||||
if d.Format.ID == DF_PRODOS_800KB {
|
||||
if d.Format.ID == DF_PRODOS_800KB || d.Format.ID == DF_PRODOS_CUSTOM {
|
||||
data, _ = d.PRODOS800GetBlock(refnum)
|
||||
} else {
|
||||
data, _ = d.PRODOSGetBlock(refnum)
|
||||
|
@ -966,7 +1037,7 @@ func (d *DSKWrapper) PRODOSGetCatalog(startblock int, pattern string) (*VDH, []P
|
|||
|
||||
nextblock := int(data[2]) + 256*int(data[3])
|
||||
|
||||
//fmt.Printf("ActiveCount = %d\n", filecount)
|
||||
fmt.Printf("ActiveCount = %d\n", filecount)
|
||||
|
||||
entrypointer := 4 + PRODOS_ENTRY_SIZE
|
||||
|
||||
|
@ -982,7 +1053,7 @@ func (d *DSKWrapper) PRODOSGetCatalog(startblock int, pattern string) (*VDH, []P
|
|||
|
||||
var skipname bool = false
|
||||
if re != nil {
|
||||
//fmt.Printf("Checking [%s] against regex /%s/\n", fd.Name(), patterntmp)
|
||||
fmt.Printf("Checking [%s] against regex /%s/\n", fd.Name(), patterntmp)
|
||||
skipname = !re.MatchString(fd.Name())
|
||||
}
|
||||
|
||||
|
@ -998,7 +1069,7 @@ func (d *DSKWrapper) PRODOSGetCatalog(startblock int, pattern string) (*VDH, []P
|
|||
if activeentries < filecount {
|
||||
if blockentries == entriesperblock {
|
||||
refnum = nextblock
|
||||
if d.Format.ID == DF_PRODOS_800KB {
|
||||
if d.Format.ID == DF_PRODOS_800KB || d.Format.ID == DF_PRODOS_CUSTOM {
|
||||
data, err = d.PRODOS800GetBlock(refnum)
|
||||
} else {
|
||||
data, err = d.PRODOSGetBlock(refnum)
|
||||
|
@ -1028,7 +1099,7 @@ func (d *DSKWrapper) PRODOSReadFileSectors(fd ProDOSFileDescriptor, maxblocks in
|
|||
switch fd.GetStorageType() {
|
||||
case StorageType_Seedling:
|
||||
/* single block pointed to */
|
||||
if d.Format.ID == DF_PRODOS_800KB {
|
||||
if d.Format.ID == DF_PRODOS_800KB || d.Format.ID == DF_PRODOS_CUSTOM {
|
||||
data, _ = d.PRODOS800GetBlock(fd.IndexBlock())
|
||||
} else {
|
||||
data, _ = d.PRODOSGetBlock(fd.IndexBlock())
|
||||
|
@ -1039,7 +1110,7 @@ func (d *DSKWrapper) PRODOSReadFileSectors(fd ProDOSFileDescriptor, maxblocks in
|
|||
}
|
||||
return data[:count], e
|
||||
case StorageType_Sapling:
|
||||
if d.Format.ID == DF_PRODOS_800KB {
|
||||
if d.Format.ID == DF_PRODOS_800KB || d.Format.ID == DF_PRODOS_CUSTOM {
|
||||
index, _ = d.PRODOS800GetBlock(fd.IndexBlock())
|
||||
} else {
|
||||
index, _ = d.PRODOSGetBlock(fd.IndexBlock())
|
||||
|
@ -1137,11 +1208,13 @@ func (d *DSKWrapper) PRODOS800ChecksumBlock(b int) string {
|
|||
|
||||
func (d *DSKWrapper) PRODOSGetBlockSectors(block int) (int, int, int) {
|
||||
|
||||
track := block / PRODOS_BLOCKS_PER_TRACK
|
||||
bpt := d.Format.USPT() / 2
|
||||
|
||||
bo := block % PRODOS_BLOCKS_PER_TRACK
|
||||
track := block / bpt
|
||||
|
||||
if d.Layout == SectorOrderProDOSLinear {
|
||||
bo := block % bpt
|
||||
|
||||
if d.Layout == SectorOrderProDOSLinear || len(d.Data) >= PRODOS_800KB_DISK_BYTES {
|
||||
return track, bo * 2, bo*2 + 1
|
||||
}
|
||||
|
||||
|
@ -1390,10 +1463,16 @@ func (dsk *DSKWrapper) PRODOSDeleteFile(path string, name string) error {
|
|||
return err
|
||||
}
|
||||
|
||||
maxBlocks := 1600
|
||||
if len(dsk.Data) == STD_DISK_BYTES {
|
||||
maxBlocks = 280
|
||||
}
|
||||
|
||||
i := 0
|
||||
b := int(ib[2*i+0]) + 256*int(ib[2*i+1])
|
||||
for i < 256 && b != 0 {
|
||||
b = int(ib[2*i+0]) + 256*int(ib[2*i+1])
|
||||
b := int(ib[i]) + 256*int(ib[256+i])
|
||||
for i < 256 && b != 0 && b <= maxBlocks {
|
||||
removeBlocks = append(removeBlocks, b)
|
||||
b = int(ib[i]) + 256*int(ib[256+i])
|
||||
i++
|
||||
}
|
||||
}
|
||||
|
@ -1543,6 +1622,8 @@ func (fd *ProDOSFileDescriptor) Publish(dsk *DSKWrapper) error {
|
|||
|
||||
func (dsk *DSKWrapper) PRODOSWrite(b int, data []byte) error {
|
||||
|
||||
//log.Printf("====> Request to write block %.4x", b)
|
||||
|
||||
for len(data) < 512 {
|
||||
data = append(data, 0x00)
|
||||
}
|
||||
|
|
|
@ -74,7 +74,7 @@ func (f RDOSFormat) Spec() *RDOSFormatSpec {
|
|||
switch f {
|
||||
case RDOS_32:
|
||||
return &RDOSFormatSpec{
|
||||
SectorStride: 13,
|
||||
SectorStride: 16,
|
||||
SectorMax: 13,
|
||||
CatalogTrack: 1,
|
||||
CatalogSector: 0,
|
||||
|
@ -84,8 +84,8 @@ func (f RDOSFormat) Spec() *RDOSFormatSpec {
|
|||
return &RDOSFormatSpec{
|
||||
SectorStride: 16,
|
||||
SectorMax: 13,
|
||||
CatalogTrack: 0,
|
||||
CatalogSector: 1,
|
||||
CatalogTrack: 1,
|
||||
CatalogSector: 0,
|
||||
Ordering: SectorOrderDOS33,
|
||||
}
|
||||
case RDOS_33:
|
||||
|
@ -167,7 +167,7 @@ const (
|
|||
|
||||
var RDOSTypeMap = map[RDOSFileType][2]string{
|
||||
FileType_RDOS_Unknown: [2]string{"UNK", "Unknown"},
|
||||
FileType_RDOS_AppleSoft: [2]string{"APP", "Applesoft Basic Program"},
|
||||
FileType_RDOS_AppleSoft: [2]string{"BAS", "Applesoft Basic Program"},
|
||||
FileType_RDOS_Binary: [2]string{"BIN", "Binary File"},
|
||||
FileType_RDOS_Text: [2]string{"TXT", "ASCII Text"},
|
||||
}
|
||||
|
@ -228,11 +228,11 @@ func (fd *RDOSFileDescriptor) Name() string {
|
|||
str = strings.TrimRight(str, " ")
|
||||
switch fd.Type() {
|
||||
case FileType_RDOS_AppleSoft:
|
||||
str += ".a"
|
||||
str += ".bas"
|
||||
case FileType_RDOS_Binary:
|
||||
str += ".s"
|
||||
str += ".bin"
|
||||
case FileType_RDOS_Text:
|
||||
str += ".t"
|
||||
str += ".txt"
|
||||
}
|
||||
|
||||
return str
|
||||
|
@ -252,7 +252,7 @@ func (fd *RDOSFileDescriptor) NameUnadorned() string {
|
|||
|
||||
}
|
||||
|
||||
return str
|
||||
return strings.TrimSpace(str)
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -102,11 +102,11 @@ func analyzeDOS13(id int, dsk *disk.DSKWrapper, info *Disk) {
|
|||
Modified: time.Now(),
|
||||
}
|
||||
|
||||
_, _, data, err := dsk.AppleDOSReadFileRaw(fd)
|
||||
size, addr, data, err := dsk.AppleDOSReadFileRaw(fd)
|
||||
if err == nil {
|
||||
sum := sha256.Sum256(data)
|
||||
file.SHA256 = hex.EncodeToString(sum[:])
|
||||
file.Size = len(data)
|
||||
file.Size = size
|
||||
if *ingestMode&1 == 1 {
|
||||
if fd.Type() == disk.FileTypeAPP {
|
||||
file.Text = disk.ApplesoftDetoks(data)
|
||||
|
@ -124,8 +124,8 @@ func analyzeDOS13(id int, dsk *disk.DSKWrapper, info *Disk) {
|
|||
file.TypeCode = TypeMask_AppleDOS | TypeCode(fd.Type())
|
||||
file.LoadAddress = 0x0000
|
||||
} else if fd.Type() == disk.FileTypeBIN && len(data) >= 2 {
|
||||
file.LoadAddress = int(data[0]) + 256*int(data[1])
|
||||
file.Data = data[2:]
|
||||
file.LoadAddress = addr
|
||||
file.Data = data
|
||||
file.TypeCode = TypeMask_AppleDOS | TypeCode(fd.Type())
|
||||
} else {
|
||||
file.LoadAddress = 0x0000
|
||||
|
|
|
@ -136,11 +136,11 @@ func analyzeDOS16(id int, dsk *disk.DSKWrapper, info *Disk) {
|
|||
}
|
||||
|
||||
//l.Log("start read")
|
||||
_, _, data, err := dsk.AppleDOSReadFileRaw(fd)
|
||||
size, addr, data, err := dsk.AppleDOSReadFileRaw(fd)
|
||||
if err == nil {
|
||||
sum := sha256.Sum256(data)
|
||||
file.SHA256 = hex.EncodeToString(sum[:])
|
||||
file.Size = len(data)
|
||||
file.Size = size
|
||||
if *ingestMode&1 == 1 {
|
||||
if fd.Type() == disk.FileTypeAPP {
|
||||
file.Text = disk.ApplesoftDetoks(data)
|
||||
|
@ -158,8 +158,8 @@ func analyzeDOS16(id int, dsk *disk.DSKWrapper, info *Disk) {
|
|||
file.TypeCode = TypeMask_AppleDOS | TypeCode(fd.Type())
|
||||
file.LoadAddress = 0x0000
|
||||
} else if fd.Type() == disk.FileTypeBIN && len(data) >= 2 {
|
||||
file.LoadAddress = int(data[0]) + 256*int(data[1])
|
||||
file.Data = data[2:]
|
||||
file.LoadAddress = addr
|
||||
file.Data = data
|
||||
file.TypeCode = TypeMask_AppleDOS | TypeCode(fd.Type())
|
||||
} else {
|
||||
file.LoadAddress = 0x0000
|
||||
|
|
7
go.mod
Normal file
7
go.mod
Normal file
|
@ -0,0 +1,7 @@
|
|||
module github.com/paleotronic/diskm8
|
||||
|
||||
go 1.22.3
|
||||
|
||||
require github.com/chzyer/readline v1.5.1
|
||||
|
||||
require golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 // indirect
|
8
go.sum
Normal file
8
go.sum
Normal file
|
@ -0,0 +1,8 @@
|
|||
github.com/chzyer/logex v1.2.1 h1:XHDu3E6q+gdHgsdTPH6ImJMIp436vR6MPtH8gP05QzM=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v1.5.1 h1:upd/6fQk4src78LMRzh5vItIt361/o4uq553V8B5sGI=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v1.0.0 h1:p3BQDXSxOhOG0P9z6/hGnII4LGiEPOYBhs8asl/fC04=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5 h1:y/woIyUBFbpQGKS0u1aHF/40WUDnek3fPOyD08H5Vng=
|
||||
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
@ -36,7 +36,7 @@ func Get(id int) *Logger {
|
|||
func NewLogger(id int, app string) *Logger {
|
||||
|
||||
if app == "" {
|
||||
app = "dskalyzer"
|
||||
app = "diskm8"
|
||||
}
|
||||
|
||||
filename := fmt.Sprintf("%s_%d_%s.log", app, id, fts())
|
||||
|
|
26
main.go
26
main.go
|
@ -13,25 +13,19 @@ through as time goes by.
|
|||
*/
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
|
||||
"flag"
|
||||
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/paleotronic/diskm8/disk"
|
||||
"github.com/paleotronic/diskm8/loggy"
|
||||
|
||||
"runtime"
|
||||
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
"github.com/paleotronic/diskm8/panic"
|
||||
)
|
||||
|
||||
|
@ -95,7 +89,7 @@ var shell = flag.Bool("shell", false, "Start interactive mode")
|
|||
var shellBatch = flag.String("shell-batch", "", "Execute shell command(s) from file and exit")
|
||||
var withDisk = flag.String("with-disk", "", "Perform disk operation (-file-extract,-file-put,-file-delete)")
|
||||
var withPath = flag.String("with-path", "", "Target path for disk operation (-file-extract,-file-put,-file-delete)")
|
||||
var fileExtract = flag.String("file-extract", "", "File to delete from disk (-with-disk)")
|
||||
var fileExtract = flag.String("file-extract", "", "File to extract from disk (-with-disk)")
|
||||
var filePut = flag.String("file-put", "", "File to put on disk (-with-disk)")
|
||||
var fileDelete = flag.String("file-delete", "", "File to delete (-with-disk)")
|
||||
var fileMkdir = flag.String("dir-create", "", "Directory to create (-with-disk)")
|
||||
|
@ -106,12 +100,12 @@ func main() {
|
|||
|
||||
runtime.GOMAXPROCS(8)
|
||||
|
||||
banner()
|
||||
|
||||
//l.Default.Level = l.LevelCrit
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if *withDisk == "" && *shellBatch == "" {
|
||||
banner()
|
||||
}
|
||||
|
||||
var filterpath []string
|
||||
|
||||
if *filterPath || *shell {
|
||||
|
|
4
make.sh
4
make.sh
|
@ -1,12 +1,10 @@
|
|||
#!/bin/bash
|
||||
|
||||
ARCHES="darwin-amd64 windows-386 windows-amd64 linux-386 linux-amd64 linux-arm freebsd-arm freebsd-amd64 freebsd-386"
|
||||
ARCHES="darwin-amd64 darwin-arm64 windows-386 windows-amd64 windows-arm64 linux-386 linux-amd64 linux-arm freebsd-arm freebsd-amd64 freebsd-386"
|
||||
PUBLISH="publish"
|
||||
|
||||
mkdir -p "$PUBLISH"
|
||||
|
||||
go get github.com/chzyer/readline
|
||||
|
||||
exitState=0
|
||||
for arch in `echo $ARCHES`; do
|
||||
export GOOS=`echo $arch | awk -F"-" '{print $1}'`
|
||||
|
|
100
shell.go
100
shell.go
|
@ -262,6 +262,20 @@ func init() {
|
|||
"Mounts disk and switches to the new slot",
|
||||
},
|
||||
},
|
||||
"setvolume": &shellCommand{
|
||||
Name: "setvolume",
|
||||
Description: "Sets the ProDOS volume name",
|
||||
MinArgs: 1,
|
||||
MaxArgs: 1,
|
||||
Code: shellVolumeName,
|
||||
NeedsMount: true,
|
||||
Context: sccNone,
|
||||
Text: []string{
|
||||
"setvolume <volume name>",
|
||||
"",
|
||||
"Set ProDOS volume name. Truncated to 15 chars if too long.",
|
||||
},
|
||||
},
|
||||
"unmount": &shellCommand{
|
||||
Name: "unmount",
|
||||
Description: "unmount disk image",
|
||||
|
@ -820,10 +834,15 @@ func shellCat(args []string) int {
|
|||
}
|
||||
|
||||
bs := 256
|
||||
volumename := "no-name"
|
||||
if info.FormatID.ID == disk.DF_PASCAL || info.FormatID.ID == disk.DF_PRODOS ||
|
||||
info.FormatID.ID == disk.DF_PRODOS_800KB || info.FormatID.ID == disk.DF_PRODOS_400KB ||
|
||||
info.FormatID.ID == disk.DF_PRODOS_CUSTOM {
|
||||
bs = 512
|
||||
vdh, err := commandVolumes[commandTarget].PRODOSGetVDH(2)
|
||||
if err == nil {
|
||||
volumename = vdh.GetVolumeName()
|
||||
}
|
||||
}
|
||||
|
||||
pattern := "*"
|
||||
|
@ -833,6 +852,8 @@ func shellCat(args []string) int {
|
|||
|
||||
files, _ := globDisk(commandTarget, pattern)
|
||||
|
||||
fmt.Printf("Volume Name is %s\n\n", volumename)
|
||||
|
||||
fmt.Printf("%-33s %6s %2s %-23s %s\n", "NAME", "BLOCKS", "RO", "KIND", "ADDITONAL")
|
||||
for _, f := range files {
|
||||
add := ""
|
||||
|
@ -1042,6 +1063,36 @@ func shellMkdir(args []string) int {
|
|||
|
||||
}
|
||||
|
||||
func shellVolumeName(args []string) int {
|
||||
|
||||
fullpath, _ := filepath.Abs(commandVolumes[commandTarget].Filename)
|
||||
|
||||
_, err := analyze(0, fullpath)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
|
||||
name := strings.ToUpper(args[0])
|
||||
|
||||
if formatIn(commandVolumes[commandTarget].Format.ID, []disk.DiskFormatID{disk.DF_PRODOS, disk.DF_PRODOS_800KB, disk.DF_PRODOS_400KB, disk.DF_PRODOS_CUSTOM}) {
|
||||
vdh, err := commandVolumes[commandTarget].PRODOSGetVDH(2)
|
||||
if err != nil {
|
||||
fmt.Printf("Failed to get Volume Directory Header: %v\n", err)
|
||||
return -1
|
||||
}
|
||||
vdh.SetVolumeName(name)
|
||||
commandVolumes[commandTarget].PRODOSSetVDH(2, vdh)
|
||||
fmt.Printf("Volume name is now %s\n", vdh.GetVolumeName())
|
||||
saveDisk(commandVolumes[commandTarget], fullpath)
|
||||
} else {
|
||||
fmt.Println("Do not support setvolume on " + commandVolumes[commandTarget].Format.String() + ".")
|
||||
return 0
|
||||
}
|
||||
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
func isASCII(in []byte) bool {
|
||||
for _, v := range in {
|
||||
if v > 128 {
|
||||
|
@ -1060,15 +1111,42 @@ func shellPut(args []string) int {
|
|||
return 1
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(args[0])
|
||||
parts := strings.Split(args[0], ",")
|
||||
|
||||
data, err := ioutil.ReadFile(parts[0])
|
||||
if err != nil {
|
||||
return -1
|
||||
}
|
||||
|
||||
addr := int64(0x0801)
|
||||
name := filepath.Base(args[0])
|
||||
reTrailAddr := regexp.MustCompile("(?i)^([^,]+)([,]A(([$]|0x)[0-9a-f]+))?([,]L(([$]|0x)[0-9a-f]+))?$")
|
||||
if reTrailAddr.MatchString(name) {
|
||||
m := reTrailAddr.FindAllStringSubmatch(name, -1)
|
||||
name = m[0][1]
|
||||
saddr := m[0][3]
|
||||
slen := m[0][6]
|
||||
if saddr != "" {
|
||||
if strings.HasPrefix(saddr, "$") {
|
||||
saddr = "0x" + saddr[1:]
|
||||
}
|
||||
addr, _ = strconv.ParseInt(saddr, 0, 32)
|
||||
}
|
||||
if slen != "" {
|
||||
if strings.HasPrefix(slen, "$") {
|
||||
slen = "0x" + slen[1:]
|
||||
}
|
||||
nlen, _ := strconv.ParseInt(slen, 0, 32)
|
||||
if int(nlen) < len(data) {
|
||||
data = data[:int(nlen)]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if formatIn(commandVolumes[commandTarget].Format.ID, []disk.DiskFormatID{disk.DF_DOS_SECTORS_13, disk.DF_DOS_SECTORS_16}) {
|
||||
addr := int64(0x0801)
|
||||
name := filepath.Base(args[0])
|
||||
|
||||
kind := disk.FileTypeAPP
|
||||
|
||||
reSpecial := regexp.MustCompile("(?i)^(.+)[#](0x[a-fA-F0-9]+)[.]([A-Za-z]+)$")
|
||||
ext := strings.Trim(filepath.Ext(name), ".")
|
||||
if reSpecial.MatchString(name) {
|
||||
|
@ -1078,7 +1156,9 @@ func shellPut(args []string) int {
|
|||
addrStr := m[0][2]
|
||||
addr, _ = strconv.ParseInt(addrStr, 0, 32)
|
||||
} else {
|
||||
name = strings.Replace(name, "."+ext, "", -1)
|
||||
//name = strings.Replace(name, "."+ext, "", -1)
|
||||
l := len(ext) + 1
|
||||
name = name[:len(name)-l]
|
||||
}
|
||||
|
||||
kind = disk.AppleDOSFileTypeFromExt(ext)
|
||||
|
@ -1106,8 +1186,7 @@ func shellPut(args []string) int {
|
|||
saveDisk(commandVolumes[commandTarget], fullpath)
|
||||
|
||||
} else if formatIn(commandVolumes[commandTarget].Format.ID, []disk.DiskFormatID{disk.DF_PRODOS, disk.DF_PRODOS_800KB, disk.DF_PRODOS_400KB, disk.DF_PRODOS_CUSTOM}) {
|
||||
addr := int64(0x0801)
|
||||
name := filepath.Base(args[0])
|
||||
|
||||
ext := strings.Trim(filepath.Ext(name), ".")
|
||||
reSpecial := regexp.MustCompile("(?i)^(.+)[#](0x[a-fA-F0-9]+)[.]([A-Za-z]+)$")
|
||||
if reSpecial.MatchString(name) {
|
||||
|
@ -1117,11 +1196,18 @@ func shellPut(args []string) int {
|
|||
addrStr := m[0][2]
|
||||
addr, _ = strconv.ParseInt(addrStr, 0, 32)
|
||||
} else {
|
||||
name = strings.Replace(name, "."+ext, "", -1)
|
||||
l := len(ext) + 1
|
||||
name = name[:len(name)-l]
|
||||
}
|
||||
|
||||
kind := disk.ProDOSFileTypeFromExt(ext)
|
||||
|
||||
if strings.ToLower(ext) == "system" {
|
||||
name += "." + ext
|
||||
ext = ""
|
||||
kind = disk.ProDOSFileTypeFromExt("SYS")
|
||||
}
|
||||
|
||||
if strings.HasSuffix(args[0], ".INT.ASC") {
|
||||
kind = disk.FileType_PD_INT
|
||||
} else if strings.HasSuffix(args[0], ".APP.ASC") {
|
||||
|
|
Loading…
Reference in New Issue
Block a user