Skip to content

Instantly share code, notes, and snippets.

@jarrodnorwell
Last active November 23, 2023 12:58
Show Gist options
  • Save jarrodnorwell/0f0e3caf37361128b35fab09688158e5 to your computer and use it in GitHub Desktop.
Save jarrodnorwell/0f0e3caf37361128b35fab09688158e5 to your computer and use it in GitHub Desktop.
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