/// <summary> /// Global factory helper /// </summary> /// <param name="frame">Frame to read from</param> /// <param name="getter">Func factory to create Global given data type char</param> /// <returns>Constructed Global from getter</returns> /// <exception cref="ArgumentException">If frame aligned to a malformed Global record</exception> public static T Create <T>( MutagenFrame frame, Func <MutagenFrame, char, T> getter) where T : IMajorRecordCommon, IGlobalCommon { var initialPos = frame.Position; var majorMeta = frame.GetMajorRecordFrame(); if (majorMeta.Header.RecordType != GLOB) { throw new ArgumentException(); } T g = getter(frame, GetGlobalChar(majorMeta)); frame.Reader.Position = initialPos + frame.MetaData.Constants.MajorConstants.TypeAndLengthLength; // Read data var fltvLoc = UtilityTranslation.FindFirstSubrecord(majorMeta.Content, frame.MetaData.Constants, FLTV, navigateToContent: true); if (fltvLoc == null) { throw new ArgumentException($"Could not find FLTV."); } g.RawFloat = majorMeta.Content.Slice(fltvLoc.Value).GetFloat(); // Skip to end frame.Reader.Position = initialPos + majorMeta.Header.TotalLength; return(g); }
public void FixVMADFormIDs( MajorRecordFrame frame, long fileOffset, out int?vmadPos, out ushort objectFormat, out int processed) { vmadPos = UtilityTranslation.FindFirstSubrecord(frame.Content, Meta, RecordTypes.VMAD); if (vmadPos == null) { processed = 0; objectFormat = 0; return; } var stream = new MutagenMemoryReadStream(frame.HeaderAndContentData, Bundle) { Position = vmadPos.Value + frame.HeaderLength }; stream.Position += Meta.SubConstants.HeaderLength; // Skip version stream.Position += 2; objectFormat = stream.ReadUInt16(); var scriptCt = stream.ReadUInt16(); for (int i = 0; i < scriptCt; i++) { FixVMADScriptIDs(stream, fileOffset, objectFormat); } processed = (int)(stream.Position - vmadPos.Value - frame.HeaderLength); }
/// <summary> /// Iterates a MajorRecordFrame's subrecords and locates the first occurance of the desired type /// </summary> /// <param name="majorFrame">Frame to read from</param> /// <param name="type">Type to search for</param> /// <param name="offset">Offset within the Major Record's contents to start searching</param> /// <param name="pin">SubrecordPinFrame if found</param> /// <returns>True if matching subrecord is found</returns> public static bool TryLocateSubrecordPinFrame(this MajorRecordFrame majorFrame, RecordType type, int offset, out SubrecordPinFrame pin) { var find = UtilityTranslation.FindFirstSubrecord(majorFrame.Content.Slice(offset - majorFrame.HeaderLength), majorFrame.Meta, type, navigateToContent: false); if (find == null) { pin = default; return(false); } pin = new SubrecordPinFrame(majorFrame.Meta, majorFrame.Content.Slice(find.Value + offset - majorFrame.HeaderLength), find.Value + offset); return(true); }
/// <summary> /// Iterates a MajorRecordFrame's subrecords and locates the first occurance of the desired type /// </summary> /// <param name="majorFrame">Frame to read from</param> /// <param name="type">Type to search for</param> /// <param name="frame">SubrecordFrame if found</param> /// <returns>True if matching subrecord is found</returns> public static bool TryLocateSubrecordFrame(this MajorRecordFrame majorFrame, RecordType type, out SubrecordFrame frame) { var find = UtilityTranslation.FindFirstSubrecord(majorFrame.Content, majorFrame.Meta, type, navigateToContent: false); if (find == null) { frame = default; return(false); } frame = new SubrecordFrame(majorFrame.Meta, majorFrame.Content.Slice(find.Value)); return(true); }
/// <summary> /// Iterates a MajorRecordFrame's subrecords and locates the first occurance of the desired type /// </summary> /// <param name="majorFrame">Frame to read from</param> /// <param name="type">Type to search for</param> /// <param name="header">SubrecordHeader if found</param> /// <param name="loc">Location of the subrecord, relative to the parent record's RecordType data</param> /// <returns>True if matching subrecord is found</returns> public static bool TryLocateSubrecord(this MajorRecordFrame majorFrame, RecordType type, int offset, out SubrecordHeader header, out int loc) { var find = UtilityTranslation.FindFirstSubrecord(majorFrame.Content.Slice(offset - majorFrame.HeaderLength), majorFrame.Meta, type, navigateToContent: false); if (find == null) { header = default; loc = default; return(false); } header = new SubrecordHeader(majorFrame.Meta, majorFrame.Content.Slice(find.Value + offset - majorFrame.HeaderLength)); loc = find.Value + offset; return(true); }
/// <summary> /// Retrieves the character representing a Global's data type from a MajorRecordFrame /// </summary> /// <param name="frame">Frame to retrieve from</param> /// <returns>Character representing data type</returns> /// <exception cref="ArgumentException">If FNAM not present or malformed</exception> public static char GetGlobalChar(MajorRecordFrame frame) { var subrecordSpan = frame.Content; var fnamLocation = UtilityTranslation.FindFirstSubrecord(subrecordSpan, frame.Header.Meta, FNAM); if (fnamLocation == null) { throw new ArgumentException($"Could not find FNAM."); } var fnamMeta = frame.Header.Meta.SubrecordFrame(subrecordSpan.Slice(fnamLocation.Value)); if (fnamMeta.Content.Length != 1) { throw new ArgumentException($"FNAM had non 1 length: {fnamMeta.Content.Length}"); } return((char)fnamMeta.Content[0]); }
private void ProcessRegions( MajorRecordFrame majorFrame, long fileOffset) { var rdat = UtilityTranslation.FindFirstSubrecord(majorFrame.Content, majorFrame.Meta, RecordTypes.RDAT, navigateToContent: false); if (rdat == null) { return; } // Order RDATs by index SortedList <uint, RangeInt64> rdats = new SortedList <uint, RangeInt64>(); List <uint> raw = new List <uint>(); while (rdat != null) { var rdatHeader = majorFrame.Meta.SubrecordFrame(majorFrame.Content.Slice(rdat.Value)); var index = BinaryPrimitives.ReadUInt32LittleEndian(rdatHeader.Content); var nextRdat = UtilityTranslation.FindFirstSubrecord( majorFrame.Content, majorFrame.Meta, RecordTypes.RDAT, navigateToContent: false, offset: rdat.Value + rdatHeader.TotalLength); rdats[index] = new RangeInt64( fileOffset + majorFrame.HeaderLength + rdat.Value, nextRdat == null ? fileOffset + majorFrame.TotalLength - 1 : nextRdat.Value - 1 + fileOffset + majorFrame.HeaderLength); raw.Add(index); rdat = nextRdat; } if (raw.SequenceEqual(rdats.Keys)) { return; } foreach (var item in rdats.Reverse()) { this._Instructions.SetMove( loc: fileOffset + majorFrame.TotalLength, section: item.Value); } }
public void FindFirstSubrecord_Duplicate() { Assert.Equal(FirstTypicalLocation, UtilityTranslation.FindFirstSubrecord(GetTypical(), GameConstants.Oblivion, FirstTypicalType)); }
public void FindFirstSubrecord_Empty() { var b = new byte[0]; Assert.Null(UtilityTranslation.FindFirstSubrecord(b, GameConstants.Oblivion, SecondTypicalType)); }
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; } }