Skip to content

Commit 8a6c4f9

Browse files
committed
add: add removeheader option when exporting files and add TUs
1 parent b6da293 commit 8a6c4f9

7 files changed

Lines changed: 269 additions & 41 deletions

File tree

amsdos/amsdos_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
package amsdos
2+
3+
import (
4+
"bytes"
5+
"encoding/binary"
6+
"testing"
7+
8+
"github.com/jeromelesaux/m4client/cpc"
9+
"github.com/stretchr/testify/assert"
10+
)
11+
12+
func TestCheckAmsdos(t *testing.T) {
13+
// Create a valid AMSDOS header
14+
header := &cpc.CpcHead{
15+
User: 0,
16+
Size: 100,
17+
Size2: 100,
18+
LogicalSize: 100,
19+
Type: 2, // binary
20+
Address: 0x4000,
21+
Exec: 0x4000,
22+
}
23+
header.Checksum = header.ComputedChecksum16()
24+
25+
var buf bytes.Buffer
26+
err := binary.Write(&buf, binary.LittleEndian, header)
27+
assert.NoError(t, err)
28+
29+
// Test valid header
30+
valid, parsedHeader := CheckAmsdos(buf.Bytes())
31+
assert.True(t, valid)
32+
assert.Equal(t, header, parsedHeader)
33+
34+
// Test invalid header (wrong checksum)
35+
header.Checksum = 0
36+
buf.Reset()
37+
err = binary.Write(&buf, binary.LittleEndian, header)
38+
assert.NoError(t, err)
39+
40+
valid, _ = CheckAmsdos(buf.Bytes())
41+
assert.False(t, valid)
42+
43+
// Test empty buffer
44+
valid, _ = CheckAmsdos([]byte{})
45+
assert.False(t, valid)
46+
}

cli/main.go

Lines changed: 43 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import (
1212
"os"
1313
"path"
1414
"path/filepath"
15-
"strconv"
1615
"strings"
1716

1817
"github.com/jeromelesaux/dsk/amsdos"
@@ -58,6 +57,7 @@ var (
5857
quiet = flag.Bool("quiet", false, "Suppress unnecessary output (useful for scripting).")
5958
stdoutOpt = flag.Bool("stdout", false, "To redirect to stdout when using get file")
6059
hidden = flag.Bool("hide", false, "hide the imported file")
60+
removeHeader = flag.Bool("removeheader", false, "remove amsdos header from exported file")
6161
appVersion = "0.34"
6262
version = flag.Bool("version", false, "Display the application version and exit.")
6363
)
@@ -127,7 +127,7 @@ func main() {
127127
if err {
128128
fmt.Fprintf(os.Stderr, "Error while opening file %s error :%s\n", dskFilepath, msg)
129129
}
130-
err, msg, _ = getFileDsk(d, "*", dskFilepath, dskfolderPath)
130+
err, msg, _ = getFileDsk(d, "*", dskFilepath, dskfolderPath, *removeHeader)
131131
if err {
132132
fmt.Fprintf(os.Stderr, "Error while writing file %s in folder %s error :%s\n", dskFilepath, dskfolderPath, msg)
133133
}
@@ -138,10 +138,18 @@ func main() {
138138
}
139139

140140
if *executeAddress != "" {
141-
execAddress = parseHexadecimal16bits(*executeAddress)
141+
var err error
142+
execAddress, err = utils.ParseHex16(*executeAddress)
143+
if err != nil {
144+
exitOnError(err.Error(), "Invalid execute address format")
145+
}
142146
}
143147
if *loadingAddress != "" {
144-
loadAddress = parseHexadecimal16bits(*loadingAddress)
148+
var err error
149+
loadAddress, err = utils.ParseHex16(*loadingAddress)
150+
if err != nil {
151+
exitOnError(err.Error(), "Invalid loading address format")
152+
}
145153
}
146154
if !*quiet {
147155
fmt.Fprintf(os.Stderr, "DSK cli version [%s]\nMade by Sid (ImpAct)\n", appVersion)
@@ -362,7 +370,7 @@ func main() {
362370
}
363371
}
364372
} else {
365-
isError, msg, hint := getFileDsk(d, *get, *dskPath, directory)
373+
isError, msg, hint := getFileDsk(d, *get, *dskPath, directory, *removeHeader)
366374
if isError {
367375
exitOnError(msg, hint)
368376
}
@@ -723,35 +731,6 @@ func fileinfoDsk(d dsk.DSK, fileInDsk string) (onError bool, message, hint strin
723731
return false, "", ""
724732
}
725733

726-
func parseHexadecimal16bits(address string) (value16 uint16) {
727-
switch address[0] {
728-
case '#':
729-
value := strings.Replace(address, "#", "", -1)
730-
v, err := strconv.ParseUint(value, 16, 16)
731-
if err != nil {
732-
fmt.Fprintf(os.Stderr, "cannot get the hexadecimal value fom %s, error : %v\n", *executeAddress, err)
733-
} else {
734-
value16 = uint16(v)
735-
}
736-
case '0':
737-
value := strings.Replace(address, "0x", "", -1)
738-
v, err := strconv.ParseUint(value, 16, 16)
739-
if err != nil {
740-
fmt.Fprintf(os.Stderr, "cannot get the hexadecimal value fom %s, error : %v\n", *executeAddress, err)
741-
} else {
742-
value16 = uint16(v)
743-
}
744-
default:
745-
v, err := strconv.ParseUint(address, 10, 16)
746-
if err != nil {
747-
fmt.Fprintf(os.Stderr, "cannot get the hexadecimal value fom %s, error : %v\n", *executeAddress, err)
748-
} else {
749-
value16 = uint16(v)
750-
}
751-
}
752-
return
753-
}
754-
755734
func analyseDsk(d dsk.DSK, dskPath string) (onError bool, message, hint string) {
756735
if err := d.CheckDsk(); err != nil {
757736
return true, fmt.Sprintf("Error while read dsk file (%s) error %v\n", dskPath, err), "Check your dsk file path or Check your dsk file with option -dsk yourdsk.dsk -analyze"
@@ -807,7 +786,7 @@ func putFileDsk(d dsk.DSK, fileInDsk, dskPath string, fileType string, loadAddre
807786
return false, "", ""
808787
}
809788

810-
func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string) (onError bool, message, hint string) {
789+
func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string, removeHeader bool) (onError bool, message, hint string) {
811790
if fileInDsk == "" {
812791
return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -get -amsdosfile hello.bin"
813792
}
@@ -846,6 +825,12 @@ func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string) (onError bool,
846825
if err != nil {
847826
return true, fmt.Sprintf("Error while creating file (%s) error %v\n", filename, err), "Check your dsk with option -dsk yourdsk.dsk -analyze"
848827
}
828+
if removeHeader {
829+
isAmsdos, _ := amsdos.CheckAmsdos(content)
830+
if isAmsdos {
831+
content = content[256:] // Remove the first 256 bytes (AMS/DOS header)
832+
}
833+
}
849834
_, err = af.Write(content)
850835
if err != nil {
851836
return true, fmt.Sprintf("Error while copying content in file (%s) error %v\n", filename, err), "Check your dsk with option -dsk yourdsk.dsk -analyze"
@@ -871,6 +856,12 @@ func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string) (onError bool,
871856
return true, fmt.Sprintf("Error while creating file (%s) error %v\n", filename, err), "Check your file path"
872857
}
873858
defer af.Close()
859+
if removeHeader {
860+
isAmsdos, _ := amsdos.CheckAmsdos(content)
861+
if isAmsdos {
862+
content = content[256:] // Remove the first 256 bytes (AMS/DOS header)
863+
}
864+
}
874865
_, err = af.Write(content)
875866
if err != nil {
876867
return true, fmt.Sprintf("Error while copying content in file (%s) error %v\n", filename, err), " Check your dsk with option -dsk yourdsk.dsk -analyze"
@@ -1480,25 +1471,37 @@ func analyseDataTest(dskFilepath string) bool {
14801471

14811472
func parsing16bitsRasmAnnotation() bool {
14821473
fmt.Printf("Parsing value rasm annotation #C000 ")
1483-
v := parseHexadecimal16bits("#C000")
1474+
v, err := utils.ParseHex16("#C000")
1475+
if err != nil {
1476+
return false
1477+
}
14841478
return v == 0xC000
14851479
}
14861480

14871481
func parsing16bitsCAnnotation() bool {
14881482
fmt.Printf("Parsing value c annotation 0xC000 ")
1489-
v := parseHexadecimal16bits("0xC000")
1483+
v, err := utils.ParseHex16("0xC000")
1484+
if err != nil {
1485+
return false
1486+
}
14901487
return v == 0xC000
14911488
}
14921489

14931490
func parsing16bitsIntegerAnnotation() bool {
14941491
fmt.Printf("Parsing value integer annotation 49152 ")
1495-
v := parseHexadecimal16bits("49152")
1492+
v, err := utils.ParseHex16("49152")
1493+
if err != nil {
1494+
return false
1495+
}
14961496
return v == 0xC000
14971497
}
14981498

14991499
func parsing8bitsRasmAnnotation() bool {
15001500
fmt.Printf("Parsing value c annotation #D0 ")
1501-
v := parseHexadecimal16bits("#D0")
1501+
v, err := utils.ParseHex16("#D0")
1502+
if err != nil {
1503+
return false
1504+
}
15021505
return v == 0xD0
15031506
}
15041507

@@ -1520,7 +1523,7 @@ func getFileBinaryDataTest(filePath, dskFilepath string) bool {
15201523
if onError {
15211524
return onError
15221525
}
1523-
isError, _, _ := getFileDsk(d, filePath, dskFilepath, "")
1526+
isError, _, _ := getFileDsk(d, filePath, dskFilepath, "", false)
15241527
return isError
15251528
}
15261529

dsk/dsk.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -692,7 +692,9 @@ func GetNomAmsdos(masque string) string {
692692
copy(amsdosFile[0:filenameSize], file[0:filenameSize])
693693
amsdosFile[8] = '.'
694694
ext := strings.ToUpper(filepath.Ext(masque))
695-
copy(amsdosFile[9:12], ext[1:])
695+
if len(ext) > 1 {
696+
copy(amsdosFile[9:12], ext[1:])
697+
}
696698
return string(amsdosFile)
697699
}
698700

dsk/dsk_test.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
package dsk
22

33
import (
4+
"bytes"
5+
"crypto/rand"
6+
"encoding/binary"
47
"fmt"
8+
"os"
59
"testing"
10+
11+
"github.com/jeromelesaux/dsk/amsdos"
12+
"github.com/stretchr/testify/assert"
613
)
714

815
func TestOpenDsk(t *testing.T) {
@@ -43,3 +50,68 @@ func TestMaskBit7(t *testing.T) {
4350
fmt.Printf("%d", v1|mask)
4451
fmt.Printf("%d", byte(0b00000011))
4552
}
53+
54+
func TestFormatDsk(t *testing.T) {
55+
d := FormatDsk(9, 40, 1, DataFormat, 0)
56+
assert.NotNil(t, d)
57+
assert.Equal(t, uint8(40), d.Entry.NbTracks)
58+
assert.Equal(t, uint8(1), d.Entry.NbHeads)
59+
}
60+
61+
func TestGetNomAmsdos(t *testing.T) {
62+
tests := []struct {
63+
input string
64+
expected string
65+
}{
66+
{"file.bin", "FILE .BIN"},
67+
{"test.bas", "TEST .BAS"},
68+
{"longfilename.txt", "LONGFILE.TXT"},
69+
{"a.txt", "A .TXT"},
70+
}
71+
72+
for _, tt := range tests {
73+
t.Run(tt.input, func(t *testing.T) {
74+
result := GetNomAmsdos(tt.input)
75+
assert.Equal(t, tt.expected, result)
76+
})
77+
}
78+
}
79+
80+
func TestPut(t *testing.T) {
81+
d := FormatDsk(9, 40, 1, DataFormat, 0)
82+
data := generateData(40136)
83+
name := GetNomAmsdos("FILE.BIN")
84+
data = addHeader(data, name)
85+
assert.NoError(t, d.CopyFile(data, name, uint16(len(data)), 256, uint16(MODE_BINAIRE), false, false, false))
86+
_, err := d.GetFileIn(name, 0)
87+
assert.NoError(t, err)
88+
89+
}
90+
91+
func generateData(len int) []byte {
92+
data := make([]byte, len)
93+
rand.Read(data)
94+
return data
95+
}
96+
97+
func addHeader(data []byte, name string) []byte {
98+
header := &amsdos.StAmsdos{}
99+
header.User = MODE_BINAIRE
100+
header.Size = uint16(len(data))
101+
header.Size2 = uint16(len(data))
102+
header.LogicalSize = uint16(len(data))
103+
header.Type = MODE_BINAIRE
104+
copy(header.Filename[:], []byte(name[0:12]))
105+
header.Address = 0x0000
106+
header.Checksum = header.ComputedChecksum16()
107+
var rbuff bytes.Buffer
108+
err := binary.Write(&rbuff, binary.LittleEndian, header)
109+
if err != nil {
110+
fmt.Fprintf(os.Stdout, "error while writing in header %v\n", err)
111+
}
112+
err = binary.Write(&rbuff, binary.LittleEndian, data)
113+
if err != nil {
114+
fmt.Fprintf(os.Stdout, "error while writing in content %v\n", err)
115+
}
116+
return rbuff.Bytes()
117+
}

sna/sna_test.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,28 @@ func TestMemoryChunck(t *testing.T) {
2828
assert.Equal(t, expected, got)
2929
})
3030
}
31+
32+
func TestNewSna(t *testing.T) {
33+
header := sna.NewSnaHeader()
34+
s := sna.NewSna(header)
35+
assert.NotNil(t, s)
36+
assert.Equal(t, header, s.Header)
37+
}
38+
39+
func TestCPCValue(t *testing.T) {
40+
tests := []struct {
41+
cpc sna.CPC
42+
expected uint8
43+
}{
44+
{sna.CPC464, 0},
45+
{sna.CPC664, 1},
46+
{sna.CPC6128, 2},
47+
}
48+
49+
for _, tt := range tests {
50+
t.Run(string(tt.cpc), func(t *testing.T) {
51+
result := sna.CPCValue(tt.cpc)
52+
assert.Equal(t, tt.expected, result)
53+
})
54+
}
55+
}

utils/hex.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package utils
2+
3+
import (
4+
"strconv"
5+
"strings"
6+
)
7+
8+
// ParseHex16 parses a hexadecimal string in various formats (#C000, 0xC000, 49152) and returns uint16
9+
func ParseHex16(address string) (uint16, error) {
10+
var value16 uint16
11+
switch address[0] {
12+
case '#':
13+
value := strings.Replace(address, "#", "", -1)
14+
v, err := strconv.ParseUint(value, 16, 16)
15+
if err != nil {
16+
return 0, err
17+
}
18+
value16 = uint16(v)
19+
case '0':
20+
if len(address) > 1 && address[1] == 'x' {
21+
value := strings.Replace(address, "0x", "", -1)
22+
v, err := strconv.ParseUint(value, 16, 16)
23+
if err != nil {
24+
return 0, err
25+
}
26+
value16 = uint16(v)
27+
} else {
28+
v, err := strconv.ParseUint(address, 10, 16)
29+
if err != nil {
30+
return 0, err
31+
}
32+
value16 = uint16(v)
33+
}
34+
default:
35+
v, err := strconv.ParseUint(address, 10, 16)
36+
if err != nil {
37+
return 0, err
38+
}
39+
value16 = uint16(v)
40+
}
41+
return value16, nil
42+
}

0 commit comments

Comments
 (0)