Created
August 11, 2025 04:13
-
-
Save jarrodnorwell/169322127b3769f7ff985c6a98fe1b81 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
| import UIKit | |
| struct ISOLoader { | |
| struct PrimaryVolumeDescriptor { | |
| struct DirectoryRecord { | |
| var size: UInt8 | |
| var sectorsExtendedRecord: UInt8 | |
| var lbaLE: UInt32 | |
| var lbaBE: UInt32 | |
| var dataLengthLE: UInt32 | |
| var dataLengthBE: UInt32 | |
| var dateTimeEtc: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var gmtOffset: UInt8 | |
| var fileFlags: UInt8 | |
| var fileUnitSize: UInt8 | |
| var interleaveGapSize: UInt8 | |
| var volumeSequenceNumberLE: UInt16 | |
| var volumeSequenceNumberBE: UInt16 | |
| var identifierLength: UInt8 | |
| var data: UInt8 // this is just the start of a variable-length field | |
| } | |
| var volumeDescriptorType: UInt8 | |
| var identifier: (UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var version: UInt8 | |
| var unused0: UInt8 | |
| var systemIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var unused1: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeSpaceSizeLE: UInt32 | |
| var volumeSpaceSizeBE: (UInt8, UInt8, UInt8, UInt8) | |
| var unused2: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeSetSizeLE: UInt16 | |
| var volumeSetSizeBE: (UInt8, UInt8) | |
| var volumeSequenceNumberLE: UInt16 | |
| var volumeSequenceNumberBE: (UInt8, UInt8) | |
| var logicalBlockSizeLE: UInt16 | |
| var logicalBlockSizeBE: (UInt8, UInt8) | |
| var pathTableSizeLE: UInt32 | |
| var pathTableSizeBE: (UInt8, UInt8, UInt8, UInt8) | |
| var pathTableTypeLLocation: UInt32 | |
| var pathTableTypeLLocationOptional: UInt32 | |
| var pathTableTypeMLocation: UInt32 | |
| var pathTableTypeMLocationOptional: UInt32 | |
| var directoryRecord: DirectoryRecord | |
| var volumeSetIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var publisherIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var dataPreparerIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var applicationIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var copyrightFileIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var abstractFileIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var bibliographicFileIdentifier: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeCreationDateTime: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeModificationDateTime: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeExpirationDateTime: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var volumeEffectiveDataTime: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var fileStructureVersion: UInt8 | |
| var unused3: UInt8 | |
| var applicationData: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| var unused4: (UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, | |
| UInt8, UInt8, UInt8, UInt8, UInt8, UInt8, UInt8) | |
| } | |
| enum `Type` : String { | |
| case iso = "ISO", | |
| unknown = "Unknown" | |
| var string: String { rawValue } | |
| } | |
| var size: Int? = nil | |
| var type: `Type` = .unknown | |
| var data: Data? = nil | |
| init(from url: URL) { | |
| do { | |
| let handle: FileHandle = try .init(forReadingFrom: url) | |
| data = try handle.readToEnd() | |
| size = try url.resourceValues(forKeys: [.fileSizeKey]).fileSize | |
| guard let data else { | |
| return | |
| } | |
| let string: Data = .init([0x01, 0x43, 0x44, 0x30, 0x30, 0x31, 0x01, 0x00]) | |
| let result: Int32 = data.advanced(by: 0x8000)[0..<8].withUnsafeBytes { data in | |
| string.withUnsafeBytes { string in | |
| guard let dataBaseAddress: UnsafeRawPointer = data.baseAddress, | |
| let stringBaseAddress: UnsafeRawPointer = string.baseAddress else { | |
| return 1 | |
| } | |
| return memcmp(dataBaseAddress, stringBaseAddress, 8) | |
| } | |
| } | |
| print(result == 0 ? "ISO file found" : "ISO file not found") | |
| } catch { | |
| print(error, error.localizedDescription) | |
| } | |
| } | |
| func parse(with pvd: PrimaryVolumeDescriptor) throws { | |
| /* | |
| guard let data else { | |
| return | |
| } | |
| var offset: UInt32 = pvd.directoryRecord.lbaLE * 0x800 | |
| while true { | |
| let oldOffset: UInt32 = offset | |
| let arr: [UInt8] = data.advanced(by: .init(offset))[0..<256].withUnsafeBytes { data in | |
| data.load(as: [UInt8].self) | |
| } | |
| offset += .init(arr[0]) | |
| } | |
| */ | |
| } | |
| func pvd(from url: URL) throws -> PrimaryVolumeDescriptor? { | |
| guard let data else { | |
| return nil | |
| } | |
| let result: PrimaryVolumeDescriptor = data.advanced(by: 0x8000)[0..<2048].withUnsafeBytes { data in | |
| data.load(as: PrimaryVolumeDescriptor.self) | |
| } | |
| return result | |
| } | |
| } | |
| class ViewController : UIViewController { | |
| override func viewDidLoad() { | |
| super.viewDidLoad() | |
| view.backgroundColor = .systemBackground | |
| guard let url: URL = Bundle.main.url(forResource: "Ratchet & Clank - Size Matters", withExtension: "iso") else { | |
| return | |
| } | |
| let loader: ISOLoader = .init(from: url) | |
| do { | |
| guard let size = loader.size else { | |
| return | |
| } | |
| print(loader.type.string, ByteCountFormatter().string(fromByteCount: .init(size))) | |
| guard let pvd: ISOLoader.PrimaryVolumeDescriptor = try loader.pvd(from: url) else { | |
| return | |
| } | |
| dump(pvd) | |
| try loader.parse(with: pvd) | |
| } catch { | |
| print(error, error.localizedDescription) | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment