From e62413530a9f3f860338035a5b6368a4e992d050 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Wed, 30 Aug 2023 19:26:04 +0200 Subject: [PATCH 01/10] chore: customary filenames --- LICENCE.md => LICENSE.md | 0 makefile => Makefile | 4 ++-- readme.md => README.md | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) rename LICENCE.md => LICENSE.md (100%) rename makefile => Makefile (95%) rename readme.md => README.md (69%) diff --git a/LICENCE.md b/LICENSE.md similarity index 100% rename from LICENCE.md rename to LICENSE.md diff --git a/makefile b/Makefile similarity index 95% rename from makefile rename to Makefile index d20c216..296062f 100644 --- a/makefile +++ b/Makefile @@ -1,7 +1,7 @@ CC=go RM=rm MV=mv - +MODULE=$(awk '/^module / {print $2}' go.mod) SOURCEDIR=./cli SOURCES := $(shell find $(SOURCEDIR) -name '*.go') @@ -61,7 +61,7 @@ lint: golangci-lint run --timeout 5m ./... vulncheck: - govulncheck ./... + govulncheck $(MODULE) test: diff --git a/readme.md b/README.md similarity index 69% rename from readme.md rename to README.md index d8eb131..3cc39bf 100644 --- a/readme.md +++ b/README.md @@ -10,7 +10,3 @@ - Sna version 1 - Sna version 2 - Sna Version 3 (only in read) - -## To compile the command line application ## - -to compile the executable : _go build -o dsk cli/main.go_ From 32bfaaab2b98a2474dc3a5a5142546d24a18057e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Wed, 30 Aug 2023 19:30:38 +0200 Subject: [PATCH 02/10] chore: makefile cleanup (.PHONY, move .DEFAULT_GOAL to top) --- Makefile | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 296062f..5572705 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,7 @@ -CC=go +.DEFAULT_GOAL:=build +.PHONY: build init clean compile deps get-linter get-vulncheck lint vulncheck test + +GO=go RM=rm MV=mv MODULE=$(awk '/^module / {print $2}' go.mod) @@ -17,9 +20,6 @@ else appversion=$(VERSION) endif -.DEFAULT_GOAL:=build - - build: clean init @echo "Update packages" go get -d ./... @@ -44,7 +44,7 @@ clean: rm -fr ${BINARIES}/ compile: - GOOS=${OS} GOARCH=${ARCH} ${CC} build ${LDFLAGS} -o ${BINARIES}/dsk-${OS}-${ARCH}${EXT} $(SOURCEDIR)/main.go + GOOS=${OS} GOARCH=${ARCH} ${GO} build ${LDFLAGS} -o ${BINARIES}/dsk-${OS}-${ARCH}${EXT} $(SOURCEDIR)/main.go zip ${BINARIES}/dsk-$(appversion)-${OS}-${ARCH}.zip ${BINARIES}/dsk-${OS}-${ARCH}${EXT} deps: get-linter get-vulncheck @@ -63,6 +63,5 @@ lint: vulncheck: govulncheck $(MODULE) - test: - ${CC} test ./... -cover + ${GO} test ./... -cover From 5c00baa6d5063e48210504ba6a73568b2c5f4094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Wed, 30 Aug 2023 19:31:30 +0200 Subject: [PATCH 03/10] chore: upgrade to go1.21 --- cli/main.go | 6 +++--- go.mod | 12 +++++++++--- go.sum | 11 ++++------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/cli/main.go b/cli/main.go index 7f6b1b0..032e044 100644 --- a/cli/main.go +++ b/cli/main.go @@ -7,7 +7,7 @@ import ( "flag" "fmt" "io" - "io/ioutil" + "io/fs" "math/rand" "os" "path" @@ -74,7 +74,7 @@ func main() { } if *autoextract != "" { - files, err := ioutil.ReadDir(*autoextract) + files, err := fs.ReadDir(os.DirFS("/"), *autoextract) if err != nil { exitOnError(err.Error(), "Please check your folder path") } @@ -905,7 +905,7 @@ func rawImportDsk(d dsk.DSK, fileInDsk, dskPath string, track, sector int) (onEr return true, fmt.Sprintf("Cannot open file %s error :%v\n", fileInDsk, err), "Check your file path" } defer fr.Close() - buf, err := ioutil.ReadAll(fr) + buf, err := io.ReadAll(fr) if err != nil { if err != nil { return true, fmt.Sprintf("Cannot read file %s error :%v\n", fileInDsk, err), "Check your file path" diff --git a/go.mod b/go.mod index 6ebefc8..7466fdb 100644 --- a/go.mod +++ b/go.mod @@ -1,8 +1,14 @@ module github.com/jeromelesaux/dsk -go 1.16 +go 1.21 require ( - github.com/jeromelesaux/m4client v0.0.0-20200309212559-efb59c22369c - github.com/stretchr/testify v1.8.1 + github.com/jeromelesaux/m4client v0.0.0-20230327092026-4e80fd2b1474 + github.com/stretchr/testify v1.8.4 +) + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index ffbd324..4ef5a9d 100644 --- a/go.sum +++ b/go.sum @@ -1,17 +1,14 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/jeromelesaux/m4client v0.0.0-20200309212559-efb59c22369c h1:4X+oOjzaO6OKBLX8a3TCywm3htzQGyCsi+czrc3E5X8= -github.com/jeromelesaux/m4client v0.0.0-20200309212559-efb59c22369c/go.mod h1:JO0ijl8YXO6FxogJoC3yAFbBjSAnkwFquc8W6C7k94E= +github.com/jeromelesaux/m4client v0.0.0-20230327092026-4e80fd2b1474 h1:CWtkpA4Q63pkhHWRrd/e98JD4YVZhk3SZu58bUkQsuI= +github.com/jeromelesaux/m4client v0.0.0-20230327092026-4e80fd2b1474/go.mod h1:Xc0HzC2TZ7fbDWyB8LosvziBQ5QvvwVt3xxAIEavWRc= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= -github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From b24434f2997dde9022db493d43005e94f049496a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Wed, 30 Aug 2023 19:36:41 +0200 Subject: [PATCH 04/10] chore: move compilation instructions to top of README.md --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index 3cc39bf..15a6ae8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ # DSK and SNA lib a golang library to handle dsk image file (cli to get the executable) # +## Compilation + +to compile the executable : _go build -o dsk cli/main.go_ + + ## DSK format supported ## - DSK From 0552345171253e83f61152ca901981905cd990de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=E2=80=9Cikari=E2=80=9D=20Pokorski?= Date: Wed, 30 Aug 2023 19:40:17 +0200 Subject: [PATCH 05/10] Create go.yml --- .github/workflows/go.yml | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 .github/workflows/go.yml diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml new file mode 100644 index 0000000..4927f09 --- /dev/null +++ b/.github/workflows/go.yml @@ -0,0 +1,28 @@ +# This workflow will build a golang project +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go + +name: Go + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Set up Go + uses: actions/setup-go@v4 + with: + go-version: '1.21' + + - name: Build + run: go build -v ./... + + - name: Test + run: go test -v ./... From 3f30118a95c4c07529c8bed44b8962fb7b3c29da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Thu, 31 Aug 2023 09:43:34 +0200 Subject: [PATCH 06/10] "englishify" the code a little, group cmds and otions separately --- cli/main.go | 187 +++++++++-------- dsk.go | 595 ++++++++++++++++++++++++++-------------------------- dsk_test.go | 27 +-- test.dsk | Bin 194816 -> 194816 bytes utils.go | 4 +- 5 files changed, 408 insertions(+), 405 deletions(-) diff --git a/cli/main.go b/cli/main.go index 032e044..ca15a32 100644 --- a/cli/main.go +++ b/cli/main.go @@ -19,46 +19,50 @@ import ( ) var ( - help = flag.Bool("help", false, "display extended help.") - list = flag.Bool("list", false, "List content of dsk.") - track = flag.Int("track", 39, "Track number (format).") - heads = flag.Int("head", 1, "Number of heads in the DSK (format)") - sector = flag.Int("sector", 9, "Sector number (format).") - format = flag.Bool("format", false, "Format the followed dsk or sna.") - dskType = flag.Int("dsktype", 0, "DSK Type :\n\t0 : DSK\n\t1 : EDSK\n\t3 : SNA\n") - dskPath = flag.String("dsk", "", "Dsk path to handle.") - fileInDsk = flag.String("amsdosfile", "", "File to handle in (or to insert in) the dsk.") - hexa = flag.Bool("hex", false, "List the amsdosfile in hexadecimal.") - info = flag.Bool("info", false, "Get informations of the amsdosfile (size, execute and loading address). Or get sna informations.") - ascii = flag.Bool("ascii", false, "list the amsdosfile in ascii mode.") - desassemble = flag.Bool("desassemble", false, "list the amsdosfile desassembled.") - get = flag.Bool("get", false, "Get the file in the dsk.") - remove = flag.Bool("remove", false, "Remove the amsdosfile from the current dsk.") - basic = flag.Bool("basic", false, "List a basic amsdosfile.") - put = flag.Bool("put", false, "Put the amsdosfile in the current dsk.") - executeAddress = flag.String("exec", "", "Execute address of the inserted file. (hexadecimal #170 allowed.)") - loadingAddress = flag.String("load", "", "Loading address of the inserted file. (hexadecimal #170 allowed.)") - user = flag.Int("user", 0, "User number of the inserted file.") - force = flag.Bool("force", false, "Force overwriting of the inserted file.") - fileType = flag.String("type", "", "Type of the inserted file \n\tascii : type ascii\n\tbinary : type binary\n") - snaPath = flag.String("sna", "", "SNA file to handle") - analyse = flag.Bool("analyze", false, "Returns the DSK header") - cpcType = flag.Int("cpctype", 2, "CPC type (sna import feature): \n\tCPC464 : 0\n\tCPC664: 1\n\tCPC6128 : 2\n\tUnknown : 3\n\tCPCPlus6128 : 4\n\tCPCPlus464 : 5\n\tGX4000 : 6\n\t") - screenMode = flag.Int("screenmode", 1, "screen mode parameter for the sna.") - addHeader = flag.Bool("addheader", false, "Add header to the standalone file (must be set with exec, load and type options).") - vendorFormat = flag.Bool("vendor", false, "Format in vendor format (sectors number #09, end track #27)") - dataFormat = flag.Bool("data", true, "Format in vendor format (sectors number #09, end track #27)") - rawimport = flag.Bool("rawimport", false, "raw imports the amsdosfile, this option is associated with -dsk, -track and -sector.\nThis option will do a raw copy of the file starting to track and sector values.\nfor instance : dsk -dsk mydskfile.dsk -amsdosfile file.bin -rawimport -track 1 -sector 0") - rawexport = flag.Bool("rawexport", false, "raw exports the amsdosfile, this option is associated with -dsk, -track and -sector.\nThis option will do a raw extract of the content beginning to track and sector values and will stop when size is reached.\nfor instance : dsk -dsk mydskfile.dsk -amsdosfile file.bin -rawexport -track 1 -sector 0 -size 16384") - size = flag.Int("size", 0, "Size to extract in rawexport, see rawexport for more details.") - autotest = flag.Bool("autotest", false, "Executs all tests.") - autoextract = flag.String("autoextract", "", "Extract all DSK contained in the folder path") - snaVersion = flag.Int("snaversion", 1, "Set the sna version (1 or 2 available).") - appVersion = "0.18" + // commands + help = flag.Bool("help", false, "Display extended help") + list = flag.Bool("list", false, "List content of DSK") + format = flag.Bool("format", false, "Format the followed DSK or SNA") + hex = flag.Bool("hex", false, "List the AMSDOS file in hexadecimal") + info = flag.Bool("info", false, "Get informations of the AMSDOS file (size, execute and loading address) or get SNA information") + ascii = flag.Bool("ascii", false, "List the AMSDOS file in ASCII mode") + disassemble = flag.Bool("disassemble", false, "List the AMSDOS file disassembled") + get = flag.Bool("get", false, "Get the file in the DSK") + remove = flag.Bool("remove", false, "Remove the AMSDOS file from the current DSK") + basic = flag.Bool("basic", false, "List a basic AMSDOS file") + put = flag.Bool("put", false, "Put the AMSDOS file in the current DSK") + autotest = flag.Bool("autotest", false, "Executes all tests") + autoExtract = flag.String("autoExtract", "", "Extract all DSK contained in the folder path") + + // options + track = flag.Int("track", 39, "Track number (format)") + heads = flag.Int("head", 1, "Number of heads in the DSK (format)") + sector = flag.Int("sector", 9, "Sector number (format)") + cliDskType = flag.Int("dsktype", 0, "DSK Type :\n\t0 : DSK\n\t1 : EDSK\n\t3 : SNA\n") + dskType *dsk.DskFileType = nil + dskPath = flag.String("dsk", "", "Dsk path to handle") + fileInDsk = flag.String("amsdosfile", "", "File to handle in (or to insert in) the dsk") + executeAddress = flag.String("exec", "", "Execute address of the inserted file. (hexadecimal #170 allowed.)") + loadingAddress = flag.String("load", "", "Loading address of the inserted file. (hexadecimal #170 allowed.)") + user = flag.Int("user", 0, "User number of the inserted file") + force = flag.Bool("force", false, "Force overwriting of the inserted file") + fileType = flag.String("type", "", "Type of the inserted file \n\tascii : type ascii\n\tbinary : type binary\n") + snaPath = flag.String("sna", "", "SNA file to handle") + analyse = flag.Bool("analyze", false, "Returns the DSK header") + cpcType = flag.Int("cpctype", 2, "CPC type (sna import feature): \n\tCPC464 : 0\n\tCPC664: 1\n\tCPC6128 : 2\n\tUnknown : 3\n\tCPCPlus6128 : 4\n\tCPCPlus464 : 5\n\tGX4000 : 6\n\t") + screenMode = flag.Int("screenmode", 1, "screen mode parameter for the sna") + addHeader = flag.Bool("addheader", false, "Add header to the standalone file (must be set with exec, load and type options)") + vendorFormat = flag.Bool("vendor", false, "Format in vendor format (sectors number #09, end track #27)") + dataFormat = flag.Bool("data", true, "Format in vendor format (sectors number #09, end track #27)") + rawImport = flag.Bool("rawImport", false, "raw imports the amsdosfile, this option is associated with -dsk, -track and -sector.\nThis option will do a raw copy of the file starting to track and sector values.\nfor instance : dsk -dsk mydskfile.dsk -amsdosfile file.bin -rawImport -track 1 -sector 0") + rawExport = flag.Bool("rawExport", false, "raw exports the amsdosfile, this option is associated with -dsk, -track and -sector.\nThis option will do a raw extract of the content beginning to track and sector values and will stop when size is reached.\nfor instance : dsk -dsk mydskfile.dsk -amsdosfile file.bin -rawExport -track 1 -sector 0 -size 16384") + size = flag.Int("size", 0, "Size to extract in rawExport, see rawExport for more details") + snaVersion = flag.Int("snaversion", 1, "Set the sna version (1 or 2 available)") + appVersion = "0.18" ) func main() { - var cmdRunned bool = false + var cmdRan bool = false var execAddress, loadAddress uint16 flag.Parse() @@ -73,16 +77,16 @@ func main() { os.Exit(0) } - if *autoextract != "" { - files, err := fs.ReadDir(os.DirFS("/"), *autoextract) + if *autoExtract != "" { + files, err := fs.ReadDir(os.DirFS("/"), *autoExtract) if err != nil { exitOnError(err.Error(), "Please check your folder path") } for _, file := range files { if !file.IsDir() { if strings.ToUpper(path.Ext(file.Name())) == ".DSK" { - dskfolderPath := *autoextract + string(filepath.Separator) + strings.Replace(file.Name(), path.Ext(file.Name()), "", -1) - dskFilepath := *autoextract + string(filepath.Separator) + file.Name() + dskfolderPath := *autoExtract + string(filepath.Separator) + strings.Replace(file.Name(), path.Ext(file.Name()), "", -1) + dskFilepath := *autoExtract + string(filepath.Separator) + file.Name() err = os.Mkdir(dskfolderPath, os.ModePerm) if err != nil && !errors.Is(err, os.ErrExist) { exitOnError(err.Error(), "Please check your folder path") @@ -112,7 +116,7 @@ func main() { fmt.Fprintf(os.Stderr, "DSK cli version [%s]\nMade by Sid (ImpAct)\n", appVersion) if *snaPath != "" { if *info { - cmdRunned = true + cmdRan = true isError, msg, hint := infoSna(*snaPath) if isError { exitOnError(msg, hint) @@ -120,14 +124,14 @@ func main() { os.Exit(0) } if *format { - cmdRunned = true + cmdRan = true isError, msg, hint := formatSna(*snaPath, *snaVersion) if isError { exitOnError(msg, hint) } } - if *hexa { - cmdRunned = true + if *hex { + cmdRan = true sna, err := dsk.ReadSna(*snaPath) if err != nil { exitOnError(err.Error(), "Check your sna path") @@ -137,7 +141,7 @@ func main() { os.Exit(0) } if *put { - cmdRunned = true + cmdRan = true if *fileInDsk != "" { cpcTYPE := dsk.CPCType(*cpcType) crtc := dsk.UM6845R @@ -157,7 +161,7 @@ func main() { } } if *get { - cmdRunned = true + cmdRan = true if *fileInDsk != "" { content, err := dsk.ExportFromSna(*snaPath) if err != nil { @@ -215,7 +219,7 @@ func main() { exitOnError("No dsk set.", "") } if *format { - cmdRunned = true + cmdRan = true isError, msg, hint := formatDsk(*dskPath, *sector, *track, *heads, 0, *vendorFormat, *dataFormat) if isError { @@ -229,7 +233,7 @@ func main() { exitOnError(msg, hint) } if *list { - cmdRunned = true + cmdRan = true isError, msg, hint := listDsk(d, *dskPath) if isError { exitOnError(msg, hint) @@ -237,7 +241,7 @@ func main() { } if *analyse { - cmdRunned = true + cmdRan = true isError, msg, hint := analyseDsk(d, *dskPath) if isError { exitOnError(msg, hint) @@ -245,23 +249,23 @@ func main() { } if *info { - cmdRunned = true + cmdRan = true isError, msg, hint := fileinfoDsk(d, *fileInDsk) if isError { exitOnError(msg, hint) } } - if *hexa { - cmdRunned = true + if *hex { + cmdRan = true isError, msg, hint := hexaFileDsk(d, *fileInDsk) if isError { exitOnError(msg, hint) } } - if *desassemble { - cmdRunned = true + if *disassemble { + cmdRan = true isError, msg, hint := desassembleFileDsk(d, *fileInDsk) if isError { exitOnError(msg, hint) @@ -269,7 +273,7 @@ func main() { } if *ascii { - cmdRunned = true + cmdRan = true isError, msg, hint := asciiFileDsk(d, *fileInDsk) if isError { exitOnError(msg, hint) @@ -277,7 +281,7 @@ func main() { } if *basic { - cmdRunned = true + cmdRan = true if *fileInDsk == "" { exitOnError("amsdosfile option is empty, set it.", "dsk -dsk output.dsk -basic -amsdosfile hello.bin") } @@ -305,23 +309,23 @@ func main() { } if *get { - cmdRunned = true + cmdRan = true isError, msg, hint := getFileDsk(d, *fileInDsk, *dskPath, "") if isError { exitOnError(msg, hint) } } - if *rawimport { - cmdRunned = true + if *rawImport { + cmdRan = true isError, msg, hint := rawImportDsk(d, *fileInDsk, *dskPath, *track, *sector) if isError { exitOnError(msg, hint) } } - if *rawexport { - cmdRunned = true + if *rawExport { + cmdRan = true isError, msg, hint := rawExportDsk(d, *fileInDsk, *dskPath, *track, *sector, *size) if isError { exitOnError(msg, hint) @@ -329,7 +333,7 @@ func main() { } if *put { - cmdRunned = true + cmdRan = true isError, msg, hint := putFileDsk(d, *fileInDsk, *dskPath, *fileType, loadAddress, execAddress, uint16(*user)) if isError { exitOnError(msg, hint) @@ -337,7 +341,7 @@ func main() { } if *remove { - cmdRunned = true + cmdRan = true isError, msg, hint := removeFileDsk(d, *dskPath, *fileInDsk) if isError { exitOnError(msg, hint) @@ -360,7 +364,7 @@ func main() { } if *basic { - cmdRunned = true + cmdRan = true isAmsdos, _ := dsk.CheckAmsdos(content) // remove amsdos header if isAmsdos { @@ -370,8 +374,8 @@ func main() { fmt.Fprintf(os.Stderr, "File %s filesize :%d octets\n", *fileInDsk, len(content)) fmt.Fprintf(os.Stdout, "%s", dsk.Basic(content, uint16(len(content)), true)) } - if *desassemble { - cmdRunned = true + if *disassemble { + cmdRan = true var address uint16 isAmsdos, header := dsk.CheckAmsdos(content) if isAmsdos { @@ -380,8 +384,8 @@ func main() { } fmt.Println(dsk.Desass(content, uint16(len(content)), address)) } - if *hexa { - cmdRunned = true + if *hex { + cmdRan = true isAmsdos, _ := dsk.CheckAmsdos(content) // remove amsdos header if isAmsdos { @@ -390,7 +394,7 @@ func main() { fmt.Println(dsk.DisplayHex(content, 16)) } if *info { - cmdRunned = true + cmdRan = true isAmsdos, header := dsk.CheckAmsdos(content) if !isAmsdos { exitOnError(fmt.Sprintf("File (%s) does not contain amsdos header.\n", *fileInDsk), "may be a ascii file") @@ -422,7 +426,7 @@ func main() { if isAmsdos { exitOnError("The file already contains an amsdos header", "Check your file") } - filename := dsk.GetNomAmsdos(*fileInDsk) + filename := dsk.GetAmsDosName(*fileInDsk) header.Size = uint16(len(content)) header.Size2 = uint16(len(content)) copy(header.Filename[:], []byte(filename[0:12])) @@ -467,7 +471,7 @@ func main() { } } - if !cmdRunned { + if !cmdRan { sampleUsage() } @@ -504,7 +508,7 @@ func exitOnError(errorMessage, hint string) { os.Exit(-1) } -func formatDsk(dskPath string, sector, track, heads, extendedDskType int, vendorFormat bool, dataFormat bool) (onError bool, message, hint string) { +func formatDsk(dskPath string, sector, track, heads int, extendedDskType dsk.DskFileType, vendorFormat bool, dataFormat bool) (onError bool, message, hint string) { _, err := os.Stat(dskPath) if err == nil { if !*force { @@ -563,7 +567,7 @@ func listDsk(d dsk.DSK, dskPath string) (onError bool, message, hint string) { for _, i := range d.GetFilesIndices() { size := fmt.Sprintf("%.3d ko", d.GetFilesize(d.Catalogue[i])) totalUsed += d.GetFilesize(d.Catalogue[i]) - filename := fmt.Sprintf("%s.%s", d.Catalogue[i].Nom, d.Catalogue[i].Ext) + filename := fmt.Sprintf("%s.%s", d.Catalogue[i].Name, d.Catalogue[i].Ext) fmt.Fprintf(os.Stderr, "[%.2d] : %s : %d %s\n", i, filename, int(d.Catalogue[i].User), size) } fmt.Fprintf(os.Stderr, "Dsk %.3d Ko used\n", totalUsed) @@ -679,13 +683,13 @@ func putFileDsk(d dsk.DSK, fileInDsk, dskPath string, fileType string, loadAddre switch fileType { case "ascii": informations := fmt.Sprintf("execute address [#%.4x], loading address [#%.4x]\n", execAddress, loadAddress) - if err := d.PutFile(fileInDsk, dsk.MODE_ASCII, 0, 0, user, false, false); err != nil { + if err := d.PutFile(fileInDsk, dsk.SaveModeAscii, 0, 0, user, false, false); err != nil { return true, fmt.Sprintf("Error while inserted file (%s) in dsk (%s) error :%v\n", fileInDsk, dskPath, err), "Check your dsk with option -dsk yourdsk.dsk -analyze" } resumeAction(dskPath, "put ascii", fileInDsk, informations) case "binary": informations := fmt.Sprintf("execute address [#%.4x], loading address [#%.4x]\n", execAddress, loadAddress) - if err := d.PutFile(fileInDsk, dsk.MODE_BINAIRE, loadAddress, execAddress, user, false, false); err != nil { + if err := d.PutFile(fileInDsk, dsk.SaveModeBinary, loadAddress, execAddress, user, false, false); err != nil { return true, fmt.Sprintf("Error while inserted file (%s) in dsk (%s) error :%v\n", fileInDsk, dskPath, err), "Check your dsk with option -dsk yourdsk.dsk -analyze" } resumeAction(dskPath, "put binary", fileInDsk, informations) @@ -716,9 +720,9 @@ func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string) (onError bool, } var lastFilename string for indice, v := range d.Catalogue { - if v.User != dsk.USER_DELETED && v.NbPages != 0 { + if v.User != dsk.USER_DELETED && v.PageCount != 0 { var nom, ext string - nom = dsk.ToAscii(v.Nom[:]) + nom = dsk.ToAscii(v.Name[:]) ext = dsk.ToAscii(v.Ext[:]) filename := fmt.Sprintf("%s.%s", nom, ext) if lastFilename == filename { @@ -784,7 +788,7 @@ func removeFileDsk(d dsk.DSK, dskPath, fileInDsk string) (onError bool, message, fmt.Fprintf(os.Stderr, "Error while removing file %s (indice:%d) error :%v\n", fileInDsk, indice, err) } else { fmt.Fprintf(os.Stderr, "File (%.8s.%.3s) deleted in dsk (%s)\n", - amsdosFile.Nom, + amsdosFile.Name, amsdosFile.Ext, dskPath) f, err := os.Create(dskPath) @@ -819,7 +823,7 @@ func asciiFileDsk(d dsk.DSK, fileInDsk string) (onError bool, message, hint stri func desassembleFileDsk(d dsk.DSK, fileInDsk string) (onError bool, message, hint string) { if fileInDsk == "" { - return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -desassemble -amsdosfile hello.bin" + return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -disassemble -amsdosfile hello.bin" } amsdosFile := dsk.GetNomDir(fileInDsk) indice := d.FileExists(amsdosFile) @@ -897,7 +901,7 @@ func rawImportDataInDsk(d dsk.DSK, fileInDsk, dskPath string, track, sector int, func rawImportDsk(d dsk.DSK, fileInDsk, dskPath string, track, sector int) (onError bool, message, hint string) { if fileInDsk == "" { - return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -put -amsdosfile hello.bin -rawimport -track 1 -sector 0" + return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -put -amsdosfile hello.bin -rawImport -track 1 -sector 0" } fr, err := os.Open(fileInDsk) @@ -922,7 +926,7 @@ func rawImportDsk(d dsk.DSK, fileInDsk, dskPath string, track, sector int) (onEr func rawExportDsk(d dsk.DSK, fileInDsk, dskPath string, track, sector, size int) (onError bool, message, hint string) { if fileInDsk == "" { - return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -put -amsdosfile hello.bin -rawimport -track 1 -sector 0" + return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -put -amsdosfile hello.bin -rawImport -track 1 -sector 0" } if track == 39 { @@ -1195,13 +1199,14 @@ func resetArguments() { *heads = 1 *sector = 9 *format = false - *dskType = 0 + *cliDskType = 0 + *dskType = dsk.DskFileType(*cliDskType) *dskPath = "" *fileInDsk = "" - *hexa = false + *hex = false *info = false *ascii = false - *desassemble = false + *disassemble = false *get = false *remove = false *basic = false @@ -1218,8 +1223,8 @@ func resetArguments() { *addHeader = false *vendorFormat = false *dataFormat = true - *rawimport = false - *rawexport = false + *rawImport = false + *rawExport = false *size = 0 *autotest = false } @@ -1423,7 +1428,7 @@ func asciiFileBinaryDataTest(filePath, dskFilepath string) bool { } func desassembleFileBinaryDataTest(filePath, dskFilepath string) bool { - *desassemble = true + *disassemble = true d, onError, _, _ := openDsk(dskFilepath) if onError { return onError @@ -1433,7 +1438,7 @@ func desassembleFileBinaryDataTest(filePath, dskFilepath string) bool { } func hexaFileBinaryDataTest(filePath, dskFilepath string) bool { - *hexa = true + *hex = true d, onError, _, _ := openDsk(dskFilepath) if onError { return onError @@ -1443,7 +1448,7 @@ func hexaFileBinaryDataTest(filePath, dskFilepath string) bool { } func rawImportFileBinaryDataTest(filePath, dskFilepath string) bool { - *rawimport = true + *rawImport = true sector := 0 track := 1 d, onError, _, _ := openDsk(dskFilepath) @@ -1455,7 +1460,7 @@ func rawImportFileBinaryDataTest(filePath, dskFilepath string) bool { } func rawExportFileBinaryDataTest(filePath, dskFilepath string, length int) bool { - *rawexport = true + *rawExport = true sector := 0 track := 1 d, onError, _, _ := openDsk(dskFilepath) diff --git a/dsk.go b/dsk.go index 447febc..32002d5 100644 --- a/dsk.go +++ b/dsk.go @@ -24,14 +24,27 @@ var ( ErrorUnsupportedMultiHeadDsk = errors.New("multi-side dsk ! Expected 1 head") ErrorBadSectorNumber = errors.New("dsk has wrong sector number") ErrorCatalogueExceed = errors.New("catalogue indice exceed") - ErrorNoBloc = errors.New("error no more block available") + ErrDiskFull = errors.New("error no more free blocks available") ErrorNoDirEntry = errors.New("error no more dir entry available") ErrorFileSizeExceed = errors.New("filesize exceed") ) +type SaveMode uint8 + +const ( + SaveModeAscii SaveMode = iota + SaveModeBinary +) + +type DskFileType int + +const ( + DskTypeBasic DskFileType = iota + DskTypeExtended + DskTypeSNA +) + var ( - MODE_ASCII uint8 = 0 - MODE_BINAIRE uint8 = 1 EXTENDED_DSK_TYPE = 1 DSK_TYPE = 0 DataFormat DskFormat = 0 @@ -45,31 +58,30 @@ type DskFormat = int type StAmsdos = cpc.CpcHead type CPCEMUEnt struct { - Debut [0x22]byte // "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" + Header [0x22]byte // "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" Creator [0xE]byte - NbTracks uint8 - NbHeads uint8 - DataSize uint16 // 0x1300 = 256 + ( 512 * nbsecteurs ) + Tracks uint8 // number of tracks + Heads uint8 // number of heads (1 or 2) + DataSize uint16 // 0x1300 = 256 + ( 512 * sectors ) } func (e *CPCEMUEnt) ToString() string { - return fmt.Sprintf("Debut:%s\nCreator:%s\nnbTracks:%d, nbHeads:%d, DataSize:%d", - e.Debut, e.Creator, e.NbTracks, e.NbHeads, e.DataSize) + return fmt.Sprintf("Header:%s\nCreator:%s\nnbTracks:%d, nbHeads:%d, DataSize:%d", + e.Header, e.Creator, e.Tracks, e.Heads, e.DataSize) } type CPCEMUSect struct { // length 8 - C uint8 // track, - H uint8 // head - R uint8 // sect - N uint8 // size - Un1 uint16 - SizeByte uint16 // Taille secteur en octets - // + C uint8 // track, + H uint8 // head + S uint8 // sect + N uint8 // size + Un1 uint16 + Size uint16 // Sector size in bytes } func (c *CPCEMUSect) ToString() string { - return fmt.Sprintf("C:%d,H:%d,R:%d,N:%d,Un1:%d:SizeByte:%d", //,DataSize:%d", - c.C, c.H, c.R, c.N, c.Un1, c.SizeByte) //, len(c.Data)) + return fmt.Sprintf("C:%d,H:%d,S:%d,N:%d,Un1:%d:Size:%d", //,DataSize:%d", + c.C, c.H, c.S, c.N, c.Un1, c.Size) //, len(c.Data)) } func (c *CPCEMUSect) Write(w io.Writer) error { @@ -81,8 +93,8 @@ func (c *CPCEMUSect) Write(w io.Writer) error { fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.H error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.R); err != nil { - fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.R error :%v\n", err) + if err := binary.Write(w, binary.LittleEndian, &c.S); err != nil { + fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.S error :%v\n", err) return err } if err := binary.Write(w, binary.LittleEndian, &c.N); err != nil { @@ -93,8 +105,8 @@ func (c *CPCEMUSect) Write(w io.Writer) error { fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.Un1 error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.SizeByte); err != nil { - fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.SizeByte error :%v\n", err) + if err := binary.Write(w, binary.LittleEndian, &c.Size); err != nil { + fmt.Fprintf(os.Stderr, "Error while writing CPCEmuSect.Size error :%v\n", err) return err } // fmt.Fprintf(os.Stdout,"Sector %s\n",c.ToString()) @@ -110,8 +122,8 @@ func (c *CPCEMUSect) Read(r io.Reader) error { fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.H error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.R); err != nil { - fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.R error :%v\n", err) + if err := binary.Read(r, binary.LittleEndian, &c.S); err != nil { + fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.S error :%v\n", err) return err } if err := binary.Read(r, binary.LittleEndian, &c.N); err != nil { @@ -122,8 +134,8 @@ func (c *CPCEMUSect) Read(r io.Reader) error { fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.Un1 error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.SizeByte); err != nil { - fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.SizeByte error :%v\n", err) + if err := binary.Read(r, binary.LittleEndian, &c.Size); err != nil { + fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.Size error :%v\n", err) return err } // fmt.Fprintf(os.Stdout,"Sector %s\n",c.ToString()) @@ -131,136 +143,135 @@ func (c *CPCEMUSect) Read(r io.Reader) error { } type CPCEMUTrack struct { // length 18 bytes - ID [0x10]byte // "Track-Info\r\n" - Track uint8 - Head uint8 - Unused [2]byte - SectSize uint8 // 2 - NbSect uint8 // 9 - Gap3 uint8 // 0x4E - OctRemp uint8 // 0xE5 - Sect [29]CPCEMUSect - Data []byte + ID [0x10]byte // "Track-Info\r\n" + Track uint8 + Head uint8 + Unused [2]byte // kept here to keep 1:1 with the disk format + SectSize uint8 // 2 + SectorCount uint8 // 9 + Gap3 uint8 // 0x4E + OctRemp uint8 // 0xE5 + Sect [29]CPCEMUSect // 29 sectors max + Data []byte } -func (c *CPCEMUTrack) Read(r io.Reader) error { - if err := binary.Read(r, binary.LittleEndian, &c.ID); err != nil { +func (t *CPCEMUTrack) Read(r io.Reader) error { + if err := binary.Read(r, binary.LittleEndian, &t.ID); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.ID error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.Track); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.Track); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.Track error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.Head); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.Head); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.Head error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.Unused); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.Unused); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.Unused error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.SectSize); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.SectSize); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.SectSize error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.NbSect); err != nil { - fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.NbSect error :%v\n", err) + if err := binary.Read(r, binary.LittleEndian, &t.SectorCount); err != nil { + fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.SectorCount error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.Gap3); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.Gap3); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.Gap3 error :%v\n", err) return err } - if err := binary.Read(r, binary.LittleEndian, &c.OctRemp); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.OctRemp); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEMUTrack.OctRemp error :%v\n", err) return err } // fmt.Fprintf(os.Stdout,"Track:%s\n",c.ToString()) - var i uint8 var sectorSize uint16 - for i = 0; i < c.NbSect && i < 29; i++ { + for i := uint8(0); i < t.SectorCount && i < 29; i++ { sect := &CPCEMUSect{} if err := sect.Read(r); err != nil { fmt.Fprintf(os.Stderr, "Cannot read sector (%d) error :%v\n", i, err) return err } - c.Sect[i] = *sect - sectorSize += c.Sect[i].SizeByte + t.Sect[i] = *sect + sectorSize += t.Sect[i].Size } - for i = c.NbSect; i < 29; i++ { + for i := t.SectorCount; i < 29; i++ { sect := &CPCEMUSect{} err := sect.Read(r) if err != nil { fmt.Fprintf(os.Stderr, "error while reading sector (%d), error :%v\n", i, err) } } - if int(sectorSize) > int(c.SectSize)*0x100*int(c.NbSect) { + if int(sectorSize) > int(t.SectSize)*0x100*int(t.SectorCount) { fmt.Fprintf(os.Stderr, "Warning : Sector size [%d] differs from the amount of data found [%d], enlarge data part\n", - int(c.SectSize)*0x100*int(c.NbSect), + int(t.SectSize)*0x100*int(t.SectorCount), sectorSize) - c.Data = make([]byte, sectorSize) + t.Data = make([]byte, sectorSize) } else { - c.Data = make([]byte, int(c.SectSize)*0x100*int(c.NbSect)) + t.Data = make([]byte, int(t.SectSize)*0x100*int(t.SectorCount)) } - if err := binary.Read(r, binary.LittleEndian, &c.Data); err != nil { + if err := binary.Read(r, binary.LittleEndian, &t.Data); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.Data error :%v\n", err) return err } return nil } -func (c *CPCEMUTrack) Write(w io.Writer) error { - if err := binary.Write(w, binary.LittleEndian, &c.ID); err != nil { +func (t *CPCEMUTrack) Write(w io.Writer) error { + if err := binary.Write(w, binary.LittleEndian, &t.ID); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.ID error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.Track); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.Track); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.Track error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.Head); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.Head); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.Head error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.Unused); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.Unused); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.Unused error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.SectSize); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.SectSize); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.SectSize error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.NbSect); err != nil { - fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.NbSect error :%v\n", err) + if err := binary.Write(w, binary.LittleEndian, &t.SectorCount); err != nil { + fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.SectorCount error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.Gap3); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.Gap3); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.Gap3 error :%v\n", err) return err } - if err := binary.Write(w, binary.LittleEndian, &c.OctRemp); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.OctRemp); err != nil { fmt.Fprintf(os.Stderr, "Error while writing CPCEMUTrack.OctRemp error :%v\n", err) return err } // fmt.Fprintf(os.Stdout,"Track:%s\n",c.ToString()) var i uint8 - for i = 0; i < c.NbSect; i++ { - if err := c.Sect[i].Write(w); err != nil { + for i = 0; i < t.SectorCount; i++ { + if err := t.Sect[i].Write(w); err != nil { fmt.Fprintf(os.Stderr, "Cannot read sector (%d) error :%v\n", i, err) return err } } - for i = c.NbSect; i < 29; i++ { + for i = t.SectorCount; i < 29; i++ { sect := &CPCEMUSect{} err := sect.Write(w) if err != nil { fmt.Fprintf(os.Stderr, "error while writing sector (%d), error :%v\n", i, err) } } - if err := binary.Write(w, binary.LittleEndian, &c.Data); err != nil { + if err := binary.Write(w, binary.LittleEndian, &t.Data); err != nil { fmt.Fprintf(os.Stderr, "Error while reading CPCEmuSect.Data error :%v\n", err) return err } @@ -269,17 +280,17 @@ func (c *CPCEMUTrack) Write(w io.Writer) error { func (t *CPCEMUTrack) ToString() string { return fmt.Sprintf("ID:%s, Track:%d, Head:%d, SectSize:%d, nbSect:%d,Gap3:%d", - t.ID, t.Track, t.Head, t.SectSize, t.NbSect, t.Gap3) + t.ID, t.Track, t.Head, t.SectSize, t.SectorCount, t.Gap3) } type StDirEntry struct { - User uint8 - Nom [8]byte - Ext [3]byte - NumPage uint8 - Unused [2]uint8 - NbPages uint8 - Blocks [16]uint8 + User uint8 // User number (0-15), 0xE5=deleted file + Name [8]byte + Ext [3]byte + NumPage uint8 // page number (if a file is big, it will have more than one entry) + Unused [2]uint8 + PageCount uint8 // number of pages + Blocks [16]uint8 // blocks used by the file } type DSK struct { @@ -287,9 +298,9 @@ type DSK struct { TrackSizeTable []byte // dsk format [0xCC]byte Tracks []CPCEMUTrack BitMap [256]byte - Catalogue [64]StDirEntry + Catalogue [64]StDirEntry // 64 entries in directory (64*32=2048 bytes) catalogueLoaded bool - Extended bool + Extended bool // extended dsk format flag } func (d *DSK) CleanBitmap() { @@ -305,14 +316,14 @@ func (d *DSK) Read(r io.Reader) error { } mv := make([]byte, 4) extended := make([]byte, 16) - copy(mv, d.Entry.Debut[0:4]) - copy(extended, d.Entry.Debut[0:16]) + copy(mv, d.Entry.Header[0:4]) + copy(extended, d.Entry.Header[0:16]) if string(mv) != "MV -" && string(extended) != "EXTENDED CPC DSK" { return ErrorUnsupportedDskFormat } if string(extended) == "EXTENDED CPC DSK" { d.Extended = true - d.TrackSizeTable = make([]byte, d.Entry.NbHeads*(d.Entry.NbTracks)) + d.TrackSizeTable = make([]byte, d.Entry.Heads*(d.Entry.Tracks)) } else { d.TrackSizeTable = make([]byte, 0xCC) } @@ -322,16 +333,15 @@ func (d *DSK) Read(r io.Reader) error { } if d.Extended { - offset := make([]byte, (0x100 - (52 + uint(d.Entry.NbHeads*d.Entry.NbTracks)))) + offset := make([]byte, (0x100 - (52 + uint(d.Entry.Heads*d.Entry.Tracks)))) if err := binary.Read(r, binary.LittleEndian, &offset); err != nil { fmt.Fprintf(os.Stderr, "Cannot read CPCEmuEnt padding 0x100 error :%v\n", err) return err } } - d.Tracks = make([]CPCEMUTrack, d.Entry.NbTracks) - var i uint8 - for i = 0; i < d.Entry.NbTracks; i++ { - // fmt.Fprintf(os.Stdout,"Loading track %d, total: %d\n", i, cpcEntry.NbTracks) + d.Tracks = make([]CPCEMUTrack, d.Entry.Tracks) + for i := uint8(0); i < d.Entry.Tracks; i++ { + // fmt.Fprintf(os.Stdout,"Loading track %d, total: %d\n", i, cpcEntry.Tracks) track := &CPCEMUTrack{} if err := track.Read(r); err != nil { fmt.Fprintf(os.Stderr, "Error track (%d) error :%v\n", i, err) @@ -343,71 +353,71 @@ func (d *DSK) Read(r io.Reader) error { return nil } -func FormatDsk(nbSect, nbTrack, nbHead uint8, diskFormat DskFormat, extendedDskType int) *DSK { +func FormatDsk(sectors, tracks, heads uint8, diskFormat DskFormat, extendedDskType DskFileType) *DSK { dsk := &DSK{} entry := CPCEMUEnt{} - if extendedDskType == EXTENDED_DSK_TYPE || diskFormat == VendorFormat { + if extendedDskType == DskTypeExtended || diskFormat == VendorFormat { dsk.Extended = true - copy(entry.Debut[:], "EXTENDED CPC DSK File\r\nDisk-Info\r\n") + copy(entry.Header[:], "EXTENDED CPC DSK File\r\nDisk-Info\r\n") } else { - copy(entry.Debut[:], "MV - CPCEMU Disk-File\r\nDisk-Info\r\n") + copy(entry.Header[:], "MV - CPCEMU Disk-File\r\nDisk-Info\r\n") } copy(entry.Creator[:], "Sid DSK"[:]) - entry.DataSize = 0x100 + (SECTSIZE * uint16(nbSect)) - entry.NbTracks = nbTrack - entry.NbHeads = nbHead - if extendedDskType == EXTENDED_DSK_TYPE { - dsk.TrackSizeTable = make([]byte, entry.NbHeads*(entry.NbTracks)) + entry.DataSize = 0x100 + (SECTSIZE * uint16(sectors)) + entry.Tracks = tracks + entry.Heads = heads + if extendedDskType == DskTypeExtended { + dsk.TrackSizeTable = make([]byte, entry.Heads*(entry.Tracks)) for i := 0; i < len(dsk.TrackSizeTable); i++ { - dsk.TrackSizeTable[i] = byte(0x100 + (SECTSIZE*uint16(nbSect))/256 + 1) + dsk.TrackSizeTable[i] = byte(0x100 + (SECTSIZE*uint16(sectors))/256 + 1) } } else { switch diskFormat { case DataFormat: dsk.TrackSizeTable = make([]byte, 0xCC) case VendorFormat: - dsk.TrackSizeTable = make([]byte, entry.NbHeads*(entry.NbTracks)) + dsk.TrackSizeTable = make([]byte, entry.Heads*(entry.Tracks)) for i := 0; i < len(dsk.TrackSizeTable); i++ { - dsk.TrackSizeTable[i] = byte(0x100 + (SECTSIZE*uint16(nbSect))/256 + 1) + dsk.TrackSizeTable[i] = byte(0x100 + (SECTSIZE*uint16(sectors))/256 + 1) } default: fmt.Fprintf(os.Stderr, "Unknown format track.") } } dsk.Entry = entry - dsk.Tracks = make([]CPCEMUTrack, nbTrack*nbHead) + dsk.Tracks = make([]CPCEMUTrack, tracks*heads) var i uint8 - if nbHead == 1 { - for i = 0; i < nbTrack; i++ { + if heads == 1 { + for i = 0; i < tracks; i++ { switch diskFormat { case DataFormat: - dsk.FormatTrack(i, i, 0, 0xC1, nbSect) + dsk.FormatTrack(i, i, 0, 0xC1, sectors) case VendorFormat: - dsk.FormatTrack(i, i, 0, 0x41, nbSect) + dsk.FormatTrack(i, i, 0, 0x41, sectors) default: fmt.Fprintf(os.Stderr, "Unknown format track.") } } } else { index := 0 - for i = 0; i < nbTrack; i++ { + for i = 0; i < tracks; i++ { switch diskFormat { case DataFormat: - dsk.FormatTrack(uint8(index), uint8(i), 0, 0xC1, nbSect) + dsk.FormatTrack(uint8(index), i, 0, 0xC1, sectors) case VendorFormat: - dsk.FormatTrack(uint8(index), uint8(i), 0, 0x41, nbSect) + dsk.FormatTrack(uint8(index), i, 0, 0x41, sectors) default: fmt.Fprintf(os.Stderr, "Unknown format track.") } index += 2 } index = 1 - for i = 1; i < nbTrack; i++ { + for i = 1; i < tracks; i++ { switch diskFormat { case DataFormat: - dsk.FormatTrack(uint8(index), uint8(i), 1, 0xC1, nbSect) + dsk.FormatTrack(uint8(index), i, 1, 0xC1, sectors) case VendorFormat: - dsk.FormatTrack(uint8(index), uint8(i), 1, 0x41, nbSect) + dsk.FormatTrack(uint8(index), i, 1, 0x41, sectors) default: fmt.Fprintf(os.Stderr, "Unknown format track.") } @@ -423,11 +433,11 @@ func (d *DSK) FormatTrack(indexTrack, track, head, minSect, nbSect uint8) { t.Track = track t.Head = head t.SectSize = 2 - t.NbSect = nbSect + t.SectorCount = nbSect t.Gap3 = 0x4E t.OctRemp = 0xE5 // - // Gestion "entrelacement" des secteurs + // Interleaving sectors // var s uint8 var ss uint8 @@ -435,19 +445,19 @@ func (d *DSK) FormatTrack(indexTrack, track, head, minSect, nbSect uint8) { for s = 0; s < nbSect; { t.Sect[s].C = track t.Sect[s].H = head - t.Sect[s].R = (ss + minSect) + t.Sect[s].S = (ss + minSect) t.Sect[s].N = 2 - t.Sect[s].SizeByte = 0x200 - sectorSize += t.Sect[s].SizeByte + t.Sect[s].Size = 0x200 + sectorSize += t.Sect[s].Size ss++ s++ if s < nbSect { t.Sect[s].C = track t.Sect[s].H = head - t.Sect[s].R = (ss + minSect + 4) + t.Sect[s].S = (ss + minSect + 4) t.Sect[s].N = 2 - t.Sect[s].SizeByte = 0x200 - sectorSize += t.Sect[s].SizeByte + t.Sect[s].Size = 0x200 + sectorSize += t.Sect[s].Size s++ } } @@ -457,7 +467,7 @@ func (d *DSK) FormatTrack(indexTrack, track, head, minSect, nbSect uint8) { } if len(d.Tracks) < int(track+1) { d.Tracks = append(d.Tracks, t) - d.Entry.NbTracks++ + d.Entry.Tracks++ } else { d.Tracks[indexTrack] = t } @@ -473,14 +483,14 @@ func (d *DSK) Write(w io.Writer) error { return err } if d.Extended { - offset := make([]byte, (0x100 - (52 + uint(d.Entry.NbHeads*d.Entry.NbTracks)))) + offset := make([]byte, (0x100 - (52 + uint(d.Entry.Heads*d.Entry.Tracks)))) if err := binary.Write(w, binary.LittleEndian, &offset); err != nil { fmt.Fprintf(os.Stderr, "Cannot write CPCEmuEnt padding 0x100 error :%v\n", err) return err } } var i uint8 - for i = 0; i < d.Entry.NbTracks; i++ { + for i = 0; i < d.Entry.Tracks; i++ { if err := d.Tracks[i].Write(w); err != nil { fmt.Fprintf(os.Stderr, "Error track (%d) error :%v\n", i, err) } @@ -518,36 +528,36 @@ func ReadDsk(filePath string) (*DSK, error) { } func (d *DSK) CheckDsk() error { - // if d.Entry.NbHeads == 1 { + // if d.Entry.Heads == 1 { minSectFirst := d.GetMinSect() if minSectFirst != 0x41 && minSectFirst != 0xc1 && minSectFirst != 0x01 { fmt.Fprintf(os.Stderr, "Bad sector %.2x\n", minSectFirst) return ErrorBadSectorNumber } - if d.Entry.NbTracks > 80 { - d.Entry.NbTracks = 80 + if d.Entry.Tracks > 80 { + d.Entry.Tracks = 80 } var track uint8 - for track = 0; track < d.Entry.NbTracks; track++ { + for track = 0; track < d.Entry.Tracks; track++ { tr := d.Tracks[track] if !d.Extended { - if tr.NbSect != 9 { - fmt.Fprintf(os.Stderr, "Warning : track :%d has %d sectors ! wanted 9\n", track, tr.NbSect) + if tr.SectorCount != 9 { + fmt.Fprintf(os.Stderr, "Warning : track :%d has %d sectors ! wanted 9\n", track, tr.SectorCount) } } var minSect, maxSect, s uint8 minSect = 0xFF maxSect = 0 - nbSecteur := int(tr.NbSect) + nbSecteur := int(tr.SectorCount) if nbSecteur > len(tr.Sect) { nbSecteur = len(tr.Sect) } for s = 0; s < uint8(nbSecteur); s++ { - if minSect > tr.Sect[s].R { - minSect = tr.Sect[s].R + if minSect > tr.Sect[s].S { + minSect = tr.Sect[s].S } - if maxSect < tr.Sect[s].R { - maxSect = tr.Sect[s].R + if maxSect < tr.Sect[s].S { + maxSect = tr.Sect[s].S } } if !d.Extended { @@ -564,24 +574,23 @@ func (d *DSK) CheckDsk() error { // return ErrorUnsupportedMultiHeadDsk } -// Recherche le plus petit secteur d'une piste +// GetMinSect finds the lowest sector number of a track func (d *DSK) GetMinSect() uint8 { var Sect uint8 = 0xFF var s uint8 tr := d.Tracks[0] - // fmt.Fprintf(os.Stdout, "Track 0 nbSect :%d \n", tr.NbSect) - for s = 0; s < tr.NbSect; s++ { - // fmt.Fprintf(os.Stdout, "Sector %d, R %d\n", s, tr.Sect[s].R) - if Sect > tr.Sect[s].R { - Sect = tr.Sect[s].R + // fmt.Fprintf(os.Stdout, "Track 0 nbSect :%d \n", tr.SectorCount) + for s = 0; s < tr.SectorCount; s++ { + // fmt.Fprintf(os.Stdout, "Sector %d, S %d\n", s, tr.Sect[s].S) + if Sect > tr.Sect[s].S { + Sect = tr.Sect[s].S } } return Sect } -// Retourne la position d'un secteur dans le fichier DSK, position dans la structure Data +// GetPosData returns the position of a sector in the DSK file, position in the Data structure func (d *DSK) GetPosData(track, sect uint8, SectPhysique bool) uint16 { - // Recherche position secteur tr := d.Tracks[track] var SizeByte uint16 var Pos uint16 @@ -591,17 +600,17 @@ func (d *DSK) GetPosData(track, sect uint8, SectPhysique bool) uint16 { // fmt.Fprintf(os.Stdout,"Track:%d,Secteur:%d\n",track,sect) // Pos += 256 - for s = 0; s < tr.NbSect; s++ { - if (tr.Sect[s].R == sect && SectPhysique) || (s == sect && !SectPhysique) { + for s = 0; s < tr.SectorCount; s++ { + if (tr.Sect[s].S == sect && SectPhysique) || (s == sect && !SectPhysique) { break } - SizeByte = tr.Sect[s].SizeByte + SizeByte = tr.Sect[s].Size if SizeByte != 0 { Pos += SizeByte } else { Pos += (128 << tr.Sect[s].N) } - // fmt.Fprintf(os.Stderr, "sizebyte:%d, t:%d,s:%d,tr.Sect[s].SizeByte:%d, tr->Sect[ s ].N:%d, Pos:%d\n", SizeByte,t,s,tr.Sect[s].SizeByte,tr.Sect[ s ].N,Pos) + // fmt.Fprintf(os.Stderr, "sizebyte:%d, t:%d,s:%d,tr.Sect[s].Size:%d, tr->Sect[ s ].N:%d, Pos:%d\n", Size,t,s,tr.Sect[s].Size,tr.Sect[ s ].N,Pos) } return Pos @@ -617,7 +626,7 @@ func (d *DSK) GetFile(path string, indice int) error { if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue, error :%v\n", err) } - copy(nomIndice, d.Catalogue[i].Nom[:]) + copy(nomIndice, d.Catalogue[i].Name[:]) copy(nomIndice, d.Catalogue[i].Ext[:]) fw, err := os.Create(path) if err != nil { @@ -626,9 +635,9 @@ func (d *DSK) GetFile(path string, indice int) error { } defer fw.Close() for { - // Longueur du fichier + // File length (?) var j uint8 - l := (d.Catalogue[i].NbPages + 7) >> 3 + l := (d.Catalogue[i].PageCount + 7) >> 3 for j = 0; j < l; j++ { tailleBloc := 1024 p := d.ReadBloc(int(d.Catalogue[i].Blocks[j])) @@ -647,7 +656,7 @@ func (d *DSK) GetFile(path string, indice int) error { lMax -= 1024 } i++ - copy(current, d.Catalogue[i].Nom[:]) + copy(current, d.Catalogue[i].Name[:]) copy(current, d.Catalogue[i].Ext[:]) if i > 64 { return ErrorCatalogueExceed @@ -671,31 +680,31 @@ func (d *DSK) DskSize() uint16 { // for _, s := range t.Sect { // size += uint16(s.N) // } - // // size /= uint16(t.NbSect) + // // size /= uint16(t.SectorCount) // } - return d.Entry.DataSize * uint16(d.Entry.NbTracks) + return d.Entry.DataSize * uint16(d.Entry.Tracks) } -func GetNomAmsdos(masque string) string { +func GetAmsDosName(mask string) string { amsdosFile := make([]byte, 12) for i := 0; i < 12; i++ { amsdosFile[i] = ' ' } - file := strings.ToUpper(strings.TrimSuffix(filepath.Base(masque), filepath.Ext(filepath.Base(masque)))) + file := strings.ToUpper(strings.TrimSuffix(filepath.Base(mask), filepath.Ext(filepath.Base(mask)))) filenameSize := len(file) if filenameSize > 8 { filenameSize = 8 } copy(amsdosFile[0:filenameSize], file[0:filenameSize]) amsdosFile[8] = '.' - ext := strings.ToUpper(filepath.Ext(masque)) + ext := strings.ToUpper(filepath.Ext(mask)) copy(amsdosFile[9:12], ext[1:]) return string(amsdosFile) } -func (d *DSK) PutFile(masque string, typeModeImport uint8, loadAddress, exeAddress, userNumber uint16, isSystemFile, readOnly bool) error { +func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAddress, userNumber uint16, isSystemFile, readOnly bool) error { buff := make([]byte, 0x20000) - cFileName := GetNomAmsdos(masque) + cFileName := GetAmsDosName(masque) header := &StAmsdos{} var addHeader bool var err error @@ -724,19 +733,19 @@ func (d *DSK) PutFile(masque string, typeModeImport uint8, loadAddress, exeAddre fmt.Fprintf(os.Stderr, "No header found for file :%s, error :%v\n", masque, err) } - if typeModeImport == MODE_ASCII && fileLength%128 != 0 { + if typeModeImport == SaveModeAscii && fileLength%128 != 0 { buff[fileLength] = 0x1A } var isAmsdos bool // - // Regarde si le fichier contient une en-tete ou non + // Check if the file contains a header or not // if err == nil && header.Checksum == header.ComputedChecksum16() { isAmsdos = true } if !isAmsdos { - // Creer une en-tete amsdos par defaut + // Create a default amsdos header fmt.Fprintf(os.Stderr, "Create header... (%s)\n", masque) header = &StAmsdos{} header.User = byte(userNumber) @@ -746,52 +755,52 @@ func (d *DSK) PutFile(masque string, typeModeImport uint8, loadAddress, exeAddre copy(header.Filename[:], []byte(cFileName[0:12])) header.Address = loadAddress if loadAddress != 0 { - typeModeImport = MODE_BINAIRE + typeModeImport = SaveModeBinary } header.Exec = exeAddress if exeAddress != 0 || loadAddress != 0 { - typeModeImport = MODE_BINAIRE + typeModeImport = SaveModeBinary } - if typeModeImport == MODE_BINAIRE && exeAddress != 0 { + if typeModeImport == SaveModeBinary && exeAddress != 0 { header.Type = 1 } - // Il faut recalculer le checksum en comptant es adresses ! + // We must recalculate the checksum by counting addresses! header.Checksum = header.ComputedChecksum16() } else { fmt.Fprintf(os.Stderr, "File has already header...(%s)\n", masque) } // - // En fonction du mode d'importation... + // Depending on the import mode... // switch typeModeImport { - case MODE_ASCII: + case SaveModeAscii: // // Importation en mode ASCII // if isAmsdos { - // Supprmier en-tete si elle existe + // Remove header if it exists fmt.Fprintf(os.Stderr, "Removing header...(%s)\n", masque) copy(buff[0:], buff[binary.Size(StAmsdos{}):]) } - case MODE_BINAIRE: + case SaveModeBinary: // - // Importation en mode BINAIRE + // Binary mode import // if !isAmsdos { // - // Indique qu'il faudra ajouter une en-tete + // Indicates that a header must be added // addHeader = true } } // - // Si fichier ok pour etre import + // If file is ok to be imported // if addHeader { - // Ajoute l'en-tete amsdos si necessaire + // Add the amsdos header if necessary var rbuff bytes.Buffer err = binary.Write(&rbuff, binary.LittleEndian, header) @@ -810,55 +819,57 @@ func (d *DSK) PutFile(masque string, typeModeImport uint8, loadAddress, exeAddre if fileLength > 65536 { return ErrorFileSizeExceed } - // if (MODE_BINAIRE) ClearAmsdos(Buff); //Remplace les octets inutilises par des 0 dans l'en-tete + // if (MODE_BINAIRE) ClearAmsdos(Buff); // Replace unused bytes by 0 in the header return d.CopyFile(buff, cFileName, uint16(fileLength), 256, userNumber, isSystemFile, readOnly) } -// Copie un fichier sur le DSK +// CopyFile copies a file on the DSK // -// la taille est determine par le nombre de NbPages -// regarder pourquoi different d'une autre DSK +// the size is determined by the number of PageCount +// check why different from another DSK func (d *DSK) CopyFile(bufFile []byte, fileName string, fileLength, maxBloc, userNumber uint16, isSystemFile, readOnly bool) error { - var nbPages, taillePage int + var nbPages int d.FillBitmap() - dirLoc := d.GetNomDir(fileName) - var posFile uint16 // Construit l'entree pour mettre dans le catalogue - for posFile = 0; posFile < fileLength; { // Pour chaque bloc du fichier - posDir, err := d.RechercheDirLibre() // Trouve une entree libre dans le CAT + dirLoc := d.GetDirEntry(fileName) + var posFile uint16 // Build the entry to put in the catalog + for posFile = 0; posFile < fileLength; { // For each block of the file + posDir, err := d.FindFreeDirEntry() // Find first free entry in the catalog if err == nil { - dirLoc.User = uint8(userNumber) // Remplit l'entree : User 0 + dirLoc.User = uint8(userNumber) // User number if isSystemFile { dirLoc.Ext[0] |= 0x80 } if readOnly { dirLoc.Ext[0] |= 0x80 } - dirLoc.NumPage = uint8(nbPages) // Numero de l'entree dans le fichier + dirLoc.NumPage = uint8(nbPages) // entry number in the file nbPages++ - taillePage = ((int(fileLength) - int(posFile) + 127) >> 7) // Taille de la page (on arrondit par le haut) - if taillePage > 128 { // Si y'a plus de 16k il faut plusieurs pages - taillePage = 128 + blocksCount := (int(fileLength) - int(posFile) + 127) >> 7 // Page size (we add 127 to round up) + + if blocksCount > 128 { // Limit to 128 blocks (128K) per file + blocksCount = 128 } - dirLoc.NbPages = uint8(taillePage) - l := (dirLoc.NbPages + 7) >> 3 // Nombre de blocs=TaillePage/8 arrondi par le haut + dirLoc.PageCount = uint8(blocksCount) + l := (dirLoc.PageCount + 7) >> 3 // Number of blocks=PageCount/8 rounded up for i := 0; i < 16; i++ { dirLoc.Blocks[i] = 0 } var j uint8 - for j = 0; j < l; j++ { // Pour chaque bloc de la page - bloc := d.RechercheBlocLibre(int(maxBloc)) // Met le fichier sur la disquette - // fmt.Fprintf(os.Stdout,"Bloc:%d, MaxBloc:%d\n",bloc,maxBloc) - if bloc != 0 { - dirLoc.Blocks[j] = bloc - err = d.WriteBloc(int(bloc), bufFile, posFile) + for j = 0; j < l; j++ { // For each block of the file + block := d.FindFreeBlock(int(maxBloc)) // Find first free block + // fmt.Fprintf(os.Stdout,"Bloc:%d, MaxBloc:%d\n",block,maxBloc) + if block != 0 { + // and put it on the disk + dirLoc.Blocks[j] = block + err = d.WriteBloc(int(block), bufFile, posFile) if err != nil { - fmt.Fprintf(os.Stdout, "error while writing bloc %v\n", err) + fmt.Fprintf(os.Stdout, "error while writing block %v\n", err) } - posFile += 1024 // Passe au bloc suivant + posFile += 1024 // Go to the next block } else { - return ErrorNoBloc + return ErrDiskFull } } // fmt.Fprintf(os.Stdout, "posDir:%d dirloc:%v\n", posDir, dirLoc) @@ -877,7 +888,7 @@ func (d *DSK) FillBitmap() int { for i := 0; i < len(d.BitMap); i++ { d.BitMap[i] = 0 } - d.BitMap[0] = 1 + d.BitMap[0] = 1 // The first two blocks are reserved for the catalog d.BitMap[1] = 1 var nbKo int for i := 0; i < 64; i++ { @@ -895,32 +906,32 @@ func (d *DSK) FillBitmap() int { return nbKo } -func (d *DSK) GetNomDir(nomFile string) StDirEntry { +func (d *DSK) GetDirEntry(fileName string) StDirEntry { e := StDirEntry{} for i := 0; i < 8; i++ { - e.Nom[i] = ' ' + e.Name[i] = ' ' } for i := 0; i < 3; i++ { e.Ext[i] = ' ' } - copy(e.Ext[:], []byte(nomFile[9:12])) - copy(e.Nom[:], []byte(nomFile[0:8])) + copy(e.Ext[:], fileName[9:12]) + copy(e.Name[:], fileName[0:8]) return e } func (d *DSK) CopyRawFile(bufFile []byte, fileLength uint16, track, sector int) (int, int, error) { d.FillBitmap() - var posFile uint16 // Construit l'entree pour mettre dans le catalogue + var posFile uint16 // Build the entry to put in the catalog var err error var written int - for posFile = 0; posFile < fileLength; { // Pour chaque bloc du fichier + for posFile = 0; posFile < fileLength; { // For each block of the file track, sector, written, err = d.WriteAtTrackSector(track, sector, bufFile, posFile) if err != nil { return track, sector, err } - posFile += uint16(written) // Passe à la position suivante + posFile += uint16(written) // Move to the next block } return track, sector, nil } @@ -929,10 +940,10 @@ func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uin var dataWritten int minSect := d.GetMinSect() // - // Ajuste le nombre de pistes si depassement capacite + // Adjusts the number of tracks if capacity is exceeded // - if track > int(d.Entry.NbTracks-1) { - if d.Entry.NbHeads == 1 { + if track > int(d.Entry.Tracks-1) { + if d.Entry.Heads == 1 { d.FormatTrack(0, uint8(track), 0, minSect, 9) } else { currentHead := d.Tracks[track-1].Head @@ -947,7 +958,7 @@ func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uin track++ sect = 0 } - sectorSize := uint16(d.Tracks[track].Sect[sect].SizeByte) + sectorSize := d.Tracks[track].Sect[sect].Size pos := d.GetPosData(uint8(track), uint8(sect)+minSect, true) maxSize := sectorSize if len(bufBloc) < int(offset+maxSize) { @@ -962,8 +973,8 @@ func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uin track++ sect = 0 } - if track > int(d.Entry.NbTracks-1) { - if d.Entry.NbHeads == 1 { + if track > int(d.Entry.Tracks-1) { + if d.Entry.Heads == 1 { d.FormatTrack(0, uint8(track), 0, minSect, 9) } else { currentHead := d.Tracks[track-1].Head @@ -974,7 +985,7 @@ func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uin } } } - sectorSize = uint16(d.Tracks[track].Sect[sect].SizeByte) + sectorSize = d.Tracks[track].Sect[sect].Size pos = d.GetPosData(uint8(track), uint8(sect)+minSect, true) maxSize = sectorSize*2 + offset if (len(bufBloc) - dataWritten - int(offset)) < int(maxSize) { @@ -997,10 +1008,10 @@ func (d *DSK) WriteBloc(bloc int, bufBloc []byte, offset uint16) error { } } // - // Ajuste le nombre de pistes si depassement capacite + // Adjust the number of tracks if overflowing // - if track > int(d.Entry.NbTracks-1) { - if d.Entry.NbHeads == 1 { + if track > int(d.Entry.Tracks-1) { + if d.Entry.Heads == 1 { d.FormatTrack(0, uint8(track), 0, minSect, 9) } else { currentHead := d.Tracks[track-1].Head @@ -1022,8 +1033,8 @@ func (d *DSK) WriteBloc(bloc int, bufBloc []byte, offset uint16) error { track++ sect = 0 } - if track > int(d.Entry.NbTracks-1) { - if d.Entry.NbHeads == 1 { + if track > int(d.Entry.Tracks-1) { + if d.Entry.Heads == 1 { d.FormatTrack(0, uint8(track), 0, minSect, 9) } else { currentHead := d.Tracks[track-1].Head @@ -1042,12 +1053,12 @@ func (d *DSK) WriteBloc(bloc int, bufBloc []byte, offset uint16) error { func (d *DSK) ExtractRawFile(fileLength uint16, track, sector int) (int, int, []byte) { d.FillBitmap() content := make([]byte, 0) - var posFile uint16 // Construit l'entree pour mettre dans le catalogue + var posFile uint16 // Build the entry to put in the catalog var buf []byte - for posFile = 0; posFile < fileLength; { // Pour chaque bloc du fichier + for posFile = 0; posFile < fileLength; { // For each block of the file track, sector, buf = d.ReadAtTrackSector(track, sector) - posFile += uint16(len(buf)) // Passe à la position suivante + posFile += uint16(len(buf)) // Move to the next block content = append(content, buf...) } return track, sector, content @@ -1062,7 +1073,7 @@ func (d *DSK) ReadAtTrackSector(track, sect int) (int, int, []byte) { track++ } } - sectorSize := uint16(d.Tracks[track].Sect[sect].SizeByte) + sectorSize := d.Tracks[track].Sect[sect].Size pos := d.GetPosData(uint8(track), uint8(sect)+minSect, true) bufBloc1 := make([]byte, sectorSize) copy(bufBloc1, d.Tracks[track].Data[pos:pos+sectorSize]) @@ -1073,7 +1084,7 @@ func (d *DSK) ReadAtTrackSector(track, sect int) (int, int, []byte) { track++ sect = 0 } - sectorSize = uint16(d.Tracks[track].Sect[sect].SizeByte) + sectorSize = d.Tracks[track].Sect[sect].Size bufBloc2 := make([]byte, sectorSize) pos = d.GetPosData(uint8(track), uint8(sect)+minSect, true) @@ -1113,11 +1124,8 @@ func (d *DSK) ReadBloc(bloc int) []byte { return bufBloc } -// -// Recherche un bloc libre et le remplit -// - -func (d *DSK) RechercheBlocLibre(maxBloc int) uint8 { +// FindFreeBlock finds first free block to be filled +func (d *DSK) FindFreeBlock(maxBloc int) uint8 { for i := 2; i < maxBloc; i++ { if d.BitMap[i] == 0 { d.BitMap[i] = 1 @@ -1127,11 +1135,8 @@ func (d *DSK) RechercheBlocLibre(maxBloc int) uint8 { return 0 } -// -// Recherche une entree de repertoire libre -// - -func (d *DSK) RechercheDirLibre() (uint8, error) { +// FindFreeDirEntry finds first free directory entry +func (d *DSK) FindFreeDirEntry() (uint8, error) { for i := 0; i < 64; i++ { dir, _ := d.GetInfoDirEntry(uint8(i)) if dir.User == USER_DELETED { @@ -1149,7 +1154,7 @@ func (d *DSK) DisplayCatalogue() { for i := 0; i < 64; i++ { entry := d.Catalogue[i] if entry.User != USER_DELETED && entry.NumPage != 0 { - fmt.Fprintf(os.Stderr, "%s.%s : %d\n", entry.Nom, entry.Ext, entry.User) + fmt.Fprintf(os.Stderr, "%s.%s : %d\n", entry.Name, entry.Ext, entry.User) } } } @@ -1163,8 +1168,8 @@ func (d *DSK) GetEntryyNameInCatalogue(num int) string { for i := 0; i < 64; i++ { entry := d.Catalogue[i] if entry.User != USER_DELETED && entry.NumPage != 0 && i == num { - nom = fmt.Sprintf("%.8s.%.3s", entry.Nom, entry.Ext) - // fmt.Fprintf(os.Stdout,"%s.%s : %d\n",entry.Nom,entry.Ext,entry.User ) + nom = fmt.Sprintf("%.8s.%.3s", entry.Name, entry.Ext) + // fmt.Fprintf(os.Stdout,"%s.%s : %d\n",entry.Name,entry.Ext,entry.User ) return nom } } @@ -1182,14 +1187,14 @@ func (d *DSK) GetEntrySizeInCatalogue(num int) string { var p, t int for { if d.Catalogue[p+i].User == entry.User { - t += int(d.Catalogue[p+i].NbPages) + t += int(d.Catalogue[p+i].PageCount) } p++ if d.Catalogue[p+i].NumPage != 0 || p+i >= 64 { break } } - return fmt.Sprintf("%d ko", (t+7)>>3) + return fmt.Sprintf("%d KiB", (t+7)>>3) } } return "" @@ -1199,9 +1204,9 @@ func (d *DSK) GetFilesize(s StDirEntry) int { t := 0 for i := 0; i < 64; i++ { if d.Catalogue[i].User != USER_DELETED { - if d.Catalogue[i].Nom == s.Nom && + if d.Catalogue[i].Name == s.Name && d.Catalogue[i].Ext == s.Ext { - t += int(d.Catalogue[i].NbPages) + t += int(d.Catalogue[i].PageCount) } } } @@ -1213,7 +1218,7 @@ func (d *DSK) GetFilesIndices() []int { cache := make(map[string]bool) for i := 0; i < 64; i++ { if d.Catalogue[i].User != USER_DELETED { - filename := fmt.Sprintf("%s.%s", d.Catalogue[i].Nom, d.Catalogue[i].Ext) + filename := fmt.Sprintf("%s.%s", d.Catalogue[i].Name, d.Catalogue[i].Ext) if !cache[filename] { cache[filename] = true indices = append(indices, i) @@ -1314,6 +1319,7 @@ func (d *DSK) GetType(langue int, ams *StAmsdos) string { return "ASCII" } +// FileExists checks if a file exists on the DSK and returns its index in the directory func (d *DSK) FileExists(entry StDirEntry) int { for i := 0; i < 64; i++ { dir, err := d.GetInfoDirEntry(uint8(i)) @@ -1321,12 +1327,12 @@ func (d *DSK) FileExists(entry StDirEntry) int { fmt.Fprintf(os.Stderr, "Error while getting info dir entry (%d) error :%v\n", i, err) } else { for q := 0; q < 8; q++ { - dir.Nom[q] &= 127 + dir.Name[q] &= 127 } for q := 0; q < 3; q++ { dir.Ext[q] &= 127 } - if dir.User != USER_DELETED && dir.Nom == entry.Nom && dir.Ext == entry.Ext { + if dir.User != USER_DELETED && dir.Name == entry.Name && dir.Ext == entry.Ext { return i } } @@ -1337,7 +1343,7 @@ func (d *DSK) FileExists(entry StDirEntry) int { func (d *DSK) GetFileIn(filename string, indice int) ([]byte, error) { i := indice - lMax := 0x1000000 + bytesLeft := 0x1000000 b := make([]byte, 0) firstBlock := true /* tabDir := make([]StDirEntry, 64) @@ -1348,51 +1354,47 @@ func (d *DSK) GetFileIn(filename string, indice int) ([]byte, error) { if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } - entryIndice := d.Catalogue[i] - var cumul, tailleFichier int + dirEntry := d.Catalogue[i] + var bytesRead, totalSize int var isAmsdos bool for { - l := (d.Catalogue[i].NbPages + 7) >> 3 + l := (d.Catalogue[i].PageCount + 7) >> 3 for j := 0; uint8(j) < l; j++ { - tailleBloc := 1024 + blockSize := 1024 bloc := d.ReadBloc(int(d.Catalogue[i].Blocks[j])) if firstBlock { var header *cpc.CpcHead isAmsdos, header = CheckAmsdos(bloc) if isAmsdos { - tailleFichier = int(header.Size) + 0x80 + totalSize = int(header.Size) + 0x80 } firstBlock = false } - var nbOctets int - if lMax > tailleBloc { - nbOctets = tailleBloc - } else { - nbOctets = lMax - } - if nbOctets > 0 { + var byteCount = min(bytesLeft, blockSize) + if byteCount > 0 { b = append(b, bloc...) - cumul += nbOctets + bytesRead += byteCount } - lMax -= 1024 + bytesLeft -= byteCount } i++ if i >= 64 { return b, errors.New("cannot get the file, Exceed catalogue indice") } - if entryIndice.Nom != d.Catalogue[i].Nom || entryIndice.Ext != d.Catalogue[i].Ext { + if dirEntry.Name != d.Catalogue[i].Name || dirEntry.Ext != d.Catalogue[i].Ext { break } } - if tailleFichier <= 0 || tailleFichier <= cumul { - tailleFichier = cumul + // totalSize can stay 0 if the file doesn't have the AMSDOS header (size is unknown) + if totalSize <= 0 || totalSize <= bytesRead { + totalSize = bytesRead } /*if !isAmsdos { keepOn := true - for i = tailleFichier - 1; i >= 0; i-- { + for i = totalSize - 1; i >= 0; i-- { if b[i] == 0 { - tailleFichier-- + totalSize-- } else { keepOn = false } @@ -1402,68 +1404,64 @@ func (d *DSK) GetFileIn(filename string, indice int) ([]byte, error) { } }*/ - return b[0:tailleFichier], nil + return b[0:totalSize], nil } func (d *DSK) ViewFile(indice int) ([]byte, int, error) { i := indice - lMax := 0x1000000 + bytesLeft := 0x1000000 b := make([]byte, 0) firstBlock := true err := d.GetCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } - entryIndice := d.Catalogue[i] - var tailleFichier, cumul int + dirEntry := d.Catalogue[i] + var fileSize, bytesRead int for { - l := (d.Catalogue[i].NbPages + 7) >> 3 + l := (d.Catalogue[i].PageCount + 7) >> 3 var j uint8 for j = 0; j < l; j++ { - tailleBloc := 1024 - bloc := d.ReadBloc(int(d.Catalogue[i].Blocks[j])) + blockSize := 1024 + block := d.ReadBloc(int(d.Catalogue[i].Blocks[j])) if firstBlock { - isAmsdos, header := CheckAmsdos(bloc) + isAmsdos, header := CheckAmsdos(block) if isAmsdos { - t := make([]byte, len(bloc)) - copy(t, bloc[HeaderSize:]) - bloc = t - tailleBloc -= HeaderSize - tailleFichier = int(header.Size) + t := make([]byte, len(block)) + copy(t, block[HeaderSize:]) + block = t + blockSize -= HeaderSize + fileSize = int(header.Size) } firstBlock = false } - var nbOctets int - if lMax > tailleBloc { - nbOctets = tailleBloc - } else { - nbOctets = lMax - } - if nbOctets > 0 { - b = append(b, bloc...) - cumul += nbOctets + var byteCount = min(bytesLeft, blockSize) + if byteCount > 0 { + b = append(b, block...) + bytesRead += byteCount } - lMax -= 1024 + bytesLeft -= 1024 } i++ if i >= 64 { - return b, cumul, errors.New("cannot get the file, Exceed catalogue indice") + return b, bytesRead, errors.New("cannot get the file, Exceed catalogue indice") } - if entryIndice.Nom != d.Catalogue[i].Nom || entryIndice.Ext != d.Catalogue[i].Ext { + if dirEntry.Name != d.Catalogue[i].Name || dirEntry.Ext != d.Catalogue[i].Ext { break } } - if tailleFichier == 0 { - tailleFichier = cumul + if fileSize == 0 { + fileSize = bytesRead } + // for files padded with 0xE5, remove the padding from total size for i := len(b) - 1; i >= 0; i-- { if b[i] == 0xE5 { - tailleFichier = i + fileSize = i } else { break } } - return b, tailleFichier, nil + return b, fileSize, nil } func CheckAmsdos(buf []byte) (bool, *StAmsdos) { @@ -1478,29 +1476,28 @@ func CheckAmsdos(buf []byte) (bool, *StAmsdos) { return false, &StAmsdos{} } -func (d *DSK) RemoveFile(indice uint8) error { +func (d *DSK) RemoveFile(index uint8) error { err := d.GetCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } - entryIndice := d.Catalogue[indice] + firstDirEntry := d.Catalogue[index] + // this assumes files are contiguous in the directory, is it always the case? for { - - entry, err := d.GetInfoDirEntry(indice) + entry, err := d.GetInfoDirEntry(index) if err != nil { return ErrorNoDirEntry } - if entryIndice.Nom != d.Catalogue[indice].Nom || entryIndice.Ext != d.Catalogue[indice].Ext { + if firstDirEntry.Name != d.Catalogue[index].Name || firstDirEntry.Ext != d.Catalogue[index].Ext { break } - d.Catalogue[indice].User = USER_DELETED + d.Catalogue[index].User = USER_DELETED entry.User = USER_DELETED - if err := d.SetInfoDirEntry(indice, entry); err != nil { + if err := d.SetInfoDirEntry(index, entry); err != nil { return ErrorNoDirEntry } - indice++ - + index++ } return nil } diff --git a/dsk_test.go b/dsk_test.go index 6c2e2f4..da3dc8a 100644 --- a/dsk_test.go +++ b/dsk_test.go @@ -1,32 +1,33 @@ package dsk import ( + "github.com/stretchr/testify/assert" "testing" ) func TestOpenDsk(t *testing.T) { - formated := FormatDsk(9, 40, 1, DataFormat, 0) - if err := WriteDsk("test.dsk", formated); err != nil { - t.Fatalf("error :%v", err) + formatted := FormatDsk(9, 40, 1, DataFormat, 0) + if err := WriteDsk("test.dsk", formatted); err != nil { + t.Fatal(err) } - t.Logf("(%s)=(%s)\n", "/opt/data/sonic-pa.bas", GetNomAmsdos("/opt/data/sonic-pa.bas")) - if err := formated.PutFile("ironman.scr", MODE_BINAIRE, 0, 0, 0, false, false); err != nil { - t.Fatalf("Error:%v", err) + t.Logf("(%s)=(%s)\n", "/opt/data/sonic-pa.bas", GetAmsDosName("/opt/data/sonic-pa.bas")) + if err := formatted.PutFile("ironman.scr", SaveModeBinary, 0, 0, 0, false, false); err != nil { + t.Fatal(err) } - err := WriteDsk("test.dsk", formated) + err := WriteDsk("test.dsk", formatted) if err != nil { t.Fatal(err) } dsk, err := ReadDsk("ironman.dsk") if err != nil { - t.Fatalf("error %v", err) + t.Fatal(err) } - t.Logf("NBtracks:%d\n", dsk.Entry.NbTracks) - t.Logf("Head :%d\n", dsk.Entry.NbHeads) + assert.Equal(t, uint8(42), dsk.Entry.Tracks, "bad number of tracks: %v", dsk.Entry.Tracks) + assert.Equal(t, uint8(1), dsk.Entry.Heads, "bad number of heads: %v", dsk.Entry.Heads) if err := dsk.CheckDsk(); err != nil { - t.Fatalf("error %v", err) + t.Fatal(err) } - t.Logf("%s\n", dsk.GetEntryyNameInCatalogue(1)) - t.Logf("%s\n", dsk.GetEntrySizeInCatalogue(1)) + assert.Equal(t, "IRONMAN .SCR", dsk.GetEntryyNameInCatalogue(1)) + assert.Equal(t, "16 KiB", dsk.GetEntrySizeInCatalogue(1)) } diff --git a/test.dsk b/test.dsk index da7476d2d877461a50ba0e6ef9a98e466c83d32c..0c460b492567688e1ee72afd3ad75449c732d32e 100644 GIT binary patch delta 26 hcmZqp#NF_Tdx8>saAt~vOR#sNW~(OSR!yesKLCat3Eltz delta 26 dcmZqp#NF_Tdx8=>0|Yc`wrVnN)nvN<0{~~-2fP3P diff --git a/utils.go b/utils.go index 59a2bf3..037a494 100644 --- a/utils.go +++ b/utils.go @@ -9,7 +9,7 @@ import ( func GetNomDir(s string) StDirEntry { entry := StDirEntry{} for i := 0; i < 8; i++ { - entry.Nom[i] = 32 + entry.Name[i] = 32 } for i := 0; i < 3; i++ { entry.Ext[i] = 32 @@ -20,7 +20,7 @@ func GetNomDir(s string) StDirEntry { if inputFile[i] == '.' { break } - entry.Nom[index] = inputFile[i] + entry.Name[index] = inputFile[i] index++ } From 3673d491726e3ddf2dc9e516f68bca5dba79702a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Thu, 31 Aug 2023 09:44:08 +0200 Subject: [PATCH 07/10] not sure if this is good but go.work and local replacement in go.mod --- go.mod | 4 ++++ go.work | 5 +++++ 2 files changed, 9 insertions(+) create mode 100644 go.work diff --git a/go.mod b/go.mod index 7466fdb..94f3e7e 100644 --- a/go.mod +++ b/go.mod @@ -12,3 +12,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) + +replace ( + github.com/jeromelesaux/dsk => ./ + ) \ No newline at end of file diff --git a/go.work b/go.work new file mode 100644 index 0000000..dcaad55 --- /dev/null +++ b/go.work @@ -0,0 +1,5 @@ +go 1.21 + +use ( + . +) \ No newline at end of file From 940951c0ecc79894e4205bf25d64f6126d1720e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Wed, 27 Dec 2023 22:57:57 +0100 Subject: [PATCH 08/10] Rename things to sound more natural, add formatting test' --- cli/main.go | 6 +-- dsk.go | 134 +++++++++++++++++++++++++--------------------------- dsk_test.go | 55 ++++++++++++++++++++- test.dsk | Bin 194816 -> 194816 bytes 4 files changed, 120 insertions(+), 75 deletions(-) diff --git a/cli/main.go b/cli/main.go index ca15a32..e3cb30b 100644 --- a/cli/main.go +++ b/cli/main.go @@ -560,11 +560,11 @@ func infoSna(snaPath string) (onError bool, message, hint string) { } func listDsk(d dsk.DSK, dskPath string) (onError bool, message, hint string) { - if err := d.GetCatalogue(); err != nil { + if err := d.RefreshCatalogue(); err != nil { return true, fmt.Sprintf("Error while getting catalogue in dsk file (%s) error %v\n", dskPath, err), "Check your dsk file with option -dsk yourdsk.dsk -analyze" } totalUsed := 0 - for _, i := range d.GetFilesIndices() { + for _, i := range d.GetFileIndices() { size := fmt.Sprintf("%.3d ko", d.GetFilesize(d.Catalogue[i])) totalUsed += d.GetFilesize(d.Catalogue[i]) filename := fmt.Sprintf("%s.%s", d.Catalogue[i].Name, d.Catalogue[i].Ext) @@ -714,7 +714,7 @@ func getFileDsk(d dsk.DSK, fileInDsk, dskPath, directory string) (onError bool, return true, "amsdosfile option is empty, set it.", "dsk -dsk output.dsk -get -amsdosfile hello.bin" } if fileInDsk == "*" { - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "Error while getting the catalogue in dsk error :%v\n", err) } diff --git a/dsk.go b/dsk.go index 32002d5..5fba666 100644 --- a/dsk.go +++ b/dsk.go @@ -13,20 +13,20 @@ import ( "github.com/jeromelesaux/m4client/cpc" ) -var ( +const ( USER_DELETED uint8 = 0xE5 SECTSIZE uint16 = 512 NOT_FOUND int = -1 ) +// 0xE5 in binary is 11100101, which on MFM encoding is 0101010101010101 (0x5555) var ( - ErrorUnsupportedDskFormat = errors.New("unsupported DSK Format") - ErrorUnsupportedMultiHeadDsk = errors.New("multi-side dsk ! Expected 1 head") - ErrorBadSectorNumber = errors.New("dsk has wrong sector number") - ErrorCatalogueExceed = errors.New("catalogue indice exceed") - ErrDiskFull = errors.New("error no more free blocks available") - ErrorNoDirEntry = errors.New("error no more dir entry available") - ErrorFileSizeExceed = errors.New("filesize exceed") + ErrorUnsupportedDskFormat = errors.New("unsupported DSK Format") + ErrorBadSectorNumber = errors.New("dsk has wrong sector number") + ErrorCatalogueExceed = errors.New("catalogue indices exceed") + ErrDiskFull = errors.New("error no more free blocks available") + ErrorNoDirEntry = errors.New("error no more dir entry available") + ErrorFileSizeExceed = errors.New("filesize exceed") ) type SaveMode uint8 @@ -55,7 +55,7 @@ const HeaderSize = 0x80 type DskFormat = int -type StAmsdos = cpc.CpcHead +type AmsDosHeader = cpc.CpcHead type CPCEMUEnt struct { Header [0x22]byte // "MV - CPCEMU Disk-File\r\nDisk-Info\r\n" @@ -294,13 +294,12 @@ type StDirEntry struct { } type DSK struct { - Entry CPCEMUEnt - TrackSizeTable []byte // dsk format [0xCC]byte - Tracks []CPCEMUTrack - BitMap [256]byte - Catalogue [64]StDirEntry // 64 entries in directory (64*32=2048 bytes) - catalogueLoaded bool - Extended bool // extended dsk format flag + Entry CPCEMUEnt + TrackSizeTable []byte // dsk format [0xCC]byte + Tracks []CPCEMUTrack + BitMap [256]byte + Catalogue [64]StDirEntry // 64 entries in directory (64*32=2048 bytes) + Extended bool // extended dsk format flag } func (d *DSK) CleanBitmap() { @@ -529,7 +528,7 @@ func ReadDsk(filePath string) (*DSK, error) { func (d *DSK) CheckDsk() error { // if d.Entry.Heads == 1 { - minSectFirst := d.GetMinSect() + minSectFirst := d.FirstSectorId() if minSectFirst != 0x41 && minSectFirst != 0xc1 && minSectFirst != 0x01 { fmt.Fprintf(os.Stderr, "Bad sector %.2x\n", minSectFirst) return ErrorBadSectorNumber @@ -574,8 +573,8 @@ func (d *DSK) CheckDsk() error { // return ErrorUnsupportedMultiHeadDsk } -// GetMinSect finds the lowest sector number of a track -func (d *DSK) GetMinSect() uint8 { +// FirstSectorId finds the lowest sector number of a track +func (d *DSK) FirstSectorId() uint8 { var Sect uint8 = 0xFF var s uint8 tr := d.Tracks[0] @@ -622,7 +621,7 @@ func (d *DSK) GetFile(path string, indice int) error { nomIndice := make([]byte, 16) lMax := 0x1000000 cumul := 0 - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue, error :%v\n", err) } @@ -670,7 +669,7 @@ func (d *DSK) GetFile(path string, indice int) error { } func (d *DSK) DskSize() uint16 { - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue, error :%v\n", err) } @@ -705,11 +704,11 @@ func GetAmsDosName(mask string) string { func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAddress, userNumber uint16, isSystemFile, readOnly bool) error { buff := make([]byte, 0x20000) cFileName := GetAmsDosName(masque) - header := &StAmsdos{} + header := &AmsDosHeader{} var addHeader bool var err error - err = d.GetCatalogue() + err = d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue, error :%v\n", err) } @@ -747,7 +746,7 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd if !isAmsdos { // Create a default amsdos header fmt.Fprintf(os.Stderr, "Create header... (%s)\n", masque) - header = &StAmsdos{} + header = &AmsDosHeader{} header.User = byte(userNumber) header.Size = uint16(fileLength) header.Size2 = uint16(fileLength) @@ -782,7 +781,7 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd if isAmsdos { // Remove header if it exists fmt.Fprintf(os.Stderr, "Removing header...(%s)\n", masque) - copy(buff[0:], buff[binary.Size(StAmsdos{}):]) + copy(buff[0:], buff[binary.Size(AmsDosHeader{}):]) } case SaveModeBinary: // @@ -812,9 +811,9 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd fmt.Fprintf(os.Stdout, "error while writing in content %v\n", err) } buff = rbuff.Bytes() - // memmove( &Buff[ sizeof( StAmsdos ) ], Buff, Lg ); - // memcpy( Buff, e, sizeof( StAmsdos ) ); - // Lg += sizeof( StAmsdos ); + // memmove( &Buff[ sizeof( AmsDosHeader ) ], Buff, Lg ); + // memcpy( Buff, e, sizeof( AmsDosHeader ) ); + // Lg += sizeof( AmsDosHeader ); } if fileLength > 65536 { return ErrorFileSizeExceed @@ -829,8 +828,8 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd // check why different from another DSK func (d *DSK) CopyFile(bufFile []byte, fileName string, fileLength, maxBloc, userNumber uint16, isSystemFile, readOnly bool) error { var nbPages int - d.FillBitmap() - dirLoc := d.GetDirEntry(fileName) + d.MapDisk() + dirLoc := d.NewDirEntry(fileName) var posFile uint16 // Build the entry to put in the catalog for posFile = 0; posFile < fileLength; { // For each block of the file posDir, err := d.FindFreeDirEntry() // Find first free entry in the catalog @@ -884,13 +883,14 @@ func (d *DSK) CopyFile(bufFile []byte, fileName string, fileLength, maxBloc, use return nil } -func (d *DSK) FillBitmap() int { +// MapDisk updates the disk bitmap, based on the catalog - returns the number of blocks used +func (d *DSK) MapDisk() int { for i := 0; i < len(d.BitMap); i++ { d.BitMap[i] = 0 } d.BitMap[0] = 1 // The first two blocks are reserved for the catalog d.BitMap[1] = 1 - var nbKo int + var blocksUsed int for i := 0; i < 64; i++ { dir, _ := d.GetInfoDirEntry(uint8(i)) if dir.User != USER_DELETED { @@ -898,15 +898,15 @@ func (d *DSK) FillBitmap() int { b := dir.Blocks[j] if b > 1 && d.BitMap[b] != 1 { d.BitMap[b] = 1 - nbKo++ + blocksUsed++ } } } } - return nbKo + return blocksUsed } -func (d *DSK) GetDirEntry(fileName string) StDirEntry { +func (d *DSK) NewDirEntry(fileName string) StDirEntry { e := StDirEntry{} for i := 0; i < 8; i++ { e.Name[i] = ' ' @@ -921,7 +921,7 @@ func (d *DSK) GetDirEntry(fileName string) StDirEntry { } func (d *DSK) CopyRawFile(bufFile []byte, fileLength uint16, track, sector int) (int, int, error) { - d.FillBitmap() + d.MapDisk() var posFile uint16 // Build the entry to put in the catalog var err error @@ -938,7 +938,7 @@ func (d *DSK) CopyRawFile(bufFile []byte, fileLength uint16, track, sector int) func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uint16) (int, int, int, error) { var dataWritten int - minSect := d.GetMinSect() + minSect := d.FirstSectorId() // // Adjusts the number of tracks if capacity is exceeded // @@ -999,7 +999,7 @@ func (d *DSK) WriteAtTrackSector(track int, sect int, bufBloc []byte, offset uin func (d *DSK) WriteBloc(bloc int, bufBloc []byte, offset uint16) error { track := (bloc << 1) / 9 sect := (bloc << 1) % 9 - minSect := d.GetMinSect() + minSect := d.FirstSectorId() if minSect == 0x41 { track += 2 } else { @@ -1051,7 +1051,7 @@ func (d *DSK) WriteBloc(bloc int, bufBloc []byte, offset uint16) error { } func (d *DSK) ExtractRawFile(fileLength uint16, track, sector int) (int, int, []byte) { - d.FillBitmap() + d.MapDisk() content := make([]byte, 0) var posFile uint16 // Build the entry to put in the catalog var buf []byte @@ -1065,7 +1065,7 @@ func (d *DSK) ExtractRawFile(fileLength uint16, track, sector int) (int, int, [] } func (d *DSK) ReadAtTrackSector(track, sect int) (int, int, []byte) { - minSect := d.GetMinSect() + minSect := d.FirstSectorId() if minSect == 0x41 { track += 2 } else { @@ -1100,7 +1100,7 @@ func (d *DSK) ReadBloc(bloc int) []byte { bufBloc := make([]byte, SECTSIZE*2) track := (bloc << 1) / 9 sect := (bloc << 1) % 9 - minSect := d.GetMinSect() + minSect := d.FirstSectorId() if minSect == 0x41 { track += 2 } else { @@ -1147,7 +1147,7 @@ func (d *DSK) FindFreeDirEntry() (uint8, error) { } func (d *DSK) DisplayCatalogue() { - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } @@ -1159,15 +1159,15 @@ func (d *DSK) DisplayCatalogue() { } } -func (d *DSK) GetEntryyNameInCatalogue(num int) string { - err := d.GetCatalogue() +func (d *DSK) GetCatEntryName(num int) string { + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } var nom string for i := 0; i < 64; i++ { entry := d.Catalogue[i] - if entry.User != USER_DELETED && entry.NumPage != 0 && i == num { + if entry.User != USER_DELETED && entry.NumPage == 0 && i == num { nom = fmt.Sprintf("%.8s.%.3s", entry.Name, entry.Ext) // fmt.Fprintf(os.Stdout,"%s.%s : %d\n",entry.Name,entry.Ext,entry.User ) return nom @@ -1176,14 +1176,14 @@ func (d *DSK) GetEntryyNameInCatalogue(num int) string { return nom } -func (d *DSK) GetEntrySizeInCatalogue(num int) string { - err := d.GetCatalogue() +func (d *DSK) GetCatEntrySizeStr(num int) string { + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } for i := 0; i < 64; i++ { entry := d.Catalogue[i] - if entry.User != USER_DELETED && entry.NumPage != 0 && i == num { + if entry.User != USER_DELETED && entry.NumPage == 0 && i == num { var p, t int for { if d.Catalogue[p+i].User == entry.User { @@ -1194,7 +1194,7 @@ func (d *DSK) GetEntrySizeInCatalogue(num int) string { break } } - return fmt.Sprintf("%d KiB", (t+7)>>3) + return fmt.Sprintf("%d KiB", (t+7)>>3) // (t+7)/8 to round up } } return "" @@ -1213,7 +1213,7 @@ func (d *DSK) GetFilesize(s StDirEntry) int { return (t + 7) >> 3 } -func (d *DSK) GetFilesIndices() []int { +func (d *DSK) GetFileIndices() []int { indices := make([]int, 0) cache := make(map[string]bool) for i := 0; i < 64; i++ { @@ -1229,10 +1229,7 @@ func (d *DSK) GetFilesIndices() []int { return indices } -func (d *DSK) GetCatalogue() error { - if d.catalogueLoaded { - return nil - } +func (d *DSK) RefreshCatalogue() error { for i := 0; i < 64; i++ { dirEntry, err := d.GetInfoDirEntry(uint8(i)) if err != nil { @@ -1240,12 +1237,11 @@ func (d *DSK) GetCatalogue() error { } d.Catalogue[i] = dirEntry } - d.catalogueLoaded = true return nil } func (d *DSK) SetInfoDirEntry(numDir uint8, e StDirEntry) error { - minSect := d.GetMinSect() + minSect := d.FirstSectorId() s := (numDir >> 4) + minSect var t uint8 if minSect == 0x41 { @@ -1264,8 +1260,6 @@ func (d *DSK) SetInfoDirEntry(numDir uint8, e StDirEntry) error { entry := data.Bytes() for i := 0; i < 16; i++ { pos := d.GetPosData(t, s, true) - // fmt.Fprintf(os.Stdout, "t:%d,s:%d,pos:%d\n", t, s, pos) - // fmt.Fprintf(os.Stdout,"offset:%d\n",((uint16(numDir)&15)<<5) + d.GetPosData(t, s, true)) copy(d.Tracks[t].Data[((uint16(numDir)&15)<<5)+pos:((uint16(numDir)&15)<<5)+pos+uint16(binary.Size(entry))], entry[:]) } return nil @@ -1273,7 +1267,7 @@ func (d *DSK) SetInfoDirEntry(numDir uint8, e StDirEntry) error { func (d *DSK) GetInfoDirEntry(numDir uint8) (StDirEntry, error) { dir := StDirEntry{} - minSect := d.GetMinSect() + minSect := d.FirstSectorId() s := (numDir >> 4) + minSect var t uint8 if minSect == 0x41 { @@ -1300,7 +1294,7 @@ func (d *DSK) GetInfoDirEntry(numDir uint8) (StDirEntry, error) { return dir, nil } -func (d *DSK) GetType(langue int, ams *StAmsdos) string { +func (d *DSK) FileTypeStr(ams *AmsDosHeader) string { if ams.Checksum == ams.ComputedChecksum16() { switch ams.Type { case 0: @@ -1308,11 +1302,11 @@ func (d *DSK) GetType(langue int, ams *StAmsdos) string { case 1: return "BASIC(P)" case 2: - return "BINAIRE" + return "BINARY" case 3: - return "BINAIRE(P)" + return "BINARY(P)" default: - return "INCONNU" + return "UNKNOWN" } } @@ -1350,7 +1344,7 @@ func (d *DSK) GetFileIn(filename string, indice int) ([]byte, error) { for j := 0; j < 64; j++ { tabDir[j], _ = d.GetInfoDirEntry(uint8(j)) }*/ - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } @@ -1412,7 +1406,7 @@ func (d *DSK) ViewFile(indice int) ([]byte, int, error) { bytesLeft := 0x1000000 b := make([]byte, 0) firstBlock := true - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } @@ -1464,20 +1458,20 @@ func (d *DSK) ViewFile(indice int) ([]byte, int, error) { return b, fileSize, nil } -func CheckAmsdos(buf []byte) (bool, *StAmsdos) { - header := &StAmsdos{} +func CheckAmsdos(buf []byte) (bool, *AmsDosHeader) { + header := &AmsDosHeader{} rbuff := bytes.NewReader(buf) if err := binary.Read(rbuff, binary.LittleEndian, header); err != nil { - return false, &StAmsdos{} + return false, &AmsDosHeader{} } if header.Checksum == header.ComputedChecksum16() { return true, header } - return false, &StAmsdos{} + return false, &AmsDosHeader{} } func (d *DSK) RemoveFile(index uint8) error { - err := d.GetCatalogue() + err := d.RefreshCatalogue() if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue error :%v\n", err) } diff --git a/dsk_test.go b/dsk_test.go index da3dc8a..8569b45 100644 --- a/dsk_test.go +++ b/dsk_test.go @@ -1,7 +1,9 @@ package dsk import ( + "bytes" "github.com/stretchr/testify/assert" + "os" "testing" ) @@ -28,6 +30,55 @@ func TestOpenDsk(t *testing.T) { if err := dsk.CheckDsk(); err != nil { t.Fatal(err) } - assert.Equal(t, "IRONMAN .SCR", dsk.GetEntryyNameInCatalogue(1)) - assert.Equal(t, "16 KiB", dsk.GetEntrySizeInCatalogue(1)) + assert.Equal(t, "IRONMAN .SCR", dsk.GetCatEntryName(0)) + assert.Equal(t, "16 KiB", dsk.GetCatEntrySizeStr(0)) +} + +func TestFormatDsk(t *testing.T) { + formatted := FormatDsk(9, 40, 1, DataFormat, 0) + if err := WriteDsk("test.dsk", formatted); err != nil { + t.Fatal(err) + } + assert.Equal(t, 40, len(formatted.Tracks)) + for tr := range formatted.Tracks { + track := formatted.Tracks[tr] // reference to track + assert.Equal(t, uint8(9), track.SectorCount) + for s := uint8(0); s < track.SectorCount; s++ { + sector := track.Sect[s] + assert.Equal(t, uint16(512), sector.Size) + buffer := bytes.NewBuffer(make([]byte, 1024)) + err := sector.Write(buffer) + if err != nil { + t.Fatal(err) + return + } + for b := uint16(0); b < sector.Size; b++ { + b, err := buffer.ReadByte() + if err != nil { + t.Fatal(err) + return + } + assert.Equal(t, uint8(0), b) + } + } + } +} + +func TestFileContentDsk(t *testing.T) { + disk := FormatDsk(9, 40, 1, DataFormat, 0) + if err := disk.PutFile("ironman.scr", SaveModeBinary, 0, 0, 0, false, false); err != nil { + t.Fatal(err) + } + assert.Equal(t, "IRONMAN .SCR", disk.GetCatEntryName(0)) + assert.Equal(t, "16 KiB", disk.GetCatEntrySizeStr(0)) + disk.NewDirEntry("IRONMAN .SCR") + fileBytes, err := disk.GetFileIn("IRONMAN .SCR", 0) + if err != nil { + t.Fatal(err) + } + original, err := os.ReadFile("ironman.scr") + if err != nil { + t.Fatal(err) + } + assert.Equal(t, original, fileBytes) } diff --git a/test.dsk b/test.dsk index 0c460b492567688e1ee72afd3ad75449c732d32e..4859f76472f19916c2372a66923d83846a35a7be 100644 GIT binary patch delta 81 zcmZqp#NF_TdjZp?Spq_n1w=kTXmu!U1*LtVbOMwvhSF_dx>;amyTDAw?E*8IKEwk6 DU0)$P literal 194816 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 Date: Thu, 28 Dec 2023 11:25:15 +0100 Subject: [PATCH 09/10] Rename StAmsdos to AmsdosHeader again, fix tests to work with new paths --- amsdos/amsdos.go | 10 +++++----- dsk/dsk.go | 32 ++++++++++++++++---------------- dsk/dsk_test.go | 9 +++++++-- 3 files changed, 28 insertions(+), 23 deletions(-) diff --git a/amsdos/amsdos.go b/amsdos/amsdos.go index 2ffdec0..438e1b1 100644 --- a/amsdos/amsdos.go +++ b/amsdos/amsdos.go @@ -7,16 +7,16 @@ import ( "github.com/jeromelesaux/m4client/cpc" ) -type StAmsdos = cpc.CpcHead +type AmsDosHeader = cpc.CpcHead -func CheckAmsdos(buf []byte) (bool, *StAmsdos) { - header := &StAmsdos{} +func CheckAmsdos(buf []byte) (bool, *AmsDosHeader) { + header := &AmsDosHeader{} rbuff := bytes.NewReader(buf) if err := binary.Read(rbuff, binary.LittleEndian, header); err != nil { - return false, &StAmsdos{} + return false, &AmsDosHeader{} } if header.Checksum == header.ComputedChecksum16() { return true, header } - return false, &StAmsdos{} + return false, &AmsDosHeader{} } diff --git a/dsk/dsk.go b/dsk/dsk.go index 6befe7c..daae929 100644 --- a/dsk/dsk.go +++ b/dsk/dsk.go @@ -703,10 +703,10 @@ func GetAmsDosName(mask string) string { return string(amsdosFile) } -func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAddress, userNumber uint16, isSystemFile, readOnly bool) error { +func (d *DSK) PutFile(path string, typeModeImport SaveMode, loadAddress, exeAddress, userNumber uint16, isSystemFile, readOnly bool) error { buff := make([]byte, 0x20000) - cFileName := GetAmsDosName(masque) - header := &amsdos.StAmsdos{} + cFileName := GetAmsDosName(path) + header := &amsdos.AmsDosHeader{} var addHeader bool var err error @@ -714,31 +714,31 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd if err != nil { fmt.Fprintf(os.Stderr, "error while getting the catalogue, error :%v\n", err) } - fr, err := os.Open(masque) + fr, err := os.Open(path) if err != nil { - fmt.Fprintf(os.Stderr, "Cannot read file (%s) error :%v\n", masque, err) + fmt.Fprintf(os.Stderr, "Cannot read file (%s) error :%v\n", path, err) return err } fileLength, err := fr.Read(buff) if err != nil { - fmt.Fprintf(os.Stderr, "Cannot read the content of the file (%s) with error %v\n", masque, err) + fmt.Fprintf(os.Stderr, "Cannot read the content of the file (%s) with error %v\n", path, err) return err } - fmt.Fprintf(os.Stderr, "file (%s) read (%d bytes).\n", masque, fileLength) + fmt.Fprintf(os.Stderr, "file (%s) read (%d bytes).\n", path, fileLength) _, err = fr.Seek(0, io.SeekStart) if err != nil { fmt.Fprintf(os.Stderr, "error while seeking in file error :%v\n", err) } if err = binary.Read(fr, binary.LittleEndian, header); err != nil { - fmt.Fprintf(os.Stderr, "No header found for file :%s, error :%v\n", masque, err) + fmt.Fprintf(os.Stderr, "No header found for file :%s, error :%v\n", path, err) } if typeModeImport == SaveModeAscii && fileLength%128 != 0 { buff[fileLength] = 0x1A } - if typeModeImport == MODE_PROTECTED && fileLength%128 != 0 { + if typeModeImport == SaveModeProtected && fileLength%128 != 0 { buff[fileLength] = 0x1A } @@ -751,8 +751,8 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd } if !isAmsdos { // Create a default amsdos header - fmt.Fprintf(os.Stderr, "Create header... (%s)\n", masque) - header = &amsdos.StAmsdos{} + fmt.Fprintf(os.Stderr, "Create header... (%s)\n", path) + header = &amsdos.AmsDosHeader{} header.User = byte(userNumber) header.Size = uint16(fileLength) header.Size2 = uint16(fileLength) @@ -766,13 +766,13 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd if exeAddress != 0 || loadAddress != 0 { typeModeImport = SaveModeBinary } - header.Type = typeModeImport + header.Type = byte(typeModeImport) // We must recalculate the checksum by counting addresses! header.Checksum = header.ComputedChecksum16() } else { - fmt.Fprintf(os.Stderr, "File has already header...(%s)\n", masque) + fmt.Fprintf(os.Stderr, "File has already header...(%s)\n", path) } // // Depending on the import mode... @@ -784,8 +784,8 @@ func (d *DSK) PutFile(masque string, typeModeImport SaveMode, loadAddress, exeAd // if isAmsdos { // Remove header if it exists - fmt.Fprintf(os.Stderr, "Removing header...(%s)\n", masque) - copy(buff[0:], buff[binary.Size(amsdos.StAmsdos{}):]) + fmt.Fprintf(os.Stderr, "Removing header...(%s)\n", path) + copy(buff[0:], buff[binary.Size(amsdos.AmsDosHeader{}):]) } case SaveModeBinary: // @@ -1298,7 +1298,7 @@ func (d *DSK) GetInfoDirEntry(numDir uint8) (StDirEntry, error) { return dir, nil } -func (d *DSK) FileTypeStr(ams *amsdos.StAmsdos) string { +func (d *DSK) FileTypeStr(ams *amsdos.AmsDosHeader) string { if ams.Checksum == ams.ComputedChecksum16() { switch ams.Type { case 0: diff --git a/dsk/dsk_test.go b/dsk/dsk_test.go index 5d49c9f..58f5b13 100644 --- a/dsk/dsk_test.go +++ b/dsk/dsk_test.go @@ -66,7 +66,12 @@ func TestFormatDsk(t *testing.T) { func TestFileContentDsk(t *testing.T) { disk := FormatDsk(9, 40, 1, DataFormat, 0) - if err := disk.PutFile("ironman.scr", SaveModeBinary, 0, 0, 0, false, false); err != nil { + currentWorkingDirectory, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + ironmanPath := currentWorkingDirectory + "/../testdata/ironman.scr" + if err := disk.PutFile(ironmanPath, SaveModeBinary, 0, 0, 0, false, false); err != nil { t.Fatal(err) } assert.Equal(t, "IRONMAN .SCR", disk.GetCatEntryName(0)) @@ -76,7 +81,7 @@ func TestFileContentDsk(t *testing.T) { if err != nil { t.Fatal(err) } - original, err := os.ReadFile("ironman.scr") + original, err := os.ReadFile(ironmanPath) if err != nil { t.Fatal(err) } From 84a523cf68252031ff5b722e448deb6e12fb422e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cezar=20=C2=ABikari=C2=BB=20Pokorski?= <_@ikari.software> Date: Thu, 18 Apr 2024 18:38:02 +0200 Subject: [PATCH 10/10] upgrade to go 1.22 --- go.mod | 6 ++---- go.work | 8 ++++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/go.mod b/go.mod index 94f3e7e..b40d4a3 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/jeromelesaux/dsk -go 1.21 +go 1.22 require ( github.com/jeromelesaux/m4client v0.0.0-20230327092026-4e80fd2b1474 @@ -13,6 +13,4 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect ) -replace ( - github.com/jeromelesaux/dsk => ./ - ) \ No newline at end of file +replace github.com/jeromelesaux/dsk => ./ diff --git a/go.work b/go.work index dcaad55..0f7548f 100644 --- a/go.work +++ b/go.work @@ -1,5 +1,5 @@ -go 1.21 +go 1.22 -use ( - . -) \ No newline at end of file +toolchain go1.22.2 + +use .