public IReadOnlyCollection <String> ReadLines() { unsafe { Int32 headerOffset = Positive(_br.ReadInt32()); Int32 headerCount = Positive(_br.ReadInt32()); Int32 headerSize = checked (headerCount * sizeof(GVHeaderEntry)); Int32 contentOffset = Positive(_br.ReadInt32()); Int32 contentSize = Positive(_br.ReadInt32()); _stream.SetPosition(headerOffset); Byte[] header = _stream.ReadBytes(headerSize); _stream.SetPosition(contentOffset); Byte[] content = _stream.ReadBytes(contentSize); String[] result = new String[headerCount]; fixed(Byte *headerBytes = header) fixed(Byte * contentBytes = content) { GVHeaderEntry *entriesPtr = (GVHeaderEntry *)headerBytes; SByte * contentPtr = (SByte *)contentBytes; for (Int32 i = 0; i < headerCount; i++) { if (IsEmpty(entriesPtr, i)) { result[i] = $"{i:D4}|"; continue; } GVHeaderEntry entry = entriesPtr[i]; CheckEntry(i, ref entry, contentPtr); Int32 offset = InRange(entry.Offset, minValue: 0, maxValue: contentSize - 1); Int32 size = InRange(entry.Size, minValue: 2, maxValue: contentSize - offset); // \0 terminated strings UInt32 value = entry.Value; UInt32 mask = entry.Mask; String name = new String(contentPtr, offset, size - 1, Encoding.ASCII); if ((value & mask) != value) { throw new InvalidDataException($"Value 0x[{value:X8}] of the variable [{i:D4}:{name}] is out of mask [{mask:X8}]."); } FormatTypeAndValue(mask, value, out var formattedType, out var formattedValue); result[i] = $"{i:D4}| {formattedType} {name} = {formattedValue}"; } } return(result); } }
private static unsafe Boolean IsEmpty(GVHeaderEntry *entriesPtr, Int32 i) { Int64 *raw = (Int64 *)(entriesPtr + i); return(raw[0] == 0 && raw[1] == 0); }