diff options
| author | Dustin Mierau <dustin@mierau.me> | 2024-01-12 09:50:18 -0800 |
|---|---|---|
| committer | Dustin Mierau <dustin@mierau.me> | 2024-01-12 09:50:18 -0800 |
| commit | 2f210991684c87073dd7534d2d0416d91cf50275 (patch) | |
| tree | 03dc2edb225ab583fbc286de37f927194377a085 /Hotline/Utility | |
| parent | 768bc9136c91b5dc31eef96088e7d1796243d81b (diff) | |
Experimenting with a simpler stream/delegate bsaed socket library for Tracker listing for now. Added consume methods to Data and [UInt8] arrays to make reading bytes off the wire easier to read..
Diffstat (limited to 'Hotline/Utility')
| -rw-r--r-- | Hotline/Utility/FoundationExtensions.swift | 261 |
1 files changed, 261 insertions, 0 deletions
diff --git a/Hotline/Utility/FoundationExtensions.swift b/Hotline/Utility/FoundationExtensions.swift index 3183b39..edb8bee 100644 --- a/Hotline/Utility/FoundationExtensions.swift +++ b/Hotline/Utility/FoundationExtensions.swift @@ -5,6 +5,267 @@ enum Endianness { case little } +extension Array where Element == UInt8 { + init(_ val: UInt8) { + self.init() + self.appendUInt8(val) + } + + init(_ val: UInt16) { + self.init() + self.appendUInt16(val) + } + + init(_ val: UInt32) { + self.init() + self.appendUInt32(val) + } + + init(_ val: UInt64) { + self.init() + self.appendUInt64(val) + } + + mutating func consumeUInt8() -> UInt8? { + guard let val = self.readUInt8(at: 0) else { + return nil + } + + self.removeFirst(1) + return val + } + + mutating func consumeUInt16() -> UInt16? { + guard let val = self.readUInt16(at: 0) else { + return nil + } + + self.removeFirst(2) + return val + } + + mutating func consumeUInt32() -> UInt32? { + guard let val = self.readUInt32(at: 0) else { + return nil + } + + self.removeFirst(4) + return val + } + + mutating func consumeUInt64() -> UInt64? { + guard let val = self.readUInt64(at: 0) else { + return nil + } + + self.removeFirst(8) + return val + } + + mutating func consumeBytes(_ length: Int) -> Data? { + guard let val = self.readData(at: 0, length: length) else { + return nil + } + + self.removeFirst(length) + return val + } + + mutating func consumeDate() -> Date? { + guard let date = self.readDate(at: 0) else { + return nil + } + + self.removeFirst(2 + 2 + 4) + return date + } + + mutating func consumePString() -> String? { + let (str, len) = self.readPString(at: 0) + guard let str = str else { + return nil + } + if len == 0 { + return "" + } + + self.removeFirst(len) + return str + } + + mutating func consumeString(_ length: Int) -> String? { + guard let val = self.readString(at: 0, length: length) else { + return nil + } + + self.removeFirst(length) + return val + } + + func readUInt8(at offset: Int) -> UInt8? { + guard offset >= 0, offset + 1 <= self.count else { + return nil + } + return self[offset] + } + + func readUInt16(at offset: Int) -> UInt16? { + guard offset >= 0, offset + 2 <= self.count else { + return nil + } + + return (UInt16(self[offset]) << 8) + UInt16(self[offset + 1]) + } + + func readUInt32(at offset: Int) -> UInt32? { + guard offset >= 0, offset + 4 <= self.count else { + return nil + } + + return (UInt32(self[offset]) << 24) + (UInt32(self[offset + 1]) << 16) + (UInt32(self[offset + 2]) << 8) + UInt32(self[offset + 3]) + } + + func readUInt64(at offset: Int) -> UInt64? { + guard offset >= 0, offset + 8 <= self.count else { + return nil + } + + let leftSide: UInt64 = (UInt64(self[offset]) << 56) + + (UInt64(self[offset + 1]) << 48) + + (UInt64(self[offset + 2]) << 40) + + (UInt64(self[offset + 3]) << 32) + + let rightSide: UInt64 = (UInt64(self[offset + 4]) << 24) + + (UInt64(self[offset + 5]) << 16) + + (UInt64(self[offset + 6]) << 8) + + UInt64(self[offset + 7]) + + return leftSide + rightSide + } + + func readDate(at offset: Int) -> Date? { + guard offset >= 0, offset + 2 + 2 + 4 <= self.count else { + return nil + } + + if + let year = self.readUInt16(at: offset), + let ms = self.readUInt16(at: offset + 2), + let secs = self.readUInt32(at: offset + 2 + 2) { + return convertHotlineDate(year: year, seconds: secs, milliseconds: ms) + } + + return nil + } + + func readData(at offset: Int, length: Int) -> Data? { + guard offset >= 0, offset + length <= self.count else { + return nil + } + return Data(self[offset..<(offset + length)]) + } + + func readString(at offset: Int, length: Int) -> String? { + guard let subdata = self.readData(at: offset, length: length) else { + return nil + } + + if subdata.count == 0 { + return "" + } + + let allowedEncodings = [ + NSUTF8StringEncoding, + NSShiftJISStringEncoding, + NSUnicodeStringEncoding, + NSWindowsCP1251StringEncoding + ] + + var decodedNSString: NSString? + let rawValue = NSString.stringEncoding(for: subdata, encodingOptions: [.allowLossyKey: false], convertedString: &decodedNSString, usedLossyConversion: nil) + + if allowedEncodings.contains(rawValue) { + return decodedNSString as? String + } + + else if rawValue > 1 { + print("ENCODING FOUND \(rawValue)") + } + + var macStr = String(data: subdata, encoding: .macOSRoman) + if macStr == nil { + macStr = String(data: subdata, encoding: .nonLossyASCII) + } + + return macStr + } + + func readPString(at offset: Int) -> (String?, Int) { + guard offset >= 0, offset + 1 <= self.count else { + return (nil, 0) + } + let len = Int(self.readUInt8(at: offset)!) + guard offset + 1 + len <= self.count else { + return (nil, 0) + } + return (self.readString(at: offset+1, length: len), 1 + len) + } + + func readLongPString(at offset: Int) -> (String?, Int) { + guard offset >= 0, offset + 2 <= self.count else { + return (nil, 0) + } + let len = Int(self.readUInt16(at: offset)!) + guard len > 0 else { + return ("", 0) + } + guard offset + 2 + len <= self.count else { + return (nil, 0) + } + return (self.readString(at: offset+2, length: len), len) + } + + mutating func appendUInt8(_ value: UInt8, endianness: Endianness = .big) { + let val = endianness == .big ? value.bigEndian : value.littleEndian + self.append(val) + } + + mutating func appendUInt16(_ value: UInt16, endianness: Endianness = .big) { + let val = endianness == .big ? value.bigEndian : value.littleEndian + let bytes: [UInt8] = [ + UInt8((val >> 8) & 0xFF), + UInt8(val & 0xFF) + ] + self.append(contentsOf: bytes) + } + + mutating func appendUInt32(_ value: UInt32, endianness: Endianness = .big) { + let val = endianness == .big ? value.bigEndian : value.littleEndian + let bytes: [UInt8] = [ + UInt8((val >> 24) & 0xFF), + UInt8((val >> 16) & 0xFF), + UInt8((val >> 8) & 0xFF), + UInt8(val & 0xFF) + ] + self.append(contentsOf: bytes) + } + + mutating func appendUInt64(_ value: UInt64, endianness: Endianness = .big) { + let val: UInt64 = endianness == .big ? value.bigEndian : value.littleEndian + let bytes: [UInt8] = [ + UInt8((val >> 56) & 0xFF), + UInt8((val >> 48) & 0xFF), + UInt8((val >> 40) & 0xFF), + UInt8((val >> 32) & 0xFF), + UInt8((val >> 24) & 0xFF), + UInt8((val >> 16) & 0xFF), + UInt8((val >> 8) & 0xFF), + UInt8(val & 0xFF) + ] + self.append(contentsOf: bytes) + } +} + extension Data { init(_ val: UInt8) { self.init() |