-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrgssad.go
172 lines (136 loc) · 3.78 KB
/
rgssad.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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
package rpgo
// RPGMakerDecrypter.Decryptre/RGSSAD.cs
import (
"bytes"
"encoding/binary"
"io"
"os"
"path/filepath"
"strings"
)
// RGSSAD
//
// The generic structure of a RPG Maker encrypted archive.
type RGSSAD struct {
Filepath string
Data []byte
ByteReader bytes.Reader
ArchivedFiles []RPGMakerArchivedFile
}
// NewRGSSAD
//
// Creates a new RGSSAD structure and configures it for use.
//
// Returns a pointer to the created structure.
func NewRGSSAD(filepath string) (created *RGSSAD, err error) {
created = new(RGSSAD)
created.Filepath = filepath
f, _ := os.Open(created.Filepath)
defer f.Close()
created.Data, err = io.ReadAll(f)
if err != nil {
return
}
created.ByteReader = *bytes.NewReader(created.Data)
return
}
// GetVersion
//
// Gets the RPGMakerVersion of the encrypted archive.
//
// Returns the version and nil for error on success; RPGMakerInvalid and an
// error otherwise.
func (rpg *RGSSAD) GetVersion() (RPGMakerVersion, error) {
var header string
header, err := ReadCString(&rpg.ByteReader, 7)
if header != RGSSADHeader || err != nil {
return RPGMakerInvalid, err
}
result, err := rpg.ByteReader.ReadByte()
return RPGMakerVersion(result), err
}
// ExtractFile
//
// Extracts the given archived file from the encrypted archive.
//
// Returns nil on success, error otherwise.
func (rpg *RGSSAD) ExtractFile(archivedFile RPGMakerArchivedFile, outputDirectoryPath string, overwriteExisting bool, createDirectory bool) (err error) {
var outputPath string
if createDirectory {
subDirectories := strings.Split(archivedFile.Name, string(filepath.Separator))
subDirectories = subDirectories[:len(subDirectories)-1]
outputPath = outputDirectoryPath
for _, itm := range subDirectories {
outputPath = filepath.Join(outputPath, itm)
err = os.Mkdir(outputPath, os.ModeDir)
_, err2 := os.Stat(filepath.Dir(outputPath))
if os.IsNotExist(err) && os.IsNotExist(err2) {
return err
}
}
outputPath = filepath.Join(outputPath, strings.Split(archivedFile.Name, string(filepath.Separator))[len(subDirectories)])
} else {
splitted := strings.Split(archivedFile.Name, string(filepath.Separator))
filename := splitted[len(splitted)-1]
outputPath = filepath.Join(outputDirectoryPath, filename)
}
_, err = rpg.ByteReader.Seek(archivedFile.Offset, io.SeekStart)
if err != nil {
return
}
data := make([]byte, archivedFile.Size)
_, err = rpg.ByteReader.Read(data)
if err != nil {
return
}
if _, err = os.Stat(outputPath); os.IsNotExist(err) || overwriteExisting {
var finalFile *os.File
finalFile, err = os.Create(outputPath)
if err != nil {
return
}
_, err = finalFile.Write(rpg.decryptFileData(data, uint32(archivedFile.Key)))
if err != nil {
return
}
return finalFile.Close()
}
return nil
}
// ExtractAllFiles
//
// Extracts all files from the archive.
//
// Returns nil on success, error otherwise.
func (rpg *RGSSAD) ExtractAllFiles(outputDirectoryPath string, overrideExisting bool) (err error) {
for _, archivedFile := range rpg.ArchivedFiles {
err = rpg.ExtractFile(archivedFile, outputDirectoryPath, overrideExisting, true)
if err != nil {
return err
}
}
return nil
}
// Decrypts the file data from a byte slice into another byte slice.
//
// This function is meant for internal use only in ExtractFile.
func (*RGSSAD) decryptFileData(encryptedFileData []byte, key uint32) []byte {
decryptedFileData := make([]byte, len(encryptedFileData))
tempKey := key
keyBytes := make([]byte, 4)
binary.LittleEndian.PutUint32(keyBytes, key)
i := 0
j := 0
for i < len(encryptedFileData) {
if j == 4 {
j = 0
tempKey *= 7
tempKey += 3
binary.LittleEndian.PutUint32(keyBytes, tempKey)
}
decryptedFileData[i] = byte(encryptedFileData[i] ^ keyBytes[j])
i++
j++
}
return decryptedFileData
}