Last active
November 23, 2023 12:58
-
-
Save jarrodnorwell/0f0e3caf37361128b35fab09688158e5 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct NDSHeader { | |
struct CCharAddress { | |
let offset: Int | |
let size: Int | |
let title: String | |
var bytes: UnsafeMutablePointer<UInt8> | |
func string() -> String? { | |
guard let string = String(bytesNoCopy: bytes + offset, length: size, encoding: .utf8, freeWhenDone: false) else { | |
return nil | |
} | |
return string | |
} | |
} | |
struct UInt8Address { | |
let offset: Int | |
let size: Int | |
let title: String | |
var bytes: UnsafeMutablePointer<UInt8> | |
var pointer: UnsafeMutablePointer<UInt8>? = nil | |
mutating func allocate() { | |
pointer = .allocate(capacity: size) | |
} | |
func string() -> String? { | |
guard let pointer else { | |
return nil | |
} | |
memcpy(pointer, bytes + offset, size) | |
switch title { | |
case "Unit Code": | |
guard let unitCode = UnitCode(rawValue: Int(pointer.pointee)) else { | |
return nil | |
} | |
return unitCode.description | |
case "Device Capacity": | |
let byteCountFormatter = ByteCountFormatter() | |
byteCountFormatter.allowedUnits = [.useKB, .useMB] | |
byteCountFormatter.countStyle = .binary | |
return byteCountFormatter.string(fromByteCount: 0x400 * 0x400 * Int64(pointer.pointee)) | |
case "NDS Region": | |
guard let ndsRegion = NDSRegion(rawValue: Int(pointer.pointee)) else { | |
return nil | |
} | |
return ndsRegion.description | |
default: | |
return .init(format: "0x%02x", pointer.pointee) | |
} | |
} | |
} | |
enum UnitCode : Int, CustomStringConvertible { | |
case nds = 0x00 | |
case ndsDSi = 0x02 | |
case dsi = 0x03 | |
var description: String { | |
switch self { | |
case .nds: | |
"NDS" | |
case .ndsDSi: | |
"NDS + DSi" | |
case .dsi: | |
"DSi" | |
} | |
} | |
} | |
enum NDSRegion : Int, CustomStringConvertible { | |
case normal = 0x00 | |
case korea = 0x40 | |
case china = 0x80 | |
var description: String { | |
switch self { | |
case .normal: | |
"Normal" | |
case .korea: | |
"Korea" | |
case .china: | |
"China" | |
} | |
} | |
} | |
} | |
class ROM { | |
var pointer: UnsafeMutablePointer<UInt8> = .allocate(capacity: 0x1000) | |
var url: URL | |
init(_ url: URL) { | |
self.url = url | |
} | |
func header() { | |
do { | |
let handle = try FileHandle(forReadingFrom: url) | |
guard var data = try handle.read(upToCount: 0x1000) else { | |
throw NDSRomHeaderError.invalidData | |
} | |
data.withUnsafeMutableBytes { | |
guard let bytes = $0.bindMemory(to: UInt8.self).baseAddress else { | |
return | |
} | |
self.pointer = bytes | |
} | |
let ccharAddresses: [NDSHeader.CCharAddress] = [ | |
.init(offset: 0x00, size: 0x0C, title: "Game Title", bytes: pointer), | |
.init(offset: 0x0C, size: 0x04, title: "Game Code", bytes: pointer), | |
.init(offset: 0x10, size: 0x02, title: "Maker Code", bytes: pointer), | |
] | |
var uint8Addresses: [NDSHeader.UInt8Address] = [ | |
.init(offset: 0x12, size: 0x01, title: "Unit Code", bytes: pointer), | |
.init(offset: 0x13, size: 0x01, title: "Encryption Seed Select", bytes: pointer), | |
.init(offset: 0x14, size: 0x01, title: "Device Capacity", bytes: pointer), | |
.init(offset: 0x1D, size: 0x01, title: "NDS Region", bytes: pointer) | |
] | |
ccharAddresses.forEach { ccharAddress in | |
print("\(ccharAddress.title):", ccharAddress.string() ?? "Invalid data") | |
} | |
for (offset, element) in uint8Addresses.enumerated() { | |
uint8Addresses[offset].allocate() | |
print("\(element.title):", uint8Addresses[offset].string() ?? "Invalid data") | |
} | |
} catch { | |
print(error.localizedDescription) | |
} | |
} | |
} | |
/* | |
Usage: ROM(/* url to *.nds */).header() | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment