forked from PekanMmd/Pokemon-XD-Code
-
Notifications
You must be signed in to change notification settings - Fork 0
/
PBRExtensions.swift
338 lines (268 loc) · 7.59 KB
/
PBRExtensions.swift
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
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
//
// PBRExtensions.swift
// GoDToolCL
//
// Created by The Steez on 24/12/2018.
//
import Foundation
enum XGRegions : UInt32 {
case US = 0x52504245 // RPBE
case EU = 0x52504250 // RPBP
case JP = 0x5250424A // RPBJ
var index : Int {
switch self {
// arbitrary values
case .US: return 0
case .EU: return 1
case .JP: return 2
}
}
}
enum XGGame {
case Colosseum
case XD
case PBR
}
let documentsPath = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] + "/Revolution-Tool"
let region = XGRegions.US
let game = XGGame.PBR
var verbose = false
var increaseFileSizes = true
let date = Date(timeIntervalSinceNow: 0)
var logString = ""
func printg(_ args: Any...) {
for arg in args {
print(arg, separator: " ", terminator: " ")
}
print("") // automatically adds new line
for arg in args {
logString = logString + String(describing: arg) + " "
}
logString = logString + "\n"
XGUtility.saveString(logString, toFile: .log(date))
}
class XGUtility {
class func saveObject(_ obj: AnyObject, toFile file: XGFiles) {
if !file.folder.exists {
file.folder.createDirectory()
}
NSKeyedArchiver.archiveRootObject(obj, toFile: file.path)
}
class func saveData(_ data: Data, toFile file: XGFiles) -> Bool {
if !file.folder.exists {
file.folder.createDirectory()
}
do {
try data.write(to: URL(fileURLWithPath: file.path), options: [.atomic])
} catch {
return false
}
return true
}
class func saveString(_ str: String, toFile file: XGFiles) {
if let string = str.data(using: String.Encoding.utf8) {
if !saveData(string, toFile: file) {
// if printging to a log fails, don't keep printging :)
if file.folder.name != XGFolders.Logs.name {
printg("Couldn't save string to file: \(file.path)")
} else {
print("Couldn't save string to file: \(file.path)")
}
}
} else {
// if printging to a log fails, don't keep printging :)
if file.folder.name != XGFolders.Logs.name {
printg("Couldn't encode string for file: \(file.path)")
} else {
print("Couldn't encode string for file: \(file.path)")
}
}
}
class func saveJSON(_ json: AnyObject, toFile file: XGFiles) {
if !file.folder.exists {
file.folder.createDirectory()
}
do {
try JSONSerialization.data(withJSONObject: json, options: .prettyPrinted).write(to: URL(fileURLWithPath: file.path), options: [.atomic])
} catch {
printg("couldn't save json to file: \(file.path)")
}
}
class func loadJSONFromFile(_ file: XGFiles) -> AnyObject? {
do {
let json = try JSONSerialization.jsonObject(with: file.data!.data as Data, options: [])
return json as AnyObject?
} catch {
printg("couldn't load json from file: \(file.path)")
return nil
}
}
// extraction
class func extractMainFiles() {
XGFolders.setUpFolderFormat()
printg("extracting required files...")
let requiredFiles : [XGFiles] = [.dol, .fsys("common"), .fsys("mes_common"), .fsys("deck")]
var fileMissing = false
for file in requiredFiles {
if !file.exists {
printg("Error: required file '\(file.path)' doesn't exist")
fileMissing = true
}
}
if fileMissing { return }
let fsys = XGFiles.fsys("deck").fsysData
var tid = 0
var pid = 0
for i in 0 ..< fsys.numberOfEntries {
if !XGFiles.dckt(i).exists {
let data = fsys.decompressedDataForFileWithIndex(index: i)!
if let type = XGDeckTypes(rawValue: data.getWordAtOffset(0)) {
switch type {
case .DCKA: data.file = .dcka
case .DCKP: data.file = .dckp(pid); pid += 1
case .DCKT: data.file = .dckt(tid); tid += 1
case .none: data.file = .nameAndFolder("deck_\(i)", .Decks)
}
} else {
data.file = .nameAndFolder("deck_\(i)", .Decks)
}
data.save()
}
}
let common = XGFiles.fsys("common").fsysData
for i in 0 ... 32 {
if !XGFiles.common(i).exists {
let data = common.decompressedDataForFileWithIndex(index: i)!
data.file = .common(i)
data.save()
}
}
if !XGFiles.msg("mes_common").exists {
let msg = XGFiles.fsys("mes_common").fsysData.decompressedDataForFileWithIndex(index: 1)!
msg.file = .msg("mes_common")
msg.save()
}
printg("extraction complete!")
}
class func extractAllFiles() {
XGFolders.setUpFolderFormat()
extractMainFiles()
// XGThreadManager.manager.runInBackgroundAsync {
printg("extracting fsys files...")
for file in XGFolders.FSYS.files where file.fileType == .fsys {
let fsys = file.fsysData
if let msg = fsys.decompressedDataForFileWithFiletype(type: .msg) {
if msg.get2BytesAtOffset(12) == 0x5553 { // US in unicode
msg.file = .msg(msg.file.fileName.removeFileExtensions())
msg.save()
}
}
let folder = XGFolders.ISOExport(fsys.fileName.removeFileExtensions())
folder.createDirectory()
fsys.extractFilesToFolder(folder: folder, decode: true)
}
printg("extraction complete!")
// }
}
class func getFSYSForIdentifier(id: UInt32) -> XGFsys? {
for file in XGFolders.FSYS.files where file.fileName.contains(".fsys") {
let fsys = file.data!
let entries = fsys.get4BytesAtOffset(kNumberOfEntriesOffset)
for i in 0 ..< entries {
let details = fsys.get4BytesAtOffset(0x60)
let identifier = fsys.getWordAtOffset(details + (i * kSizeOfArchiveEntry))
if identifier == id {
return file.fsysData
}
}
}
return nil
}
class func compileDecks() {
printg("Compiling decks...")
var pid = 0
var tid = 0
var deckDict = [Int : XGFiles]()
for i in 0 ... 25 {
deckDict[i] = .dckt(tid)
tid += 1
}
for i in 0 ... 7 {
deckDict[i + 26] = .dckp(pid)
pid += 1
}
deckDict[34] = .dckt(tid)
tid += 1
for i in 0 ... 5 {
deckDict[35 + i] = .dckp(pid)
pid += 1
}
for i in 0 ... 1 {
deckDict[41 + i] = .dckt(tid)
tid += 1
}
for i in 0 ... 1 {
deckDict[43 + i] = .dckp(pid)
pid += 1
}
deckDict[45] = .dcka
let file = XGFiles.fsys("deck")
if file.exists {
let fsys = XGFiles.fsys("deck").fsysData
for (index, file) in deckDict {
if file.exists {
if verbose {
printg("Compiling deck:", file.path)
}
fsys.shiftAndReplaceFileWithIndexEfficiently(index, withFile: file.compress(), save: false)
}
}
fsys.save()
printg("Finished compiling decks.")
} else {
printg("Couldn't compile decks as \(file.path) doesn't exist")
}
}
}
var allStringTables = [XGStringTable]()
var stringsLoaded = false
func loadAllStrings() {
if !stringsLoaded {
// very specific order based on the id at index 0xb in the .msg file
// files with the same id are identical but may be loaded at different times
// the first stringid in the next file is one greater than the last stringid of the previous file
allStringTables = [XGFiles.msg("mes_common").stringTable, XGFiles.msg("mes_fight_e").stringTable, XGFiles.msg("mes_name_e").stringTable]
stringsLoaded = true
}
}
func getStringWithID(id: Int) -> XGString? {
loadAllStrings()
if id == 0 {
return nil
}
var currentID = id
for table in allStringTables {
if table.containsStringWithId(currentID) {
if let s = table.stringWithID(currentID) {
return s
}
} else {
currentID -= table.numberOfEntries
}
}
return nil
}
func getStringSafelyWithID(id: Int) -> XGString {
loadAllStrings()
return getStringWithID(id: id) ?? XGString(string: "-", file: nil, sid: nil)
}
func getStringsContaining(substring: String) -> [XGString] {
loadAllStrings()
var found = [XGString]()
for table in allStringTables {
for str in table.allStrings() where str.containsSubstring(substring) {
found.append(str)
}
}
return found
}