static partial void CustomBinaryEndImport(MutagenFrame frame, IWorldspaceInternal obj) { try { if (!frame.Reader.TryReadGroup(out var groupHeader)) { return; } if (groupHeader.GroupType == (int)GroupTypeEnum.WorldChildren) { obj.SubCellsTimestamp = BinaryPrimitives.ReadInt32LittleEndian(groupHeader.LastModifiedData); var formKey = FormKeyBinaryTranslation.Instance.Parse(groupHeader.ContainedRecordTypeData, frame.MetaData.MasterReferences !); if (formKey != obj.FormKey) { throw new ArgumentException("Cell children group did not match the FormID of the parent worldspace."); } } else { frame.Reader.Position -= groupHeader.HeaderLength; return; } var subFrame = MutagenFrame.ByLength(frame.Reader, groupHeader.ContentLength); for (int i = 0; i < 3; i++) { if (subFrame.Complete) { return; } var subType = HeaderTranslation.GetNextSubrecordType(frame.Reader, out var subLen); switch (subType.TypeInt) { case 0x44414F52: // "ROAD": obj.Road = Road.CreateFromBinary(subFrame); break; case 0x4C4C4543: // "CELL": obj.TopCell = Cell.CreateFromBinary(subFrame); break; case 0x50555247: // "GRUP": obj.SubCells.SetTo( Mutagen.Bethesda.Binary.ListBinaryTranslation <WorldspaceBlock> .Instance.Parse( frame: frame, triggeringRecord: RecordTypes.GRUP, transl: LoquiBinaryTranslation <WorldspaceBlock> .Instance.Parse)); break; default: return; } } } catch (Exception ex) { throw RecordException.Enrich(ex, obj); } }
private static void AlignGroupsByRules( MutagenBinaryReadStream inputStream, MutagenWriter writer, AlignmentRules alignmentRules, RecordLocatorResults fileLocs) { while (!inputStream.Complete) { // Import until next listed major record long noRecordLength; if (fileLocs.GrupLocations.TryGetInDirection( inputStream.Position, higher: true, result: out var nextRec)) { noRecordLength = nextRec.Value.Location.Min - inputStream.Position; } else { noRecordLength = inputStream.Remaining; } inputStream.WriteTo(writer.BaseStream, (int)noRecordLength); // If complete overall, return if (inputStream.Complete) { break; } var groupMeta = inputStream.GetGroup(); if (!groupMeta.IsGroup) { throw new ArgumentException(); } writer.Write(inputStream.ReadSpan(groupMeta.HeaderLength)); if (!alignmentRules.GroupAlignment.TryGetValue(groupMeta.GroupType, out var groupRules)) { continue; } var storage = new Dictionary <RecordType, List <ReadOnlyMemorySlice <byte> > >(); var rest = new List <ReadOnlyMemorySlice <byte> >(); using (var frame = MutagenFrame.ByLength(inputStream, groupMeta.ContentLength)) { while (!frame.Complete) { var majorMeta = inputStream.GetMajorRecord(); var bytes = inputStream.ReadMemory(checked ((int)majorMeta.TotalLength)); var type = majorMeta.RecordType; if (groupRules.Contains(type)) { storage.GetOrAdd(type).Add(bytes); } else { rest.Add(bytes); } } } foreach (var rule in groupRules) { if (storage.TryGetValue(rule, out var storageBytes)) { foreach (var item in storageBytes) { writer.Write(item); } } } foreach (var item in rest) { writer.Write(item); } } }
/// <summary> /// Decompresses mod stream into an output. /// Will open up two input streams, so a Func factory is given as input. /// </summary> /// <param name="streamCreator">A func to create an input stream</param> /// <param name="outputStream">Stream to write output to</param> /// <param name="interest">Optional specification of which record types to process</param> public static void Decompress( Func <IMutagenReadStream> streamCreator, Stream outputStream, RecordInterest?interest = null) { using var inputStream = streamCreator(); using var inputStreamJumpback = streamCreator(); using var writer = new System.IO.BinaryWriter(outputStream, Encoding.Default, leaveOpen: true); long runningDiff = 0; var fileLocs = RecordLocator.GetFileLocations( inputStream, interest: interest, additionalCriteria: (stream, recType, len) => { return(stream.GetMajorRecord().IsCompressed); }); // Construct group length container for later use Dictionary <long, (uint Length, long Offset)> grupMeta = new Dictionary <long, (uint Length, long Offset)>(); inputStream.Position = 0; while (!inputStream.Complete) { // Import until next listed major record long noRecordLength; if (fileLocs.ListedRecords.TryGetInDirection( inputStream.Position, higher: true, result: out var nextRec)) { var recordLocation = fileLocs.ListedRecords.Keys[nextRec.Key]; noRecordLength = recordLocation - inputStream.Position; } else { noRecordLength = inputStream.Length - inputStream.Position; } inputStream.WriteTo(outputStream, (int)noRecordLength); // If complete overall, return if (inputStream.Complete) { break; } var majorMeta = inputStream.ReadMajorRecord(readSafe: true); var len = majorMeta.ContentLength; using (var frame = MutagenFrame.ByLength( reader: inputStream, length: len)) { // Decompress var decompressed = frame.Decompress(); var decompressedLen = decompressed.TotalLength; var lengthDiff = decompressedLen - len; var majorMetaSpan = majorMeta.HeaderData.ToArray(); // Write major Meta var writableMajorMeta = inputStream.MetaData.Constants.MajorRecordWritable(majorMetaSpan.AsSpan()); writableMajorMeta.IsCompressed = false; writableMajorMeta.ContentLength = (uint)(len + lengthDiff); writer.Write(majorMetaSpan); writer.Write(decompressed.ReadRemainingSpan(readSafe: false)); // If no difference in lengths, move on if (lengthDiff == 0) { continue; } // Modify parent group lengths foreach (var grupLoc in fileLocs.GetContainingGroupLocations(nextRec.Value.FormKey)) { if (!grupMeta.TryGetValue(grupLoc, out var loc)) { loc.Offset = runningDiff; inputStreamJumpback.Position = grupLoc + 4; loc.Length = inputStreamJumpback.ReadUInt32(); } grupMeta[grupLoc] = ((uint)(loc.Length + lengthDiff), loc.Offset); } runningDiff += lengthDiff; } } foreach (var item in grupMeta) { var grupLoc = item.Key; outputStream.Position = grupLoc + 4 + item.Value.Offset; writer.Write(item.Value.Length); } }