forked from Happy-Ferret/sym
-
Notifications
You must be signed in to change notification settings - Fork 2
/
file.go
148 lines (136 loc) · 3.63 KB
/
file.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
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Package sym provides access to Playstation 1 symbol files (*.SYM).
package sym
import (
"bufio"
"bytes"
"encoding/binary"
"fmt"
"io"
"os"
"strings"
"github.com/lunixbochs/struc"
"github.com/pkg/errors"
)
// A File is PS1 symbol file.
type File struct {
// File header.
Hdr *FileHeader
// Symbols.
Syms []*Symbol
// Parser options.
Opts *Options
}
// String returns the string representation of the symbol file.
func (f *File) String() string {
buf := &strings.Builder{}
offset := 0
fmt.Fprintln(buf, f.Hdr)
offset += binary.Size(*f.Hdr)
var line int
for _, sym := range f.Syms {
bodyStr := sym.Body.String()
switch body := sym.Body.(type) {
case *IncSLD:
if line == 0 {
panic(fmt.Errorf("cannot use IncSLD symbol before associated SetSLD symbol"))
}
line++
bodyStr = fmt.Sprintf("Inc SLD linenum (to %d)", line)
case *IncSLDByte:
if line == 0 {
panic(fmt.Errorf("cannot use IncSLDByte symbol before associated SetSLD symbol"))
}
line += int(body.Inc)
bodyStr = fmt.Sprintf("Inc SLD linenum by byte %d (to %d)", body.Inc, line)
case *IncSLDWord:
if line == 0 {
panic(fmt.Errorf("cannot use IncSLDWord symbol before associated SetSLD symbol"))
}
line += int(body.Inc)
bodyStr = fmt.Sprintf("Inc SLD linenum by word %d (to %d)", body.Inc, line)
case *SetSLD:
line = int(body.Line)
case *SetSLD2:
line = int(body.Line)
}
if len(bodyStr) == 0 {
// Symbol without body.
fmt.Fprintf(buf, "%06x: %s\n", offset, sym.Hdr)
} else {
fmt.Fprintf(buf, "%06x: %s %s\n", offset, sym.Hdr, bodyStr)
}
offset += sym.Size()
}
return buf.String()
}
// A FileHeader is a PS1 symbol file header.
type FileHeader struct {
// File signature; MND.
Signature [3]byte `struc:"[3]byte"`
// File format version.
Version uint8 `struc:"uint8"`
// Target unit.
TargetUnit uint32 `struc:"uint32,little"`
}
// String returns the string representation of the symbol file header.
func (hdr *FileHeader) String() string {
const format = `
Header : %s version %d
Target unit %d`
return fmt.Sprintf(format, hdr.Signature, hdr.Version, hdr.TargetUnit)
}
// ParseFile parses the given PS1 symbol file.
func ParseFile(path string, opts *Options) (*File, error) {
if opts.Verbose { fmt.Printf("Opening '%s'...\n", path) }
f, err := os.Open(path)
if err != nil {
return nil, errors.WithStack(err)
}
defer f.Close()
return Parse(f, opts)
}
// ParseBytes parses the given PS1 symbol file, reading from b.
func ParseBytes(b []byte, opts *Options) (*File, error) {
return Parse(bytes.NewReader(b), opts)
}
// Parse parses the given PS1 symbol file, reading from r.
func Parse(r io.Reader, opts *Options) (*File, error) {
// Parse file header.
f := &File{}
br := bufio.NewReader(r)
hdr, err := parseFileHeader(br)
if err != nil {
return nil, errors.WithStack(err)
}
f.Hdr = hdr
f.Opts = opts
if f.Opts.Verbose { fmt.Printf("Parsing flattened tags...\n") }
// Parse symbols.
for {
sym, err := parseSymbol(br)
if err != nil {
if errors.Cause(err) == io.EOF {
break
}
return f, errors.WithStack(err)
}
f.Syms = append(f.Syms, sym)
}
if f.Opts.Verbose { fmt.Printf("Created %d symbol tags.\n", len(f.Syms)) }
return f, nil
}
// parseFileHeader parses and returns a PS1 symbol file header.
func parseFileHeader(r io.Reader) (*FileHeader, error) {
hdr := &FileHeader{}
if err := struc.Unpack(r, hdr); err != nil {
return nil, errors.WithStack(err)
}
// Verify Smacker signature.
switch string(hdr.Signature[:]) {
case "MND":
// valid signature.
default:
return nil, errors.Errorf(`invalid SYM signature; expected "MND", got %q`, string(hdr.Signature[:]))
}
return hdr, nil
}