aboutsummaryrefslogtreecommitdiff
path: root/Hotline/Utility
diff options
context:
space:
mode:
authorDustin Mierau <dustin@mierau.me>2024-01-12 09:50:18 -0800
committerDustin Mierau <dustin@mierau.me>2024-01-12 09:50:18 -0800
commit2f210991684c87073dd7534d2d0416d91cf50275 (patch)
tree03dc2edb225ab583fbc286de37f927194377a085 /Hotline/Utility
parent768bc9136c91b5dc31eef96088e7d1796243d81b (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.swift261
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()