82 lines
2.6 KiB
Swift
82 lines
2.6 KiB
Swift
/**
|
|
* Header that appears at the front of every data chunk in a resource.
|
|
*/
|
|
/// Header size: `8 Bytes`
|
|
struct ChunkHeader {
|
|
// INTERNAL USAGE. No correspondence in data:
|
|
private let _offset: Index
|
|
internal let _bytes: RawBytes
|
|
|
|
/// Type identifier for this chunk. The meaning of this value depends on the containing chunk.
|
|
let type: ChunkType // UInt16
|
|
/// Size of the chunk header (in bytes).
|
|
/// Adding this value to the address of the chunk allows you to find its associated data (if any).
|
|
let headerSize: UInt16
|
|
/// Total size of this chunk (in bytes).
|
|
/// This is the chunkSize plus the size of any data associated with the chunk.
|
|
/// Adding this value to the chunk allows you to completely skip its contents (including any child chunks).
|
|
/// If this value is the same as chunkSize, there is no data associated with the chunk.
|
|
let chunkSize: UInt32
|
|
|
|
init(_ idx: Index, _ bytes: RawBytes, expect: ChunkType? = nil) throws {
|
|
let rawType = bytes.int16(idx)
|
|
guard let typ = ChunkType(rawValue: rawType) else {
|
|
throw AXMLError("Unexpected chunk type \(rawType)")
|
|
}
|
|
if let expect, expect != typ {
|
|
throw AXMLError("Expected chunk type \(expect) but got \(typ)")
|
|
}
|
|
_offset = idx
|
|
_bytes = bytes
|
|
type = typ
|
|
headerSize = bytes.int16(idx + 2)
|
|
chunkSize = bytes.int32(idx + 4)
|
|
}
|
|
|
|
/// Go over all child elements by iterating over them one-by-one
|
|
func iterChildren(filter: ((ChunkHeader) -> Bool)? = nil, _ block: (_ abort: inout Bool, _ chunk: ChunkHeader) throws -> Void) throws {
|
|
var nextIdx = index(.startOfData)
|
|
let lastIdx = index(.afterChunk)
|
|
while nextIdx < lastIdx {
|
|
let child = try ChunkHeader(nextIdx, _bytes)
|
|
nextIdx = child.index(.afterChunk)
|
|
guard filter?(child) ?? true else {
|
|
continue
|
|
}
|
|
var abort = false
|
|
try block(&abort, child)
|
|
if abort {
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Collect all children with `iterChildren` and return full list
|
|
func children(filter: ((ChunkHeader) -> Bool)? = nil) throws -> [ChunkHeader] {
|
|
var result: [ChunkHeader] = []
|
|
try iterChildren(filter: filter) { result.append($1) }
|
|
return result
|
|
}
|
|
|
|
/// Convert relative offset to absolute offset
|
|
func index(_ relative: RelativeIndex) -> Index {
|
|
switch relative {
|
|
case .startOfChunk: _offset
|
|
case .startOfHeader: _offset + 8 // chunk header length
|
|
case .startOfData: _offset + Index(headerSize)
|
|
case .afterChunk: _offset + Index(chunkSize)
|
|
}
|
|
}
|
|
|
|
enum RelativeIndex {
|
|
case startOfChunk
|
|
case startOfHeader
|
|
case startOfData
|
|
case afterChunk
|
|
}
|
|
|
|
func byteReader(at offset: RelativeIndex, skip: Index = 0) -> RawBytes.Reader {
|
|
.init(data: _bytes, index: index(offset) + skip)
|
|
}
|
|
}
|