private void Init(ReadOnlyMemorySlice <byte> data, StringsFileFormat type) { try { _type = type; var count = BinaryPrimitives.ReadUInt32LittleEndian(data); var dataSize = checked ((int)BinaryPrimitives.ReadUInt32LittleEndian(data.Slice(4))); var indexData = data.Slice(8, checked ((int)(count * 2 * 4))); int loc = 0; for (int i = 0; i < count; i++) { _locations.Add( BinaryPrimitives.ReadUInt32LittleEndian(indexData.Slice(loc)), checked ((int)BinaryPrimitives.ReadUInt32LittleEndian(indexData.Slice(loc + 4)))); loc += 8; } _stringData = data.Slice(8 + indexData.Length, dataSize); } catch (ArgumentException) { throw new ArgumentException("Strings file had duplicate entries."); } catch (OverflowException) { throw new ArgumentException("Strings file was too big for current systems"); } }
public BsaFileNameBlock(BsaReader bsa, long position) { Names = new Lazy <ReadOnlyMemorySlice <byte>[]>( mode: System.Threading.LazyThreadSafetyMode.ExecutionAndPublication, valueFactory: () => { using var stream = bsa.GetStream(); stream.BaseStream.Position = position; ReadOnlyMemorySlice <byte> data = stream.ReadBytes(checked ((int)bsa.TotalFileNameLength)); ReadOnlyMemorySlice <byte>[] names = new ReadOnlyMemorySlice <byte> [bsa.FileCount]; for (int i = 0; i < bsa.FileCount; i++) { var index = data.Span.IndexOf(default(byte)); if (index == -1) { throw new InvalidDataException("Did not end all of its strings in null bytes"); } names[i] = data.Slice(0, index + 1); var str = names[i].ReadStringTerm(bsa.HeaderType); data = data.Slice(index + 1); } // Data doesn't seem to need to be fully consumed. // Official BSAs have overflow of zeros return(names); }); }
public static IReadOnlyList <string> FactoryByCountLength <T>( ReadOnlyMemorySlice <byte> mem, BinaryOverlayFactoryPackage package, byte countLength, BinaryOverlay.SpanFactory <string> getter) { var count = countLength switch { 4 => BinaryPrimitives.ReadUInt32LittleEndian(mem), _ => throw new NotImplementedException(), }; int[] locs = new int[count]; int loc = 0; for (int i = 0; i < count - 1; i++) { locs[i] = loc; var len = BinaryPrimitives.ReadUInt16LittleEndian(mem.Slice(loc)); loc += len + 2; } return(FactoryByArray( mem.Slice(countLength), package, getter, locs)); }
private T ConstructWrapper(int pos) { ReadOnlyMemorySlice <byte> slice = this._data.Slice(pos); var majorMeta = _package.MetaData.Constants.MajorRecord(slice); if (majorMeta.IsCompressed) { uint uncompressedLength = BinaryPrimitives.ReadUInt32LittleEndian(slice.Slice(majorMeta.HeaderLength)); byte[] buf = new byte[majorMeta.HeaderLength + checked ((int)uncompressedLength)]; // Copy major meta bytes over slice.Span.Slice(0, majorMeta.HeaderLength).CopyTo(buf.AsSpan()); // Set length bytes BinaryPrimitives.WriteUInt32LittleEndian(buf.AsSpan().Slice(Constants.HeaderLength), uncompressedLength); // Remove compression flag BinaryPrimitives.WriteInt32LittleEndian(buf.AsSpan().Slice(_package.MetaData.Constants.MajorConstants.FlagLocationOffset), majorMeta.MajorRecordFlags & ~Constants.CompressedFlag); // Copy uncompressed data over using (var stream = new ZlibStream(new ByteMemorySliceStream(slice.Slice(majorMeta.HeaderLength + 4)), CompressionMode.Decompress)) { stream.Read(buf, majorMeta.HeaderLength, checked ((int)uncompressedLength)); } slice = new MemorySlice <byte>(buf); } return(LoquiBinaryOverlayTranslation <T> .Create( stream : new OverlayStream(slice, _package), package : _package, recordTypeConverter : null)); }
private FolderRecord[] LoadFolderRecords() { using var rdr = GetStream(); rdr.BaseStream.Position = _folderRecordOffset; var folderHeaderLength = FolderRecord.HeaderLength(HeaderType); ReadOnlyMemorySlice <byte> folderHeaderData = rdr.ReadBytes(checked ((int)(folderHeaderLength * _folderCount))); var ret = new FolderRecord[_folderCount]; for (var idx = 0; idx < _folderCount; idx += 1) { ret[idx] = new FolderRecord(this, folderHeaderData.Slice(idx * folderHeaderLength, folderHeaderLength), idx); } // Slice off appropriate file header data per folder int fileCountTally = 0; foreach (var folder in ret) { folder.ProcessFileRecordHeadersBlock(rdr, fileCountTally); fileCountTally = checked ((int)(fileCountTally + folder.FileCount)); } if (HasFileNames) { var filenameBlock = new FileNameBlock(this, rdr.BaseStream.Position); foreach (var folder in ret) { folder.FileNameBlock = filenameBlock; } } return(ret); }
public static IReadOnlyList <T> FactoryByCountLength( ReadOnlyMemorySlice <byte> mem, BinaryOverlayFactoryPackage package, int itemLength, byte countLength, BinaryOverlay.SpanFactory <T> getter) { uint count; switch (countLength) { case 4: count = BinaryPrimitives.ReadUInt32LittleEndian(mem); break; default: throw new NotImplementedException(); } if (((mem.Length - countLength) / itemLength) < count) { throw new ArgumentException("Item count and expected size did not match."); } return(new BinaryOverlayListByStartIndex( mem.Slice(countLength, checked ((int)(count * itemLength))), package, getter, itemLength)); }
public static ReadOnlyMemorySlice <byte> ExtractGroupMemory(ReadOnlyMemorySlice <byte> span, GameConstants meta) { var groupMeta = meta.Group(span); var len = groupMeta.ContentLength; len += (byte)meta.GroupConstants.LengthAfterLength; return(span.Slice(meta.GroupConstants.TypeAndLengthLength, checked ((int)len))); }
public static ReadOnlyMemorySlice <byte> ExtractRecordMemory(ReadOnlyMemorySlice <byte> span, GameConstants meta) { var majorMeta = meta.MajorRecord(span); var len = majorMeta.ContentLength; len += (byte)meta.MajorConstants.LengthAfterLength; return(span.Slice(meta.MajorConstants.TypeAndLengthLength, checked ((int)len))); }
public static IReadOnlyList <T> FactoryByLazyParse <T>( ReadOnlyMemorySlice <byte> mem, BinaryOverlayFactoryPackage package, byte countLength, BinaryOverlay.Factory <T> getter) { return(FactoryByLazyParse(mem.Slice(countLength), package, getter)); }
public static string ReadStringLenTerm(this ReadOnlyMemorySlice <byte> bytes, VersionType version) { if (bytes.Length <= 1) { return(string.Empty); } return(GetEncoding(version).GetString(bytes.Slice(1, bytes[0]))); }
public static PathGridPointBinaryOverlay Factory( ReadOnlyMemorySlice <byte> stream, BinaryOverlayFactoryPackage package) { return(new PathGridPointBinaryOverlay( bytes: stream.Slice(0, 16), package: package)); }
public T this[int index] { get { var startIndex = index * _itemLength; return(_getter(_mem.Slice(startIndex, _itemLength), _package)); } }
public T this[int index] { get { var startIndex = index * this._totalItemLength; var subMeta = _package.MetaData.Constants.Subrecord(_mem.Slice(startIndex)); if (!this._recordTypes.Contains(subMeta.RecordType)) { throw new ArgumentException($"Unexpected record type: {subMeta.RecordType}"); } if (subMeta.ContentLength != this._itemLength) { throw new ArgumentException($"Unexpected record length: {subMeta.ContentLength} != {this._itemLength}"); } return(_getter(_mem.Slice(startIndex + _package.MetaData.Constants.SubConstants.HeaderLength, _itemLength), _package)); } }
internal static IEnumerable <SubrecordPinFrame> EnumerateSubrecords(ReadOnlyMemorySlice <byte> span, GameConstants meta, int loc) { while (loc < span.Length) { var subHeader = new SubrecordPinFrame(meta, span.Slice(loc), loc); yield return(subHeader); loc += subHeader.TotalLength; } }
internal static IEnumerable <SubrecordPinFrame> EnumerateSubrecords(ReadOnlyMemorySlice <byte> span, GameConstants meta, int loc, ICollection <RecordType> lengthOverflowTypes) { while (loc < span.Length) { var subFrame = new SubrecordPinFrame(meta, span.Slice(loc), loc); if (lengthOverflowTypes.Contains(subFrame.RecordType)) { // Length overflow record var nextLen = subFrame.AsUInt32(); loc += subFrame.TotalLength; var nextSpan = span.Slice(loc, checked ((int)(nextLen + meta.SubConstants.HeaderLength))); var subHeader = new SubrecordHeader(meta, nextSpan); yield return(SubrecordPinFrame.FactoryNoTrim(subHeader, nextSpan, loc)); loc += checked ((int)(subHeader.HeaderLength + nextLen)); continue; } yield return(subFrame); loc += subFrame.TotalLength; } }
public INavmeshTriangleGetter this[int index] { get { var triangleLoc = _trianglesStartPos + (index * 16); var triangle = NavmeshTriangleBinaryOverlay.NavmeshTriangleFactory( _data.Slice(triangleLoc), _package); triangle.IsCover = _isCover[index]; return(triangle); } }
public static ReadOnlyMemorySlice <T> EnumSliceFromFixedSize <T>( ReadOnlyMemorySlice <byte> mem, int amount, byte enumLength) where T : struct, Enum { T[] ret = new T[amount]; for (int i = 0; i < amount; i++) { ret[i] = EnumExt.Parse <T>(BinaryPrimitives.ReadInt32LittleEndian(mem.Slice(i * enumLength)), default(T)); } return(ret); }
/// <summary> /// Parses span data and enumerates pairs of record type -> locations /// /// It is assumed the span contains only subrecords /// </summary> /// <param name="span">Bytes containing subrecords</param> /// <param name="meta">Metadata to use in subrecord parsing</param> /// <returns>Enumerable of KeyValue pairs of encountered RecordTypes and their locations relative to the input span</returns> public static IEnumerable <KeyValuePair <RecordType, int> > EnumerateSubrecords(ReadOnlyMemorySlice <byte> span, GameConstants meta) { int loc = 0; while (span.Length > loc) { var subMeta = meta.Subrecord(span.Slice(loc)); var len = subMeta.TotalLength; yield return(new KeyValuePair <RecordType, int>(subMeta.RecordType, loc)); loc += len; } }
public static ReadOnlyMemorySlice <byte> DecompressSpan(ReadOnlyMemorySlice <byte> slice, GameConstants meta) { var majorMeta = meta.MajorRecord(slice); if (majorMeta.IsCompressed) { uint uncompressedLength = BinaryPrimitives.ReadUInt32LittleEndian(slice.Slice(majorMeta.HeaderLength)); byte[] buf = new byte[majorMeta.HeaderLength + checked ((int)uncompressedLength)]; // Copy major meta bytes over slice.Span.Slice(0, majorMeta.HeaderLength).CopyTo(buf.AsSpan()); // Set length bytes BinaryPrimitives.WriteUInt32LittleEndian(buf.AsSpan().Slice(Constants.HeaderLength), uncompressedLength); // Remove compression flag BinaryPrimitives.WriteInt32LittleEndian(buf.AsSpan().Slice(meta.MajorConstants.FlagLocationOffset), majorMeta.MajorRecordFlags & ~Constants.CompressedFlag); // Copy uncompressed data over using (var stream = new ZlibStream(new ByteMemorySliceStream(slice.Slice(majorMeta.HeaderLength + 4)), CompressionMode.Decompress)) { stream.Read(buf, majorMeta.HeaderLength, checked ((int)uncompressedLength)); } slice = new MemorySlice <byte>(buf); } return(slice); }
public static ReadOnlyMemorySlice <T> LoquiSliceFromFixedSize <T>( ReadOnlyMemorySlice <byte> mem, int amount, int length, BinaryOverlayFactoryPackage package, RecordTypeConverter?recordTypeConverter, BinaryOverlay.ConverterFactory <T> getter) { T[] ret = new T[amount]; for (int i = 0; i < amount; i++) { ret[i] = getter(new OverlayStream(mem.Slice(i * length), package), package, recordTypeConverter); } return(ret); }
public static IReadOnlyList <T> FactoryByCountLength <T>( ReadOnlyMemorySlice <byte> mem, BinaryOverlayFactoryPackage package, int itemLength, byte countLength, PluginBinaryOverlay.SpanFactory <T> getter) { var count = countLength switch { 4 => BinaryPrimitives.ReadUInt32LittleEndian(mem), _ => throw new NotImplementedException(), }; if (((mem.Length - countLength) / itemLength) < count) { throw new ArgumentException("Item count and expected size did not match."); } return(new BinaryOverlayListByStartIndex <T>( mem.Slice(countLength, checked ((int)(count * itemLength))), package, getter, itemLength)); }
/// <summary> /// Constructor /// </summary> /// <param name="meta">Game metadata to use as reference for alignment</param> /// <param name="span">Span to overlay on, aligned to the start of the Sub Record's header</param> public SubrecordHeader(GameConstants meta, ReadOnlyMemorySlice <byte> span) { this.Meta = meta; this.HeaderData = span.Slice(0, meta.SubConstants.HeaderLength); }
/// <summary> /// Factory /// </summary> /// <param name="header">Existing SubrecordHeader struct</param> /// <param name="span">Span to overlay on, aligned to the start of the header</param> public static SubrecordFrame Factory(SubrecordHeader header, ReadOnlyMemorySlice <byte> span) { return(new SubrecordFrame(header, span.Slice(0, header.TotalLength))); }
/// <summary> /// Constructor /// </summary> /// <param name="meta">Game metadata to use as reference for alignment</param> /// <param name="span">Span to overlay on, aligned to the start of the header</param> public SubrecordFrame(GameConstants meta, ReadOnlyMemorySlice <byte> span) { Header = meta.Subrecord(span); HeaderAndContentData = span.Slice(0, Header.TotalLength); }
/// <summary> /// Constructor /// </summary> /// <param name="meta">Game metadata to use as reference for alignment</param> /// <param name="span">Span to overlay on, aligned to the start of the Group's header</param> public GroupHeader(GameConstants meta, ReadOnlyMemorySlice <byte> span) { this.Meta = meta; this.HeaderData = span.Slice(0, meta.GroupConstants.HeaderLength); }
/// <summary> /// Constructor /// </summary> /// <param name="header">Existing GroupHeader struct</param> /// <param name="span">Span to overlay on, aligned to the start of the Group's header</param> public GroupFrame(GroupHeader header, ReadOnlyMemorySlice <byte> span) { this._header = header; this.HeaderAndContentData = span.Slice(0, checked ((int)this._header.TotalLength)); }
public T this[int index] => _getter(_mem.Slice(_locations[index]), _package, _recordTypeConverter);
public T this[int index] => _getter(_mem.Slice(_locations[index]), _package);
/// <summary> /// Constructor /// </summary> /// <param name="constants">Record constants to use as reference for alignment</param> /// <param name="span">Span to overlay on, aligned to the start of the header</param> public VariableHeader(RecordHeaderConstants constants, ReadOnlyMemorySlice <byte> span) { Constants = constants; Span = span.Slice(0, constants.HeaderLength); }
/// <summary> /// Constructor /// </summary> /// <param name="meta">Game metadata to use as reference for alignment</param> /// <param name="span">Span to overlay on, aligned to the start of the header</param> public GroupFrame(GameConstants meta, ReadOnlyMemorySlice <byte> span) { this._header = meta.Group(span); this.HeaderAndContentData = span.Slice(0, checked ((int)this._header.TotalLength)); }