-
Notifications
You must be signed in to change notification settings - Fork 1
/
arc.go
120 lines (100 loc) · 2.87 KB
/
arc.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
package arclib
import (
"errors"
"strings"
)
const (
ARCHeader = 0x55AA382D
)
var (
ErrInvalidRootNode = errors.New("root node was not a directory")
ErrInvalidMagic = errors.New("invalid ARC magic")
ErrUnknownNode = errors.New("unknown node type")
)
// ARC describes a hierarchy suitable for serialization and deserialization of an ARC file.
type ARC struct {
// RootRecord holds the root record for this ARC.
//
// It's important to note that this root record is nameless.
// Many officially-provided ARCs within Nintendo games have one folder
// containing all data, typically named "arc".
// This folder has "arc" as one of its contents.
RootRecord ARCDir
}
// OpenDir returns the directory at the given path, or an errir if not possible.
func (a *ARC) OpenDir(path string) (*ARCDir, error) {
components := strings.Split(path, "/")
// The root node is where we start our loop.
current := &a.RootRecord
for _, dirName := range components {
testDir, err := current.GetDir(dirName)
if err != nil {
return nil, err
}
current = testDir
}
return current, nil
}
// OpenFile returns a file at the given path, or an error if not possible.
func (a *ARC) OpenFile(path string) (*ARCFile, error) {
components := strings.Split(path, "/")
var dirs []string
var filename string
if len(components) == 1 {
dirs = []string{}
filename = components[0]
} else {
// Directories are all but the last element in our split string.
dirs = components[0 : len(components)-1]
filename = components[len(components)-1]
}
// Obtain directories leading up if needed
if len(dirs) != 0 {
dir, err := a.OpenDir(strings.Join(dirs, "/"))
if err != nil {
return nil, err
}
// Retrieve our file from the last directory in our hierarchy.
return dir.GetFile(filename)
} else {
// Retrieve our file from the root record.
return a.RootRecord.GetFile(filename)
}
}
// ReadFile returns the contents of a file at the given path.
func (a *ARC) ReadFile(path string) ([]byte, error) {
file, err := a.OpenFile(path)
if err != nil {
return nil, err
}
return file.Data, nil
}
// WriteFile writes the passed contents at the given path.
func (a *ARC) WriteFile(path string, contents []byte) error {
file, err := a.OpenFile(path)
if err != nil {
return err
}
file.Write(contents)
return nil
}
// FileSize returns the size in bytes of the file for the given path.
func (a *ARC) FileSize(path string) (int, error) {
file, err := a.OpenFile(path)
if err != nil {
return 0, err
}
return file.Size(), nil
}
// miniRead exists to help with iterating through our ARC.
type miniRead struct {
contents []byte
position int
}
// readLen reads the specified length and returns its bytes.
// Opposed to bytes.NewReader, we do not throw an error for bounds.
func (m *miniRead) readLen(len int) []byte {
contents := m.contents[m.position : m.position+len]
m.position += len
return contents
}