diff --git a/.idea/runConfigurations/go_build_github_com_krylphi_autotiler.xml b/.idea/runConfigurations/go_build_github_com_krylphi_autotiler.xml index 755ea85..d973ec9 100644 --- a/.idea/runConfigurations/go_build_github_com_krylphi_autotiler.xml +++ b/.idea/runConfigurations/go_build_github_com_krylphi_autotiler.xml @@ -2,7 +2,7 @@ - + diff --git a/Makefile b/Makefile index f13f69e..f3d5308 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,10 @@ GOPATH ?= $(shell go env GOPATH) VERSION ?=$(shell git describe --tags --always) PACKAGES = $(shell go list -f {{.Dir}} ./... | grep -v /vendor/ | grep -v /proto ) DATE = $(shell date -R) +FILE_IN?=./examples/2x3_packed.png +FILE_OUT=./out/output.local.png + + BIN_NAME=autotiler @@ -38,7 +42,7 @@ src-fmt: gofmt -s -w ${PACKAGES} gci write --skip-generated --skip-vendor --section Standard --section Default --section "Prefix(github.com/krylphi)" --section "Prefix(github.com/krylphi/$(BIN_NAME))" ${PACKAGES} -.PHONY: unpack-example -unpack-example: - go run . ./examples/2x3_packed.png output.local.png\ +.PHONY: unpack +unpack: + go run . -in $(FILE_IN) -o $(FILE_OUT) -e all diff --git a/README.md b/README.md index ac25470..acffeca 100644 --- a/README.md +++ b/README.md @@ -24,6 +24,8 @@ Autotiler unpacks tileset like this to formats more suitable for map generation e.g. ```go run . -in ./examples/2x3_packed.png -p 1``` - this will create tilesets with 1 px margin and 2px spacing. * grab complete tilesets from directory specified in `-o`. +* you can pass several `-in` and `-o` parameters to unpack several tilesets at once. They will match the order. In case there are fewer `-o` parameters, the default name will be used and results will be placed in current directory. +* alternatively you can just run `make unpack FILE_IN=` and it will place all results in `./out` directory * don't worry about filenames, as program will automatically prefix output files with necessary information. E.g. for options `-o ./out/output.local.png -e 16` output files will be `./out/16x1_terrain1_output.local.png` and `./out/16x1_terrain2_output.local.png` * enjoy * alternatively you can build an application using `make build` command to use it as a standalone application without Go diff --git a/internal/unpack/unpacker.go b/internal/unpack/unpacker.go index 9c7fab5..0f79889 100644 --- a/internal/unpack/unpacker.go +++ b/internal/unpack/unpacker.go @@ -63,9 +63,15 @@ type Unpacker struct { xTiles int yTiles int padding int + options Options } -func NewUnpacker(src image.Image, xTiles, yTiles, padding int) *Unpacker { +type Options struct { + Padding int + MissingTerrainTwo bool +} + +func NewUnpacker(src image.Image, xTiles, yTiles int, options Options) *Unpacker { // todo auto detect tileWidth := src.Bounds().Dx() / xTiles tileHeight := src.Bounds().Dy() / yTiles @@ -76,7 +82,7 @@ func NewUnpacker(src image.Image, xTiles, yTiles, padding int) *Unpacker { tileHeight: tileHeight, xTiles: xTiles, yTiles: yTiles, - padding: padding, + options: options, } } @@ -135,8 +141,8 @@ func (u *Unpacker) drawFullTile(canvas *image.NRGBA, data quadTileData, idx, out y := xy[1] line := idx / outXTiles row := idx % outXTiles - paddingY := u.padding + line*2*u.padding - paddingX := u.padding + row*2*u.padding + paddingY := u.options.Padding + line*2*u.options.Padding + paddingX := u.options.Padding + row*2*u.options.Padding shiftX := i%2*u.tileWidth/2 + paddingX shiftY := i>>1*u.tileHeight/2 + paddingY @@ -168,11 +174,11 @@ func (u *Unpacker) drawFullSingleTile(tile *image.NRGBA, data quadTileData) { } func (u *Unpacker) paddedTileWidth() int { - return u.tileWidth + u.padding*2 + return u.tileWidth + u.options.Padding*2 } func (u *Unpacker) paddedTileHeight() int { - return u.tileHeight + u.padding*2 + return u.tileHeight + u.options.Padding*2 } //nolint:unused //debug function diff --git a/main.go b/main.go index b2d07da..e568589 100644 --- a/main.go +++ b/main.go @@ -31,6 +31,7 @@ import ( "log" "os" "path/filepath" + "slices" "strconv" "strings" @@ -44,6 +45,10 @@ const ( outKey = "o" ) +const ( + missingTerrain2 = "missing-terrain-two" +) + const ( export16 = "16" export28 = "28" @@ -51,47 +56,79 @@ const ( exportAll = "all" ) +type param struct { + value string + options []string +} + func main() { - args := parseArgs() + args, err := parseArgs() + if err != nil { + log.Print(err) + os.Exit(1) + } + printArgs(args) + inFiles, ok := args[inKey] if !ok { log.Print("Missing input file") os.Exit(1) } - inputFile := inFiles[0] // todo add handling for multiple inputs - imgFile, err := os.Open(inputFile) + outFiles, outs := args[outKey] + for i, inFile := range inFiles { + outputFile := fmt.Sprintf("%d.local.png", i) + if outs { + if len(outFiles) > i { + outputFile = outFiles[i].value + } + } + err := export(args, inFile, outputFile) + if err != nil { + log.Print(err) + continue + } + } +} + +func export(args map[string][]param, inputFile param, outputFile string) error { // todo wrap errors + imgFile, err := os.Open(inputFile.value) if err != nil { - panic(err) + return err } defer imgFile.Close() img, _, err := image.Decode(imgFile) if err != nil { - panic(err) + return err } - padding, err := strconv.Atoi(args[paddingKey][0]) - if err != nil { - panic(err) + padding := 0 + if paddings, ok := args[paddingKey]; ok { + padding, err = strconv.Atoi(paddings[0].value) + if err != nil { + return err + } } - unpacker := unpack.NewUnpacker(img, 2, 3, padding) + options := unpack.Options{ + Padding: padding, + MissingTerrainTwo: slices.Contains(inputFile.options, missingTerrain2), + } + + unpacker := unpack.NewUnpacker(img, 2, 3, options) if err := unpacker.Init(2); err != nil { - panic(err) + return err } exports, ok := args[exportKey] var exportTypes []string - if !ok || len(exports) == 0 || exports[0] == exportAll { + if !ok || len(exports) == 0 || exports[0].value == exportAll { exportTypes = []string{export16, export28, export48} } else { - exportTypes = exports - } - - outputFile := "out.local.png" - outFiles, ok := args[outKey] - if ok { - outputFile = outFiles[0] + exportTypes = make([]string, len(exports)) + for e := range exports { + exportTypes[e] = exports[e].value + } } for e := range exportTypes { @@ -100,60 +137,84 @@ func main() { case export16: err := produceTileset(unpacker.From6to16Terrain1, outputFile, "16x1_terrain1") if err != nil { - panic(err) + return err } err = produceTileset(unpacker.From6to16Terrain2, outputFile, "16x1_terrain2") if err != nil { - panic(err) + return err } case export28: err := produceTileset(unpacker.From6to28, outputFile, "14x2") if err != nil { - panic(err) + return err } case export48: err := produceTileset(unpacker.From6to48Terrain1, outputFile, "12x4_terrain1") if err != nil { - panic(err) + return err } err = produceTileset(unpacker.From6to48Terrain2, outputFile, "12x4_terrain2") if err != nil { - panic(err) + return err } } } + return nil } -func parseArgs() map[string][]string { +func parseArgs() (map[string][]param, error) { if len(os.Args) < 2 { log.Print( - "Usage: autotiler -in [-o ] [-p ] [-e ]\n" + - " -e can be repeated\n") + "Usage: autotiler -in [-o ] [-p ] [-e ] [--missing-terrain-two]\n" + + " -in, -o and -e can be repeated\n") os.Exit(1) } - res := make(map[string][]string) + args := make(map[string][]param) allTilesets := false for i := 1; i < len(os.Args); i += 2 { + if strings.HasPrefix(os.Args[i], "--") || !strings.HasPrefix(os.Args[i], "-") { + return nil, fmt.Errorf("Invalid argument: %s", os.Args[i]) + } key := strings.TrimPrefix(os.Args[i], "-") - v, ok := res[key] - value := os.Args[i+1] + v, ok := args[key] + value := param{ + value: os.Args[i+1], + options: []string{}, + } if key == exportKey && allTilesets { continue } - if key == exportKey && strings.EqualFold(value, exportAll) { + if key == exportKey && strings.EqualFold(value.value, exportAll) { allTilesets = true - v = []string{"all"} - res[key] = v + v = []param{{ + value: "all", + }} + args[key] = v continue } + if len(os.Args) > i+2 { + if strings.HasPrefix(os.Args[i+2], "--") { + value.options = append(value.options, os.Args[i+2]) + i++ + } + for { + if len(os.Args) <= i || !strings.HasPrefix(os.Args[i], "--") { + break + } + if strings.HasPrefix(os.Args[i], "--") { + value.options = append(value.options, os.Args[i+2]) + i++ + } + } + } if ok { v = append(v, value) } else { - v = []string{value} + v = []param{value} } - res[key] = v + args[key] = v } - return res + return args, nil } func produceTileset(unpackFunction func() (*image.NRGBA, error), outputPath, exportType string) error { @@ -182,9 +243,10 @@ func produceTileset(unpackFunction func() (*image.NRGBA, error), outputPath, exp } //nolint:unused //debug function -func printArgs(args map[string][]string) { - for key, params := range args { - for _, param := range params { +func printArgs(args map[string][]param) { + log.Printf("Args:\n") + for key, arg := range args { + for _, param := range arg { log.Printf("%s: %s\n", key, param) } }