Skip to content

Commit 591363b

Browse files
committed
Add Pi CLI
First supported command is "convert". It converts files from one format into another. With this feature you can convert pico-8 file into Pi formats: sprite-sheet.png and audio.sfx
1 parent 7141243 commit 591363b

File tree

11 files changed

+395
-0
lines changed

11 files changed

+395
-0
lines changed

cli/cli.go

+57
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// (c) 2022-2023 Jacek Olszak
2+
// This code is licensed under MIT license (see LICENSE for details)
3+
4+
package main
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
"github.com/urfave/cli/v2"
11+
12+
"github.com/elgopher/pi/cli/internal/convert"
13+
)
14+
15+
func main() {
16+
app := cli.App{
17+
Usage: "Pi Command Line Interface",
18+
Commands: []*cli.Command{
19+
convertCmd(),
20+
},
21+
}
22+
err := app.Run(os.Args)
23+
if err != nil {
24+
_, _ = os.Stderr.WriteString(err.Error())
25+
}
26+
}
27+
28+
func convertCmd() *cli.Command {
29+
return &cli.Command{
30+
Name: "convert",
31+
Usage: "Converts one file format into another one",
32+
Description: "Format of input and output file is deducted based on files extension.",
33+
Flags: []cli.Flag{
34+
&cli.StringFlag{
35+
Name: "format",
36+
Aliases: []string{"f"},
37+
Usage: "Format of input file. Overrides what CLI deducted based on input file extension. For now, the only supported input format is p8.",
38+
},
39+
},
40+
ArgsUsage: "input.file output.file",
41+
Action: func(context *cli.Context) error {
42+
inputFile := context.Args().Get(0)
43+
outputFile := context.Args().Get(1)
44+
45+
if context.Args().Len() > 2 {
46+
return fmt.Errorf("too many arguments")
47+
}
48+
49+
command := convert.Command{
50+
InputFormat: convert.InputFormat(context.String("format")),
51+
InputFile: inputFile,
52+
OutputFile: outputFile,
53+
}
54+
return command.Run()
55+
},
56+
}
57+
}

cli/go.mod

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
module github.com/elgopher/pi/cli
2+
3+
go 1.20
4+
5+
require github.com/urfave/cli/v2 v2.25.7
6+
7+
require (
8+
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
9+
github.com/russross/blackfriday/v2 v2.1.0 // indirect
10+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
11+
)

cli/go.sum

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
2+
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
3+
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
4+
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
5+
github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs=
6+
github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ=
7+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
8+
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=

cli/internal/convert/convert.go

+63
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// (c) 2022-2023 Jacek Olszak
2+
// This code is licensed under MIT license (see LICENSE for details)
3+
4+
package convert
5+
6+
import (
7+
"fmt"
8+
"strings"
9+
10+
"github.com/elgopher/pi/cli/internal/convert/internal/p8"
11+
)
12+
13+
type InputFormat string
14+
15+
const (
16+
InputFormatP8 = "p8"
17+
)
18+
19+
type Command struct {
20+
InputFormat
21+
InputFile string
22+
OutputFile string
23+
}
24+
25+
func (o Command) Run() error {
26+
if o.InputFormat != "" && o.InputFormat != InputFormatP8 {
27+
return fmt.Errorf("input format %s not supported", o.InputFormat)
28+
}
29+
30+
if o.InputFormat == "" {
31+
if strings.HasSuffix(o.InputFile, ".p8") {
32+
o.InputFormat = InputFormatP8
33+
} else {
34+
return fmt.Errorf("cannot deduct the format of %s input file", o.InputFile)
35+
}
36+
}
37+
38+
if o.InputFile == "" {
39+
return fmt.Errorf("input file not provided")
40+
}
41+
42+
if o.OutputFile == "" {
43+
return fmt.Errorf("output file not provided")
44+
}
45+
46+
fmt.Printf("Converting %s to %s... ", o.InputFile, o.OutputFile)
47+
fmt.Printf("Using %s input format... ", o.InputFormat)
48+
if err := o.convert(); err != nil {
49+
return err
50+
}
51+
fmt.Println("Done")
52+
return nil
53+
}
54+
55+
func (o Command) convert() error {
56+
if o.InputFormat == InputFormatP8 {
57+
if err := p8.ConvertToAudioSfx(o.InputFile, o.OutputFile); err != nil {
58+
return err
59+
}
60+
}
61+
62+
return nil
63+
}
+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
package p8
2+
3+
import (
4+
"bufio"
5+
"fmt"
6+
"io"
7+
"os"
8+
"strconv"
9+
"strings"
10+
11+
"github.com/elgopher/pi/audio"
12+
)
13+
14+
func ConvertToAudioSfx(inputFile, outputFile string) error {
15+
p8file, err := Load(inputFile)
16+
if err != nil {
17+
return fmt.Errorf("loading p8 file failed: %w", err)
18+
}
19+
20+
effects, err := p8file.SoundEffects()
21+
if err != nil {
22+
return fmt.Errorf("loading sound effects failed: %w", err)
23+
}
24+
audio.Sfx = effects
25+
26+
patterns, err := p8file.MusicPatterns()
27+
if err != nil {
28+
return fmt.Errorf("loading music patterns failed: %w", err)
29+
}
30+
audio.Pat = patterns
31+
32+
bytes, err := audio.Save()
33+
if err != nil {
34+
return fmt.Errorf("saving audio failed: %w", err)
35+
}
36+
37+
err = os.WriteFile(outputFile, bytes, 0644)
38+
if err != nil {
39+
return fmt.Errorf("writing %s failed: %w", outputFile, err)
40+
}
41+
42+
return nil
43+
}
44+
45+
func Load(filename string) (*File, error) {
46+
file := &File{}
47+
48+
f, err := os.Open(filename)
49+
if err != nil {
50+
return nil, fmt.Errorf("opening %s file failed: %w", filename, err)
51+
}
52+
53+
reader := bufio.NewReader(f)
54+
_, err = readHeader(reader)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
for {
60+
err = file.readSection(reader)
61+
if err == io.EOF {
62+
return file, nil
63+
}
64+
}
65+
}
66+
67+
func readHeader(r *bufio.Reader) (version int, err error) {
68+
firstLine, err := r.ReadString('\n')
69+
if err != nil {
70+
return 0, fmt.Errorf("error reading first line from p8 file header: %w", err)
71+
}
72+
73+
if firstLine != "pico-8 cartridge // http://www.pico-8.com" {
74+
return 0, fmt.Errorf("input file is not p8 cartridge file. Header expected")
75+
}
76+
77+
versionLine, err := r.ReadString('\n')
78+
if !strings.HasPrefix(versionLine, "version ") {
79+
return 0, fmt.Errorf("input file is not p8 cartridge file. Version in header expected, but not found")
80+
}
81+
82+
versionString := strings.SplitN(versionLine, " ", 2)[1]
83+
84+
version, err = strconv.Atoi(versionString)
85+
if err != nil {
86+
return 0, fmt.Errorf("input file is not p8 cartridge file. Version number in header expected, but found %s", versionString)
87+
}
88+
89+
return version, nil
90+
}
91+
92+
type File struct {
93+
}
94+
95+
func (p *File) readSection(reader *bufio.Reader) error {
96+
sectionName, err := reader.ReadString('\n')
97+
if err != nil {
98+
return err
99+
}
100+
101+
fmt.Println(sectionName)
102+
103+
return nil
104+
}
105+
106+
func (p *File) SoundEffects() ([64]audio.SoundEffect, error) {
107+
return [64]audio.SoundEffect{}, nil
108+
}
109+
110+
func (p *File) MusicPatterns() ([64]audio.Pattern, error) {
111+
return [64]audio.Pattern{}, nil
112+
}
+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
pico-8 cartridge // http://www.pico-8.com
2+
version 41
3+
__lua__
4+
function _draw()
5+
end
6+
function _lua
7+
__gfx__
8+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
9+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
10+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
11+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
12+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
13+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
pico-8 cartridge // http://www.pico-8.com
2+
version 41
3+
__gfx__
4+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
5+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
6+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
8+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
9+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
pico-8 cartridge // http://www.pico-8.com
2+
version 41
3+
__gfx__
4+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
5+
00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
6+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
7+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
8+
00077000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
9+
00700700000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
10+
__sfx__
11+
000100000000027050270502705027050260502605026050260502605026050260502605026050260502605026050260502605027050270502705027050280500000000000000000000000000000000000000000
12+
00100000000000000000000000003505032050310502e0502d0502b0502a050290502805027050260502505024050240502305000000000000000000000000000000000000000000000000000000000000000000
13+
__music__
14+
00 01424344
15+

0 commit comments

Comments
 (0)