public static void WriteBlocksParallel( ICellBlockGetter block, MasterReferenceReader masters, int targetIndex, Stream[] streamDepositArray) { var subBlocks = block.SubBlocks; Stream[] streams = new Stream[(subBlocks?.Count ?? 0) + 1]; byte[] groupBytes = new byte[GameConstants.Oblivion.GroupConstants.HeaderLength]; BinaryPrimitives.WriteInt32LittleEndian(groupBytes.AsSpan(), RecordTypes.GRUP.TypeInt); var groupByteStream = new MemoryStream(groupBytes); using (var stream = new MutagenWriter(groupByteStream, GameConstants.Oblivion, dispose: false)) { stream.Position += 8; CellBlockBinaryWriteTranslation.WriteEmbedded(block, stream); } streams[0] = groupByteStream; if (subBlocks != null) { Parallel.ForEach(subBlocks, (cellSubBlock, state, counter) => { WriteSubBlocksParallel( cellSubBlock, masters, (int)counter + 1, streams); }); } PluginUtilityTranslation.CompileSetGroupLength(streams, groupBytes); streamDepositArray[targetIndex] = new CompositeReadStream(streams, resetPositions: true); }
public FormKey Parse( ReadOnlySpan<byte> span, MasterReferenceReader masterReferences) { var id = BinaryPrimitives.ReadUInt32LittleEndian(span); return FormKey.Factory(masterReferences, id); }
public static void WriteSubBlocksParallel( IWorldspaceSubBlockGetter subBlock, MasterReferenceReader masters, int targetIndex, Stream[] streamDepositArray) { var items = subBlock.Items; var bundle = new WritingBundle(GameConstants.Oblivion) { MasterReferences = masters }; Stream[] streams = new Stream[(items?.Count ?? 0) + 1]; byte[] groupBytes = new byte[GameConstants.Oblivion.GroupConstants.HeaderLength]; BinaryPrimitives.WriteInt32LittleEndian(groupBytes.AsSpan(), RecordTypes.GRUP.TypeInt); var groupByteStream = new MemoryStream(groupBytes); using (var stream = new MutagenWriter(groupByteStream, bundle, dispose: false)) { stream.Position += 8; WorldspaceSubBlockBinaryWriteTranslation.WriteEmbedded(subBlock, stream); } streams[0] = groupByteStream; if (items != null) { Parallel.ForEach(items, (cell, state, counter) => { MemoryTributary trib = new MemoryTributary(); cell.WriteToBinary(new MutagenWriter(trib, bundle, dispose: false)); streams[(int)counter + 1] = trib; }); } PluginUtilityTranslation.CompileSetGroupLength(streams, groupBytes); streamDepositArray[targetIndex] = new CompositeReadStream(streams, resetPositions: true); }
public static void WriteCellsParallel( IListGroupGetter <ICellBlockGetter> group, MasterReferenceReader masters, int targetIndex, Stream[] streamDepositArray) { if (group.Records.Count == 0) { return; } Stream[] streams = new Stream[group.Records.Count + 1]; byte[] groupBytes = new byte[GameConstants.Oblivion.GroupConstants.HeaderLength]; BinaryPrimitives.WriteInt32LittleEndian(groupBytes.AsSpan(), RecordTypes.GRUP.TypeInt); var groupByteStream = new MemoryStream(groupBytes); using (var stream = new MutagenWriter(groupByteStream, GameConstants.Oblivion, dispose: false)) { stream.Position += 8; ListGroupBinaryWriteTranslation.WriteEmbedded <ICellBlockGetter>(group, stream); } streams[0] = groupByteStream; Parallel.ForEach(group.Records, (cellBlock, state, counter) => { WriteBlocksParallel( cellBlock, masters, (int)counter + 1, streams); }); PluginUtilityTranslation.CompileSetGroupLength(streams, groupBytes); streamDepositArray[targetIndex] = new CompositeReadStream(streams, resetPositions: true); }
public static MasterReferenceReader ConstructWriteMasters(IModGetter mod, BinaryWriteParameters param) { MasterReferenceReader ret = new MasterReferenceReader(mod.ModKey); HashSet <ModKey> modKeys = new HashSet <ModKey>(); switch (param.MastersListSync) { case BinaryWriteParameters.MastersListSyncOption.NoCheck: modKeys.Add(mod.MasterReferences.Select(m => m.Master)); break; case BinaryWriteParameters.MastersListSyncOption.Iterate: modKeys.Add( // All FormKeys of links mod.LinkFormKeys.Select(f => f.ModKey) // All FormKeys of records themselves .And(mod.EnumerateMajorRecords().Select(m => m.FormKey.ModKey))); break; default: throw new NotImplementedException(); } modKeys.Remove(mod.ModKey); modKeys.Remove(ModKey.Null); ret.SetTo(modKeys.Select(m => new MasterReference() { Master = m })); return(ret); }
public static void WriteDialogTopicsParallel( IGroupGetter <IDialogTopicGetter> group, MasterReferenceReader masters, int targetIndex, Stream[] streamDepositArray) { WriteGroupParallel(group, masters, targetIndex, streamDepositArray); }
public void Write <T>( MutagenWriter writer, IEDIDLink <T> item, MasterReferenceReader masterReferences) where T : class, IMajorRecordCommonGetter { this.Write( writer, item, masterReferences); }
public BinaryWriteParameters GetWriteParam(MasterReferenceReader masterRefs, StringsWriter stringsWriter) { return(new BinaryWriteParameters() { ModKey = BinaryWriteParameters.ModKeyOption.NoCheck, MastersListContent = BinaryWriteParameters.MastersListContentOption.NoCheck, RecordCount = BinaryWriteParameters.RecordCountOption.NoCheck, NextFormID = BinaryWriteParameters.NextFormIDOption.NoCheck, FormIDUniqueness = BinaryWriteParameters.FormIDUniquenessOption.NoCheck, MasterFlag = BinaryWriteParameters.MasterFlagOption.NoCheck, MastersListOrdering = masterRefs, StringsWriter = stringsWriter, }); }
/// <summary> /// Constructor that opens a read stream to a path /// </summary> /// <param name="path">Path to read from</param> /// <param name="release">Game Release the stream is for</param> /// <param name="bufferSize">Size of internal buffer</param> /// <param name="offsetReference">Optional offset reference position to use</param> public MutagenBinaryReadStream( ModPath path, GameRelease release, int bufferSize = 4096, long offsetReference = 0) : base(path, bufferSize) { this._path = path; this.MetaData = new ParsingBundle(release, MasterReferenceReader.FromPath(path, release)) { ModKey = path.ModKey }; this.OffsetReference = offsetReference; }
/// <summary> /// Constructor that opens a read stream to a path /// </summary> /// <param name="path">Path to read from</param> /// <param name="release">Game Release the stream is for</param> /// <param name="bufferSize">Size of internal buffer</param> /// <param name="offsetReference">Optional offset reference position to use</param> /// <param name="fileSystem">FileSystem to read from</param> public MutagenBinaryReadStream( ModPath path, GameRelease release, int bufferSize = 4096, long offsetReference = 0, IFileSystem?fileSystem = null) : base(fileSystem.GetOrDefault().File.OpenRead(path), bufferSize) { this._path = path; this.MetaData = new ParsingBundle(release, MasterReferenceReader.FromPath(path, release, fileSystem: fileSystem)) { ModKey = path.ModKey }; this.OffsetReference = offsetReference; }
public FormKey Parse( ReadOnlySpan <byte> span, MasterReferenceReader masterReferences) { var id = BinaryPrimitives.ReadUInt32LittleEndian(span); var modID = span[3]; if (modID < masterReferences.Masters.Count) { return(new FormKey( masterReferences.Masters[modID].Master, id)); } return(new FormKey( masterReferences.CurrentMod, id)); }
/// <summary> /// Constructs a FormKey from a list of masters and the raw uint /// </summary> /// <param name="masterReferences">Master reference list to refer to</param> /// <param name="idWithModID">Mod index and Record ID to use</param> /// <returns>Converted FormID</returns> public static FormKey Factory(MasterReferenceReader masterReferences, uint idWithModID) { var modID = ModIndex.GetModIndexByteFromUInt(idWithModID); if (modID >= masterReferences.Masters.Count) { return(new FormKey( masterReferences.CurrentMod, idWithModID)); } var master = masterReferences.Masters[modID]; return(new FormKey( master.Master, idWithModID)); }
public static void AddImplicitMasters(RunSynthesisMutagenPatcher settings, ExtendedList <LoadOrderListing> loadOrderListing) { HashSet <ModKey> referencedMasters = new HashSet <ModKey>(); foreach (var item in loadOrderListing.OnlyEnabled()) { MasterReferenceReader reader = MasterReferenceReader.FromPath(Path.Combine(settings.DataFolderPath, item.ModKey.FileName), settings.GameRelease); referencedMasters.Add(reader.Masters.Select(m => m.Master)); } for (int i = 0; i < loadOrderListing.Count; i++) { var listing = loadOrderListing[i]; if (!listing.Enabled && referencedMasters.Contains(listing.ModKey)) { loadOrderListing[i] = new LoadOrderListing(listing.ModKey, enabled: true); } } }
/// <summary> /// Constructor that wraps an existing stream /// </summary> /// <param name="stream">Stream to wrap and read from</param> /// <param name="modKey">ModKey</param> /// <param name="release">Game Release the stream is for</param> /// <param name="bufferSize">Size of internal buffer</param> /// <param name="dispose">Whether to dispose the source stream</param> /// <param name="offsetReference">Optional offset reference position to use</param> public MutagenBinaryReadStream( Stream stream, ModKey modKey, GameRelease release, int bufferSize = 4096, bool dispose = true, long offsetReference = 0) : base(stream, bufferSize, dispose) { var startPos = stream.Position; this.MetaData = new ParsingBundle(release, MasterReferenceReader.FromStream(stream, modKey, release)) { ModKey = modKey }; stream.Position = startPos; this.OffsetReference = offsetReference; }
public static void WriteGroupParallel <T>( IGroupGetter <T> group, MasterReferenceReader masters, int targetIndex, GameConstants gameConstants, Stream[] streamDepositArray) where T : class, ISkyrimMajorRecordGetter, IBinaryItem { if (group.RecordCache.Count == 0) { return; } var cuts = group.Cut(CutCount).ToArray(); Stream[] subStreams = new Stream[cuts.Length + 1]; byte[] groupBytes = new byte[gameConstants.GroupConstants.HeaderLength]; BinaryPrimitives.WriteInt32LittleEndian(groupBytes.AsSpan(), RecordTypes.GRUP.TypeInt); var groupByteStream = new MemoryStream(groupBytes); using (var stream = new MutagenWriter(groupByteStream, gameConstants, dispose: false)) { stream.Position += 8; GroupBinaryWriteTranslation.WriteEmbedded <T>(group, stream); } subStreams[0] = groupByteStream; Parallel.ForEach(cuts, (cutItems, state, counter) => { MemoryTributary trib = new MemoryTributary(); var bundle = new WritingBundle(gameConstants) { MasterReferences = masters }; using (var stream = new MutagenWriter(trib, bundle, dispose: false)) { foreach (var item in cutItems) { item.WriteToBinary(stream); } } subStreams[(int)counter + 1] = trib; }); UtilityTranslation.CompileSetGroupLength(subStreams, groupBytes); streamDepositArray[targetIndex] = new CompositeReadStream(subStreams, resetPositions: true); }
public static void Align( ModPath inputPath, FilePath outputPath, GameRelease release, AlignmentRules alignmentRules, TempFolder?temp = null) { var interest = new RecordInterest(alignmentRules.Alignments.Keys) { EmptyMeansInterested = false }; var parsingBundle = new ParsingBundle(GameConstants.Get(release), MasterReferenceReader.FromPath(inputPath, release)); var fileLocs = RecordLocator.GetFileLocations(inputPath.Path, release, interest); temp ??= new TempFolder(); using (temp) { var alignedMajorRecordsFile = Path.Combine(temp.Dir.Path, "alignedRules"); using (var inputStream = new MutagenBinaryReadStream(inputPath.Path, parsingBundle)) { using var writer = new MutagenWriter(new FileStream(alignedMajorRecordsFile, FileMode.Create), release); AlignMajorRecordsByRules(inputStream, writer, alignmentRules, fileLocs); } var alignedGroupsFile = Path.Combine(temp.Dir.Path, "alignedGroups"); using (var inputStream = new MutagenBinaryReadStream(alignedMajorRecordsFile, parsingBundle)) { using var writer = new MutagenWriter(new FileStream(alignedGroupsFile, FileMode.Create), release); AlignGroupsByRules(inputStream, writer, alignmentRules, fileLocs); } fileLocs = RecordLocator.GetFileLocations(alignedGroupsFile, release, interest); var alignedCellsFile = Path.Combine(temp.Dir.Path, "alignedCells"); using (var mutaReader = new BinaryReadStream(alignedGroupsFile)) { using var writer = new MutagenWriter(alignedCellsFile, release); foreach (var grup in fileLocs.GrupLocations) { if (grup <= mutaReader.Position) { continue; } var noRecordLength = grup - mutaReader.Position; mutaReader.WriteTo(writer.BaseStream, (int)noRecordLength); // If complete overall, return if (mutaReader.Complete) { break; } mutaReader.WriteTo(writer.BaseStream, 12); var grupType = (GroupTypeEnum)mutaReader.ReadUInt32(); writer.Write((int)grupType); switch (grupType) { case GroupTypeEnum.CellChildren: AlignCellChildren(mutaReader, writer); break; default: break; } } mutaReader.WriteTo(writer.BaseStream, checked ((int)mutaReader.Remaining)); } fileLocs = RecordLocator.GetFileLocations(alignedCellsFile, release, interest); using (var mutaReader = new MutagenBinaryReadStream(alignedCellsFile, parsingBundle)) { using var writer = new MutagenWriter(outputPath.Path, GameConstants.Get(release)); foreach (var grup in fileLocs.GrupLocations) { if (grup <= mutaReader.Position) { continue; } var noRecordLength = grup - mutaReader.Position; mutaReader.WriteTo(writer.BaseStream, (int)noRecordLength); // If complete overall, return if (mutaReader.Complete) { break; } mutaReader.WriteTo(writer.BaseStream, 12); var grupType = (GroupTypeEnum)mutaReader.ReadUInt32(); writer.Write((int)grupType); switch (grupType) { case GroupTypeEnum.WorldChildren: AlignWorldChildren(mutaReader, writer); break; default: break; } } mutaReader.WriteTo(writer.BaseStream, checked ((int)mutaReader.Remaining)); } } }
public async Task Process( TempFolder tmpFolder, Subject <string> logging, ModPath sourcePath, string preprocessedPath, string outputPath) { this.Logging = logging; this.TempFolder = tmpFolder; this.SourcePath = sourcePath; this.Masters = MasterReferenceReader.FromPath(SourcePath, GameRelease); this.Bundle = new ParsingBundle(GameRelease, Masters); this._NumMasters = GetNumMasters(); this._AlignedFileLocs = RecordLocator.GetFileLocations(new ModPath(ModKey, preprocessedPath), this.GameRelease); var preprocessedBytes = File.ReadAllBytes(preprocessedPath); IMutagenReadStream streamGetter() => new MutagenMemoryReadStream(preprocessedBytes, Bundle); using (var stream = streamGetter()) { lock (_lengthTracker) { foreach (var grup in this._AlignedFileLocs.GrupLocations.And(this._AlignedFileLocs.ListedRecords.Keys)) { stream.Position = grup + 4; this._lengthTracker[grup] = stream.ReadUInt32(); } } await this.PreProcessorJobs(streamGetter); await Task.WhenAll(ExtraJobs(streamGetter)); this.AddDynamicProcessorInstructions(); Parallel.ForEach(this.DynamicProcessors.Keys .And(this.DynamicStreamProcessors.Keys) .And(RecordType.Null) .Distinct(), ParallelOptions, type => ProcessDynamicType(type, streamGetter)); lock (_lengthTracker) { foreach (var grup in this._lengthTracker) { stream.Position = grup.Key + 4; if (grup.Value == stream.ReadUInt32()) { continue; } this._Instructions.SetSubstitution( loc: grup.Key + 4, sub: BitConverter.GetBytes(grup.Value)); } } } var config = this._Instructions.GetConfig(); using (var processor = new BinaryFileProcessor( new FileStream(preprocessedPath, FileMode.Open, FileAccess.Read), config)) { try { using var outStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write); processor.CopyTo(outStream); } catch (Exception) { if (File.Exists(outputPath)) { File.Delete(outputPath); } throw; } } }
public IMasterReferenceReader FromPath(ModPath path) { return(MasterReferenceReader.FromPath(path, _gameReleaseContext.Release, fileSystem: _fileSystem)); }
public Test BinaryPassthroughTest() { (TempFolder tmp, Test processedTest) = SetupProcessedFiles(); var outputPath = Path.Combine(tmp.Dir.Path, $"{this.Nickname}_NormalExport"); var processedPath = ProcessedPath(tmp); var orderedPath = Path.Combine(tmp.Dir.Path, $"{this.Nickname}_Ordered"); var binaryOverlayPath = Path.Combine(tmp.Dir.Path, $"{this.Nickname}_BinaryOverlay"); var copyInPath = Path.Combine(tmp.Dir.Path, $"{this.Nickname}_CopyIn"); var strsProcessedPath = Path.Combine(tmp.Dir.Path, "Strings/Processed"); var masterRefs = MasterReferenceReader.FromPath(new ModPath(ModKey, this.FilePath.Path), GameRelease); // Do normal if (Settings.TestNormal) { var strsWriteDir = Path.Combine(tmp.Dir.Path, "Strings", $"{this.Nickname}_Normal"); bool doStrings = false; var passthrough = TestBattery.RunTest( "Binary Normal Passthrough", this.GameRelease, this.Target, parallel: Settings.Parallel, toDo: async(o) => { o.OnNext(FilePath.ToString()); var mod = await ImportBinary(this.FilePath.Path); doStrings = mod.UsingLocalization; foreach (var record in mod.EnumerateMajorRecords()) { record.IsCompressed = false; } var writeParam = GetWriteParam(masterRefs, doStrings ? new StringsWriter(mod.ModKey, strsWriteDir) : null); mod.WriteToBinary(outputPath, writeParam); GC.Collect(); using var stream = new MutagenBinaryReadStream(processedPath, this.GameRelease); writeParam.StringsWriter?.Dispose(); AssertFilesEqual( stream, outputPath, amountToReport: 15); }); processedTest.AddAsChild(passthrough); if (doStrings) { foreach (var item in AssertStringsEqual( "Binary Normal", strsProcessedPath, strsWriteDir)) { passthrough.AddAsChild(item); } } } if (Settings.TestBinaryOverlay) { var strsWriteDir = Path.Combine(tmp.Dir.Path, "Strings", $"{this.Nickname}_Overlay"); bool doStrings = false; var passthrough = TestBattery.RunTest( "Binary Overlay Passthrough", this.GameRelease, this.Target, parallel: Settings.Parallel, toDo: async(o) => { o.OnNext(FilePath.ToString()); using (var wrapper = await ImportBinaryOverlay(this.FilePath.Path)) { doStrings = wrapper.UsingLocalization; var writeParam = GetWriteParam(masterRefs, doStrings ? new StringsWriter(wrapper.ModKey, strsWriteDir) : null); wrapper.WriteToBinary(binaryOverlayPath, writeParam); writeParam.StringsWriter?.Dispose(); } using var stream = new MutagenBinaryReadStream(processedPath, this.GameRelease); PassthroughTest.AssertFilesEqual( stream, binaryOverlayPath, amountToReport: 15); }); processedTest.AddAsChild(passthrough); if (doStrings) { foreach (var item in AssertStringsEqual( "Binary Overlay", strsProcessedPath, strsWriteDir)) { passthrough.AddAsChild(item); } } } if (Settings.TestCopyIn) { var strsWriteDir = Path.Combine(tmp.Dir.Path, "Strings", $"{this.Nickname}_CopyIn"); bool doStrings = false; var passthrough = TestBattery.RunTest( "Copy In Passthrough", this.GameRelease, this.Target, parallel: Settings.Parallel, toDo: async(o) => { o.OnNext(FilePath.ToString()); var copyIn = await ImportCopyIn(this.FilePath.Path); doStrings = copyIn.UsingLocalization; var writeParam = GetWriteParam(masterRefs, doStrings ? new StringsWriter(copyIn.ModKey, strsWriteDir) : null); copyIn.WriteToBinary(copyInPath, writeParam); writeParam.StringsWriter?.Dispose(); using var stream = new MutagenBinaryReadStream(processedPath, this.GameRelease); PassthroughTest.AssertFilesEqual( stream, copyInPath, amountToReport: 15); }); processedTest.AddAsChild(passthrough); if (doStrings) { foreach (var item in AssertStringsEqual( "Copy In", strsProcessedPath, strsWriteDir)) { passthrough.AddAsChild(item); } } } return(processedTest); }
public IMasterReferenceReader FromStream(Stream stream, ModKey modKey, bool disposeStream = true) { return(MasterReferenceReader.FromStream(stream, modKey, _gameReleaseContext.Release, disposeStream)); }
public IMasterReferenceReader FromStream(IMutagenReadStream stream) { return(MasterReferenceReader.FromStream(stream)); }
public static void WriteWorldspacesParallel( IGroupGetter <IWorldspaceGetter> group, MasterReferenceReader masters, int targetIndex, Stream[] streamDepositArray) { var cache = group.RecordCache; if (cache == null || cache.Count == 0) { return; } Stream[] streams = new Stream[cache.Count + 1]; var bundle = new WritingBundle(GameConstants.Oblivion) { MasterReferences = masters }; byte[] groupBytes = new byte[GameConstants.Oblivion.GroupConstants.HeaderLength]; BinaryPrimitives.WriteInt32LittleEndian(groupBytes.AsSpan(), RecordTypes.GRUP.TypeInt); var groupByteStream = new MemoryStream(groupBytes); using (var stream = new MutagenWriter(groupByteStream, GameConstants.Oblivion, dispose: false)) { stream.Position += 8; GroupBinaryWriteTranslation.WriteEmbedded <IWorldspaceGetter>(group, stream); } streams[0] = groupByteStream; Parallel.ForEach(group, (worldspace, worldspaceState, worldspaceCounter) => { var worldTrib = new MemoryTributary(); using (var writer = new MutagenWriter(worldTrib, bundle, dispose: false)) { using (HeaderExport.Header( writer: writer, record: RecordTypes.WRLD, type: ObjectType.Record)) { WorldspaceBinaryWriteTranslation.WriteEmbedded( item: worldspace, writer: writer); WorldspaceBinaryWriteTranslation.WriteRecordTypes( item: worldspace, writer: writer, recordTypeConverter: null); } } var road = worldspace.Road; var topCell = worldspace.TopCell; var subCells = worldspace.SubCells; if (subCells?.Count == 0 && road == null && topCell == null) { streams[worldspaceCounter + 1] = worldTrib; return; } Stream[] subStreams = new Stream[(subCells?.Count ?? 0) + 1]; var worldGroupTrib = new MemoryTributary(); var worldGroupWriter = new MutagenWriter(worldGroupTrib, bundle, dispose: false); worldGroupWriter.Write(RecordTypes.GRUP.TypeInt); worldGroupWriter.Write(Zeros.Slice(0, GameConstants.Oblivion.GroupConstants.LengthLength)); FormKeyBinaryTranslation.Instance.Write( worldGroupWriter, worldspace.FormKey); worldGroupWriter.Write((int)GroupTypeEnum.WorldChildren); worldGroupWriter.Write(worldspace.SubCellsTimestamp); road?.WriteToBinary(worldGroupWriter); topCell?.WriteToBinary(worldGroupWriter); subStreams[0] = worldGroupTrib; if (subCells != null) { Parallel.ForEach(subCells, (block, blockState, blockCounter) => { WriteBlocksParallel( block, masters, (int)blockCounter + 1, subStreams); }); } worldGroupWriter.Position = 4; worldGroupWriter.Write((uint)(subStreams.NotNull().Select(s => s.Length).Sum())); streams[worldspaceCounter + 1] = new CompositeReadStream(worldTrib.AsEnumerable().And(subStreams), resetPositions: true); }); PluginUtilityTranslation.CompileSetGroupLength(streams, groupBytes); streamDepositArray[targetIndex] = new CompositeReadStream(streams, resetPositions: true); }
public ParsingBundle(GameConstants constants, MasterReferenceReader masterReferences) { this.Constants = constants; this.MasterReferences = masterReferences; }
public static OwnerTarget GetBinaryOwner(ReadOnlySpan <byte> span, RecordInfoCache cache, MasterReferenceReader masters) { FormID form = new FormID(BinaryPrimitives.ReadUInt32LittleEndian(span)); FormKey formKey = FormKey.Factory(masters, form.Raw); if (cache.IsOfRecordType <Npc>(formKey)) { return(new NpcOwner() { Npc = new FormLink <INpcGetter>(FormKeyBinaryTranslation.Instance.Parse(span, masters)), Global = new FormLink <IGlobalGetter>(FormKeyBinaryTranslation.Instance.Parse(span.Slice(4), masters)) }); } else if (cache.IsOfRecordType <Faction>(formKey)) { return(new FactionOwner() { Faction = new FormLink <IFactionGetter>(FormKeyBinaryTranslation.Instance.Parse(span, masters)), RequiredRank = BinaryPrimitives.ReadInt32LittleEndian(span.Slice(4)) }); } else { return(new NoOwner() { RawOwnerData = BinaryPrimitives.ReadUInt32LittleEndian(span), RawVariableData = BinaryPrimitives.ReadUInt32LittleEndian(span.Slice(4)) }); } }