public static long Recreate(IStorage fragmentMeta, FileStream writer, IFileSystem newBaseFolderFs) { var Segments = new List<DeltaFragmentSegment>(); if (fragmentMeta.GetSize() < 0x40) { throw new InvalidDataException("Delta file is too small."); } var magic = new byte[4]; fragmentMeta.Read(magic, 0, 4, 0); var reader = new FileReader(new StorageFile(fragmentMeta, OpenMode.Read)); if (Utils.ArraysEqual(magic, DeltaTools.LCA3Macic)) { reader.Position = DeltaTools.LCA3Macic.Length; var linkedNcaFilenameSize = reader.ReadUInt8(); var linkedNcafilename = Encoding.ASCII.GetString(reader.ReadBytes(DeltaTools.LCA3Macic.Length + 1, linkedNcaFilenameSize, true)); var newLinkedFile = newBaseFolderFs.OpenFile(linkedNcafilename, OpenMode.Read).AsStream(); newLinkedFile.CopyStream(writer, newLinkedFile.Length); return reader.Position; } var Header = new DeltaFragmentHeader(new StorageFile(fragmentMeta, OpenMode.Read)); reader.Position = 0; if (Header.Magic == DeltaTools.Tdv0Magic) { var headerData = reader.ReadBytes(0, (int) Header.FragmentHeaderSize, true); headerData[0] = 0x4E; //N (TDV0 to NDV0) writer.Write(headerData, 0, (int) Header.FragmentHeaderSize); } else if (Header.Magic == DeltaTools.Cdv0Magic) { reader.Position = 4; } else { throw new InvalidDataException("TDV0/CDV0 magic value is missing."); } var pos = reader.Position; var baseNcaFilenameSize = reader.ReadUInt8(); var filenameOffset = Encoding.ASCII.GetString(reader.ReadBytes(pos + 1, baseNcaFilenameSize, true)).Split(':'); var newBaseFile = newBaseFolderFs.OpenFile(filenameOffset[0], OpenMode.Read).AsStream(); long offset = 0; var endOffset = Header.NewSize; if (filenameOffset.Length > 2) { offset = long.Parse(filenameOffset[1], NumberStyles.HexNumber); endOffset = long.Parse(filenameOffset[2], NumberStyles.HexNumber); } const int maxBS = 10485760; //10 MB int bs; var FragmentBlock = new byte[maxBS]; while (offset < endOffset) { Console.WriteLine($"ReadSegmentHeader on {offset}"); ReadSegmentHeader(reader, writer, out var size, out var seek); Console.WriteLine($"size: {size}, seek: {seek}, readerPos: {reader.Position}, writerPos: {writer.Position}"); if (seek > 0) { var segment = new DeltaFragmentSegment { SourceOffset = offset, Size = seek, IsInOriginal = true }; Segments.Add(segment); offset += seek; } if (size > 0) { var segment = new DeltaFragmentSegment { SourceOffset = reader.Position, Size = size, IsInOriginal = false }; newBaseFile.Position = offset; var fragmentOffsetEnd = offset + segment.Size; while (newBaseFile.Position < fragmentOffsetEnd) { bs = (int) Math.Min(fragmentOffsetEnd - newBaseFile.Position, maxBS); newBaseFile.Read(FragmentBlock, 0, bs); writer.Write(FragmentBlock, 0, bs); } Segments.Add(segment); offset += size; } } newBaseFile.Dispose(); return reader.Position; }
public static long Save(IStorage delta, FileStream writer, string foundBaseNCA) { var filenameOffset = foundBaseNCA.Split(':'); if (delta.GetSize() < 0x40) { throw new InvalidDataException("Delta file is too small."); } if (foundBaseNCA.Length > 255) { throw new IndexOutOfRangeException("Base NCA filename isn't allowed to be longer then 255 characters"); } var Header = new DeltaFragmentHeader(new StorageFile(delta, OpenMode.Read)); var reader = new FileReader(new StorageFile(delta, OpenMode.Read)); reader.Position = 0; if (filenameOffset.Length == 1 && Header.Magic != DeltaTools.Ndv0Magic) { writer.Write(DeltaTools.LCA3Macic, 0, DeltaTools.LCA3Macic.Length); writer.WriteByte((byte)foundBaseNCA.Length); writer.Write(Encoding.ASCII.GetBytes(foundBaseNCA), 0, foundBaseNCA.Length); return(0); } if (Header.Magic == DeltaTools.Ndv0Magic) { var fragmentSize = Header.FragmentHeaderSize + Header.FragmentBodySize; //if (!isSplitNdv0 && delta.Length < fragmentSize) //{ // throw new InvalidDataException( // $"Delta file is smaller than the header indicates. (0x{fragmentSize} bytes)"); //} var headerData = reader.ReadBytes((int)Header.FragmentHeaderSize); headerData[0] = 0x54; //T (NDV0 to TDV0) writer.Write(headerData, 0, (int)Header.FragmentHeaderSize); } else { writer.Write(Encoding.ASCII.GetBytes(DeltaTools.Cdv0Magic), 0, DeltaTools.Cdv0Magic.Length); } writer.WriteByte((byte)foundBaseNCA.Length); writer.Write(Encoding.ASCII.GetBytes(foundBaseNCA), 0, foundBaseNCA.Length); var foundBaseNCAEndOffsetPos = foundBaseNCA.LastIndexOf(':') + 1; var foundBaseNCAEndOffsetLen = foundBaseNCA.Length - foundBaseNCAEndOffsetPos; var SplitNdv0EndOffsetPos = writer.Position - foundBaseNCAEndOffsetLen; long offset = 0; long deltaSize = delta.GetSize(); Console.WriteLine($"reader={reader.Position} writer={writer.Position}"); while (reader.Position < deltaSize) { ReadSegmentHeader(reader, writer, out var size, out var seek); if (seek > 0) { offset += seek; } if (size > 0) { offset += size; } reader.Position += size; } if (reader.Position == deltaSize) { if (filenameOffset.Length > 2) { var startOffset = long.Parse(filenameOffset[1], NumberStyles.HexNumber); var endOffset = startOffset + offset; var realEndOffset = endOffset.ToString($"X{foundBaseNCAEndOffsetLen}"); var posReal = writer.Position; writer.Position = SplitNdv0EndOffsetPos; writer.Write(Encoding.ASCII.GetBytes(realEndOffset), 0, realEndOffset.Length); writer.Position = posReal; } //Size of data untimmed in this function call return(offset); } throw new InvalidDataException("Fragment file seems to be corrupted!"); }