-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloader.go
113 lines (98 loc) · 2.72 KB
/
loader.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package loader
import (
"bufio"
"encoding/binary"
"fmt"
"io"
"os"
"path/filepath"
"github.com/tylerwgrass/cruciterm/puzzle"
"golang.org/x/text/encoding/charmap"
"golang.org/x/text/transform"
)
var ErrFileParse = fmt.Errorf("could not parse file");
var ErrFileNotSupported = fmt.Errorf("file type not supported")
func LoadFile(path string) (puzzle.PuzzleDefinition, error) {
var ext = filepath.Ext(path)
if ext == ".puz" {
return loadPuzFile(path)
}
return puzzle.PuzzleDefinition{}, ErrFileNotSupported
}
// .puz file definition: https://code.google.com/archive/p/puz/wikis/FileFormat.wiki
func loadPuzFile(path string) (puzzle.PuzzleDefinition, error) {
file, err := os.Open(path)
if err != nil {
return puzzle.PuzzleDefinition{}, err
}
defer file.Close()
puz := puzzle.PuzzleDefinition{}
if err := parseHeader(&puz, file); err != nil {
return puzzle.PuzzleDefinition{}, ErrFileParse
}
if err := parseState(&puz, file); err != nil {
return puzzle.PuzzleDefinition{}, ErrFileParse
}
if err = parseContent(&puz, file); err != nil {
return puzzle.PuzzleDefinition{}, ErrFileParse
}
return puz, nil
}
func parseHeader(puz *puzzle.PuzzleDefinition, file *os.File) error {
if _, err := file.Seek(0x18, io.SeekStart); err != nil {
return err
}
ver := make([]byte, 0x4)
if _, err := file.Read(ver); err != nil {
return err
}
puz.Version = string(ver)
if _, err := file.Seek(0x2C, io.SeekStart); err != nil {
return err
}
dimensions := make([]byte, 0x4)
if _, err := file.Read(dimensions); err != nil {
return err
}
puz.NumCols = int(dimensions[0])
puz.NumRows = int(dimensions[1])
puz.NumClues = int(binary.LittleEndian.Uint16(dimensions[2:]))
return nil
}
func parseState(puz *puzzle.PuzzleDefinition, file *os.File) error {
if _, err := file.Seek(0x34, io.SeekStart); err != nil {
return err
}
numCells := puz.NumCols * puz.NumRows
puzzleState := make([]byte, numCells * 2)
if _, err := file.Read(puzzleState); err != nil {
return err
}
puz.Answer = string(puzzleState[:numCells])
puz.CurrentState = string(puzzleState[numCells:])
return nil
}
func parseContent(puz *puzzle.PuzzleDefinition, file *os.File) error {
decoder := transform.NewReader(file, charmap.ISO8859_1.NewDecoder())
reader := bufio.NewReader(decoder)
delim := byte(0)
content := make([]string, puz.NumClues + 4)
index := 0
for index < len(content) {
contentBytes, err := reader.ReadBytes(delim)
if err != nil {
if err.Error() == "EOF" {
break
}
return err
}
content[index] = string(contentBytes[:len(contentBytes) - 1])
index++
}
puz.Title = content[0]
puz.Author = content[1]
puz.Copyright = content[2]
puz.Notes = content[len(content)-1]
puz.AssignClues(content[3:len(content)-1])
return nil
}