public static void CustomStringImports(MutagenFrame frame, IConditionData item) { if (!frame.Reader.TryGetSubrecordFrame(out var subMeta)) { return; } if (!(item is IFunctionConditionData funcData)) { return; } switch (subMeta.RecordType.TypeInt) { case 0x31534943: // CIS1 funcData.ParameterOneString = BinaryStringUtility.ProcessWholeToZString(subMeta.Content); break; case 0x32534943: // CIS2 funcData.ParameterTwoString = BinaryStringUtility.ProcessWholeToZString(subMeta.Content); break; default: return; } frame.Position += subMeta.TotalLength; }
string?GetIconCustom() { if (_IconLocation.HasValue) { return(BinaryStringUtility.ProcessWholeToZString(HeaderTranslation.ExtractSubrecordMemory(_data, _IconLocation.Value, _package.MetaData.Constants))); } if (_SecondaryIconLocation.HasValue) { return(BinaryStringUtility.ProcessWholeToZString(HeaderTranslation.ExtractSubrecordMemory(_data, _SecondaryIconLocation.Value, _package.MetaData.Constants))); } return(default);
public static void FillCloudTexture(IMutagenReadStream stream, RecordType nextRecordType, string?[] textures) { int layer = nextRecordType.TypeInt - TextureIntBase; if (layer > 29 || layer < 0) { throw new ArgumentException(); } var subRec = stream.ReadSubrecordFrame(); textures[layer] = BinaryStringUtility.ProcessWholeToZString(subRec.Content, stream.MetaData.Encodings.NonTranslated); }
public void GetMemorySlice() { var archive = Archive.CreateReader(GameRelease.SkyrimSE, TestBsa); archive.TryGetFolder("derp\\some_FoldeR", out var folder) .Should().BeTrue(); if (folder == null) { throw new NullReferenceException(); } var file = folder.Files.First(); BinaryStringUtility.ProcessWholeToZString(file.GetMemorySlice()) .Should().Be("Found me"); }
public static partial void FillBinaryBipedObjectNamesCustom(MutagenFrame frame, IRaceInternal item) { for (int i = 0; i < NumBipedObjectNames; i++) { if (!frame.Reader.TryReadSubrecordFrame(RecordTypes.NAME, out var subHeader)) { break; } BipedObject type = (BipedObject)i; var val = BinaryStringUtility.ProcessWholeToZString(subHeader.Content, frame.MetaData.Encodings.NonTranslated); if (!string.IsNullOrEmpty(val)) { item.BipedObjectNames[type] = val; } } }
public void AsStream() { var archive = Archive.CreateReader(GameRelease.SkyrimSE, TestBsa); archive.TryGetFolder("derp\\some_FoldeR", out var folder) .Should().BeTrue(); if (folder == null) { throw new NullReferenceException(); } var file = folder.Files.First(); var stream = file.AsStream(); byte[] b = new byte[stream.Length]; stream.Remaining().Should().Be(8); stream.Read(b); stream.Remaining().Should().Be(0); BinaryStringUtility.ProcessWholeToZString(b) .Should().Be("Found me"); }
/// <summary> /// Takes a span aligned to a major record, and attempts to locate the game setting type. /// Will throw if the span is misaligned or doesn't start at a valid major record header. /// </summary> /// <param name="span">Data beginning at the start of a major record</param> /// <param name="meta">Game meta information to use in parsing</param> /// <returns>A response of the GameSettingType if found, or a reason if not.</returns> public static GetResponse <GameSettingType> GetGameSettingType(ReadOnlyMemorySlice <byte> span, GameConstants meta) { var majorMeta = meta.MajorRecordFrame(span); var edidLoc = UtilityTranslation.FindFirstSubrecord(majorMeta.Content, meta, Constants.EditorID); if (edidLoc == null) { return(GetResponse <GameSettingType> .Fail($"EDID was not located")); } var edidMeta = meta.SubrecordFrame(majorMeta.Content.Slice(edidLoc.Value)); var edid = BinaryStringUtility.ProcessWholeToZString(edidMeta.Content); if (edid.Length == 0) { return(GetResponse <GameSettingType> .Fail("No EDID parsed.")); } if (!TryGetGameSettingType(edid[0], out var settingType)) { return(GetResponse <GameSettingType> .Fail($"Unknown game setting type: {edid[0]}")); } return(GetResponse <GameSettingType> .Succeed(settingType)); }
/// <summary> /// Interprets a subrecord's content as a string. /// </summary> /// <param name="frame">Frame to read from</param> /// <returns>Subrecord's content as a string, null trimmed if applicable</returns> public static string AsString(this SubrecordFrame frame) { return(BinaryStringUtility.ProcessWholeToZString(frame.Content)); }
public static IEnumerable <APerkEffect> ParseEffects(IMutagenReadStream stream) { while (stream.TryReadSubrecordFrame(RecordTypes.PRKE, out var prkeFrame)) { var type = (Perk.EffectType)prkeFrame.Content[0]; var rank = prkeFrame.Content[1]; var priority = prkeFrame.Content[2]; APerkEffect effect; if (stream.TryReadSubrecordFrame(RecordTypes.DATA, out var dataFrame)) { switch (type) { case Perk.EffectType.Quest: effect = new PerkQuestEffect() { Quest = new FormLink <IQuestGetter>(FormKeyBinaryTranslation.Instance.Parse(dataFrame.Content, stream.MetaData.MasterReferences !)), Stage = dataFrame.Content[4], Unknown = dataFrame.Content.Slice(5, 3).ToArray(), }; effect.Conditions.SetTo( ListBinaryTranslation <PerkCondition> .Instance.Parse( reader: new MutagenFrame(stream), transl: (MutagenFrame r, out PerkCondition listSubItem) => { return(LoquiBinaryTranslation <PerkCondition> .Instance.Parse( frame: r, item: out listSubItem !)); })); break; case Perk.EffectType.Ability: effect = new PerkAbilityEffect() { Ability = new FormLink <ISpellGetter>(FormKeyBinaryTranslation.Instance.Parse(dataFrame.Content, stream.MetaData.MasterReferences !)), }; effect.Conditions.SetTo( ListBinaryTranslation <PerkCondition> .Instance.Parse( reader: new MutagenFrame(stream), transl: (MutagenFrame r, out PerkCondition listSubItem) => { return(LoquiBinaryTranslation <PerkCondition> .Instance.Parse( frame: r, item: out listSubItem !)); })); break; case Perk.EffectType.EntryPoint: var entryPt = (APerkEntryPointEffect.EntryType)dataFrame.Content[0]; var func = (APerkEntryPointEffect.FunctionType)dataFrame.Content[1]; var tabCount = dataFrame.Content[2]; var conditions = ListBinaryTranslation <PerkCondition> .Instance.Parse( reader : new MutagenFrame(stream), transl : (MutagenFrame r, out PerkCondition listSubItem) => { return(LoquiBinaryTranslation <PerkCondition> .Instance.Parse( frame: r, item: out listSubItem !)); }) .ToList(); ReadOnlyMemorySlice <byte>?epf2 = null; ReadOnlyMemorySlice <byte>?epf3 = null; ReadOnlyMemorySlice <byte>?epfd = null; ReadOnlyMemorySlice <byte>?epft = null; while (stream.TryReadSubrecordFrame(out var subFrame)) { switch (subFrame.RecordTypeInt) { case RecordTypeInts.EPF2: epf2 = subFrame.Content; break; case RecordTypeInts.EPF3: epf3 = subFrame.Content; break; case RecordTypeInts.EPFD: epfd = subFrame.Content; break; case RecordTypeInts.EPFT: epft = subFrame.Content; break; default: stream.Position -= subFrame.Content.Length; goto searchDone; } } searchDone: APerkEntryPointEffect entryPointEffect; switch (func) { case APerkEntryPointEffect.FunctionType.SetValue: case APerkEntryPointEffect.FunctionType.AddValue: case APerkEntryPointEffect.FunctionType.MultiplyValue: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } float?f; if (epft == null && epfd == null) { f = null; } else { if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointModifyValue)} did not have expected EPFT record"); } if (!epfd.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointModifyValue)} did not have expected EPFD record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.Float) { throw new ArgumentException($"{nameof(PerkEntryPointModifyValue)} did not have expected parameter type flag: {epft.Value[0]}"); } f = epfd.Value.Float(); } entryPointEffect = new PerkEntryPointModifyValue() { Value = f, Modification = func switch { APerkEntryPointEffect.FunctionType.SetValue => PerkEntryPointModifyValue.ModificationType.Set, APerkEntryPointEffect.FunctionType.MultiplyValue => PerkEntryPointModifyValue.ModificationType.Multiply, APerkEntryPointEffect.FunctionType.AddValue => PerkEntryPointModifyValue.ModificationType.Add, _ => throw new ArgumentException(), } }; break; case APerkEntryPointEffect.FunctionType.AddRangeToValue: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointAddRangeToValue)} did not have expected EPFT record"); } if (!epfd.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointAddRangeToValue)} did not have expected EPFD record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.FloatFloat) { throw new ArgumentException($"{nameof(PerkEntryPointAddRangeToValue)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointAddRangeToValue() { From = epfd.Value.Float(), To = epfd.Value.Slice(4).Float(), }; break; case APerkEntryPointEffect.FunctionType.SetToActorValueMult: case APerkEntryPointEffect.FunctionType.MultiplyActorValueMult: case APerkEntryPointEffect.FunctionType.MultiplyOnePlusActorValueMult: case APerkEntryPointEffect.FunctionType.AddActorValueMult: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointModifyActorValue)} did not have expected EPFT record"); } if (!epfd.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointModifyActorValue)} did not have expected EPFD record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.FloatFloat) { throw new ArgumentException($"{nameof(PerkEntryPointModifyActorValue)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointModifyActorValue() { ActorValue = (ActorValue)BinaryPrimitives.ReadInt32LittleEndian(epfd.Value), Value = epfd.Value.Slice(4).Float(), Modification = func switch { APerkEntryPointEffect.FunctionType.SetToActorValueMult => PerkEntryPointModifyActorValue.ModificationType.SetToAVMult, APerkEntryPointEffect.FunctionType.AddActorValueMult => PerkEntryPointModifyActorValue.ModificationType.AddAVMult, APerkEntryPointEffect.FunctionType.MultiplyActorValueMult => PerkEntryPointModifyActorValue.ModificationType.MultiplyAVMult, APerkEntryPointEffect.FunctionType.MultiplyOnePlusActorValueMult => PerkEntryPointModifyActorValue.ModificationType.MultiplyOnePlusAVMult, _ => throw new ArgumentException(), } }; break; case APerkEntryPointEffect.FunctionType.AbsoluteValue: case APerkEntryPointEffect.FunctionType.NegativeAbsoluteValue: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (epft.HasValue && epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.None) { throw new ArgumentException($"{nameof(PerkEntryPointAbsoluteValue)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointAbsoluteValue() { Negative = func == APerkEntryPointEffect.FunctionType.NegativeAbsoluteValue }; break; case APerkEntryPointEffect.FunctionType.AddLeveledList: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointAddLeveledItem)} did not have expected EPFT record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.LeveledItem) { throw new ArgumentException($"{nameof(PerkEntryPointAddLeveledItem)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointAddLeveledItem() { Item = new FormLink <ILeveledItemGetter>(epfd.HasValue ? FormKeyBinaryTranslation.Instance.Parse(epfd.Value, stream.MetaData.MasterReferences !) : FormKey.Null) }; break; case APerkEntryPointEffect.FunctionType.AddActivateChoice: if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointAddActivateChoice)} did not have expected EPFT record"); } if (!epf3.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointAddActivateChoice)} did not have expected EPF3 record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.SpellWithStrings) { throw new ArgumentException($"{nameof(PerkEntryPointAddActivateChoice)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointAddActivateChoice() { Spell = new FormLinkNullable <ISpellGetter>(epfd.HasValue ? FormKeyBinaryTranslation.Instance.Parse(epfd.Value, stream.MetaData.MasterReferences !) : default(FormKey?)), ButtonLabel = epf2.HasValue ? StringBinaryTranslation.Instance.Parse(epf2.Value, StringsSource.Normal, stream.MetaData) : null, Flags = new PerkScriptFlag() { Flags = (PerkScriptFlag.Flag)BinaryPrimitives.ReadInt16LittleEndian(epf3.Value), FragmentIndex = BinaryPrimitives.ReadUInt16LittleEndian(epf3.Value.Slice(2)) }, }; break; case APerkEntryPointEffect.FunctionType.SelectSpell: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointSelectSpell)} did not have expected EPFT record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.Spell) { throw new ArgumentException($"{nameof(PerkEntryPointSelectSpell)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointSelectSpell() { Spell = new FormLink <ISpellGetter>(epfd.HasValue ? FormKeyBinaryTranslation.Instance.Parse(epfd.Value, stream.MetaData.MasterReferences !) : FormKey.Null), }; break; case APerkEntryPointEffect.FunctionType.SelectText: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointSelectText)} did not have expected EPFT record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.String) { throw new ArgumentException($"{nameof(PerkEntryPointSelectText)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointSelectText() { Text = epfd.HasValue ? BinaryStringUtility.ProcessWholeToZString(epfd.Value, stream.MetaData.Encodings.NonTranslated) : string.Empty }; break; case APerkEntryPointEffect.FunctionType.SetText: if (epf2.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF2, $"{nameof(PerkEntryPointModifyValue)} had EPF2 unexpectedly"); } if (epf3.HasValue) { stream.MetaData.ReportIssue(RecordTypes.EPF3, $"{nameof(PerkEntryPointModifyValue)} had EPF3 unexpectedly"); } if (!epft.HasValue) { throw new ArgumentException($"{nameof(PerkEntryPointSetText)} did not have expected EPFT record"); } if (epft.Value[0] != (byte)APerkEntryPointEffect.ParameterType.LString) { throw new ArgumentException($"{nameof(PerkEntryPointSetText)} did not have expected parameter type flag: {epft.Value[0]}"); } entryPointEffect = new PerkEntryPointSetText() { Text = epfd.HasValue ? StringBinaryTranslation.Instance.Parse(epfd.Value, StringsSource.Normal, stream.MetaData) : (TranslatedString)string.Empty, }; break; default: throw new NotImplementedException(); } entryPointEffect.EntryPoint = entryPt; entryPointEffect.PerkConditionTabCount = tabCount; entryPointEffect.Conditions.SetTo(conditions); effect = entryPointEffect; break; default: throw new NotImplementedException(); } } else { effect = type switch { Perk.EffectType.Quest => new PerkQuestEffect(), Perk.EffectType.Ability => new PerkAbilityEffect(), _ => throw new ArgumentException($"Expected DATA subrecord that did not exist."), }; } effect.Rank = rank; effect.Priority = priority; if (stream.TryReadSubrecordFrame(RecordTypes.EPFT, out var epftFrame) && epftFrame.ContentLength != 1 && epftFrame.Content[0] != (byte)APerkEntryPointEffect.ParameterType.None) { throw new ArgumentException($"Encountered an unexpected epft frame."); } stream.TryReadSubrecordFrame(RecordTypes.PRKF, out var _); yield return(effect); } }
public static void FillPackageData(IMutagenReadStream stream, int expectedCount, IDictionary <sbyte, APackageData> data) { // Retrieve the expected types, then skip rest of data var valuesPosition = stream.Position; var types = new List <string>(expectedCount); while (stream.TryReadSubrecordFrame(out var subRecord)) { switch (subRecord.RecordTypeInt) { case 0x4D414E41: // ANAM types.Add(BinaryStringUtility.ProcessWholeToZString(subRecord.Content)); break; case 0x4D414E43: // CNAM case 0x4D414E42: // BNAM case 0x43495054: // TPIC case 0x41445450: // PTDA case 0x4F544450: // PDTO case 0x54444C50: // PLDT // Skip break; default: stream.Position -= subRecord.TotalLength; goto done_types; } } done_types: // Parse package data APackageData? lastPackage = null; int itemIndex = 0; var packages = new List <APackageData>(expectedCount); while (stream.TryGetSubrecordFrame(out var subRecord)) { switch (subRecord.RecordTypeInt) { case 0x4D414E55: // UNAM var index = (sbyte)subRecord.Content[0]; lastPackage = data.GetOrAdd <sbyte, APackageData>( index, () => { return((types[itemIndex++]) switch { IntKey => new PackageDataInt(), FloatKey => new PackageDataFloat(), BoolKey => new PackageDataBool(), LocationKey => new PackageDataLocation(), SingleRefKey => new PackageDataTarget() { Type = PackageDataTarget.Types.SingleRef }, TargetSelectorKey => new PackageDataTarget() { Type = PackageDataTarget.Types.Target }, TopicKey => new PackageDataTopic(), ObjectListKey => new PackageDataObjectList(), _ => throw new NotImplementedException(), }); });
private void ProcessPackages( MajorRecordFrame majorFrame, long fileOffset) { // Reorder Idle subrecords // Reorder data values var xnamPos = UtilityTranslation.FindFirstSubrecord(majorFrame.Content, majorFrame.Meta, RecordTypes.XNAM); if (xnamPos == null) { throw new ArgumentException(); } if (!majorFrame.TryLocateSubrecordFrame(RecordTypes.PKCU, out var pkcuRec)) { throw new ArgumentException(); } var count = pkcuRec.Content.Int32(); if (count == 0) { return; } var anamPos = UtilityTranslation.FindFirstSubrecord(majorFrame.Content, majorFrame.Meta, RecordTypes.ANAM); RecordType pldt = new RecordType("PLDT"); RecordType ptda = new RecordType("PTDA"); RecordType pdto = new RecordType("PDTO"); RecordType tpic = new RecordType("TPIC"); RecordType unam = new RecordType("UNAM"); RecordType bnam = new RecordType("BNAM"); RecordType pnam = new RecordType("PNAM"); // Reorder data values to be in index ordering if (anamPos.HasValue && anamPos.Value < xnamPos.Value) { var startLoc = anamPos.Value; var dataValues = new List <(sbyte Index, ReadOnlyMemorySlice <byte> Data)>(); var curLoc = startLoc; while (anamPos.HasValue && anamPos.Value < xnamPos.Value) { var anamRecord = majorFrame.Meta.SubrecordFrame(majorFrame.Content.Slice(anamPos.Value)); var recs = UtilityTranslation.FindNextSubrecords( majorFrame.Content.Slice(anamPos.Value + anamRecord.TotalLength), majorFrame.Meta, out var _, RecordTypes.ANAM, RecordTypes.CNAM, pldt, ptda, pdto, tpic); int finalLoc; if (recs[0] == null) { finalLoc = recs.NotNull().Max(); } else if (recs[0] == 0) { dataValues.Add( (-1, majorFrame.Content.Slice(anamPos.Value, anamRecord.TotalLength))); curLoc = anamPos.Value + anamRecord.TotalLength; anamPos = anamPos.Value + anamRecord.TotalLength; continue; } else { finalLoc = recs .NotNull() .Where(i => i < recs[0] !.Value) .Max(); } var finalRec = majorFrame.Meta.Subrecord(majorFrame.Content.Slice(anamPos.Value + anamRecord.TotalLength + finalLoc)); var dataSlice = majorFrame.Content.Slice(anamPos.Value, anamRecord.TotalLength + finalLoc + finalRec.TotalLength); if (BinaryStringUtility.ProcessWholeToZString(anamRecord.Content) == "Bool" && recs[1] != null) { // Ensure bool value is 1 or 0 var cnam = majorFrame.Meta.SubrecordFrame(majorFrame.Content.Slice(anamPos.Value + anamRecord.TotalLength + recs[1].Value)); if (cnam.Content.Length != 1) { throw new ArgumentException(); } if (cnam.Content[0] > 1) { var bytes = dataSlice.ToArray(); int boolIndex = anamRecord.TotalLength + recs[1].Value + cnam.HeaderLength; bytes[boolIndex] = (byte)(bytes[boolIndex] > 0 ? 1 : 0); dataSlice = bytes; } } dataValues.Add((-1, dataSlice)); curLoc = anamPos.Value + anamRecord.TotalLength + finalLoc + finalRec.TotalLength; anamPos = anamPos.Value + anamRecord.TotalLength + recs[0]; } var unamLocs = UtilityTranslation.ParseRepeatingSubrecord( majorFrame.Content.Slice(curLoc), majorFrame.Meta, unam, out var _); if (unamLocs == null || unamLocs.Length != dataValues.Count || unamLocs.Length != count) { throw new ArgumentException(); } for (sbyte i = 0; i < unamLocs.Length; i++) { var unamRec = majorFrame.Meta.SubrecordFrame(majorFrame.Content.Slice(curLoc + unamLocs[i])); dataValues[i] = ( (sbyte)unamRec.Content[0], dataValues[i].Data); } var subLoc = startLoc; foreach (var item in dataValues.OrderBy(i => i.Index)) { _Instructions.SetSubstitution( fileOffset + majorFrame.HeaderLength + subLoc, item.Data.ToArray()); subLoc += item.Data.Length; } foreach (var item in dataValues.OrderBy(i => i.Index)) { byte[] bytes = new byte[] { 0x55, 0x4E, 0x41, 0x4D, 0x01, 0x00, 0x00 }; bytes[6] = (byte)item.Index; _Instructions.SetSubstitution( fileOffset + majorFrame.HeaderLength + subLoc, bytes.ToArray()); subLoc += bytes.Length; } } // Reorder inputs var unamPos = UtilityTranslation.FindFirstSubrecord(majorFrame.Content.Slice(xnamPos.Value), majorFrame.Meta, unam); if (!unamPos.HasValue) { return; } unamPos += xnamPos.Value; var writeLoc = fileOffset + majorFrame.HeaderLength + unamPos.Value; var inputValues = new List <(sbyte Index, ReadOnlyMemorySlice <byte> Data)>(); while (unamPos.HasValue) { var unamRecord = majorFrame.Meta.SubrecordFrame(majorFrame.Content.Slice(unamPos.Value)); var recs = UtilityTranslation.FindNextSubrecords( majorFrame.Content.Slice(unamPos.Value + unamRecord.TotalLength), majorFrame.Meta, out var _, unam, bnam, pnam); int finalLoc; if (recs[0] == null) { finalLoc = recs.NotNull().Max(); } else if (recs[0] == 0) { inputValues.Add( ((sbyte)unamRecord.Content[0], majorFrame.Content.Slice(unamPos.Value, unamRecord.TotalLength))); unamPos = unamPos.Value + unamRecord.TotalLength; continue; } else { finalLoc = recs .NotNull() .Where(i => i < recs[0] !.Value) .Max(); } var finalRec = majorFrame.Meta.Subrecord(majorFrame.Content.Slice(unamPos.Value + unamRecord.TotalLength + finalLoc)); inputValues.Add( ((sbyte)unamRecord.Content[0], majorFrame.Content.Slice(unamPos.Value, unamRecord.TotalLength + finalLoc + finalRec.TotalLength))); unamPos = unamPos.Value + unamRecord.TotalLength + recs[0]; } foreach (var item in inputValues.OrderBy(i => i.Index)) { _Instructions.SetSubstitution( writeLoc, item.Data.ToArray()); writeLoc += item.Data.Length; } }
/// <summary> /// Interprets a subrecord's content as a string. /// </summary> /// <param name="frame">Frame to read from</param> /// <param name="encoding">Encoding to use</param> /// <returns>Subrecord's content as a string, null trimmed if applicable</returns> public static string AsString(this SubrecordFrame frame, IMutagenEncoding encoding) { return(BinaryStringUtility.ProcessWholeToZString(frame.Content, encoding)); }