|
5 | 5 | * http://stregasgate.com |
6 | 6 | */ |
7 | 7 |
|
8 | | -public struct RawGeometryEncoder { |
9 | | - public func encode(_ value: RawGeometry) throws(EncodingError) -> Data { |
10 | | - return try RawGeometryEncodableRepresenation_v1.encode(value) |
11 | | - } |
12 | | - |
13 | | - public enum EncodingError: Error { |
14 | | - /// The encoding failed due to an unknown reason |
15 | | - case encodingFailed |
16 | | - } |
17 | | - |
18 | | - public init() { |
19 | | - |
20 | | - } |
21 | | -} |
| 8 | +fileprivate let magic: UInt32 = 0x47_45_4F_4D // "GEOM" |
22 | 9 |
|
23 | | -public struct RawGeometryDecoder { |
24 | | - public func decode(_ data: Data) throws(DecodingError) -> RawGeometry { |
25 | | - guard data.isEmpty == false else { throw .decodingFailed } |
26 | | - let header = data.withUnsafeBytes { data in |
27 | | - data.load(as: RawGeometryCodableHeader.self) |
28 | | - } |
29 | | - guard header.isValid else { throw .invalidFormat } |
30 | | - guard let version = header.version else { throw .unsupportedVersion } |
31 | | - guard header.documentLength == data.count else { throw .decodingFailed } |
32 | | - switch version { |
33 | | - case .v1: |
34 | | - return try RawGeometryEncodableRepresenation_v1.decode(data) |
35 | | - } |
36 | | - } |
37 | | - |
38 | | - public enum DecodingError: Error { |
39 | | - /// The data is empty or corrupted. |
40 | | - case decodingFailed |
41 | | - /// The data does not appear to be intended for this decoder. |
42 | | - case invalidFormat |
43 | | - /// This version of GateEngine doesn't support loading this file. |
44 | | - /// - note: It's best to encode and decode with the same GateEngine version. |
45 | | - case unsupportedVersion |
46 | | - } |
47 | | - |
48 | | - public init() { |
| 10 | +public struct RawGeometryEncoder { |
| 11 | + public func encode(_ rawGeometry: RawGeometry) throws(GateEngineError) -> Data { |
| 12 | + var header = BinaryCodableHeader(magic: magic) |
49 | 13 |
|
50 | | - } |
51 | | -} |
52 | | - |
53 | | -fileprivate struct RawGeometryCodableHeader { |
54 | | - static let magic1: UInt32 = 0x45544147 // "GATE" |
55 | | - static let magic2: UInt32 = 0x47574152 // "RAWG" |
56 | | - let magic1: UInt32 |
57 | | - let magic2: UInt32 |
58 | | - let _version: UInt8 |
59 | | - private let _reserved1: UInt8 = 0 |
60 | | - private let _reserved2: UInt8 = 0 |
61 | | - private let _reserved3: UInt8 = 0 |
62 | | - let documentLength: UInt32 = 0 |
63 | | - |
64 | | - enum Version: UInt8 { |
65 | | - case v1 = 1 |
66 | | - } |
67 | | - |
68 | | - var version: Version? { |
69 | | - return Version(rawValue: _version) |
70 | | - } |
71 | | - |
72 | | - init(version: Version) { |
73 | | - self.magic1 = Self.magic1 |
74 | | - self.magic2 = Self.magic2 |
75 | | - self._version = version.rawValue |
76 | | - } |
77 | | - |
78 | | - var isValid: Bool { |
79 | | - return self.magic1 == Self.magic1 && self.magic2 == Self.magic2 |
80 | | - } |
81 | | -} |
82 | | - |
83 | | -fileprivate struct RawGeometryEncodableRepresenation_v1 { |
84 | | - struct Header { |
85 | | - var version: RawGeometryCodableHeader |
86 | | - var positionValueCount: UInt32 |
87 | | - var uvSetCount: UInt8 |
88 | | - var normalValuesCount: UInt32 |
89 | | - var tangentValuesCount: UInt32 |
90 | | - var colorValuesCount: UInt32 |
91 | | - var indicesCount: UInt32 |
| 14 | + guard let version = header.version else { throw .failedToEncode("Malformed header.") } |
92 | 15 |
|
93 | | - init(rawGeometry: RawGeometry) { |
94 | | - self.version = RawGeometryCodableHeader(version: .v1) |
95 | | - self.positionValueCount = UInt32(rawGeometry.positions.count) |
96 | | - self.uvSetCount = UInt8(rawGeometry.uvSets.count) |
97 | | - self.normalValuesCount = UInt32(rawGeometry.normals.count) |
98 | | - self.tangentValuesCount = UInt32(rawGeometry.tangents.count) |
99 | | - self.colorValuesCount = UInt32(rawGeometry.colors.count) |
100 | | - self.indicesCount = UInt32(rawGeometry.indices.count) |
101 | | - } |
102 | | - } |
103 | | - |
104 | | - static func encode(_ rawGeometry: RawGeometry) throws(RawGeometryEncoder.EncodingError) -> Data { |
105 | | - var data = Data() |
106 | | - withUnsafeBytes(of: Header(rawGeometry: rawGeometry)) { |
| 16 | + var data: ContiguousArray<UInt8> = [] |
| 17 | + withUnsafeBytes(of: header) { |
107 | 18 | data.append(contentsOf: $0) |
108 | 19 | } |
109 | | - rawGeometry.positions.withUnsafeBytes { positions in |
110 | | - data.append(contentsOf: positions) |
111 | | - } |
112 | | - for uvSetIndex in rawGeometry.uvSets.indices { |
113 | | - let uvsCount = UInt32(rawGeometry.uvSets[uvSetIndex].count) |
114 | | - withUnsafeBytes(of: uvsCount) { count in |
115 | | - data.append(contentsOf: count) |
116 | | - } |
117 | | - rawGeometry.uvSets[uvSetIndex].withUnsafeBytes { uvs in |
118 | | - data.append(contentsOf: uvs) |
119 | | - } |
120 | | - } |
121 | | - rawGeometry.normals.withUnsafeBytes { normals in |
122 | | - data.append(contentsOf: normals) |
123 | | - } |
124 | | - rawGeometry.tangents.withUnsafeBytes { tangents in |
125 | | - data.append(contentsOf: tangents) |
126 | | - } |
127 | | - rawGeometry.colors.withUnsafeBytes { colors in |
128 | | - data.append(contentsOf: colors) |
129 | | - } |
130 | | - rawGeometry.indices.withUnsafeBytes { indices in |
131 | | - data.append(contentsOf: indices) |
| 20 | + |
| 21 | + do { |
| 22 | + try rawGeometry.encode(into: &data, version: version) |
| 23 | + }catch let error as GateEngineError { |
| 24 | + // rethrow any GateEngineError |
| 25 | + throw error |
| 26 | + }catch{ |
| 27 | + // Throw generic failure for other errors |
| 28 | + throw GateEngineError.failedToEncode("Unknown error: \(error.localizedDescription)") |
132 | 29 | } |
133 | 30 |
|
134 | 31 | // Write documentLength |
| 32 | + header.documentLength = UInt32(data.count) |
135 | 33 | data.withUnsafeMutableBytes { data in |
136 | | - withUnsafePointer(to: UInt32(data.count), { count in |
137 | | - data.baseAddress!.advanced(by: 12).copyMemory(from: count, byteCount: MemoryLayout<UInt32>.size) |
| 34 | + withUnsafePointer(to: header, { header in |
| 35 | + data.baseAddress!.copyMemory(from: header, byteCount: MemoryLayout<BinaryCodableHeader>.size) |
138 | 36 | }) |
139 | 37 | } |
140 | 38 |
|
141 | | - return data |
| 39 | + return Data(data) |
142 | 40 | } |
143 | 41 |
|
144 | | - static func decode(_ data: Data) throws(RawGeometryDecoder.DecodingError) -> RawGeometry { |
145 | | - return data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) -> RawGeometry in |
146 | | - var offset: Int = 0 |
147 | | - let header = data.load(fromByteOffset: offset, as: Header.self) |
148 | | - offset += MemoryLayout<Header>.size |
149 | | - |
150 | | - let positionsCount = Int(header.positionValueCount) |
151 | | - let positions: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: positionsCount)) |
152 | | - offset += MemoryLayout<Float>.size * positionsCount |
153 | | - |
154 | | - var uvSets: [[Float]] = [] |
155 | | - for _ in 0 ..< header.uvSetCount { |
156 | | - let uvCount: Int = Int(data.load(fromByteOffset: offset, as: UInt32.self)) |
157 | | - offset += MemoryLayout<UInt32>.size |
158 | | - let uvs: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: uvCount)) |
159 | | - uvSets.append(uvs) |
160 | | - offset += MemoryLayout<Float>.size * uvCount |
161 | | - } |
162 | | - |
163 | | - let normalsCount = Int(header.normalValuesCount) |
164 | | - let normals: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: normalsCount)) |
165 | | - offset += MemoryLayout<Float>.size * normalsCount |
166 | | - |
167 | | - let tangentsCount: Int = Int(header.tangentValuesCount) |
168 | | - let tangents: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: tangentsCount)) |
169 | | - offset += MemoryLayout<Float>.size * tangentsCount |
170 | | - |
171 | | - let colorsCount: Int = Int(header.colorValuesCount) |
172 | | - let colors: [Float] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: Float.self), count: colorsCount)) |
173 | | - offset += MemoryLayout<Float>.size * colorsCount |
174 | | - |
175 | | - let indicesCount: Int = Int(header.indicesCount) |
176 | | - let indices: [UInt16] = Array(UnsafeBufferPointer(start: data.baseAddress!.advanced(by: offset).assumingMemoryBound(to: UInt16.self), count: indicesCount)) |
177 | | - offset += MemoryLayout<UInt16>.size * indicesCount |
178 | | - |
179 | | - return RawGeometry( |
180 | | - positions: positions, |
181 | | - uvSets: uvSets, |
182 | | - normals: normals, |
183 | | - tangents: tangents, |
184 | | - colors: colors, |
185 | | - indices: indices |
186 | | - ) |
187 | | - }) |
| 42 | + public init() { |
| 43 | + |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +public struct RawGeometryDecoder { |
| 48 | + public func decode(_ data: Data) throws(GateEngineError) -> RawGeometry { |
| 49 | + do { |
| 50 | + return try data.withUnsafeBytes({ (data: UnsafeRawBufferPointer) throws -> RawGeometry in |
| 51 | + var offset: Int = 0 |
| 52 | + let header = data.load(fromByteOffset: offset, as: BinaryCodableHeader.self) |
| 53 | + offset += MemoryLayout<BinaryCodableHeader>.size |
| 54 | + |
| 55 | + guard let version = header.validatedVersionMatching(magic: magic, documentLength: UInt32(data.count)) else { |
| 56 | + throw GateEngineError.failedToDecode("Malformed header.") |
| 57 | + } |
| 58 | + |
| 59 | + return try RawGeometry(decoding: data, at: &offset, version: version) |
| 60 | + }) |
| 61 | + }catch let error as GateEngineError { |
| 62 | + // rethrow any GateEngineError |
| 63 | + throw error |
| 64 | + }catch{ |
| 65 | + // Throw generic failure for other errors |
| 66 | + throw GateEngineError.failedToDecode("Unknown error: \(error.localizedDescription)") |
| 67 | + } |
| 68 | + } |
| 69 | + |
| 70 | + public init() { |
| 71 | + |
188 | 72 | } |
189 | 73 | } |
0 commit comments