From 9ad8eccc18fda26cd4cddc708e108708ae65fa18 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Thu, 10 Dec 2020 22:44:10 -0400 Subject: [PATCH 1/6] Add initial Diff support, really bad, but it works for now. --- HSDLib.sln | 10 +- HSDRaw/HSDRaw.csproj | 2 +- HSDRaw/HSDRawFile.cs | 8 +- HSDRawViewer/App.config | 14 +- HSDRawViewer/Converters/TOBJConverter.cs | 65 +- HSDRawViewer/HSDRawViewer.csproj | 4 + HSDRawViewer/MainForm.Designer.cs | 24 + HSDRawViewer/MainForm.cs | 150 +++-- HSDRawViewer/Properties/Resources.Designer.cs | 2 +- HSDRawViewer/Properties/Settings.Designer.cs | 2 +- VCDiff/.gitattributes | 17 + VCDiff/.gitignore | 303 ++++++++++ VCDiff/Decoders/BodyDecoder.cs | 363 ++++++++++++ VCDiff/Decoders/CustomCodeTableDecoder.cs | 106 ++++ VCDiff/Decoders/InstructionDecoder.cs | 105 ++++ VCDiff/Decoders/VCDecoder.cs | 233 ++++++++ VCDiff/Decoders/WindowDecoder.cs | 557 ++++++++++++++++++ VCDiff/Encoders/BlockHash.cs | 447 ++++++++++++++ VCDiff/Encoders/ChunkEncoder.cs | 182 ++++++ VCDiff/Encoders/InstructionMap.cs | 178 ++++++ VCDiff/Encoders/RollingHash.cs | 126 ++++ VCDiff/Encoders/VCCoder.cs | 105 ++++ VCDiff/Encoders/WindowEncoder.cs | 290 +++++++++ VCDiff/Includes/Include.cs | 153 +++++ VCDiff/Properties/AssemblyInfo.cs | 36 ++ VCDiff/README.markdown | 134 +++++ VCDiff/Shared/AddressCache.cs | 281 +++++++++ VCDiff/Shared/Adler32.cs | 139 +++++ VCDiff/Shared/ByteBuffer.cs | 127 ++++ VCDiff/Shared/ByteStreamReader.cs | 229 +++++++ VCDiff/Shared/ByteStreamWriter.cs | 110 ++++ VCDiff/Shared/Checksum.cs | 21 + VCDiff/Shared/CodeTable.cs | 271 +++++++++ VCDiff/Shared/IByteBuffer.cs | 35 ++ VCDiff/Shared/VarIntBE.cs | 192 ++++++ VCDiff/VCDiff.csproj | 76 +++ VCDiff/VCDiff.dll | Bin 0 -> 40960 bytes VCDiff/VCDiff.sln | 22 + 38 files changed, 5041 insertions(+), 78 deletions(-) create mode 100644 VCDiff/.gitattributes create mode 100644 VCDiff/.gitignore create mode 100644 VCDiff/Decoders/BodyDecoder.cs create mode 100644 VCDiff/Decoders/CustomCodeTableDecoder.cs create mode 100644 VCDiff/Decoders/InstructionDecoder.cs create mode 100644 VCDiff/Decoders/VCDecoder.cs create mode 100644 VCDiff/Decoders/WindowDecoder.cs create mode 100644 VCDiff/Encoders/BlockHash.cs create mode 100644 VCDiff/Encoders/ChunkEncoder.cs create mode 100644 VCDiff/Encoders/InstructionMap.cs create mode 100644 VCDiff/Encoders/RollingHash.cs create mode 100644 VCDiff/Encoders/VCCoder.cs create mode 100644 VCDiff/Encoders/WindowEncoder.cs create mode 100644 VCDiff/Includes/Include.cs create mode 100644 VCDiff/Properties/AssemblyInfo.cs create mode 100644 VCDiff/README.markdown create mode 100644 VCDiff/Shared/AddressCache.cs create mode 100644 VCDiff/Shared/Adler32.cs create mode 100644 VCDiff/Shared/ByteBuffer.cs create mode 100644 VCDiff/Shared/ByteStreamReader.cs create mode 100644 VCDiff/Shared/ByteStreamWriter.cs create mode 100644 VCDiff/Shared/Checksum.cs create mode 100644 VCDiff/Shared/CodeTable.cs create mode 100644 VCDiff/Shared/IByteBuffer.cs create mode 100644 VCDiff/Shared/VarIntBE.cs create mode 100644 VCDiff/VCDiff.csproj create mode 100644 VCDiff/VCDiff.dll create mode 100644 VCDiff/VCDiff.sln diff --git a/HSDLib.sln b/HSDLib.sln index f6861bbe..44f38ed7 100644 --- a/HSDLib.sln +++ b/HSDLib.sln @@ -1,12 +1,14 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27004.2005 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30406.217 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HSDRawViewer", "HSDRawViewer\HSDRawViewer.csproj", "{755580A7-D09D-4EEC-9129-2DD7A6951A31}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "HSDRaw", "HSDRaw\HSDRaw.csproj", "{ED9126AF-8A1E-465D-840A-8754A945414D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VCDiff", "VCDiff\VCDiff.csproj", "{4D676F4E-E66D-4A41-861F-600F0FFCD052}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {ED9126AF-8A1E-465D-840A-8754A945414D}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED9126AF-8A1E-465D-840A-8754A945414D}.Release|Any CPU.ActiveCfg = Release|Any CPU {ED9126AF-8A1E-465D-840A-8754A945414D}.Release|Any CPU.Build.0 = Release|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/HSDRaw/HSDRaw.csproj b/HSDRaw/HSDRaw.csproj index e0154c09..25a76097 100644 --- a/HSDRaw/HSDRaw.csproj +++ b/HSDRaw/HSDRaw.csproj @@ -1,4 +1,4 @@ - + netstandard2.0 diff --git a/HSDRaw/HSDRawFile.cs b/HSDRaw/HSDRawFile.cs index 2b107ea9..6f87652e 100644 --- a/HSDRaw/HSDRawFile.cs +++ b/HSDRaw/HSDRawFile.cs @@ -404,9 +404,9 @@ public static int BinarySearch(List a, int item) /// Saves dat data to filepath /// /// - public void Save(string fileName, bool bufferAlign = true, bool optimize = true, bool trim = false) + public Stream Save(string fileName, bool bufferAlign = true, bool optimize = true, bool trim = false) { - Save(new FileStream(fileName, FileMode.Create), bufferAlign, optimize, trim); + return Save(new FileStream(fileName, FileMode.Create), bufferAlign, optimize, trim); } /// @@ -561,7 +561,7 @@ public void SetStructFlags() /// /// /// - public void Save(Stream stream, bool bufferAlign = true, bool optimize = true, bool trim = false) + public Stream Save(Stream stream, bool bufferAlign = true, bool optimize = true, bool trim = false) { if (Roots.Count > 0 && Roots[0].Data is MEX_Data) bufferAlign = false; @@ -711,7 +711,9 @@ public void Save(Stream stream, bool bufferAlign = true, bool optimize = true, b writer.Write(Roots.Count); writer.Write(References.Count); writer.Write(VersionChars); + } + return stream; } diff --git a/HSDRawViewer/App.config b/HSDRawViewer/App.config index 5e333e9e..d5b4a218 100644 --- a/HSDRawViewer/App.config +++ b/HSDRawViewer/App.config @@ -1,18 +1,18 @@ - + - + - + - - + + - - + + diff --git a/HSDRawViewer/Converters/TOBJConverter.cs b/HSDRawViewer/Converters/TOBJConverter.cs index d6d50311..ed13cff4 100644 --- a/HSDRawViewer/Converters/TOBJConverter.cs +++ b/HSDRawViewer/Converters/TOBJConverter.cs @@ -197,43 +197,50 @@ public static HSD_TOBJ ImportTOBJFromFile(string filePath, GXTexFmt imgFmt, GXTl /// public static HSD_TOBJ ImportTOBJFromFile() { - var TOBJ = new HSD_TOBJ() + try { - MagFilter = GXTexFilter.GX_LINEAR, - Flags = TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COLORMAP_MODULATE | TOBJ_FLAGS.ALPHAMAP_MODULATE, - HScale = 1, - WScale = 1, - WrapS = GXWrapMode.CLAMP, - WrapT = GXWrapMode.CLAMP, - SX = 1, - SY = 1, - SZ = 1, - GXTexGenSrc = 4, - Blending = 1 - }; - var f = Tools.FileIO.OpenFile(ApplicationSettings.ImageFileFilter); - if (f != null) - { - using (TextureImportDialog settings = new TextureImportDialog()) + var TOBJ = new HSD_TOBJ() { - if (FormatFromString(f, out GXTexFmt fmt, out GXTlutFmt pal)) + MagFilter = GXTexFilter.GX_LINEAR, + Flags = TOBJ_FLAGS.COORD_UV | TOBJ_FLAGS.LIGHTMAP_DIFFUSE | TOBJ_FLAGS.COLORMAP_MODULATE | TOBJ_FLAGS.ALPHAMAP_MODULATE, + HScale = 1, + WScale = 1, + WrapS = GXWrapMode.CLAMP, + WrapT = GXWrapMode.CLAMP, + SX = 1, + SY = 1, + SZ = 1, + GXTexGenSrc = 4, + Blending = 1 + }; + + var f = Tools.FileIO.OpenFile(ApplicationSettings.ImageFileFilter); + if (f != null) + { + using (TextureImportDialog settings = new TextureImportDialog()) { - settings.PaletteFormat = pal; - settings.TextureFormat = fmt; - } - - if (settings.ShowDialog() == DialogResult.OK) - using (Bitmap bmp = new Bitmap(f)) + if (FormatFromString(f, out GXTexFmt fmt, out GXTlutFmt pal)) { - settings.ApplySettings(bmp); - InjectBitmap(bmp, TOBJ, settings.TextureFormat, settings.PaletteFormat); - return TOBJ; + settings.PaletteFormat = pal; + settings.TextureFormat = fmt; } + + if (settings.ShowDialog() == DialogResult.OK) + using (Bitmap bmp = new Bitmap(f)) + { + settings.ApplySettings(bmp); + InjectBitmap(bmp, TOBJ, settings.TextureFormat, settings.PaletteFormat); + return TOBJ; + } + } } - } - return null; + return null; + } catch (Exception e) + { + return null; + } } /// diff --git a/HSDRawViewer/HSDRawViewer.csproj b/HSDRawViewer/HSDRawViewer.csproj index f3069a5e..0863b9f7 100644 --- a/HSDRawViewer/HSDRawViewer.csproj +++ b/HSDRawViewer/HSDRawViewer.csproj @@ -799,6 +799,10 @@ {ed9126af-8a1e-465d-840a-8754a945414d} HSDRaw + + {4d676f4e-e66d-4a41-861f-600f0ffcd052} + VCDiff + diff --git a/HSDRawViewer/MainForm.Designer.cs b/HSDRawViewer/MainForm.Designer.cs index e2990918..f48b6c41 100644 --- a/HSDRawViewer/MainForm.Designer.cs +++ b/HSDRawViewer/MainForm.Designer.cs @@ -35,6 +35,8 @@ private void InitializeComponent() this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.loadDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveAsUnoptimizedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addRootFromFileToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); @@ -97,6 +99,8 @@ private void InitializeComponent() this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.openToolStripMenuItem, this.saveToolStripMenuItem1, + this.saveDiffToolStripMenuItem, + this.loadDiffToolStripMenuItem, this.saveToolStripMenuItem, this.saveAsUnoptimizedToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; @@ -119,6 +123,24 @@ private void InitializeComponent() this.saveToolStripMenuItem1.Text = "Save"; this.saveToolStripMenuItem1.Click += new System.EventHandler(this.saveToolStripMenuItem1_Click); // + // saveDiffToolStripMenuItem + // + this.saveDiffToolStripMenuItem.Name = "saveDiffToolStripMenuItem"; + this.saveDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) + | System.Windows.Forms.Keys.D))); + this.saveDiffToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.saveDiffToolStripMenuItem.Text = "Save Diff To File"; + this.saveDiffToolStripMenuItem.Click += new System.EventHandler(this.saveDiffToolStripMenuItem_Click); + // + // loadDiffToolStripMenuItem + // + this.loadDiffToolStripMenuItem.Name = "loadDiffToolStripMenuItem"; + this.loadDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) + | System.Windows.Forms.Keys.L))); + this.loadDiffToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.loadDiffToolStripMenuItem.Text = "Load Diff To File"; + this.loadDiffToolStripMenuItem.Click += new System.EventHandler(this.loadDiffToolStripMenuItem_Click); + // // saveToolStripMenuItem // this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; @@ -339,6 +361,8 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem saveToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveDiffToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem loadDiffToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem addRootFromFileToolStripMenuItem; private System.Windows.Forms.Label LocationLabel; diff --git a/HSDRawViewer/MainForm.cs b/HSDRawViewer/MainForm.cs index d2ed626d..4cf44f07 100644 --- a/HSDRawViewer/MainForm.cs +++ b/HSDRawViewer/MainForm.cs @@ -9,16 +9,21 @@ using HSDRaw.Common.Animation; using HSDRawViewer.GUI.Extra; using System.ComponentModel; +using System.IO; +using VCDiff.Encoders; +using VCDiff.Includes; +using VCDiff.Decoders; namespace HSDRawViewer { public partial class MainForm : DockContent { /// - /// + /// /// public static MainForm Instance { get; internal set; } + private const string DIFFF = ".diff"; private PropertyView _nodePropertyViewer; public CommonViewport Viewport { get; internal set; } private SubactionEditor _ScriptEditor; @@ -45,7 +50,7 @@ public int GetStructLocation(HSDStruct str) { return RawHSDFile.GetOffsetFromStruct(str); } - + public MainForm() { InitializeComponent(); @@ -58,7 +63,7 @@ public MainForm() _nodePropertyViewer = new PropertyView(); _nodePropertyViewer.Dock = DockStyle.Fill; _nodePropertyViewer.Show(dockPanel); - + //dockPanel.ShowDocumentIcon = true; dockPanel.ActiveContentChanged += (sender, args) => { @@ -170,7 +175,7 @@ public MainForm() } /// - /// + /// /// /// public void SelectNode(T cast = null) where T : HSDAccessor @@ -194,7 +199,7 @@ public void SelectNode(T cast = null) where T : HSDAccessor } /// - /// + /// /// /// public static void DeleteRoot(DataNode root) @@ -208,7 +213,7 @@ public static void DeleteRoot(DataNode root) } /// - /// + /// /// /// public void OpenFile(string filePath) @@ -240,7 +245,7 @@ public void OpenFile(string filePath) } /// - /// + /// /// /// /// @@ -274,7 +279,7 @@ private void openToolStripMenuItem_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -288,8 +293,85 @@ private void saveToolStripMenuItem_Click(object sender, EventArgs e) } } + /// - /// + /// + /// + /// + /// + private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) + { + + var originalFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + var modifiedFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + var diffFileName = Tools.FileIO.SaveFile("HSD Diff(*.dat.diff,*.usd.diff,*.ssm.diff,*.sem.diff)|*.dat.diff;*.usd.diff;*.ssm.diff;*.sem.diff"); + + if (originalFileName != null && modifiedFileName != null) + { + + + using (FileStream origStream = new FileStream(originalFileName, FileMode.Open, FileAccess.Read)) + using (FileStream modifiedStream = new FileStream(modifiedFileName, FileMode.Open, FileAccess.Read)) + using (FileStream diffStream = new FileStream(diffFileName, FileMode.Create, FileAccess.Write)) + { + + VCCoder coder = new VCCoder(origStream, modifiedStream, diffStream); + VCDiffResult result = coder.Encode(); //encodes with no checksum and not interleaved + if (result != VCDiffResult.SUCCESS) + { + //error was not able to encode properly + } + } + + + } + } + + + /// + /// + /// + /// + /// + private void loadDiffToolStripMenuItem_Click(object sender, EventArgs e) + { + + var originalFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + var diffFileName = Tools.FileIO.OpenFile("HSD Diff(*.dat.diff,*.usd.diff,*.ssm.diff,*.sem.diff)|*.dat.diff;*.usd.diff;*.ssm.diff;*.sem.diff"); + var mergedFileName = Tools.FileIO.SaveFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + + if (originalFileName != null && diffFileName != null) + { + + + using (FileStream origStream = new FileStream(originalFileName, FileMode.Open, FileAccess.Read)) + using (FileStream modifiedStream = new FileStream(diffFileName, FileMode.Open, FileAccess.Read)) + using (FileStream mergedStream = new FileStream(mergedFileName, FileMode.Create, FileAccess.Write)) + { + + VCDecoder decoder = new VCDecoder(origStream, modifiedStream, mergedStream); + VCDiffResult result = decoder.Start(); //encodes with no checksum and not interleaved + if (result != VCDiffResult.SUCCESS) + { + //error was not able to encode properly + } else + { + long bytesWritten = 0; + result = decoder.Decode(out bytesWritten); + + if(result != VCDiffResult.SUCCESS) + { + + } + } + } + + + } + } + + /// + /// /// public void SaveDAT() { @@ -298,7 +380,7 @@ public void SaveDAT() } /// - /// + /// /// /// /// @@ -341,7 +423,7 @@ private void RefreshTree() } /// - /// + /// /// /// /// @@ -421,7 +503,7 @@ public bool IsOpened(DataNode n) } /// - /// + /// /// /// /// @@ -439,7 +521,7 @@ public bool IsChildOpened(HSDStruct s) } /// - /// + /// /// /// /// @@ -511,7 +593,7 @@ public void OpenEditor() } /// - /// + /// /// /// public void TryClose(Control c) @@ -528,7 +610,7 @@ public void TryClose(Control c) /// - /// + /// /// /// /// @@ -578,7 +660,7 @@ private void MainForm_Load(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -589,9 +671,9 @@ private void aJToolToolStripMenuItem_Click(object sender, EventArgs e) d.ShowDialog(); } } - + /// - /// + /// /// /// /// @@ -603,7 +685,7 @@ private void sSMEditorToolStripMenuItem_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -616,7 +698,7 @@ private void MainForm_DragEnter(object sender, DragEventArgs e) } /// - /// + /// /// /// /// @@ -631,7 +713,7 @@ private void MainForm_DragDrop(object sender, DragEventArgs e) } /// - /// + /// /// /// /// @@ -645,7 +727,7 @@ private void saveToolStripMenuItem1_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -658,7 +740,7 @@ private void sEMEditorToolStripMenuItem_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -671,7 +753,7 @@ private void treeView1_KeyDown(object sender, KeyEventArgs e) } /// - /// + /// /// /// /// @@ -692,11 +774,11 @@ public class NewRootSettings /*[Browsable(true), TypeConverter(typeof(HSDTypeConverter))] public Type Type { get; set; } = typeof(HSDAccessor);*/ - + } /// - /// + /// /// /// /// @@ -726,7 +808,7 @@ private void addRootFromTypeToolStripMenuItem_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -749,7 +831,7 @@ private void addRootFromFileToolStripMenuItem1_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -763,7 +845,7 @@ private void treeView1_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e) } /// - /// + /// /// /// /// @@ -774,9 +856,9 @@ private void treeView1_AfterLabelEdit(object sender, NodeLabelEditEventArgs e) d.RootText = e.Label; } } - + /// - /// + /// /// /// /// @@ -786,7 +868,7 @@ private void selectAudioPlaybackDeviceToolStripMenuItem_Click(object sender, Eve } /// - /// + /// /// /// /// @@ -802,7 +884,7 @@ private void trimExcessDataToolStripMenuItem_Click(object sender, EventArgs e) } /// - /// + /// /// /// /// @@ -815,5 +897,5 @@ public HSDAccessor GetSymbol(string symbol) return null; } } - + } diff --git a/HSDRawViewer/Properties/Resources.Designer.cs b/HSDRawViewer/Properties/Resources.Designer.cs index 4215bcf6..6d7657a9 100644 --- a/HSDRawViewer/Properties/Resources.Designer.cs +++ b/HSDRawViewer/Properties/Resources.Designer.cs @@ -19,7 +19,7 @@ namespace HSDRawViewer.Properties { // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Resources { diff --git a/HSDRawViewer/Properties/Settings.Designer.cs b/HSDRawViewer/Properties/Settings.Designer.cs index 22437304..c9c4f683 100644 --- a/HSDRawViewer/Properties/Settings.Designer.cs +++ b/HSDRawViewer/Properties/Settings.Designer.cs @@ -12,7 +12,7 @@ namespace HSDRawViewer.Properties { [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.7.0.0")] internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); diff --git a/VCDiff/.gitattributes b/VCDiff/.gitattributes new file mode 100644 index 00000000..bdb0cabc --- /dev/null +++ b/VCDiff/.gitattributes @@ -0,0 +1,17 @@ +# Auto detect text files and perform LF normalization +* text=auto + +# Custom for Visual Studio +*.cs diff=csharp + +# Standard to msysgit +*.doc diff=astextplain +*.DOC diff=astextplain +*.docx diff=astextplain +*.DOCX diff=astextplain +*.dot diff=astextplain +*.DOT diff=astextplain +*.pdf diff=astextplain +*.PDF diff=astextplain +*.rtf diff=astextplain +*.RTF diff=astextplain diff --git a/VCDiff/.gitignore b/VCDiff/.gitignore new file mode 100644 index 00000000..49f18f22 --- /dev/null +++ b/VCDiff/.gitignore @@ -0,0 +1,303 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# JetBrains Rider +.idea/ +*.sln.iml + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +# Windows +# ========================= + +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msm +*.msp + +# Windows shortcuts +*.lnk diff --git a/VCDiff/Decoders/BodyDecoder.cs b/VCDiff/Decoders/BodyDecoder.cs new file mode 100644 index 00000000..5d6eeae1 --- /dev/null +++ b/VCDiff/Decoders/BodyDecoder.cs @@ -0,0 +1,363 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; +using System.IO; + +namespace VCDiff.Decoders +{ + public class BodyDecoder : IDisposable + { + WindowDecoder window; + ByteStreamWriter sout; + IByteBuffer dict; + IByteBuffer target; + AddressCache addressCache; + long decodedOnly = 0; + long bytesWritten = 0; + List targetData; + CustomCodeTableDecoder customTable; + + //the total bytes decoded + public long Decoded + { + get + { + return decodedOnly; + } + } + + /// + /// The main decoder loop for the data + /// + /// the window decoder + /// the dictionary data + /// the target data + /// the out stream + /// custom table if any. Default is null. + public BodyDecoder(WindowDecoder w, IByteBuffer dictionary, IByteBuffer target, ByteStreamWriter sout, CustomCodeTableDecoder customTable = null) + { + if (customTable != null) + { + this.customTable = customTable; + addressCache = new AddressCache((byte)customTable.NearSize, (byte)customTable.SameSize); + } + else + { + addressCache = new AddressCache(); + } + window = w; + this.sout = sout; + this.dict = dictionary; + this.target = target; + targetData = new List(); + } + + /// + /// Decode if as expecting interleave + /// + /// + public VCDiffResult DecodeInterleave() + { + VCDiffResult result = VCDiffResult.SUCCESS; + //since interleave expected then the last point that was most likely decoded was the lengths section + //so following is all data for the add run copy etc + long interleaveLength = window.InstructionAndSizesLength; + List previous = new List(); + bool didBreakBeforeComplete = false; + int lastDecodedSize = 0; + VCDiffInstructionType lastDecodedInstruction = VCDiffInstructionType.NOOP; + + while (interleaveLength > 0) + { + if (target.CanRead) { + //read in + didBreakBeforeComplete = false; + + //try to read in all interleaved bytes + //if not then it will buffer for next time + previous.AddRange(target.ReadBytes((int)interleaveLength)); + ByteBuffer incoming = new ByteBuffer(previous.ToArray()); + previous.Clear(); + long initialLength = incoming.Length; + + InstructionDecoder instrDecoder = new InstructionDecoder(incoming, this.customTable); + + while(incoming.CanRead && decodedOnly < window.DecodedDeltaLength) + { + int decodedSize = 0; + byte mode = 0; + VCDiffInstructionType instruction = VCDiffInstructionType.NOOP; + + if (lastDecodedSize > 0 && lastDecodedInstruction != VCDiffInstructionType.NOOP) + { + decodedSize = lastDecodedSize; + instruction = lastDecodedInstruction; + } + else { + instruction = instrDecoder.Next(out decodedSize, out mode); + + switch (instruction) + { + case VCDiffInstructionType.EOD: + didBreakBeforeComplete = true; + break; + case VCDiffInstructionType.ERROR: + targetData.Clear(); + return VCDiffResult.ERRROR; + default: + break; + } + } + + //if instruction is EOD then decodedSize will be 0 as well + //the last part of the buffer containing the instruction will be + //buffered for the next loop + lastDecodedInstruction = instruction; + lastDecodedSize = decodedSize; + + if (didBreakBeforeComplete) + { + //we don't have all the data so store this pointer into a temporary list to resolve next loop + didBreakBeforeComplete = true; + interleaveLength -= incoming.Position; + + if (initialLength - incoming.Position > 0) + { + previous.AddRange(incoming.ReadBytes((int)(initialLength - incoming.Position))); + } + + break; + } + + switch (instruction) + { + case VCDiffInstructionType.ADD: + result = DecodeAdd(decodedSize, incoming); + break; + case VCDiffInstructionType.RUN: + result = DecodeRun(decodedSize, incoming); + break; + case VCDiffInstructionType.COPY: + result = DecodeCopy(decodedSize, mode, incoming); + break; + default: + targetData.Clear(); + return VCDiffResult.ERRROR; + } + + if (result == VCDiffResult.EOD) + { + //we don't have all the data so store this pointer into a temporary list to resolve next loop + didBreakBeforeComplete = true; + interleaveLength -= incoming.Position; + + if (initialLength - incoming.Position > 0) + { + previous.AddRange(incoming.ReadBytes((int)(initialLength - incoming.Position))); + } + + break; + } + + //reset these as we have successfully used them + lastDecodedInstruction = VCDiffInstructionType.NOOP; + lastDecodedSize = 0; + } + + if(!didBreakBeforeComplete) + { + interleaveLength -= initialLength; + } + } + } + + if (window.HasChecksum) + { + uint adler = Checksum.ComputeAdler32(targetData.ToArray()); + + if (adler != window.Checksum) + { + result = VCDiffResult.ERRROR; + } + } + + targetData.Clear(); + return result; + } + + /// + /// Decode normally + /// + /// + public VCDiffResult Decode() + { + ByteBuffer instructionBuffer = new ByteBuffer(window.InstructionsAndSizesData); + ByteBuffer addressBuffer = new ByteBuffer(window.AddressesForCopyData); + ByteBuffer addRunBuffer = new ByteBuffer(window.AddRunData); + + InstructionDecoder instrDecoder = new InstructionDecoder(instructionBuffer, this.customTable); + + VCDiffResult result = VCDiffResult.SUCCESS; + + while (decodedOnly < window.DecodedDeltaLength && instructionBuffer.CanRead) + { + int decodedSize = 0; + byte mode = 0; + + VCDiffInstructionType instruction = instrDecoder.Next(out decodedSize, out mode); + + switch(instruction) + { + case VCDiffInstructionType.EOD: + targetData.Clear(); + return VCDiffResult.EOD; + case VCDiffInstructionType.ERROR: + targetData.Clear(); + return VCDiffResult.ERRROR; + default: + break; + } + + switch(instruction) + { + case VCDiffInstructionType.ADD: + result = DecodeAdd(decodedSize, addRunBuffer); + break; + case VCDiffInstructionType.RUN: + result = DecodeRun(decodedSize, addRunBuffer); + break; + case VCDiffInstructionType.COPY: + result = DecodeCopy(decodedSize, mode, addressBuffer); + break; + default: + targetData.Clear(); + return VCDiffResult.ERRROR; + } + } + + if(window.HasChecksum) + { + uint adler = Checksum.ComputeAdler32(targetData.ToArray()); + + if(adler != window.Checksum) + { + result = VCDiffResult.ERRROR; + } + } + + targetData.Clear(); + return result; + } + + VCDiffResult DecodeCopy(int size, byte mode, ByteBuffer addresses) + { + long here = window.SourceLength + decodedOnly; + long decoded = addressCache.DecodeAddress(here, mode, addresses); + + switch((VCDiffResult)decoded) + { + case VCDiffResult.ERRROR: + return VCDiffResult.ERRROR; + case VCDiffResult.EOD: + return VCDiffResult.EOD; + default: + if(decoded < 0 || decoded > here) + { + return VCDiffResult.ERRROR; + } + break; + } + + if(decoded + size <= window.SourceLength) + { + dict.Position = decoded; + byte[] rbytes = dict.ReadBytes(size); + sout.writeBytes(rbytes); + targetData.AddRange(rbytes); + decodedOnly += size; + return VCDiffResult.SUCCESS; + } + + ///will come back to this once + ///target data reading is implemented + /*if(decoded < window.SourceLength) + { + long partial = window.SourceLength - decoded; + dict.Position = decoded; + sout.writeBytes(dict.ReadBytes((int)partial)); + bytesWritten += partial; + size -= (int)partial; + } + + decoded -= window.SourceLength; + + while(size > (bytesDecoded - decoded)) + { + long partial = bytesDecoded - decoded; + target.Position = decoded; + sout.writeBytes(target.ReadBytes((int)partial)); + decoded += partial; + size -= (int)partial; + bytesWritten += partial; + } + + target.Position = decoded; + sout.writeBytes(target.ReadBytes(size));*/ + + return VCDiffResult.ERRROR; + } + + VCDiffResult DecodeRun(int size, ByteBuffer addRun) + { + if(addRun.Position + 1 > addRun.Length) + { + return VCDiffResult.EOD; + } + + if(!addRun.CanRead) + { + return VCDiffResult.EOD; + } + + byte b = addRun.ReadByte(); + + for(int i = 0; i < size; i++) + { + sout.writeByte(b); + targetData.Add(b); + } + + decodedOnly += size; + + return VCDiffResult.SUCCESS; + } + + VCDiffResult DecodeAdd(int size, ByteBuffer addRun) + { + if(addRun.Position + size > addRun.Length) + { + return VCDiffResult.EOD; + } + + if(!addRun.CanRead) + { + return VCDiffResult.EOD; + } + + byte[] rbytes = addRun.ReadBytes(size); + sout.writeBytes(rbytes); + targetData.AddRange(rbytes); + decodedOnly += size; + return VCDiffResult.SUCCESS; + } + + public void Dispose() + { + targetData.Clear(); + } + } +} diff --git a/VCDiff/Decoders/CustomCodeTableDecoder.cs b/VCDiff/Decoders/CustomCodeTableDecoder.cs new file mode 100644 index 00000000..a9c0bd92 --- /dev/null +++ b/VCDiff/Decoders/CustomCodeTableDecoder.cs @@ -0,0 +1,106 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Includes; +using VCDiff.Shared; +using System.IO; + +namespace VCDiff.Decoders +{ + public class CustomCodeTableDecoder + { + byte nearSize; + byte sameSize; + CodeTable table; + + public byte NearSize + { + get + { + return nearSize; + } + } + + public byte SameSize + { + get + { + return sameSize; + } + } + + public CodeTable CustomTable + { + get + { + return table; + } + } + + public CustomCodeTableDecoder() + { + + } + + public VCDiffResult Decode(IByteBuffer source) + { + VCDiffResult result = VCDiffResult.SUCCESS; + + //the custom codetable itself is a VCDiff file but it is required to be encoded with the standard table + //the length should be the first thing after the hdr_indicator if not supporting compression + //at least according to the RFC specs. + int lengthOfCodeTable = VarIntBE.ParseInt32(source); + + if (lengthOfCodeTable == 0) return VCDiffResult.ERRROR; + + ByteBuffer codeTable = new ByteBuffer(source.ReadBytes(lengthOfCodeTable)); + + //according to the RFC specifications the next two items will be the size of near and size of same + //they are bytes in the RFC spec, but for some reason Google uses the varint to read which does + //the same thing if it is a single byte + //but I am going to just read in bytes because it is the RFC standard + nearSize = codeTable.ReadByte(); + sameSize = codeTable.ReadByte(); + + if(nearSize == 0 || sameSize == 0 || nearSize > byte.MaxValue || sameSize > byte.MaxValue) + { + return VCDiffResult.ERRROR; + } + + table = new CodeTable(); + //get the original bytes of the default codetable to use as a dictionary + IByteBuffer dictionary = table.GetBytes(); + + //Decode the code table VCDiff file itself + //stream the decoded output into a memory stream + using(MemoryStream sout = new MemoryStream()) + { + VCDecoder decoder = new VCDecoder(dictionary, codeTable, sout); + result = decoder.Start(); + + if(result != VCDiffResult.SUCCESS) + { + return result; + } + + long bytesWritten = 0; + result = decoder.Decode(out bytesWritten); + + if(result != VCDiffResult.SUCCESS || bytesWritten == 0) + { + return VCDiffResult.ERRROR; + } + + //set the new table data that was decoded + if(!table.SetBytes(sout.ToArray())) + { + result = VCDiffResult.ERRROR; + } + } + + return result; + } + } +} diff --git a/VCDiff/Decoders/InstructionDecoder.cs b/VCDiff/Decoders/InstructionDecoder.cs new file mode 100644 index 00000000..67baf614 --- /dev/null +++ b/VCDiff/Decoders/InstructionDecoder.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; + +namespace VCDiff.Decoders +{ + public class InstructionDecoder + { + CodeTable table; + ByteBuffer source; + int pendingSecond; + + /// + /// Decodes the incoming instruction from the buffer + /// + /// the instruction buffer + /// custom code table if any. Default is null. + public InstructionDecoder(ByteBuffer sin, CustomCodeTableDecoder customTable = null) + { + if (customTable != null) + { + table = customTable.CustomTable; + } + else + { + table = CodeTable.DefaultTable; + } + source = sin; + pendingSecond = CodeTable.kNoOpcode; + } + + /// + /// Gets the next instruction from the buffer + /// + /// the size + /// the mode + /// + public VCDiffInstructionType Next(out int size, out byte mode) + { + byte opcode = 0; + byte instructionType = CodeTable.N; + int instructionSize = 0; + byte instructionMode = 0; + int start = (int)source.Position; + do + { + if (pendingSecond != CodeTable.kNoOpcode) + { + opcode = (byte)pendingSecond; + pendingSecond = CodeTable.kNoOpcode; + instructionType = table.inst2[opcode]; + instructionSize = table.size2[opcode]; + instructionMode = table.mode2[opcode]; + break; + } + + if (!source.CanRead) + { + size = 0; + mode = 0; + return VCDiffInstructionType.EOD; + } + + opcode = source.PeekByte(); + if (table.inst2[opcode] != CodeTable.N) + { + pendingSecond = source.PeekByte(); + } + source.Next(); + instructionType = table.inst1[opcode]; + instructionSize = table.size1[opcode]; + instructionMode = table.mode1[opcode]; + } while (instructionType == CodeTable.N); + + if (instructionSize == 0) + { + switch (size = VarIntBE.ParseInt32(source)) + { + case (int)VCDiffResult.ERRROR: + mode = 0; + size = 0; + return VCDiffInstructionType.ERROR; + case (int)VCDiffResult.EOD: + mode = 0; + size = 0; + //reset it back before we read the instruction + //otherwise when parsing interleave we will miss data + source.Position = start; + return VCDiffInstructionType.EOD; + default: + break; + } + } + else { + size = instructionSize; + } + mode = instructionMode; + return (VCDiffInstructionType)instructionType; + } + } +} diff --git a/VCDiff/Decoders/VCDecoder.cs b/VCDiff/Decoders/VCDecoder.cs new file mode 100644 index 00000000..9652dd7f --- /dev/null +++ b/VCDiff/Decoders/VCDecoder.cs @@ -0,0 +1,233 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; +using System.IO; + +namespace VCDiff.Decoders +{ + public class VCDecoder + { + ByteStreamWriter sout; + IByteBuffer delta; + IByteBuffer dict; + CustomCodeTableDecoder customTable; + bool googleVersion; + bool isStarted; + + static byte[] MagicBytes = new byte[] { 0xD6, 0xC3, 0xC4, 0x00, 0x00 }; + + public bool IsSDHCFormat + { + get + { + return googleVersion; + } + } + + public bool IsStarted + { + get + { + return isStarted; + } + } + + /// + /// Dict is the dictionary file + /// Delta is the diff file + /// Sout is the stream for output + /// + /// Dictionary + /// Target file / Diff / Delta file + /// Output Stream + public VCDecoder(Stream dict, Stream delta, Stream sout) + { + this.delta = new ByteStreamReader(delta); + this.dict = new ByteStreamReader(dict); + this.sout = new ByteStreamWriter(sout); + isStarted = false; + } + + public VCDecoder(IByteBuffer dict, IByteBuffer delta, Stream sout) + { + this.delta = delta; + this.dict = dict; + this.sout = new ByteStreamWriter(sout); + isStarted = false; + } + + /// + /// Call this before calling decode + /// This expects at least the header part of the delta file + /// is available in the stream + /// + /// + public VCDiffResult Start() + { + if (!delta.CanRead) return VCDiffResult.EOD; + + byte V = delta.ReadByte(); + + if (!delta.CanRead) return VCDiffResult.EOD; + + byte C = delta.ReadByte(); + + if (!delta.CanRead) return VCDiffResult.EOD; + + byte D = delta.ReadByte(); + + if (!delta.CanRead) return VCDiffResult.EOD; + + byte version = delta.ReadByte(); + + if (!delta.CanRead) return VCDiffResult.EOD; + + byte hdr = delta.ReadByte(); + + if (V != MagicBytes[0]) + { + return VCDiffResult.ERRROR; + } + + if (C != MagicBytes[1]) + { + return VCDiffResult.ERRROR; + } + + if (D != MagicBytes[2]) + { + return VCDiffResult.ERRROR; + } + + if (version != 0x00 && version != 'S') + { + return VCDiffResult.ERRROR; + } + + //compression not supported + if ((hdr & (int)VCDiffCodeFlags.VCDDECOMPRESS) != 0) + { + return VCDiffResult.ERRROR; + } + + //custom code table! + if((hdr & (int)VCDiffCodeFlags.VCDCODETABLE) != 0) + { + if (!delta.CanRead) return VCDiffResult.EOD; + + //try decoding the custom code table + //since we don't support the compress the next line should be the length of the code table + customTable = new CustomCodeTableDecoder(); + VCDiffResult result = customTable.Decode(delta); + + if(result != VCDiffResult.SUCCESS) + { + return result; + } + } + + googleVersion = version == 'S'; + + isStarted = true; + + //buffer all the dictionary up front + dict.BufferAll(); + + return VCDiffResult.SUCCESS; + } + + /// + /// Use this after calling Start + /// Each time the decode is called it is expected + /// that at least 1 Window header is available in the stream + /// + /// bytes decoded for all available windows + /// + public VCDiffResult Decode(out long bytesWritten) + { + if(!isStarted) + { + bytesWritten = 0; + return VCDiffResult.ERRROR; + } + + VCDiffResult result = VCDiffResult.SUCCESS; + bytesWritten = 0; + + if (!delta.CanRead) return VCDiffResult.EOD; + + while (delta.CanRead) + { + //delta is streamed in order aka not random access + WindowDecoder w = new WindowDecoder(dict.Length, delta); + + if (w.Decode(googleVersion)) + { + using (BodyDecoder body = new BodyDecoder(w, dict, delta, sout)) + { + + if (googleVersion && w.AddRunLength == 0 && w.AddressesForCopyLength == 0 && w.InstructionAndSizesLength > 0) + { + //interleaved + //decodedinterleave actually has an internal loop for waiting and streaming the incoming rest of the interleaved window + result = body.DecodeInterleave(); + + if (result != VCDiffResult.SUCCESS && result != VCDiffResult.EOD) + { + return result; + } + + bytesWritten += body.Decoded; + } + //technically add could be 0 if it is all copy instructions + //so do an or check on those two + else if (googleVersion && (w.AddRunLength > 0 || w.AddressesForCopyLength > 0) && w.InstructionAndSizesLength > 0) + { + //not interleaved + //expects the full window to be available + //in the stream + + result = body.Decode(); + + if (result != VCDiffResult.SUCCESS) + { + return result; + } + + bytesWritten += body.Decoded; + } + else if (!googleVersion) + { + //not interleaved + //expects the full window to be available + //in the stream + result = body.Decode(); + + if (result != VCDiffResult.SUCCESS) + { + return result; + } + + bytesWritten += body.Decoded; + } + else + { + //invalid file + return VCDiffResult.ERRROR; + } + } + } + else + { + return (VCDiffResult)w.Result; + } + } + + return result; + } + } +} diff --git a/VCDiff/Decoders/WindowDecoder.cs b/VCDiff/Decoders/WindowDecoder.cs new file mode 100644 index 00000000..67f83986 --- /dev/null +++ b/VCDiff/Decoders/WindowDecoder.cs @@ -0,0 +1,557 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; + +namespace VCDiff.Decoders +{ + public class WindowDecoder + { + IByteBuffer buffer; + int returnCode; + long deltaEncodingLength; + long deltaEncodingStart; + ParseableChunk chunk; + byte deltaIndicator; + long dictionarySize; + byte winIndicator; + long sourceLength; + long sourcePosition; + long targetLength; + long addRunLength; + long instructionAndSizesLength; + long addressForCopyLength; + uint checksum; + bool hasChecksum; + + byte[] addRun; + byte[] instructionAndSizes; + byte[] addressesForCopy; + + public byte[] AddRunData + { + get + { + return addRun; + } + } + + public byte[] InstructionsAndSizesData + { + get + { + return instructionAndSizes; + } + } + + public byte[] AddressesForCopyData + { + get + { + return addressesForCopy; + } + } + + public long AddRunLength + { + get + { + return addRunLength; + } + } + + public long InstructionAndSizesLength + { + get + { + return instructionAndSizesLength; + } + } + + public long AddressesForCopyLength + { + get + { + return addressForCopyLength; + } + } + + public byte WinIndicator + { + get + { + return winIndicator; + } + } + + public long SourcePosition + { + get + { + return sourcePosition; + } + } + + public long SourceLength + { + get + { + return sourceLength; + } + } + + public long DecodedDeltaLength + { + get + { + return targetLength; + } + } + + public long DeltaStart + { + get + { + return deltaEncodingStart; + } + } + + public long DeltaLength + { + get + { + return deltaEncodingStart + deltaEncodingLength; + } + } + + public byte DeltaIndicator + { + get + { + return deltaIndicator; + } + } + + public uint Checksum + { + get + { + return checksum; + } + } + + public bool HasChecksum + { + get + { + return hasChecksum; + } + } + + public int Result + { + get + { + return returnCode; + } + } + + /// + /// Parses the window from the data + /// + /// the dictionary size + /// the buffer containing the incoming data + public WindowDecoder(long dictionarySize, IByteBuffer buffer) + { + this.dictionarySize = dictionarySize; + this.buffer = buffer; + chunk = new ParseableChunk(buffer.Position, buffer.Length); + returnCode = (int)VCDiffResult.SUCCESS; + } + + /// + /// Decodes the window header - Parses it basically + /// + /// if true will check for checksum and if interleaved + /// + public bool Decode(bool googleVersion) + { + bool success = false; + + success = ParseWindowIndicatorAndSegment(dictionarySize, 0, false, out winIndicator, out sourceLength, out sourcePosition); + + if(!success) + { + return false; + } + + success = ParseWindowLengths(out targetLength); + + if(!success) + { + return false; + } + + success = ParseDeltaIndicator(); + + if(!success) + { + return false; + } + + hasChecksum = false; + if((winIndicator & (int)VCDiffWindowFlags.VCDCHECKSUM) != 0 && googleVersion) + { + hasChecksum = true; + } + + success = ParseSectionLengths(hasChecksum, out addRunLength, out instructionAndSizesLength, out addressForCopyLength, out checksum); + + if(!success) + { + return false; + } + + if(googleVersion && addRunLength == 0 && addressForCopyLength == 0 && instructionAndSizesLength > 0) + { + //interleave format + return true; + } + + if (buffer.CanRead) + { + addRun = buffer.ReadBytes((int)addRunLength); + } + if(buffer.CanRead) + { + instructionAndSizes = buffer.ReadBytes((int)instructionAndSizesLength); + } + if(buffer.CanRead) + { + addressesForCopy = buffer.ReadBytes((int)addressForCopyLength); + } + + return true; + } + + bool ParseByte(out byte value) + { + if((int)VCDiffResult.SUCCESS != returnCode) + { + value = 0; + return false; + } + if(chunk.IsEmpty) + { + value = 0; + returnCode = (int)VCDiffResult.EOD; + return false; + } + value = buffer.ReadByte(); + chunk.Position = buffer.Position; + return true; + } + + bool ParseInt32(out int value) + { + if ((int)VCDiffResult.SUCCESS != returnCode) + { + value = 0; + return false; + } + if (chunk.IsEmpty) + { + value = 0; + returnCode = (int)VCDiffResult.EOD; + return false; + } + + int parsed = VarIntBE.ParseInt32(buffer); + switch(parsed) + { + case (int)VCDiffResult.ERRROR: + value = 0; + return false; + case (int)VCDiffResult.EOD: + value = 0; + return false; + default: + break; + } + chunk.Position = buffer.Position; + value = parsed; + return true; + } + + bool ParseUInt32(out uint value) + { + if ((int)VCDiffResult.SUCCESS != returnCode) + { + value = 0; + return false; + } + if (chunk.IsEmpty) + { + value = 0; + returnCode = (int)VCDiffResult.EOD; + return false; + } + + long parsed = VarIntBE.ParseInt64(buffer); + switch (parsed) + { + case (int)VCDiffResult.ERRROR: + value = 0; + return false; + case (int)VCDiffResult.EOD: + value = 0; + return false; + default: + break; + } + if(parsed > 0xFFFFFFFF) + { + returnCode = (int)VCDiffResult.ERRROR; + value = 0; + return false; + } + chunk.Position = buffer.Position; + value = (uint)parsed; + return true; + } + + bool ParseSourceSegmentLengthAndPosition(long from, out long sourceLength, out long sourcePosition) + { + int outLength; + if(!ParseInt32(out outLength)) + { + sourceLength = 0; + sourcePosition = 0; + return false; + } + sourceLength = outLength; + if(sourceLength > from) + { + returnCode = (int)VCDiffResult.ERRROR; + sourceLength = 0; + sourcePosition = 0; + return false; + } + int outPos; + if(!ParseInt32(out outPos)) + { + sourcePosition = 0; + sourceLength = 0; + return false; + } + sourcePosition = outPos; + if(sourcePosition > from) + { + returnCode = (int)VCDiffResult.ERRROR; + sourceLength = 0; + sourcePosition = 0; + return false; + } + + long segmentEnd = sourcePosition + sourceLength; + if(segmentEnd > from) + { + returnCode = (int)VCDiffResult.ERRROR; + sourceLength = 0; + sourcePosition = 0; + return false; + } + + return true; + } + + bool ParseWindowIndicatorAndSegment(long dictionarySize, long decodedTargetSize, bool allowVCDTarget, out byte winIndicator, out long sourceSegmentLength, out long sourceSegmentPosition) + { + if(!ParseByte(out winIndicator)) + { + winIndicator = 0; + sourceSegmentLength = 0; + sourceSegmentPosition = 0; + return false; + } + + int sourceFlags = winIndicator & ((int)VCDiffWindowFlags.VCDSOURCE | (int)VCDiffWindowFlags.VCDTARGET); + + switch(sourceFlags) + { + case (int)VCDiffWindowFlags.VCDSOURCE: + return ParseSourceSegmentLengthAndPosition(dictionarySize, out sourceSegmentLength, out sourceSegmentPosition); + case (int)VCDiffWindowFlags.VCDTARGET: + if(!allowVCDTarget) + { + winIndicator = 0; + sourceSegmentLength = 0; + sourceSegmentPosition = 0; + returnCode = (int)VCDiffResult.ERRROR; + return false; + } + return ParseSourceSegmentLengthAndPosition(decodedTargetSize, out sourceSegmentLength, out sourceSegmentPosition); + case (int)VCDiffWindowFlags.VCDSOURCE | (int)VCDiffWindowFlags.VCDTARGET: + winIndicator = 0; + sourceSegmentPosition = 0; + sourceSegmentLength = 0; + return false; + } + + winIndicator = 0; + sourceSegmentPosition = 0; + sourceSegmentLength = 0; + return false; + } + + bool ParseWindowLengths(out long targetWindowLength) + { + int deltaLength; + if(!ParseInt32(out deltaLength)) + { + targetWindowLength = 0; + return false; + } + deltaEncodingLength = deltaLength; + + deltaEncodingStart = chunk.ParsedSize; + int outTargetLength; + if(!ParseInt32(out outTargetLength)) + { + targetWindowLength = 0; + return false; + } + targetWindowLength = outTargetLength; + return true; + } + + bool ParseDeltaIndicator() + { + if(!ParseByte(out deltaIndicator)) + { + returnCode = (int)VCDiffResult.ERRROR; + return false; + } + if((deltaIndicator & ((int)VCDiffCompressFlags.VCDDATACOMP | (int)VCDiffCompressFlags.VCDINSTCOMP | (int)VCDiffCompressFlags.VCDADDRCOMP)) > 0) + { + returnCode = (int)VCDiffResult.ERRROR; + return false; + } + return true; + } + + + public bool ParseSectionLengths(bool hasChecksum, out long addRunLength, out long instructionsLength, out long addressLength, out uint checksum) + { + int outAdd; + int outInstruct; + int outAddress; + ParseInt32(out outAdd); + ParseInt32(out outInstruct); + ParseInt32(out outAddress); + checksum = 0; + + if(hasChecksum) + { + ParseUInt32(out checksum); + } + + addRunLength = outAdd; + addressLength = outAddress; + instructionsLength = outInstruct; + + if(returnCode != (int)VCDiffResult.SUCCESS) + { + return false; + } + + long deltaHeaderLength = chunk.ParsedSize - deltaEncodingStart; + long totalLen = deltaHeaderLength + addRunLength + instructionsLength + addressLength; + + if(deltaEncodingLength != totalLen) + { + returnCode = (int)VCDiffResult.ERRROR; + return false; + } + + return true; + } + + public class ParseableChunk + { + long end; + long position; + long start; + + public long UnparsedSize + { + get + { + return end - position; + } + } + + public long End + { + get + { + return end; + } + } + + public bool IsEmpty + { + get + { + return 0 == UnparsedSize; + } + } + + public long Start + { + get + { + return start; + } + } + + public long ParsedSize + { + get + { + return position - start; + } + } + + public long Position + { + get + { + return position; + } + set + { + if(position < start) + { + return; + } + if(position > end) + { + return; + } + position = value; + } + } + + public ParseableChunk(long s, long len) + { + this.start = s; + this.end = s + len; + this.position = s; + } + } + } +} diff --git a/VCDiff/Encoders/BlockHash.cs b/VCDiff/Encoders/BlockHash.cs new file mode 100644 index 00000000..9a778499 --- /dev/null +++ b/VCDiff/Encoders/BlockHash.cs @@ -0,0 +1,447 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; + +namespace VCDiff.Encoders +{ + public class BlockHash + { + static int blockSize = 16; + public static int BlockSize + { + get + { + return blockSize; + } + set + { + if (value < 2) return; + blockSize = value; + } + } + static int maxMatchesToCheck = (blockSize >= 32) ? 32 : (32 * (32 / blockSize)); + const int maxProbes = 16; + IByteBuffer sourceData; + long offset; + ulong hashTableMask; + long lastBlockAdded = 0; + long[] hashTable; + long[] nextBlockTable; + long[] lastBlockTable; + long tableSize = 0; + RollingHash hasher; + + public IByteBuffer Source + { + get + { + return sourceData; + } + } + + public long SourceSize + { + get + { + return sourceData.Length; + } + } + + /// + /// Create a hash lookup table for the data + /// + /// the data to create the table for + /// the offset usually 0 + /// the hashing method + public BlockHash(IByteBuffer sin, int offset, RollingHash hasher) + { + maxMatchesToCheck = (blockSize >= 32) ? 32 : (32 * (32 / blockSize)); + this.hasher = hasher; + sourceData = sin; + this.offset = offset; + tableSize = CalcTableSize(); + + if(tableSize == 0) + { + throw new Exception("BlockHash Table Size is Invalid == 0"); + } + + hashTableMask = (ulong)tableSize - 1; + hashTable = new long[tableSize]; + nextBlockTable = new long[BlocksCount]; + lastBlockTable = new long[BlocksCount]; + lastBlockAdded = -1; + SetTablesToInvalid(); + } + + void SetTablesToInvalid() + { + for(int i = 0; i < nextBlockTable.Length; i++) + { + lastBlockTable[i] = -1; + nextBlockTable[i] = -1; + } + for(int i = 0; i < hashTable.Length; i++) + { + hashTable[i] = -1; + } + } + + long CalcTableSize() + { + long min = (sourceData.Length / sizeof(int)) + 1; + long size = 1; + + while(size < min) + { + size <<= 1; + + if(size <= 0) + { + return 0; + } + } + + if((size & (size - 1)) != 0) + { + return 0; + } + + if((sourceData.Length > 0) && (size > (min * 2))) { + return 0; + } + return size; + } + + public void AddOneIndexHash(int index, ulong hash) + { + if(index == NextIndexToAdd) + { + AddBlock(hash); + } + } + + public long NextIndexToAdd + { + get + { + return (lastBlockAdded + 1) * blockSize; + } + } + + public void AddAllBlocksThroughIndex(long index) + { + if(index > sourceData.Length) + { + return; + } + + long lastAdded = lastBlockAdded * blockSize; + if(index <= lastAdded) + { + return; + } + + if(sourceData.Length < blockSize) + { + return; + } + + long endLimit = index; + long lastLegalHashIndex = (sourceData.Length - blockSize); + + if(endLimit > lastLegalHashIndex) + { + endLimit = lastLegalHashIndex + 1; + } + + long offset = sourceData.Position + NextIndexToAdd; + long end = sourceData.Position + endLimit; + sourceData.Position = offset; + while(offset < end) + { + AddBlock(hasher.Hash(sourceData.ReadBytes(blockSize))); + offset += blockSize; + } + } + + public long BlocksCount + { + get + { + return sourceData.Length / blockSize; + } + } + + public long TableSize + { + get + { + return tableSize; + } + } + + long GetTableIndex(ulong hash) + { + return (long)(hash & hashTableMask); + } + + + /// + /// Finds the best matching block for the candidate + /// + /// the hash to look for + /// the start position + /// the target start position + /// the data left to encode + /// the target buffer + /// the match object to use + public void FindBestMatch(ulong hash, long candidateStart, long targetStart, long targetSize, IByteBuffer target, Match m) + { + int matchCounter = 0; + + for (long blockNumber = FirstMatchingBlock(hash, candidateStart, target); + blockNumber >= 0 && !TooManyMatches(ref matchCounter); + blockNumber = NextMatchingBlock(blockNumber, candidateStart, target)) + { + long sourceMatchOffset = blockNumber * blockSize; + long sourceStart = blockNumber * blockSize; + long sourceMatchEnd = sourceMatchOffset + blockSize; + long targetMatchOffset = candidateStart - targetStart; + long targetMatchEnd = targetMatchOffset + blockSize; + + long matchSize = blockSize; + + long limitBytesToLeft = Math.Min(sourceMatchOffset, targetMatchOffset); + long leftMatching = MatchingBytesToLeft(sourceMatchOffset, targetStart + targetMatchOffset, target, limitBytesToLeft); + sourceMatchOffset -= leftMatching; + targetMatchOffset -= leftMatching; + matchSize += leftMatching; + + long sourceBytesToRight = sourceData.Length - sourceMatchEnd; + long targetBytesToRight = targetSize - targetMatchEnd; + long rightLimit = Math.Min(sourceBytesToRight, targetBytesToRight); + + long rightMatching = MatchingBytesToRight(sourceMatchEnd, targetStart + targetMatchEnd, target, rightLimit); + matchSize += rightMatching; + sourceMatchEnd += rightMatching; + targetMatchEnd += rightMatching; + m.ReplaceIfBetterMatch(matchSize, sourceMatchOffset + offset, targetMatchOffset); + } + } + + public void AddBlock(ulong hash) + { + long blockNumber = lastBlockAdded + 1; + long totalBlocks = BlocksCount; + if(blockNumber >= totalBlocks) + { + return; + } + + if(nextBlockTable[blockNumber] != -1) + { + return; + } + + long tableIndex = GetTableIndex(hash); + long firstMatching = hashTable[tableIndex]; + if(firstMatching < 0) + { + hashTable[tableIndex] = blockNumber; + lastBlockTable[blockNumber] = blockNumber; + } + else + { + long lastMatching = lastBlockTable[firstMatching]; + if(nextBlockTable[lastMatching] != -1) + { + return; + } + nextBlockTable[lastMatching] = blockNumber; + lastBlockTable[firstMatching] = blockNumber; + } + lastBlockAdded = blockNumber; + } + + public void AddAllBlocks() + { + AddAllBlocksThroughIndex(sourceData.Length); + } + + public bool BlockContentsMatch(long block1, long toffset, IByteBuffer target) + { + //this sets up the positioning of the buffers + //as well as testing the first byte + sourceData.Position = block1 * blockSize; + if (!sourceData.CanRead) return false; + byte lb = sourceData.ReadByte(); + target.Position = toffset; + if (!target.CanRead) return false; + byte rb = target.ReadByte(); + + if(lb != rb) + { + return false; + } + + return BlockCompareWords(target); + } + + //this doesn't appear to be used anywhere even though it is included in googles code + public bool BlockCompareWords(IByteBuffer target) + { + //we already compared the first byte so moving on! + int i = 1; + + long srcLength = sourceData.Length; + long trgLength = target.Length; + long offset1 = sourceData.Position; + long offset2 = target.Position; + + while (i < blockSize) + { + if (i + offset1 >= srcLength || i + offset2 >= trgLength) + { + return false; + } + byte lb = sourceData.ReadByte(); + byte rb = target.ReadByte(); + if(lb != rb) + { + return false; + } + i++; + } + + return true; + } + + public long FirstMatchingBlock(ulong hash, long toffset, IByteBuffer target) + { + return SkipNonMatchingBlocks(hashTable[GetTableIndex(hash)], toffset, target); + } + + + public long NextMatchingBlock(long blockNumber, long toffset, IByteBuffer target) + { + if(blockNumber >= BlocksCount) + { + return -1; + } + + return SkipNonMatchingBlocks(nextBlockTable[blockNumber], toffset, target); + } + + public long SkipNonMatchingBlocks(long blockNumber, long toffset, IByteBuffer target) + { + int probes = 0; + while ((blockNumber >= 0) && !BlockContentsMatch(blockNumber, toffset, target)) + { + if(++probes > maxProbes) + { + return -1; + } + blockNumber = nextBlockTable[blockNumber]; + } + return blockNumber; + } + + public long MatchingBytesToLeft(long start, long tstart, IByteBuffer target, long maxBytes) + { + long bytesFound = 0; + long sindex = start; + long tindex = tstart; + + while(bytesFound < maxBytes) + { + --sindex; + --tindex; + if (sindex < 0 || tindex < 0) break; + //has to be done this way or a race condition will happen + //if the sourcce and target are the same buffer + sourceData.Position = sindex; + byte lb = sourceData.ReadByte(); + target.Position = tindex; + byte rb = target.ReadByte(); + if (lb != rb) break; + ++bytesFound; + } + return bytesFound; + } + + public long MatchingBytesToRight(long end, long tstart, IByteBuffer target, long maxBytes) + { + long sindex = end; + long tindex = tstart; + long bytesFound = 0; + long srcLength = sourceData.Length; + long trgLength = target.Length; + sourceData.Position = end; + target.Position = tstart; + while (bytesFound < maxBytes) + { + if (sindex >= srcLength || tindex >= trgLength) break; + if (!sourceData.CanRead) break; + byte lb = sourceData.ReadByte(); + if (!target.CanRead) break; + byte rb = target.ReadByte(); + if (lb != rb) break; + ++tindex; + ++sindex; + ++bytesFound; + } + return bytesFound; + } + + public bool TooManyMatches(ref int matchCounter) + { + ++matchCounter; + return (matchCounter > maxMatchesToCheck); + } + + public class Match + { + long size = 0; + long sOffset = 0; + long tOffset = 0; + + public void ReplaceIfBetterMatch(long csize, long sourcOffset, long targetOffset) + { + if(csize > size) + { + size = csize; + sOffset = sourcOffset; + tOffset = targetOffset; + } + } + + public long Size + { + get + { + return size; + } + } + + public long SourceOffset + { + get + { + return sOffset; + } + } + + public long TargetOffset + { + get + { + return tOffset; + } + } + } + } +} diff --git a/VCDiff/Encoders/ChunkEncoder.cs b/VCDiff/Encoders/ChunkEncoder.cs new file mode 100644 index 00000000..13fba908 --- /dev/null +++ b/VCDiff/Encoders/ChunkEncoder.cs @@ -0,0 +1,182 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Includes; +using VCDiff.Shared; + +namespace VCDiff.Encoders +{ + public class ChunkEncoder : IDisposable + { + static int minBlockSize = 32; + public static int MinBlockSize + { + get + { + return minBlockSize; + } + set + { + if (value < 2 || value < BlockHash.BlockSize) return; + minBlockSize = value; + } + } + + BlockHash dictionary; + IByteBuffer oldData; + IByteBuffer newData; + WindowEncoder windowEncoder; + RollingHash hasher; + bool interleaved; + bool hasChecksum; + + /// + /// Performs the actual encoding of a chunk of data into the VCDiff format + /// + /// The dictionary hash table + /// The data for the dictionary hash table + /// The rolling hash object + /// Whether to interleave the data or not + /// Whether to include checksums for each window + public ChunkEncoder(BlockHash dictionary, IByteBuffer oldData, RollingHash hash, bool interleaved = false, bool checksum = false) + { + this.hasChecksum = checksum; + this.hasher = hash; + this.oldData = oldData; + this.dictionary = dictionary; + this.interleaved = interleaved; + } + + /// + /// Encodes the data using the settings from initialization + /// + /// the target data + /// the out stream + public void EncodeChunk(IByteBuffer newData, ByteStreamWriter sout) + { + uint checksum = 0; + + ///If checksum needed + ///Generate Adler32 checksum for the incoming bytes + if(hasChecksum) + { + newData.Position = 0; + byte[] bytes = newData.ReadBytes((int)newData.Length); + checksum = Checksum.ComputeAdler32(bytes); + + bytes = null; + System.GC.Collect(); + } + + windowEncoder = new WindowEncoder(oldData.Length, checksum, this.interleaved, hasChecksum); + + oldData.Position = 0; + newData.Position = 0; + + this.newData = newData; + long nextEncode = newData.Position; + long targetEnd = newData.Length; + long startOfLastBlock = targetEnd - BlockHash.BlockSize; + long candidatePos = nextEncode; + + //create the first hash + ulong hash = hasher.Hash(newData.PeekBytes(BlockHash.BlockSize)); + + while (true) + { + //if less than block size exit and then write as an ADD + if (newData.Length - nextEncode < BlockHash.BlockSize) + { + break; + } + + //try and encode the copy and add instructions that best match + long bytesEncoded = EncodeCopyForBestMatch(hash, candidatePos, nextEncode, targetEnd); + + if (bytesEncoded > 0) + { + nextEncode += bytesEncoded; + candidatePos = nextEncode; + + if (candidatePos > startOfLastBlock) + { + break; + } + + newData.Position = candidatePos; + //cannot use rolling hash since we skipped so many + hash = hasher.Hash(newData.ReadBytes(BlockHash.BlockSize)); + } + else + { + if (candidatePos + 1 > startOfLastBlock) + { + break; + } + + //update hash requires the first byte of the last hash as well as the byte that is first byte pos + blockSize + //in order to properly calculate the rolling hash + newData.Position = candidatePos; + byte peek0 = newData.ReadByte(); + newData.Position = candidatePos + BlockHash.BlockSize; + byte peek1 = newData.ReadByte(); + hash = hasher.UpdateHash(hash, peek0, peek1); + candidatePos++; + } + } + + //Add the rest of the data that was not encoded + if (nextEncode < newData.Length) + { + int len = (int)(newData.Length - nextEncode); + newData.Position = nextEncode; + windowEncoder.Add(newData.ReadBytes(len)); + } + + //output the final window + windowEncoder.Output(sout); + } + + //currently does not support looking in target + //only the dictionary + long EncodeCopyForBestMatch(ulong hash, long candidateStart, long unencodedStart, long unencodedSize) + { + BlockHash.Match bestMatch = new BlockHash.Match(); + + dictionary.FindBestMatch(hash, candidateStart, unencodedStart, unencodedSize, newData, bestMatch); + + if (bestMatch.Size < MinBlockSize) + { + return 0; + } + + if (bestMatch.TargetOffset > 0) + { + newData.Position = unencodedStart; + windowEncoder.Add(newData.ReadBytes((int)bestMatch.TargetOffset)); + } + + windowEncoder.Copy((int)bestMatch.SourceOffset, (int)bestMatch.Size); + + return bestMatch.Size + bestMatch.TargetOffset; + } + + public void Dispose() + { + if(oldData != null) + { + oldData.Dispose(); + } + if(newData != null) + { + newData.Dispose(); + } + if(windowEncoder != null) + { + windowEncoder = null; + } + } + } +} diff --git a/VCDiff/Encoders/InstructionMap.cs b/VCDiff/Encoders/InstructionMap.cs new file mode 100644 index 00000000..ee91b7fa --- /dev/null +++ b/VCDiff/Encoders/InstructionMap.cs @@ -0,0 +1,178 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; + +namespace VCDiff.Encoders +{ + public class InstructionMap + { + CodeTable table; + OpcodeMap firstMap; + OpcodeMap2 secondMap; + + /// + /// Instruction mapping for op codes and such for using in encoding + /// + public InstructionMap() + { + table = CodeTable.DefaultTable; + firstMap = new OpcodeMap((int)VCDiffInstructionType.LAST + AddressCache.DefaultLast + 1, FindMaxSize(table.size1)); + secondMap = new OpcodeMap2((int)VCDiffInstructionType.LAST + AddressCache.DefaultLast + 1, FindMaxSize(table.size2)); + for (int opcode = 0; opcode < CodeTable.kCodeTableSize; ++opcode) + { + if (table.inst2[opcode] == CodeTable.N) + { + firstMap.Add(table.inst1[opcode], table.size1[opcode], table.mode1[opcode], (byte)opcode); + } + else if(table.inst1[opcode] == CodeTable.N) + { + firstMap.Add(table.inst1[opcode], table.size1[opcode], table.mode1[opcode], (byte)opcode); + } + } + + for(int opcode = 0; opcode < CodeTable.kCodeTableSize; ++opcode) + { + if((table.inst1[opcode] != CodeTable.N) && (table.inst2[opcode] != CodeTable.N)) { + int found = this.LookFirstOpcode(table.inst1[opcode], table.size1[opcode], table.mode1[opcode]); + if (found == CodeTable.kNoOpcode) continue; + secondMap.Add((byte)found, table.inst2[opcode], table.size2[opcode], table.mode2[opcode], (byte)opcode); + } + } + } + + public int LookFirstOpcode(byte inst, byte size, byte mode) + { + return firstMap.LookUp(inst, size, mode); + } + + public int LookSecondOpcode(byte first, byte inst, byte size, byte mode) + { + return secondMap.LookUp(first, inst, size, mode); + } + + static byte FindMaxSize(byte[] sizes) + { + byte maxSize = sizes[0]; + for(int i = 1; i < sizes.Length; i++) + { + if(maxSize < sizes[i]) + { + maxSize = sizes[i]; + } + } + return maxSize; + } + + class OpcodeMap2 + { + int[][][] opcodes2; + int maxSize; + int numInstAndModes; + + public OpcodeMap2(int numInstAndModes, int maxSize) + { + this.maxSize = maxSize; + this.numInstAndModes = numInstAndModes; + opcodes2 = new int[CodeTable.kCodeTableSize][][]; + } + + public void Add(byte first, byte inst, byte size, byte mode, byte opcode) + { + int[][] instmode = opcodes2[first]; + + if(instmode == null) + { + instmode = new int[this.numInstAndModes][]; + opcodes2[opcode] = instmode; + } + int[] sizeArray = instmode[inst + mode]; + if(sizeArray == null) + { + sizeArray = NewSizeOpcodeArray(this.maxSize + 1); + instmode[inst + mode] = sizeArray; + } + if(sizeArray[size] == CodeTable.kNoOpcode) + { + sizeArray[size] = opcode; + } + } + + int[] NewSizeOpcodeArray(int size) + { + int[] nn = new int[size]; + for(int i = 0; i < size; ++i) + { + nn[i] = CodeTable.kNoOpcode; + } + return nn; + } + + public int LookUp(byte first, byte inst, byte size, byte mode) + { + if(size > this.maxSize) + { + return CodeTable.kNoOpcode; + } + + int[][] instmode = opcodes2[first]; + if(instmode == null) + { + return CodeTable.kNoOpcode; + } + int instModePointer = (inst == CodeTable.C) ? (inst + mode) : inst; + int[] sizeArray = instmode[instModePointer]; + if(sizeArray == null) + { + return CodeTable.kNoOpcode; + } + return sizeArray[size]; + } + } + + class OpcodeMap + { + int[,] opcodes; + int maxSize; + int numInstAndModes; + + public OpcodeMap(int numInstAndModes, int maxSize) + { + this.maxSize = maxSize; + this.numInstAndModes = numInstAndModes; + opcodes = new int[numInstAndModes, maxSize+1]; + + for(int i = 0; i < numInstAndModes; ++i) + { + for(int j = 0; j < maxSize + 1; j++) + { + opcodes[i,j] = CodeTable.kNoOpcode; + } + } + } + + public void Add(byte inst, byte size, byte mode, byte opcode) + { + if(opcodes[inst + mode,size] == CodeTable.kNoOpcode) + { + opcodes[inst + mode, size] = opcode; + } + } + + public int LookUp(byte inst, byte size, byte mode) + { + int instMode = (inst == CodeTable.C) ? (inst + mode) : inst; + + if(size > maxSize) + { + return CodeTable.kNoOpcode; + } + + return opcodes[instMode, size]; + } + } + } +} diff --git a/VCDiff/Encoders/RollingHash.cs b/VCDiff/Encoders/RollingHash.cs new file mode 100644 index 00000000..d7038b1e --- /dev/null +++ b/VCDiff/Encoders/RollingHash.cs @@ -0,0 +1,126 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Encoders +{ + public class RollingHash + { + const ulong kMult = 257; + const ulong kBase = (1 << 23); + + int size = 0; + ulong[] removeTable; + ulong multiplier; + + /// + /// Rolling Hash Constructor + /// + /// block size + public RollingHash(int size) + { + this.size = size; + removeTable = new ulong[256]; + multiplier = 1; + for(int i = 0; i < size - 1; ++i) + { + multiplier = ModBase(multiplier * kMult); + } + ulong byteTimes = 0; + for(int i = 0; i < 256; ++i) + { + removeTable[i] = FindModBaseInverse(byteTimes); + byteTimes = ModBase(byteTimes + multiplier); + } + } + + /// + /// Does the MODULO operation + /// + /// + /// + static ulong ModBase(ulong op) + { + return op & (kBase - 1); + } + + /// + /// Finds the inverse of the operation + /// + /// + /// + static ulong FindModBaseInverse(ulong op) + { + return ModBase((ulong)0 - op); + } + + /// + /// Performs the next hash encoding step + /// for creating the hash + /// + /// + /// + /// + static ulong HashStep(ulong partialHash, byte next) + { + return ModBase((partialHash * kMult) + next); + } + + /// + /// only hash the first two bytes if any + /// + /// + /// + static ulong HashFirstTwoBytes(byte[] bytes) + { + if (bytes.Length == 0) return 1; + if (bytes.Length == 1) return (bytes[0] * kMult); + + return (bytes[0] * kMult) + bytes[1]; + } + + /// + /// Generate a new hash from the bytes + /// + /// The bytes to generate the hash for + /// + public ulong Hash(byte[] bytes) + { + ulong h = HashFirstTwoBytes(bytes); + for(int i = 2; i < bytes.Length; i++) + { + h = HashStep(h, bytes[i]); + } + return h; + } + + /// + /// Rolling update for the hash + /// First byte must be the first bytee that was used in the data + /// that was last encoded + /// new byte is the first byte position + Size + /// + /// the original hash + /// the original byte of the data for the first hash + /// the first byte of the new data to hash + /// + public ulong UpdateHash(ulong oldHash, byte firstByte, byte newByte) + { + ulong partial = RemoveFirstByte(oldHash, firstByte); + return HashStep(partial, newByte); + } + + /// + /// Removes the first byte from the hash + /// + /// hash + /// first byte + /// + ulong RemoveFirstByte(ulong hash, byte first) + { + return ModBase(hash + removeTable[first]); + } + } +} diff --git a/VCDiff/Encoders/VCCoder.cs b/VCDiff/Encoders/VCCoder.cs new file mode 100644 index 00000000..ecd2a60e --- /dev/null +++ b/VCDiff/Encoders/VCCoder.cs @@ -0,0 +1,105 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Includes; +using VCDiff.Shared; +using System.IO; + +namespace VCDiff.Encoders +{ + public class VCCoder + { + IByteBuffer oldData; + IByteBuffer newData; + ByteStreamWriter sout; + RollingHash hasher; + int bufferSize; + + static byte[] MagicBytes = new byte[] { 0xD6, 0xC3, 0xC4, 0x00, 0x00 }; + static byte[] MagicBytesExtended = new byte[] { 0xD6, 0xC3, 0xC4, (byte)'S', 0x00 }; + + /// + /// The easy public structure for encoding into a vcdiff format + /// Simply instantiate it with the proper streams and use the Encode() function. + /// Does not check if data is equal already. You will need to do that. + /// Returns VCDiffResult: should always return success, unless either the dict or the target streams have 0 bytes + /// See the VCDecoder for decoding vcdiff format + /// + /// The dictionary (previous data) + /// The new data + /// The output stream + /// The maximum buffer size for window chunking. It is in Megabytes. 2 would mean 2 megabytes etc. Default is 1. + public VCCoder(Stream dict, Stream target, Stream sout, int maxBufferSize = 1) + { + if (maxBufferSize <= 0) maxBufferSize = 1; + + this.oldData = new ByteStreamReader(dict); + this.newData = new ByteStreamReader(target); + this.sout = new ByteStreamWriter(sout); + hasher = new RollingHash(BlockHash.BlockSize); + + this.bufferSize = maxBufferSize * 1024 * 1024; + } + + /// + /// Encodes the file + /// + /// Set this to true to enable SDHC interleaved vcdiff google format + /// Set this to true to add checksum for encoded data windows + /// + public VCDiffResult Encode(bool interleaved = false, bool checksum = false) + { + if (newData.Length == 0 || oldData.Length == 0) + { + return VCDiffResult.ERRROR; + } + + VCDiffResult result = VCDiffResult.SUCCESS; + + oldData.Position = 0; + newData.Position = 0; + + //file header + //write magic bytes + if (!interleaved && !checksum) + { + sout.writeBytes(MagicBytes); + } + else + { + sout.writeBytes(MagicBytesExtended); + } + + //buffer the whole olddata (dictionary) + //otherwise it will be a slow process + //even Google's version reads in the entire dictionary file to memory + //it is just faster that way because of having to move the memory pointer around + //to find all the hash comparisons and stuff. + //It is much slower trying to random access read from file with FileStream class + //however the newData is read in chunks and processed for memory efficiency and speed + oldData.BufferAll(); + + //read in all the dictionary it is the only thing that needs to be + BlockHash dictionary = new BlockHash(oldData, 0, hasher); + dictionary.AddAllBlocks(); + oldData.Position = 0; + + ChunkEncoder chunker = new ChunkEncoder(dictionary, oldData, hasher, interleaved, checksum); + + while (newData.CanRead) + { + using (ByteBuffer ntarget = new ByteBuffer(newData.ReadBytes(bufferSize))) + { + chunker.EncodeChunk(ntarget, sout); + } + + //just in case + System.GC.Collect(); + } + + return result; + } + } +} diff --git a/VCDiff/Encoders/WindowEncoder.cs b/VCDiff/Encoders/WindowEncoder.cs new file mode 100644 index 00000000..2004edaa --- /dev/null +++ b/VCDiff/Encoders/WindowEncoder.cs @@ -0,0 +1,290 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; +using VCDiff.Includes; +using VCDiff.Shared; + +namespace VCDiff.Encoders +{ + public class WindowEncoder + { + bool interleaved; + int maxMode; + long dictionarySize; + long targetLength; + CodeTable table; + int lastOpcodeIndex; + AddressCache addrCache; + InstructionMap instrMap; + List instructionAndSizes; + List dataForAddAndRun; + List addressForCopy; + bool hasChecksum; + uint checksum; + + public bool HasChecksum + { + get + { + return hasChecksum; + } + } + + public bool IsInterleaved + { + get + { + return interleaved; + } + } + + public uint Checksum + { + get + { + return checksum; + } + } + + //This is a window encoder for the VCDIFF format + //if you are not including a checksum simply pass 0 to checksum + //it will be ignored + public WindowEncoder(long dictionarySize, uint checksum, bool interleaved = false, bool hasChecksum = false) + { + this.checksum = checksum; + this.hasChecksum = hasChecksum; + this.interleaved = interleaved; + this.dictionarySize = dictionarySize; + + //The encoder currently doesn't support encoding with a custom table + //will be added in later since it will be easy as decoding is already implemented + maxMode = AddressCache.DefaultLast; + table = CodeTable.DefaultTable; + addrCache = new AddressCache(); + targetLength = 0; + lastOpcodeIndex = -1; + instrMap = new InstructionMap(); + + //Separate buffers for each type if not interleaved + if (!interleaved) + { + instructionAndSizes = new List(); + dataForAddAndRun = new List(); + addressForCopy = new List(); + } + else + { + instructionAndSizes = dataForAddAndRun = addressForCopy = new List(); + } + } + + void EncodeInstruction(VCDiffInstructionType inst, int size, byte mode = 0) + { + if(lastOpcodeIndex >= 0) + { + int lastOp = instructionAndSizes[lastOpcodeIndex]; + + if(inst == VCDiffInstructionType.ADD && (table.inst1[lastOp] == CodeTable.A)) + { + //warning adding two in a row + Console.WriteLine("Warning: performing two ADD instructions in a row."); + } + int compoundOp = CodeTable.kNoOpcode; + if(size <= byte.MaxValue) + { + compoundOp = instrMap.LookSecondOpcode((byte)lastOp, (byte)inst, (byte)size, mode); + if(compoundOp != CodeTable.kNoOpcode) + { + instructionAndSizes[lastOpcodeIndex] = (byte)compoundOp; + lastOpcodeIndex = -1; + return; + } + } + + compoundOp = instrMap.LookSecondOpcode((byte)lastOp, (byte)inst, (byte)0, mode); + if(compoundOp != CodeTable.kNoOpcode) + { + instructionAndSizes[lastOpcodeIndex] = (byte)compoundOp; + //append size to instructionAndSizes + VarIntBE.AppendInt32(size, instructionAndSizes); + lastOpcodeIndex = -1; + } + } + + int opcode = CodeTable.kNoOpcode; + if(size <= byte.MaxValue) + { + opcode = instrMap.LookFirstOpcode((byte)inst, (byte)size, mode); + + if(opcode != CodeTable.kNoOpcode) + { + instructionAndSizes.Add((byte)opcode); + lastOpcodeIndex = instructionAndSizes.Count - 1; + return; + } + } + opcode = instrMap.LookFirstOpcode((byte)inst, 0, mode); + if(opcode == CodeTable.kNoOpcode) + { + return; + } + + instructionAndSizes.Add((byte)opcode); + lastOpcodeIndex = instructionAndSizes.Count - 1; + VarIntBE.AppendInt32(size, instructionAndSizes); + } + + public void Add(byte[] data) + { + EncodeInstruction(VCDiffInstructionType.ADD, data.Length); + dataForAddAndRun.AddRange(data); + targetLength += data.Length; + } + + public void Copy(int offset, int length) + { + long encodedAddr = 0; + byte mode = addrCache.EncodeAddress(offset, dictionarySize + targetLength, out encodedAddr); + EncodeInstruction(VCDiffInstructionType.COPY, length, mode); + if(addrCache.WriteAddressAsVarint(mode)) + { + VarIntBE.AppendInt64(encodedAddr, addressForCopy); + } + else + { + addressForCopy.Add((byte)encodedAddr); + } + targetLength += length; + } + + public void Run(int size, byte b) + { + EncodeInstruction(VCDiffInstructionType.RUN, size); + dataForAddAndRun.Add(b); + targetLength += size; + } + + int CalculateLengthOfTheDeltaEncoding() + { + int extraLength = 0; + + if(hasChecksum) + { + extraLength += VarIntBE.CalcInt64Length(checksum); + } + + if (!interleaved) + { + int lengthOfDelta = VarIntBE.CalcInt32Length((int)targetLength) + + 1 + + VarIntBE.CalcInt32Length(dataForAddAndRun.Count) + + VarIntBE.CalcInt32Length(instructionAndSizes.Count) + + VarIntBE.CalcInt32Length(addressForCopy.Count) + + dataForAddAndRun.Count + + instructionAndSizes.Count + + addressForCopy.Count; + + lengthOfDelta += extraLength; + + return lengthOfDelta; + } + else + { + int lengthOfDelta = VarIntBE.CalcInt32Length((int)targetLength) + + 1 + + VarIntBE.CalcInt32Length(0) + + VarIntBE.CalcInt32Length(instructionAndSizes.Count) + + VarIntBE.CalcInt32Length(0) + + 0 + + instructionAndSizes.Count; + + lengthOfDelta += extraLength; + + return lengthOfDelta; + } + } + + public void Output(ByteStreamWriter sout) + { + int lengthOfDelta = CalculateLengthOfTheDeltaEncoding(); + int windowSize = lengthOfDelta + + 1 + + VarIntBE.CalcInt32Length((int)dictionarySize) + + VarIntBE.CalcInt32Length(0); + VarIntBE.CalcInt32Length(lengthOfDelta); + + //Google's Checksum Implementation Support + if (hasChecksum) + { + sout.writeByte((byte)VCDiffWindowFlags.VCDSOURCE | (byte)VCDiffWindowFlags.VCDCHECKSUM); //win indicator + } + else + { + sout.writeByte((byte)VCDiffWindowFlags.VCDSOURCE); //win indicator + } + VarIntBE.AppendInt32((int)dictionarySize, sout); //dictionary size + VarIntBE.AppendInt32(0, sout); //dictionary start position 0 is default aka encompass the whole dictionary + + VarIntBE.AppendInt32(lengthOfDelta, sout); //length of delta + + //begin of delta encoding + Int64 sizeBeforeDelta = sout.Position; + VarIntBE.AppendInt32((int)targetLength, sout); //final target length after decoding + sout.writeByte(0x00); //uncompressed + + // [Here is where a secondary compressor would be used + // if the encoder and decoder supported that feature.] + + //non interleaved then it is separata areas for each type + if (!interleaved) + { + VarIntBE.AppendInt32(dataForAddAndRun.Count, sout); //length of add/run + VarIntBE.AppendInt32(instructionAndSizes.Count, sout); //length of instructions and sizes + VarIntBE.AppendInt32(addressForCopy.Count, sout); //length of addresses for copys + + //Google Checksum Support + if(hasChecksum) + { + VarIntBE.AppendInt64(checksum, sout); + } + + sout.writeBytes(dataForAddAndRun.ToArray()); //data section for adds and runs + sout.writeBytes(instructionAndSizes.ToArray()); //data for instructions and sizes + sout.writeBytes(addressForCopy.ToArray()); //data for addresses section copys + } + else + { + //interleaved everything is woven in and out in one block + VarIntBE.AppendInt32(0, sout); //length of add/run + VarIntBE.AppendInt32(instructionAndSizes.Count, sout); //length of instructions and sizes + other data for interleaved + VarIntBE.AppendInt32(0, sout); //length of addresses for copys + + //Google Checksum Support + if (hasChecksum) + { + VarIntBE.AppendInt64(checksum, sout); + } + + sout.writeBytes(instructionAndSizes.ToArray()); //data for instructions and sizes, in interleaved it is everything + } + //end of delta encoding + + Int64 sizeAfterDelta = sout.Position; + if(lengthOfDelta != sizeAfterDelta - sizeBeforeDelta) + { + Console.WriteLine("Delta output length does not match"); + } + dataForAddAndRun.Clear(); + instructionAndSizes.Clear(); + addressForCopy.Clear(); + if(targetLength == 0) + { + Console.WriteLine("Empty target window"); + } + addrCache = new AddressCache(); + } + } +} diff --git a/VCDiff/Includes/Include.cs b/VCDiff/Includes/Include.cs new file mode 100644 index 00000000..14c71d1f --- /dev/null +++ b/VCDiff/Includes/Include.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Includes +{ + public enum VCDiffResult + { + SUCCESS = 0, + ERRROR = -1, + EOD = -2 + } + + // The possible values for the Hdr_Indicator field, as described + // in section 4.1 of the RFC: + // + // "The Hdr_Indicator byte shows if there is any initialization data + // required to aid in the reconstruction of data in the Window sections. + // This byte MAY have non-zero values for either, both, or neither of + // the two bits VCD_DECOMPRESS and VCD_CODETABLE below: + // + // 7 6 5 4 3 2 1 0 + // +-+-+-+-+-+-+-+-+ + // | | | | | | | | | + // +-+-+-+-+-+-+-+-+ + // ^ ^ + // | | + // | +-- VCD_DECOMPRESS + // +---- VCD_CODETABLE + // + // If bit 0 (VCD_DECOMPRESS) is non-zero, this indicates that a + // secondary compressor may have been used to further compress certain + // parts of the delta encoding data [...]" + // [Secondary compressors are not supported by open-vcdiff.] + // + // "If bit 1 (VCD_CODETABLE) is non-zero, this indicates that an + // application-defined code table is to be used for decoding the delta + // instructions. [...]" + // + public enum VCDiffCodeFlags + { + VCDDECOMPRESS = 0x01, + VCDCODETABLE = 0x02 + } + + // The possible values for the Win_Indicator field, as described + // in section 4.2 of the RFC: + // + // "Win_Indicator: + // + // This byte is a set of bits, as shown: + // + // 7 6 5 4 3 2 1 0 + // +-+-+-+-+-+-+-+-+ + // | | | | | | | | | + // +-+-+-+-+-+-+-+-+ + // ^ ^ + // | | + // | +-- VCD_SOURCE + // +---- VCD_TARGET + // + // If bit 0 (VCD_SOURCE) is non-zero, this indicates that a + // segment of data from the "source" file was used as the + // corresponding source window of data to encode the target + // window. The decoder will use this same source data segment to + // decode the target window. + // + // If bit 1 (VCD_TARGET) is non-zero, this indicates that a + // segment of data from the "target" file was used as the + // corresponding source window of data to encode the target + // window. As above, this same source data segment is used to + // decode the target window. + // + // The Win_Indicator byte MUST NOT have more than one of the bits + // set (non-zero). It MAY have none of these bits set." + // + public enum VCDiffWindowFlags + { + VCDSOURCE = 0x01, + VCDTARGET = 0x02, + //Google Specific Flag + VCDCHECKSUM = 0x04 + } + + // The possible values for the Delta_Indicator field, as described + // in section 4.3 of the RFC: + // + // "Delta_Indicator: + // This byte is a set of bits, as shown: + // + // 7 6 5 4 3 2 1 0 + // +-+-+-+-+-+-+-+-+ + // | | | | | | | | | + // +-+-+-+-+-+-+-+-+ + // ^ ^ ^ + // | | | + // | | +-- VCD_DATACOMP + // | +---- VCD_INSTCOMP + // +------ VCD_ADDRCOMP + // + // VCD_DATACOMP: bit value 1. + // VCD_INSTCOMP: bit value 2. + // VCD_ADDRCOMP: bit value 4. + // + // [...] If the bit VCD_DECOMPRESS (Section 4.1) was on, each of these + // sections may have been compressed using the specified secondary + // compressor. The bit positions 0 (VCD_DATACOMP), 1 + // (VCD_INSTCOMP), and 2 (VCD_ADDRCOMP) respectively indicate, if + // non-zero, that the corresponding parts are compressed." + // [Secondary compressors are not supported, so open-vcdiff decoding will fail + // if these bits are not all zero.] + public enum VCDiffCompressFlags + { + VCDDATACOMP = 0x01, + VCDINSTCOMP = 0x02, + VCDADDRCOMP = 0x04 + } + + // The address modes used for COPY instructions, as defined in + // section 5.3 of the RFC. + // + // The first two modes (0 and 1) are defined as SELF (addressing forward + // from the beginning of the source window) and HERE (addressing backward + // from the current position in the source window + previously decoded + // target data.) + // + // After those first two modes, there are a variable number of NEAR modes + // (which take a recently-used address and add a positive offset to it) + // and SAME modes (which match a previously-used address using a "hash" of + // the lowest bits of the address.) The number of NEAR and SAME modes + // depends on the defined size of the address cache; since this number is + // variable, these modes cannot be specified as enum values. + public enum VCDiffModes + { + SELF = 0, + HERE = 1, + FIRST = 2, + MAX = byte.MaxValue + 1 + } + + public enum VCDiffInstructionType + { + NOOP = 0, + ADD = 1, + RUN = 2, + COPY = 3, + LAST = 3, + ERROR = 4, + EOD = 5 + } +} diff --git a/VCDiff/Properties/AssemblyInfo.cs b/VCDiff/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..7bbd37ec --- /dev/null +++ b/VCDiff/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("vcdiff")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("vcdiff")] +[assembly: AssemblyCopyright("Copyright © 2017")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("4d676f4e-e66d-4a41-861f-600f0ffcd052")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/VCDiff/README.markdown b/VCDiff/README.markdown new file mode 100644 index 00000000..57f43373 --- /dev/null +++ b/VCDiff/README.markdown @@ -0,0 +1,134 @@ +# open-vcdiff C# implementation + +This is a full implementation of open-vcdiff in C# based on [Google's open-vcdiff](https://github.com/google/open-vcdiff). This is written entirely in C# - no external C++ libraries required. This includes proper SDHC support with interleaving and checksums. The only thing it does not support is encoding with a custom CodeTable currently. Will be added later if requested, or feel free to add it in and send a pull request. + +It is fully compatible with Google's open-vcdiff for encoding and decoding. If you find any bugs please let me know. I tried to test as thoroughly as possible between this and Google's github version. The largest file I tested with was 10MB. Should be able to support up to 2-4GB depending on your system. + +## Requirements +Should be able to compile with any .Net platform greater than equal to 2.5. Mono should be able to compile it as well. + +# Encoding Data +The dictionary must be a file or data that is already in memory. The file must be fully read in first in order to encode properly. This is just how the algorithm works for VCDiff. The encode function is blocking. + +``` +using VCDiff.Include; +using VCDiff.Encoders; +using VCDiff.Shared; + +void DoEncode() { + using(FileStream output = new FileStream("...some output path", FileMode.Create, FileAccess.Write)) + using(FileStream dict = new FileStream("..dictionary / old file path", FileMode.Open, FileAccess.Read)) + using(FileStream target = new FileStream("..target data / new data path", FileMode.Open, FileMode.Read)) { + VCCoder coder = new VCCoder(dict, target, output); + VCDiffResult result = coder.Encode(); //encodes with no checksum and not interleaved + if(result != VCDiffResult.SUCCESS) { + //error was not able to encode properly + } + } +} + +``` + +Encoding with checksum or interleaved or both +``` +bool interleaved = true; +bool checksum = false; + +coder.Encode(interleaved, checksum); + +checksum = true; +coder.Encode(interleaved, checksum); +``` + +Modifying the default chunk size for windows + +``` +int windowSize = 2; //in Megabytes. The default is 1MB window chunks. + +VCCoder coder = new VCCoder(dict, target, output, windowSize) +``` + +Modifying the default minimum copy encode size. Which means the match must be >= MinBlockSize in order to qualify as match for copying from dictionary file. +``` +ChunkEncoder.MinBlockSize = 16; +//Default is 32 bytes. Lowering this can improve the delta compression for small files. +//Please keep it a power of 2. +//Anything lower than 2 or BlockHash.BlockSize is ignored. +``` + +Modifying the default BlockSize for hashing +``` +BlockHash.BlockSize = 32; +//increasing this for large files with lots of similar data can improve results. +//the default is 16. Please keep it a power of two. +//Lowering it for small files can also improve results. +//Anything lower than 2 will be ignored. +``` + +# Decoding Data +The dictionary must be a file or data that is already in memory. The file must be fully read in first in order to decode properly. This is just how the algorithm works for VCDiff. + +Due note the interleaved version of a delta file is meant for streaming and it is supported by the decoder already. However, non-interleaved expects access for reading the full delta file at one time. The delta file is still streamed, but must be able to read fully in sequential order. + +``` +using VCDiff.Include; +using VCDiff.Decoders; +using VCDiff.Shared; + +void DoDecode() { + using(FileStream output = new FileStream("...some output path", FileMode.Create, FileAccess.Write)) + using(FileStream dict = new FileStream("..dictionary / old file path", FileMode.Open, FileAccess.Read)) + using(FileStream target = new FileStream("..delta encoded part", FileMode.Open, FileMode.Read)) { + VCDecoder decoder = new VCDecoder(dict, target, output); + + //You must call decoder.Start() first. The header of the delta file must be available before calling decoder.Start() + + VCDiffResult result = decoder.Start(); + + if(result != VCDiffResult.SUCCESS) { + //error abort + } + + long bytesWritten = 0; + result = decoder.Decode(out bytesWritten); + + if(result != VCDiffResult.SUCCESS) { + //error decoding + } + + //if success bytesWritten will contain the number of bytes that were decoded + } +} + +``` + +Handling streaming of the interleaved format has the same setup. But instead you will continue calling decode until you know you have received everything. So, you will need to keep track of that. Everytime you loop through make sure you have enough data in the buffer to at least be able to decode the next VCDiff Window Header (which can be up to 22 bytes or so). After that the decode function will handle the waiting for the next part of the interleaved data for that VCDiff Window. The decode function is blocking. + +``` +while(bytesWritten < someSizeThatYouAreExpecting) { + //make sure we have enough data in buffer to at least try and decode the next window section + //otherwise we will probably receive an error. + if(myStream.Length < 22) continue; + + long thisChunk = 0; + result = decoder.Decode(out thisChunk); + + bytesWritten += thisChunk; + + //yes with three Rs. + if(result == VCDiffResult.ERRROR) { + //it failed to decode something + //could be an issue that the window failed to parse + //or actual data failed to decode properly + break; + } + + //otherwise continue on if you get SUCCESS or EOD (End of Data); + //because only you know when you will have the data finished loading + //the decoder doesn't care if nothing is available and it will keep trying until more is + //it is best to do this in a separate thread as it is blocking. +} +``` + +# Apache 2.0 License +This is licensed under the same license as open-vcdiff by Google. See [Apache 2.0 license](http://www.apache.org/licenses/LICENSE-2.0). \ No newline at end of file diff --git a/VCDiff/Shared/AddressCache.cs b/VCDiff/Shared/AddressCache.cs new file mode 100644 index 00000000..d1c5eddf --- /dev/null +++ b/VCDiff/Shared/AddressCache.cs @@ -0,0 +1,281 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Shared; +using VCDiff.Includes; + +namespace VCDiff.Shared +{ + public class AddressCache + { + /// + /// The address cache implementation as described in the RFC doc. + /// + const byte DefaultNearCacheSize = 4; + const byte DefaultSameCacheSize = 3; + byte nearSize; + byte sameSize; + long[] nearCache; + long[] sameCache; + int nextSlot; + + public byte NearSize + { + get + { + return nearSize; + } + } + + public byte SameSize + { + get + { + return sameSize; + } + } + + public byte FirstNear + { + get + { + return (byte)VCDiffModes.FIRST; + } + } + + public byte FirstSame + { + get + { + return (byte)(VCDiffModes.FIRST + nearSize); + } + } + + public byte Last + { + get + { + return (byte)(FirstSame + sameSize - 1); + } + } + + public static byte DefaultLast + { + get + { + return (byte)(VCDiffModes.FIRST + DefaultNearCacheSize + DefaultSameCacheSize - 1); + } + } + + public AddressCache(byte nearSize, byte sameSize) + { + this.sameSize = sameSize; + this.nearSize = nearSize; + this.nearCache = new long[nearSize]; + this.sameCache = new long[sameSize * 256]; + nextSlot = 0; + } + + public AddressCache() + { + this.sameSize = DefaultSameCacheSize; + this.nearSize = DefaultNearCacheSize; + this.nearCache = new long[nearSize]; + this.sameCache = new long[sameSize * 256]; + nextSlot = 0; + } + + static bool IsSelfMode(byte mode) + { + return mode == (byte)VCDiffModes.SELF; + } + + static bool IsHereMode(byte mode) + { + return mode == (byte)VCDiffModes.HERE; + } + + bool IsNearMode(byte mode) + { + return (mode >= FirstNear) && (mode < FirstSame); + } + + bool IsSameMode(byte mode) + { + return (mode >= FirstSame) && (mode <= Last); + } + + static long DecodeSelfAddress(long encoded) + { + return encoded; + } + + static long DecodeHereAddress(long encoded, long here) + { + return here - encoded; + } + + long DecodeNearAddress(byte mode, long encoded) + { + return NearAddress(mode - FirstNear) + encoded; + } + + long DecodeSameAddress(byte mode, byte encoded) + { + return SameAddress(((mode - FirstSame) * 256) + encoded); + } + + public bool WriteAddressAsVarint(byte mode) + { + return !IsSameMode(mode); + } + + long NearAddress(int pos) + { + return nearCache[pos]; + } + + long SameAddress(int pos) + { + return sameCache[pos]; + } + + void UpdateCache(long address) + { + if(nearSize > 0) + { + nearCache[nextSlot] = address; + nextSlot = (nextSlot + 1) % nearSize; + } + if(sameSize > 0) + { + sameCache[(int)(address % (sameSize * 256))] = address; + } + } + + public byte EncodeAddress(long address, long here, out long encoded) + { + if(address < 0) + { + encoded = 0; + return (byte)0; + } + if(address >= here) + { + encoded = 0; + return (byte)0; + } + + if(sameSize > 0) + { + int pos = (int)(address % (sameSize * 256)); + if(SameAddress(pos) == address) + { + UpdateCache(address); + encoded = (pos % 256); + return (byte)(FirstSame + (pos / 256)); + } + } + + byte bestMode = (byte)VCDiffModes.SELF; + long bestEncoded = address; + + long hereEncoded = here - address; + if(hereEncoded < bestEncoded) + { + bestMode = (byte)VCDiffModes.HERE; + bestEncoded = hereEncoded; + } + + for(int i = 0; i < nearSize; ++i) + { + long nearEncoded = address - NearAddress(i); + if((nearEncoded >= 0) && (nearEncoded < bestEncoded)) + { + bestMode = (byte)(FirstNear + i); + bestEncoded = nearEncoded; + } + } + + UpdateCache(address); + encoded = bestEncoded; + return bestMode; + } + + bool IsDecodedAddressValid(long decoded, long here) + { + if(decoded < 0) + { + return false; + } + else if(decoded >= here) + { + return false; + } + + return true; + } + + public long DecodeAddress(long here, byte mode, ByteBuffer sin) + { + long start = sin.Position; + if(here < 0) + { + return (int)VCDiffResult.ERRROR; + } + + if(!sin.CanRead) + { + return (int)VCDiffResult.EOD; + } + + long decoded = 0; + if(IsSameMode(mode)) + { + byte encoded = sin.ReadByte(); + decoded = DecodeSameAddress(mode, encoded); + } + else + { + int encoded = VarIntBE.ParseInt32(sin); + + switch(encoded) + { + case (int)VCDiffResult.ERRROR: + return encoded; + case (int)VCDiffResult.EOD: + sin.Position = start; + return encoded; + default: + break; + } + + if(IsSelfMode(mode)) + { + decoded = DecodeSelfAddress(encoded); + } + else if(IsHereMode(mode)) + { + decoded = DecodeHereAddress(encoded, here); + } + else if(IsNearMode(mode)) + { + decoded = DecodeNearAddress(mode, encoded); + } + else + { + return (int)VCDiffResult.ERRROR; + } + } + + if(!IsDecodedAddressValid(decoded, here)) + { + return (int)VCDiffResult.ERRROR; + } + UpdateCache(decoded); + return decoded; + } + } +} diff --git a/VCDiff/Shared/Adler32.cs b/VCDiff/Shared/Adler32.cs new file mode 100644 index 00000000..e26d806c --- /dev/null +++ b/VCDiff/Shared/Adler32.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Shared +{ + public class Adler32 + { + /// + /// Zlib implementation of the Adler32 Hash + /// + const uint BASE = 65521; + const uint NMAX = 5552; + + public static uint Combine(uint adler1, uint adler2, uint len) + { + uint sum1 = 0; + uint sum2 = 0; + uint rem = 0; + + rem = (uint)(len % BASE); + sum1 = adler1 & 0xffff; + sum2 = rem * sum1; + sum2 %= BASE; + sum1 += (adler2 & 0xffff) + BASE - 1; + sum2 += ((adler1 >> 16) & 0xffff) + ((adler2 >> 16) & 0xffff) + BASE - rem; + if (sum1 >= BASE) sum1 -= BASE; + if (sum1 >= BASE) sum1 -= BASE; + if (sum2 >= (BASE << 1)) sum2 -= (BASE << 1); + if (sum2 >= BASE) sum2 -= BASE; + return sum1 | (sum2 << 16); + } + + public static uint Hash(uint adler, byte[] buff) + { + if (buff == null || buff.Length == 0) return 1; + + uint sum2 = 0; + uint n = 0; + + sum2 = (adler >> 16) & 0xffff; + adler &= 0xffff; + + if(buff.Length == 1) + { + adler += buff[0]; + if(adler >= BASE) + { + adler -= BASE; + } + sum2 += adler; + if(sum2 >= BASE) + { + sum2 -= BASE; + } + return adler | (sum2 << 16); + } + + if(buff.Length < 16) + { + for(int i = 0; i < buff.Length; i++) + { + adler += buff[i]; + sum2 += adler; + } + if(adler >= BASE) + { + adler -= BASE; + } + sum2 %= BASE; + return adler | (sum2 << 16); + } + + uint len = (uint)buff.Length; + int dof = 0; + while(len >= NMAX) + { + len -= NMAX; + n = NMAX / 16; + do + { + DO16(adler, sum2, buff, dof, out adler, out sum2); + dof += 16; + } while (--n > 0); + adler %= BASE; + sum2 %= BASE; + } + + if(len > 0) + { + while(len >= 16) + { + len -= 16; + DO16(adler, sum2, buff, dof, out adler, out sum2); + dof += 16; + } + while(len-- > 0) + { + adler += buff[dof++]; + sum2 += adler; + } + adler %= BASE; + sum2 %= BASE; + } + + return adler | (sum2 << 16); + } + + static void DO1(uint adler, uint sum, byte[] buff, int i, out uint ald, out uint s) + { + adler += buff[i]; + sum += adler; + ald = adler; + s = sum; + } + static void DO2(uint adler, uint sum, byte[] buff, int i, out uint ald, out uint s) + { + DO1(adler, sum, buff, i, out ald, out s); + DO1(ald, s, buff, i + 1, out ald, out s); + } + static void DO4(uint adler, uint sum, byte[] buff, int i, out uint ald, out uint s) + { + DO2(adler, sum, buff, i, out ald, out s); + DO2(ald, s, buff, i+2, out ald, out s); + } + static void DO8(uint adler, uint sum, byte[] buff, int i, out uint ald, out uint s) + { + DO4(adler, sum, buff, i, out ald, out s); + DO4(ald, s, buff, i + 4, out ald, out s); + } + static void DO16(uint adler, uint sum, byte[] buff, int i, out uint ald, out uint s) + { + DO8(adler, sum, buff, 0 + i, out ald, out s); + DO8(ald, s, buff, 8 + i, out ald, out s); + } + } +} diff --git a/VCDiff/Shared/ByteBuffer.cs b/VCDiff/Shared/ByteBuffer.cs new file mode 100644 index 00000000..91fae0e3 --- /dev/null +++ b/VCDiff/Shared/ByteBuffer.cs @@ -0,0 +1,127 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Shared +{ + public class ByteBuffer : IByteBuffer, IDisposable + { + byte[] bytes; + int length; + long offset; + + /// + /// Basically a simple wrapper for byte[] arrays + /// for easier reading and parsing + /// + /// + public ByteBuffer(byte[] bytes) + { + offset = 0; + this.bytes = bytes; + if (bytes != null) + { + this.length = bytes.Length; + } + else + { + this.length = 0; + } + } + + public override bool CanRead + { + get + { + return offset < length; + } + } + + public override long Position + { + get + { + return offset; + } + set + { + if (value > bytes.Length || value < 0) return; + offset = value; + } + } + + public override void BufferAll() + { + //not implemented in this one + //since it already contains the full buffered data + } + + public override long Length + { + get + { + return length; + } + } + + public override byte PeekByte() + { + if (offset >= length) throw new Exception("Trying to read past End of Buffer"); + return this.bytes[offset]; + } + + public override byte[] PeekBytes(int len) + { + int end = (int)offset + len > bytes.Length ? bytes.Length : (int)offset + len; + int realLen = (int)offset + len > bytes.Length ? (int)bytes.Length - (int)offset : len; + + byte[] rbuff = new byte[realLen]; + int cc = 0; + for (long i = offset; i < end; i++) + { + rbuff[cc] = bytes[i]; + cc++; + } + return rbuff; + } + + public override byte ReadByte() + { + if (offset >= length) throw new Exception("Trying to read past End of Buffer"); + return this.bytes[offset++]; + } + + public override byte[] ReadBytes(int len) + { + int end = (int)offset + len > bytes.Length ? bytes.Length : (int)offset + len; + int realLen = (int)offset + len > bytes.Length ? (int)bytes.Length - (int)offset : len; + + byte[] rbuff = new byte[realLen]; + int cc = 0; + for (long i = offset; i < end; i++) + { + rbuff[cc] = bytes[i]; + cc++; + } + offset += len; + return rbuff; + } + + public override void Next() + { + offset++; + } + + public override void Skip(int len) + { + offset += len; + } + + public override void Dispose() + { + bytes = null; + } + } +} diff --git a/VCDiff/Shared/ByteStreamReader.cs b/VCDiff/Shared/ByteStreamReader.cs new file mode 100644 index 00000000..8f394259 --- /dev/null +++ b/VCDiff/Shared/ByteStreamReader.cs @@ -0,0 +1,229 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; + +namespace VCDiff.Shared +{ + //Wrapper Class for any stream that supports Position + //and Length to make reading bytes easier + //also has a helper function for reading all the bytes in at once + public class ByteStreamReader : IByteBuffer, IDisposable + { + Stream buffer; + int lastLenRead; + bool readAll; + List internalBuffer; + long offset; + + public ByteStreamReader(Stream stream) + { + buffer = stream; + } + + public override long Position + { + get + { + if(readAll) + { + return offset; + } + return buffer.Position; + } + set + { + if(readAll) + { + if(value >= 0) + offset = value; + } + if(buffer.CanRead && value >= 0) + buffer.Position = value; + } + } + + public override long Length + { + get + { + if(readAll) + { + return internalBuffer.Count; + } + + if (buffer.CanRead) + return buffer.Length; + + return 0; + } + } + + public override bool CanRead + { + get + { + if (readAll) + { + return offset < internalBuffer.Count; + } + + return buffer.CanRead && buffer.Position < buffer.Length; + } + } + + public override void BufferAll() + { + if (!readAll) + { + offset = 0; + internalBuffer = new List(); + readAll = true; + + byte[] buff = new byte[1024 * 8]; + + lastLenRead = buffer.Read(buff, 0, buff.Length); + + while (lastLenRead > 0 && buffer.CanRead) + { + for (int i = 0; i < lastLenRead; i++) + { + internalBuffer.Add(buff[i]); + } + + lastLenRead = buffer.Read(buff, 0, buff.Length); + } + } + } + + public override byte[] PeekBytes(int len) + { + if(readAll) + { + + int end = (int)offset + len > internalBuffer.Count ? internalBuffer.Count : (int)offset + len; + int realLen = (int)offset + len > internalBuffer.Count ? (int)internalBuffer.Count - (int)offset : len; + + byte[] rbuff = new byte[realLen]; + int rcc = 0; + for(int i = (int)offset; i < end; i++) + { + rbuff[rcc] = internalBuffer[i]; + rcc++; + } + return rbuff; + } + + long oldPos = buffer.Position; + byte[] buf = new byte[len]; + + int actualRead = buffer.Read(buf, 0, len); + lastLenRead = actualRead; + if (actualRead > 0) + { + if (actualRead == len) + { + buffer.Position = oldPos; + return buf; + } + + byte[] actualData = new byte[actualRead]; + for (int i = 0; i < actualRead; i++) + { + actualData[i] = buf[i]; + } + + buffer.Position = oldPos; + return actualData; + } + + buffer.Position = oldPos; + return new byte[0]; + } + + public override byte ReadByte() + { + if (!CanRead) throw new Exception("Trying to read past end of buffer"); + if(readAll) + { + return internalBuffer[(int)offset++]; + } + lastLenRead = buffer.ReadByte(); + if (lastLenRead > -1) + return (byte)lastLenRead; + return 0; + } + + public override byte[] ReadBytes(int len) + { + if (readAll) + { + int end = (int)offset + len > internalBuffer.Count ? internalBuffer.Count : (int)offset + len; + int realLen = (int)offset + len > internalBuffer.Count ? (int)internalBuffer.Count - (int)offset : len; + + byte[] rbuff = new byte[realLen]; + int rcc = 0; + for (int i = (int)offset; i < end; i++) + { + rbuff[rcc] = internalBuffer[i]; + rcc++; + } + offset += len; + return rbuff; + } + + byte[] buf = new byte[len]; + + int actualRead = buffer.Read(buf, 0, len); + lastLenRead = actualRead; + if(actualRead > 0) + { + if(actualRead == len) + { + return buf; + } + + byte[] actualData = new byte[actualRead]; + for(int i = 0; i < actualRead; i++) + { + actualData[i] = buf[i]; + } + + return actualData; + } + + return new byte[0]; + } + + public override byte PeekByte() + { + if (!CanRead) throw new Exception("Trying to read past end of buffer"); + if(readAll) + { + return internalBuffer[(int)offset]; + } + long lastPos = buffer.Position; + byte b = ReadByte(); + buffer.Position = lastPos; + return b; + } + + //increases the offset by 1 + public override void Next() + { + buffer.Position++; + } + + public override void Skip(int len) + { + buffer.Position += len; + } + + public override void Dispose() + { + buffer.Dispose(); + } + } +} diff --git a/VCDiff/Shared/ByteStreamWriter.cs b/VCDiff/Shared/ByteStreamWriter.cs new file mode 100644 index 00000000..dcab107f --- /dev/null +++ b/VCDiff/Shared/ByteStreamWriter.cs @@ -0,0 +1,110 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace VCDiff.Shared +{ + public class ByteStreamWriter : IDisposable + { + Stream buffer; + + bool isLittle; + + /// + /// Wrapper class for writing to streams + /// with a little bit easier functionality + /// also detects whether it is little endian + /// to encode into BE properly + /// + /// + public ByteStreamWriter(Stream s) + { + buffer = s; + isLittle = BitConverter.IsLittleEndian; + } + + public byte[] ToArray() + { + if(buffer.GetType().Equals(typeof(MemoryStream))) + { + MemoryStream buff = (MemoryStream)buffer; + return buff.ToArray(); + } + + return new byte[0]; + } + + public long Position + { + get + { + return buffer.Position; + } + } + + public void writeByte(byte b) + { + this.buffer.WriteByte(b); + } + + public void writeBytes(byte[] b) + { + this.buffer.Write(b, 0, b.Length); + } + + public void writeUInt16(ushort s) + { + byte[] bytes = BitConverter.GetBytes(s); + + if (isLittle) + { + Array.Reverse(bytes); + } + + this.writeBytes(bytes); + } + + public void writeUInt32(uint i) + { + byte[] bytes = BitConverter.GetBytes(i); + + if (isLittle) + { + Array.Reverse(bytes); + } + + this.writeBytes(bytes); + } + + public void writeFloat(float f) + { + byte[] bytes = BitConverter.GetBytes(f); + + if (isLittle) + { + Array.Reverse(bytes); + } + + this.writeBytes(bytes); + } + + public void writeDouble(double d) + { + byte[] bytes = BitConverter.GetBytes(d); + + if (isLittle) + { + Array.Reverse(bytes); + } + + this.writeBytes(bytes); + } + + public void Dispose() + { + this.buffer.Dispose(); + } + } +} diff --git a/VCDiff/Shared/Checksum.cs b/VCDiff/Shared/Checksum.cs new file mode 100644 index 00000000..447ffabb --- /dev/null +++ b/VCDiff/Shared/Checksum.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Shared +{ + public class Checksum + { + public static uint ComputeAdler32(byte[] buffer) + { + return Adler32.Hash(0, buffer); + } + + public static long UpdateAdler32(uint partial, byte[] buffer) + { + return Adler32.Hash(partial, buffer); + } + } +} diff --git a/VCDiff/Shared/CodeTable.cs b/VCDiff/Shared/CodeTable.cs new file mode 100644 index 00000000..54cff523 --- /dev/null +++ b/VCDiff/Shared/CodeTable.cs @@ -0,0 +1,271 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Includes; + +namespace VCDiff.Shared +{ + public class CodeTable + { + /// + /// Default CodeTable as described in the RFC doc + /// + public const int kNoOpcode = 0x100; + public const int kCodeTableSize = 256; + + public byte[] inst1; + public byte[] inst2; + public byte[] size1; + public byte[] size2; + public byte[] mode1; + public byte[] mode2; + + public const byte N = (byte)VCDiffInstructionType.NOOP; + public const byte A = (byte)VCDiffInstructionType.ADD; + public const byte R = (byte)VCDiffInstructionType.RUN; + public const byte C = (byte)VCDiffInstructionType.COPY; + public const byte ERR = (byte)VCDiffInstructionType.ERROR; + public const byte EOD = (byte)VCDiffInstructionType.EOD; + + public static CodeTable DefaultTable + { + get + { + return new CodeTable(); + } + } + + public CodeTable() + { + inst1 = new byte[] + { + R, // opcode 0 + A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 1-18 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 19-34 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 35-50 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 51-66 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 67-82 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 83-98 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 99-114 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 115-130 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 131-146 + C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 147-162 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 163-174 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 175-186 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 187-198 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 199-210 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 211-222 + A, A, A, A, A, A, A, A, A, A, A, A, // opcodes 223-234 + A, A, A, A, // opcodes 235-238 + A, A, A, A, // opcodes 239-242 + A, A, A, A, // opcodes 243-246 + C, C, C, C, C, C, C, C, C // opcodes 247-255 + }; + inst2 = new byte[] + { + N, // opcode 0 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 1-18 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 19-34 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 35-50 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 51-66 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 67-82 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 83-98 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 99-114 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 115-130 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 131-146 + N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, N, // opcodes 147-162 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 163-174 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 175-186 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 187-198 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 199-210 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 211-222 + C, C, C, C, C, C, C, C, C, C, C, C, // opcodes 223-234 + C, C, C, C, // opcodes 235-238 + C, C, C, C, // opcodes 239-242 + C, C, C, C, // opcodes 243-246 + A, A, A, A, A, A, A, A, A // opcodes 247-255 + }; + size1 = new byte[] + { + 0, // opcode 0 + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, // 1-18 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 19-34 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 35-50 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 51-66 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 67-82 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 83-98 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 99-114 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 115-130 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 131-146 + 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, // 147-162 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 163-174 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 175-186 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 187-198 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 199-210 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 211-222 + 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, // opcodes 223-234 + 1, 2, 3, 4, // opcodes 235-238 + 1, 2, 3, 4, // opcodes 239-242 + 1, 2, 3, 4, // opcodes 243-246 + 4, 4, 4, 4, 4, 4, 4, 4, 4 // opcodes 247-255 + }; + size2 = new byte[] + { + 0, // opcode 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 1-18 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 19-34 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 35-50 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 51-66 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 67-82 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 83-98 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 99-114 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 115-130 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 131-146 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 147-162 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 163-174 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 175-186 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 187-198 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 199-210 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 211-222 + 4, 5, 6, 4, 5, 6, 4, 5, 6, 4, 5, 6, // opcodes 223-234 + 4, 4, 4, 4, // opcodes 235-238 + 4, 4, 4, 4, // opcodes 239-242 + 4, 4, 4, 4, // opcodes 243-246 + 1, 1, 1, 1, 1, 1, 1, 1, 1 // opcodes 247-255 + }; + mode1 = new byte[] + { + 0, // opcode 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 1-18 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 19-34 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // opcodes 35-50 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // opcodes 51-66 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // opcodes 67-82 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // opcodes 83-98 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // opcodes 99-114 + 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, 6, // opcodes 115-130 + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // opcodes 131-146 + 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, // opcodes 147-162 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 163-174 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 175-186 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 187-198 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 199-210 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 211-222 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 223-234 + 0, 0, 0, 0, // opcodes 235-238 + 0, 0, 0, 0, // opcodes 239-242 + 0, 0, 0, 0, // opcodes 243-246 + 0, 1, 2, 3, 4, 5, 6, 7, 8 // opcodes 247-255 + }; + mode2 = new byte[] + { + 0, // opcode 0 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 1-18 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 19-34 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 35-50 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 51-66 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 67-82 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 83-98 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 99-114 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 115-130 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 131-146 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 147-162 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // opcodes 163-174 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // opcodes 175-186 + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, // opcodes 187-198 + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // opcodes 199-210 + 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, // opcodes 211-222 + 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, // opcodes 223-234 + 6, 6, 6, 6, // opcodes 235-238 + 7, 7, 7, 7, // opcodes 239-242 + 8, 8, 8, 8, // opcodes 243-246 + 0, 0, 0, 0, 0, 0, 0, 0, 0 // opcodes 247-255 + }; + } + + public bool SetBytes(byte[] items) + { + if(items.Length != 256 * 6) + { + return false; + } + + int offset = 0; + for(int i = 0; i < 256; i++) + { + inst1[i] = items[offset + i]; + } + offset += 256; + for(int i = 0; i < 256; i++) + { + inst2[i] = items[offset + i]; + } + offset += 256; + for(int i = 0; i < 256; i++) + { + size1[i] = items[offset + i]; + } + offset += 256; + for(int i = 0; i < 256; i++) + { + size2[i] = items[offset + i]; + } + offset += 256; + for(int i = 0; i < 256; i++) + { + mode1[i] = items[offset + i]; + } + offset += 256; + for(int i = 0; i < 256; i++) + { + mode2[i] = items[offset + i]; + } + + return true; + } + + public IByteBuffer GetBytes() + { + List bytes = new List(); + + for(int i = 0; i < inst1.Length; i++) + { + bytes.Add(inst1[i]); + } + for(int i = 0; i < inst2.Length; i++) + { + bytes.Add(inst2[i]); + } + for(int i = 0; i < size1.Length; i++) + { + bytes.Add(size1[i]); + } + for(int i = 0; i < size2.Length; i++) + { + bytes.Add(size2[i]); + } + for (int i = 0; i < mode1.Length; i++) + { + bytes.Add(mode1[i]); + } + for (int i = 0; i < mode2.Length; i++) + { + bytes.Add(mode2[i]); + } + + return new ByteBuffer(bytes.ToArray()); + } + + static bool Validate(byte maxMode) + { + return true; + } + + static bool ValidateOpCode(int opcode, byte inst, byte size, byte mode, byte maxMode) + { + return true; + } + } +} diff --git a/VCDiff/Shared/IByteBuffer.cs b/VCDiff/Shared/IByteBuffer.cs new file mode 100644 index 00000000..9e51c116 --- /dev/null +++ b/VCDiff/Shared/IByteBuffer.cs @@ -0,0 +1,35 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace VCDiff.Shared +{ + public abstract class IByteBuffer : IDisposable + { + public abstract long Length + { + get; + } + + public abstract long Position + { + get; set; + } + + public abstract bool CanRead + { + get; + } + public abstract byte[] ReadBytes(int len); + public abstract byte ReadByte(); + public abstract byte[] PeekBytes(int len); + public abstract byte PeekByte(); + public abstract void Skip(int len); + public abstract void Next(); + public abstract void BufferAll(); + + public abstract void Dispose(); + } +} diff --git a/VCDiff/Shared/VarIntBE.cs b/VCDiff/Shared/VarIntBE.cs new file mode 100644 index 00000000..145cb6a9 --- /dev/null +++ b/VCDiff/Shared/VarIntBE.cs @@ -0,0 +1,192 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using VCDiff.Includes; + +namespace VCDiff.Shared +{ + public class VarIntBE + { + /// + /// Special VarIntBE class for encoding a Variable BE Integer + /// + public const int int32Max = 5; + public const int int64Max = 9; + + public const int int32MaxValue = 0x7FFFFFFF; + public const long int64MaxValue = 0x7FFFFFFFFFFFFFFF; + + public static int ParseInt32(IByteBuffer sin) + { + int result = 0; + while (sin.CanRead) + { + result += sin.PeekByte() & 0x7f; + if ((sin.PeekByte() & 0x80) == 0) + { + sin.Next(); + return result; + } + if (result > (int32MaxValue >> 7)) + { + return (int)VCDiffResult.ERRROR; + } + result = result << 7; + sin.Next(); + } + return (int)VCDiffResult.EOD; + } + + public static long ParseInt64(IByteBuffer sin) + { + long result = 0; + while(sin.CanRead) + { + result += (long)(sin.PeekByte() & 0x7F); + if ((sin.PeekByte() & 0x80) == 0) + { + sin.Next(); + return result; + } + if (result > (int64MaxValue >> 7)) + { + return (long)VCDiffResult.ERRROR; + } + result = result << 7; + sin.Next(); + } + return (int)VCDiffResult.EOD; + } + + public static int CalcInt32Length(int v) + { + if(v < 0) + { + return 0; + } + int length = 0; + do + { + v >>= 7; + ++length; + } while (v > 0); + return length; + } + + public static int CalcInt64Length(long v) + { + if (v < 0) + { + return 0; + } + int length = 0; + do + { + v >>= 7; + ++length; + } while (v > 0); + return length; + } + + public static int AppendInt32(int v, List buffer) + { + byte[] varint = new byte[int32Max]; + int length = EncodeInt32(v, varint); + int start = int32Max - length; + for (int i = start; i < int32Max; i++) + { + buffer.Add(varint[i]); + } + return length; + } + + public static int AppendInt32(int v, ByteStreamWriter sout) + { + byte[] varint = new byte[int32Max]; + int length = EncodeInt32(v, varint); + int start = int32Max - length; + for (int i = start; i < int32Max; i++) + { + sout.writeByte(varint[i]); + } + return length; + } + + public static int AppendInt64(long v, ByteStreamWriter sout) + { + byte[] varint = new byte[int64Max]; + int length = EncodeInt64(v, varint); + int start = int64Max - length; + for(int i = start; i < int64Max; i++) + { + sout.writeByte(varint[i]); + } + return length; + } + + public static int AppendInt64(long v, List buffer) + { + byte[] varint = new byte[int64Max]; + int length = EncodeInt64(v, varint); + int start = int64Max - length; + for(int i = start; i < int64Max; i++) + { + buffer.Add(varint[i]); + } + + return length; + } + + //v cannot be negative! + //the buffer must be of size: int32Max + public static int EncodeInt32(int v, byte[] sout) + { + if(v < 0) + { + return 0; + } + + int length = 1; + int idx = int32Max - 1; + sout[idx] = (byte)(v & 0x7F); + --idx; + v >>= 7; + while(v > 0) + { + sout[idx] = (byte)((v & 0x7F) | 0x80); + --idx; + ++length; + v >>= 7; + } + + return length; + } + + //v cannot be negative! + //the buffer must be of size: int64Max + public static int EncodeInt64(long v, byte[] sout) + { + if (v < 0) + { + return 0; + } + + int length = 1; + int idx = int64Max - 1; + sout[idx] = (byte)(v & 0x7F); + --idx; + v >>= 7; + while (v > 0) + { + sout[idx] = (byte)((v & 0x7F) | 0x80); + --idx; + ++length; + v >>= 7; + } + + return length; + } + } +} diff --git a/VCDiff/VCDiff.csproj b/VCDiff/VCDiff.csproj new file mode 100644 index 00000000..31c422a0 --- /dev/null +++ b/VCDiff/VCDiff.csproj @@ -0,0 +1,76 @@ + + + + + Debug + AnyCPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052} + Library + Properties + VCDiff + VCDiff + v4.5.2 + 512 + + + true + full + false + .\ + DEBUG;TRACE + prompt + 4 + AnyCPU + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/VCDiff/VCDiff.dll b/VCDiff/VCDiff.dll new file mode 100644 index 0000000000000000000000000000000000000000..336cf01fba6d79a692d5dda470c46b728b5f1cf5 GIT binary patch literal 40960 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�l~ah(h+qPF#FVdkml7#!+&AVK}(1su!@|4xyJuz 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 QO@Fo${{MpgAJoAA04K5yvH$=8 literal 0 HcmV?d00001 diff --git a/VCDiff/VCDiff.sln b/VCDiff/VCDiff.sln new file mode 100644 index 00000000..aa0763be --- /dev/null +++ b/VCDiff/VCDiff.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VCDiff", "VCDiff.csproj", "{4D676F4E-E66D-4A41-861F-600F0FFCD052}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D676F4E-E66D-4A41-861F-600F0FFCD052}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal From ac319c8bc4771078d1d910ffe595a9169db2a963 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Sun, 13 Dec 2020 18:27:18 -0400 Subject: [PATCH 2/6] Created Diff Tool in Tool Menu option --- HSDRawViewer/GUI/Extra/DiffTool.Designer.cs | 411 ++++++++++++++++++++ HSDRawViewer/GUI/Extra/DiffTool.cs | 88 +++++ HSDRawViewer/GUI/Extra/DiffTool.resx | 120 ++++++ HSDRawViewer/HSDRawViewer.csproj | 9 + HSDRawViewer/MainForm.Designer.cs | 34 +- HSDRawViewer/MainForm.cs | 46 +-- HSDRawViewer/Tools/FileIO.cs | 42 +- 7 files changed, 708 insertions(+), 42 deletions(-) create mode 100644 HSDRawViewer/GUI/Extra/DiffTool.Designer.cs create mode 100644 HSDRawViewer/GUI/Extra/DiffTool.cs create mode 100644 HSDRawViewer/GUI/Extra/DiffTool.resx diff --git a/HSDRawViewer/GUI/Extra/DiffTool.Designer.cs b/HSDRawViewer/GUI/Extra/DiffTool.Designer.cs new file mode 100644 index 00000000..ef9bb15d --- /dev/null +++ b/HSDRawViewer/GUI/Extra/DiffTool.Designer.cs @@ -0,0 +1,411 @@ +namespace HSDRawViewer.GUI.Extra +{ + partial class DiffTool + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.groupBox1 = new System.Windows.Forms.GroupBox(); + this.groupBox3 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel(); + this.label4 = new System.Windows.Forms.Label(); + this.label5 = new System.Windows.Forms.Label(); + this.label6 = new System.Windows.Forms.Label(); + this.mergeFileInput = new System.Windows.Forms.TextBox(); + this.mergeDiffFileInput = new System.Windows.Forms.TextBox(); + this.mergeOriginalFileInput = new System.Windows.Forms.TextBox(); + this.button4 = new System.Windows.Forms.Button(); + this.button5 = new System.Windows.Forms.Button(); + this.button6 = new System.Windows.Forms.Button(); + this.mergeDiffButton = new System.Windows.Forms.Button(); + this.groupBox2 = new System.Windows.Forms.GroupBox(); + this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel(); + this.label1 = new System.Windows.Forms.Label(); + this.label2 = new System.Windows.Forms.Label(); + this.label3 = new System.Windows.Forms.Label(); + this.saveDiffFileInput = new System.Windows.Forms.TextBox(); + this.saveModifiedFileInput = new System.Windows.Forms.TextBox(); + this.saveOriginalFileInput = new System.Windows.Forms.TextBox(); + this.button1 = new System.Windows.Forms.Button(); + this.button2 = new System.Windows.Forms.Button(); + this.button3 = new System.Windows.Forms.Button(); + this.saveDiffButton = new System.Windows.Forms.Button(); + this.groupBox1.SuspendLayout(); + this.groupBox3.SuspendLayout(); + this.tableLayoutPanel2.SuspendLayout(); + this.groupBox2.SuspendLayout(); + this.tableLayoutPanel1.SuspendLayout(); + this.SuspendLayout(); + // + // groupBox1 + // + this.groupBox1.AutoSize = true; + this.groupBox1.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink; + this.groupBox1.Controls.Add(this.groupBox3); + this.groupBox1.Controls.Add(this.groupBox2); + this.groupBox1.Dock = System.Windows.Forms.DockStyle.Fill; + this.groupBox1.Location = new System.Drawing.Point(0, 0); + this.groupBox1.Name = "groupBox1"; + this.groupBox1.Size = new System.Drawing.Size(486, 285); + this.groupBox1.TabIndex = 13; + this.groupBox1.TabStop = false; + this.groupBox1.Text = "Diff Tool"; + // + // groupBox3 + // + this.groupBox3.AutoSize = true; + this.groupBox3.Controls.Add(this.tableLayoutPanel2); + this.groupBox3.Dock = System.Windows.Forms.DockStyle.Top; + this.groupBox3.Location = new System.Drawing.Point(3, 151); + this.groupBox3.Name = "groupBox3"; + this.groupBox3.Size = new System.Drawing.Size(480, 135); + this.groupBox3.TabIndex = 15; + this.groupBox3.TabStop = false; + this.groupBox3.Text = "Load Diff From File"; + // + // tableLayoutPanel2 + // + this.tableLayoutPanel2.AutoSize = true; + this.tableLayoutPanel2.BackColor = System.Drawing.Color.Transparent; + this.tableLayoutPanel2.ColumnCount = 3; + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel2.Controls.Add(this.label4, 0, 0); + this.tableLayoutPanel2.Controls.Add(this.label5, 0, 1); + this.tableLayoutPanel2.Controls.Add(this.label6, 0, 2); + this.tableLayoutPanel2.Controls.Add(this.mergeFileInput, 1, 2); + this.tableLayoutPanel2.Controls.Add(this.mergeDiffFileInput, 1, 1); + this.tableLayoutPanel2.Controls.Add(this.mergeOriginalFileInput, 1, 0); + this.tableLayoutPanel2.Controls.Add(this.button4, 2, 0); + this.tableLayoutPanel2.Controls.Add(this.button5, 2, 1); + this.tableLayoutPanel2.Controls.Add(this.button6, 2, 2); + this.tableLayoutPanel2.Controls.Add(this.mergeDiffButton, 1, 3); + this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Top; + this.tableLayoutPanel2.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel2.Name = "tableLayoutPanel2"; + this.tableLayoutPanel2.RowCount = 4; + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel2.Size = new System.Drawing.Size(474, 116); + this.tableLayoutPanel2.TabIndex = 0; + // + // label4 + // + this.label4.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label4.AutoSize = true; + this.label4.Location = new System.Drawing.Point(3, 8); + this.label4.Name = "label4"; + this.label4.Size = new System.Drawing.Size(61, 13); + this.label4.TabIndex = 0; + this.label4.Text = "Original File"; + // + // label5 + // + this.label5.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label5.AutoSize = true; + this.label5.Location = new System.Drawing.Point(13, 37); + this.label5.Name = "label5"; + this.label5.Size = new System.Drawing.Size(42, 13); + this.label5.TabIndex = 1; + this.label5.Text = "Diff File"; + // + // label6 + // + this.label6.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label6.AutoSize = true; + this.label6.Location = new System.Drawing.Point(3, 66); + this.label6.Name = "label6"; + this.label6.Size = new System.Drawing.Size(62, 13); + this.label6.TabIndex = 2; + this.label6.Text = "Merged File"; + // + // mergeFileInput + // + this.mergeFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.mergeFileInput.Location = new System.Drawing.Point(71, 61); + this.mergeFileInput.Name = "mergeFileInput"; + this.mergeFileInput.Size = new System.Drawing.Size(319, 20); + this.mergeFileInput.TabIndex = 3; + // + // mergeDiffFileInput + // + this.mergeDiffFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.mergeDiffFileInput.Location = new System.Drawing.Point(71, 32); + this.mergeDiffFileInput.Name = "mergeDiffFileInput"; + this.mergeDiffFileInput.Size = new System.Drawing.Size(319, 20); + this.mergeDiffFileInput.TabIndex = 4; + // + // mergeOriginalFileInput + // + this.mergeOriginalFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.mergeOriginalFileInput.Location = new System.Drawing.Point(71, 3); + this.mergeOriginalFileInput.Name = "mergeOriginalFileInput"; + this.mergeOriginalFileInput.Size = new System.Drawing.Size(319, 20); + this.mergeOriginalFileInput.TabIndex = 5; + // + // button4 + // + this.button4.Dock = System.Windows.Forms.DockStyle.Fill; + this.button4.Location = new System.Drawing.Point(396, 3); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(75, 23); + this.button4.TabIndex = 6; + this.button4.Text = "Select"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // button5 + // + this.button5.Dock = System.Windows.Forms.DockStyle.Fill; + this.button5.Location = new System.Drawing.Point(396, 32); + this.button5.Name = "button5"; + this.button5.Size = new System.Drawing.Size(75, 23); + this.button5.TabIndex = 7; + this.button5.Text = "Select"; + this.button5.UseVisualStyleBackColor = true; + this.button5.Click += new System.EventHandler(this.button5_Click); + // + // button6 + // + this.button6.Dock = System.Windows.Forms.DockStyle.Fill; + this.button6.Location = new System.Drawing.Point(396, 61); + this.button6.Name = "button6"; + this.button6.Size = new System.Drawing.Size(75, 23); + this.button6.TabIndex = 8; + this.button6.Text = "Select"; + this.button6.UseVisualStyleBackColor = true; + this.button6.Click += new System.EventHandler(this.button6_Click); + // + // mergeDiffButton + // + this.mergeDiffButton.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.mergeDiffButton.Location = new System.Drawing.Point(71, 90); + this.mergeDiffButton.Name = "mergeDiffButton"; + this.mergeDiffButton.Size = new System.Drawing.Size(319, 23); + this.mergeDiffButton.TabIndex = 9; + this.mergeDiffButton.Text = "Merge Diff"; + this.mergeDiffButton.UseVisualStyleBackColor = true; + this.mergeDiffButton.Click += new System.EventHandler(this.mergeDiffButton_Click); + // + // groupBox2 + // + this.groupBox2.AutoSize = true; + this.groupBox2.Controls.Add(this.tableLayoutPanel1); + this.groupBox2.Dock = System.Windows.Forms.DockStyle.Top; + this.groupBox2.Location = new System.Drawing.Point(3, 16); + this.groupBox2.Name = "groupBox2"; + this.groupBox2.Size = new System.Drawing.Size(480, 135); + this.groupBox2.TabIndex = 14; + this.groupBox2.TabStop = false; + this.groupBox2.Text = "Save Diff To File"; + // + // tableLayoutPanel1 + // + this.tableLayoutPanel1.AutoSize = true; + this.tableLayoutPanel1.BackColor = System.Drawing.Color.Transparent; + this.tableLayoutPanel1.ColumnCount = 3; + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F)); + this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle()); + this.tableLayoutPanel1.Controls.Add(this.label1, 0, 0); + this.tableLayoutPanel1.Controls.Add(this.label2, 0, 1); + this.tableLayoutPanel1.Controls.Add(this.label3, 0, 2); + this.tableLayoutPanel1.Controls.Add(this.saveDiffFileInput, 1, 2); + this.tableLayoutPanel1.Controls.Add(this.saveModifiedFileInput, 1, 1); + this.tableLayoutPanel1.Controls.Add(this.saveOriginalFileInput, 1, 0); + this.tableLayoutPanel1.Controls.Add(this.button1, 2, 0); + this.tableLayoutPanel1.Controls.Add(this.button2, 2, 1); + this.tableLayoutPanel1.Controls.Add(this.button3, 2, 2); + this.tableLayoutPanel1.Controls.Add(this.saveDiffButton, 1, 3); + this.tableLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Top; + this.tableLayoutPanel1.Location = new System.Drawing.Point(3, 16); + this.tableLayoutPanel1.Name = "tableLayoutPanel1"; + this.tableLayoutPanel1.RowCount = 4; + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle()); + this.tableLayoutPanel1.Size = new System.Drawing.Size(474, 116); + this.tableLayoutPanel1.TabIndex = 1; + // + // label1 + // + this.label1.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label1.AutoSize = true; + this.label1.Location = new System.Drawing.Point(6, 8); + this.label1.Name = "label1"; + this.label1.Size = new System.Drawing.Size(61, 13); + this.label1.TabIndex = 0; + this.label1.Text = "Original File"; + // + // label2 + // + this.label2.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label2.AutoSize = true; + this.label2.Location = new System.Drawing.Point(3, 37); + this.label2.Name = "label2"; + this.label2.Size = new System.Drawing.Size(66, 13); + this.label2.TabIndex = 1; + this.label2.Text = "Modified File"; + // + // label3 + // + this.label3.Anchor = System.Windows.Forms.AnchorStyles.None; + this.label3.AutoSize = true; + this.label3.Location = new System.Drawing.Point(3, 66); + this.label3.Name = "label3"; + this.label3.Size = new System.Drawing.Size(67, 13); + this.label3.TabIndex = 2; + this.label3.Text = "Diff File Path"; + // + // saveDiffFileInput + // + this.saveDiffFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.saveDiffFileInput.Location = new System.Drawing.Point(76, 61); + this.saveDiffFileInput.Name = "saveDiffFileInput"; + this.saveDiffFileInput.Size = new System.Drawing.Size(314, 20); + this.saveDiffFileInput.TabIndex = 3; + // + // saveModifiedFileInput + // + this.saveModifiedFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.saveModifiedFileInput.Location = new System.Drawing.Point(76, 32); + this.saveModifiedFileInput.Name = "saveModifiedFileInput"; + this.saveModifiedFileInput.Size = new System.Drawing.Size(314, 20); + this.saveModifiedFileInput.TabIndex = 4; + // + // saveOriginalFileInput + // + this.saveOriginalFileInput.Dock = System.Windows.Forms.DockStyle.Fill; + this.saveOriginalFileInput.Location = new System.Drawing.Point(76, 3); + this.saveOriginalFileInput.Name = "saveOriginalFileInput"; + this.saveOriginalFileInput.Size = new System.Drawing.Size(314, 20); + this.saveOriginalFileInput.TabIndex = 5; + // + // button1 + // + this.button1.Dock = System.Windows.Forms.DockStyle.Fill; + this.button1.Location = new System.Drawing.Point(396, 3); + this.button1.Name = "button1"; + this.button1.Size = new System.Drawing.Size(75, 23); + this.button1.TabIndex = 6; + this.button1.Text = "Select"; + this.button1.UseVisualStyleBackColor = true; + this.button1.Click += new System.EventHandler(this.button1_Click); + // + // button2 + // + this.button2.Dock = System.Windows.Forms.DockStyle.Fill; + this.button2.Location = new System.Drawing.Point(396, 32); + this.button2.Name = "button2"; + this.button2.Size = new System.Drawing.Size(75, 23); + this.button2.TabIndex = 7; + this.button2.Text = "Select"; + this.button2.UseVisualStyleBackColor = true; + this.button2.Click += new System.EventHandler(this.button2_Click); + // + // button3 + // + this.button3.Dock = System.Windows.Forms.DockStyle.Fill; + this.button3.Location = new System.Drawing.Point(396, 61); + this.button3.Name = "button3"; + this.button3.Size = new System.Drawing.Size(75, 23); + this.button3.TabIndex = 8; + this.button3.Text = "Select"; + this.button3.UseVisualStyleBackColor = true; + this.button3.Click += new System.EventHandler(this.button3_Click); + // + // saveDiffButton + // + this.saveDiffButton.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) + | System.Windows.Forms.AnchorStyles.Left) + | System.Windows.Forms.AnchorStyles.Right))); + this.saveDiffButton.Location = new System.Drawing.Point(76, 90); + this.saveDiffButton.Name = "saveDiffButton"; + this.saveDiffButton.Size = new System.Drawing.Size(314, 23); + this.saveDiffButton.TabIndex = 9; + this.saveDiffButton.Text = "Save Diff"; + this.saveDiffButton.UseVisualStyleBackColor = true; + this.saveDiffButton.Click += new System.EventHandler(this.saveDiffButton_Click); + // + // DiffTool + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(486, 285); + this.Controls.Add(this.groupBox1); + this.Name = "DiffTool"; + this.Text = "DiffTool"; + this.Load += new System.EventHandler(this.DiffTool_Load); + this.groupBox1.ResumeLayout(false); + this.groupBox1.PerformLayout(); + this.groupBox3.ResumeLayout(false); + this.groupBox3.PerformLayout(); + this.tableLayoutPanel2.ResumeLayout(false); + this.tableLayoutPanel2.PerformLayout(); + this.groupBox2.ResumeLayout(false); + this.groupBox2.PerformLayout(); + this.tableLayoutPanel1.ResumeLayout(false); + this.tableLayoutPanel1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.GroupBox groupBox1; + private System.Windows.Forms.GroupBox groupBox2; + private System.Windows.Forms.GroupBox groupBox3; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2; + private System.Windows.Forms.Label label4; + private System.Windows.Forms.Label label5; + private System.Windows.Forms.Label label6; + private System.Windows.Forms.TextBox mergeFileInput; + private System.Windows.Forms.TextBox mergeDiffFileInput; + private System.Windows.Forms.TextBox mergeOriginalFileInput; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.Button button5; + private System.Windows.Forms.Button button6; + private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1; + private System.Windows.Forms.Label label1; + private System.Windows.Forms.Label label2; + private System.Windows.Forms.Label label3; + private System.Windows.Forms.TextBox saveDiffFileInput; + private System.Windows.Forms.TextBox saveModifiedFileInput; + private System.Windows.Forms.TextBox saveOriginalFileInput; + private System.Windows.Forms.Button button1; + private System.Windows.Forms.Button button2; + private System.Windows.Forms.Button button3; + private System.Windows.Forms.Button mergeDiffButton; + private System.Windows.Forms.Button saveDiffButton; + } +} \ No newline at end of file diff --git a/HSDRawViewer/GUI/Extra/DiffTool.cs b/HSDRawViewer/GUI/Extra/DiffTool.cs new file mode 100644 index 00000000..ddd312fd --- /dev/null +++ b/HSDRawViewer/GUI/Extra/DiffTool.cs @@ -0,0 +1,88 @@ +using HSDRawViewer.Tools; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace HSDRawViewer.GUI.Extra +{ + public partial class DiffTool : Form + { + public DiffTool() + { + InitializeComponent(); + } + + private void DiffTool_Load(object sender, EventArgs e) + { + + } + + private void button1_Click(object sender, EventArgs e) + { + this.saveOriginalFileInput.Text = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + } + + private void button2_Click(object sender, EventArgs e) + { + this.saveModifiedFileInput.Text = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + } + + private void button3_Click(object sender, EventArgs e) + { + this.saveDiffFileInput.Text = Tools.FileIO.SaveFile(FileIO.DIFF_EXTENSIONS); + } + + private void button4_Click(object sender, EventArgs e) + { + this.mergeOriginalFileInput.Text = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + } + + private void button5_Click(object sender, EventArgs e) + { + this.mergeDiffFileInput.Text = Tools.FileIO.OpenFile(FileIO.DIFF_EXTENSIONS); + } + + private void button6_Click(object sender, EventArgs e) + { + this.mergeFileInput.Text = Tools.FileIO.SaveFile(FileIO.NORMAL_EXTENSIONS); + } + + private void saveDiffButton_Click(object sender, EventArgs e) + { + if (this.saveOriginalFileInput.Text == null) return; + if (this.saveModifiedFileInput.Text == null) return; + if (this.saveDiffFileInput.Text == null) return; + + using (FileStream origStream = new FileStream(this.saveOriginalFileInput.Text, FileMode.Open, FileAccess.Read)) + using (FileStream modifiedStream = new FileStream(this.saveModifiedFileInput.Text, FileMode.Open, FileAccess.Read)) + using (FileStream diffStream = new FileStream(this.saveDiffFileInput.Text, FileMode.Create, FileAccess.Write)) + { + FileIO.SaveDiffToFile(origStream, modifiedStream, diffStream); + MessageBox.Show("Diff Saved!"); + } + } + + private void mergeDiffButton_Click(object sender, EventArgs e) + { + if (this.mergeOriginalFileInput.Text == null) return; + if (this.mergeDiffFileInput.Text == null) return; + if (this.mergeFileInput.Text == null) return; + + + using (FileStream origStream = new FileStream(this.mergeOriginalFileInput.Text, FileMode.Open, FileAccess.Read)) + using (FileStream modifiedStream = new FileStream(this.mergeDiffFileInput.Text, FileMode.Open, FileAccess.Read)) + using (FileStream mergedStream = new FileStream(this.mergeFileInput.Text, FileMode.Create, FileAccess.Write)) + { + FileIO.MergeDiffToDat(origStream, modifiedStream, mergedStream); + MessageBox.Show("Merged Diff and saved to: "+ this.mergeFileInput.Text); + } + } + } +} diff --git a/HSDRawViewer/GUI/Extra/DiffTool.resx b/HSDRawViewer/GUI/Extra/DiffTool.resx new file mode 100644 index 00000000..1af7de15 --- /dev/null +++ b/HSDRawViewer/GUI/Extra/DiffTool.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/HSDRawViewer/HSDRawViewer.csproj b/HSDRawViewer/HSDRawViewer.csproj index 0863b9f7..ce461a13 100644 --- a/HSDRawViewer/HSDRawViewer.csproj +++ b/HSDRawViewer/HSDRawViewer.csproj @@ -170,6 +170,12 @@ Component + + Form + + + DiffTool.cs + Form @@ -573,6 +579,9 @@ + + DiffTool.cs + PopoutJointAnimationEditor.cs diff --git a/HSDRawViewer/MainForm.Designer.cs b/HSDRawViewer/MainForm.Designer.cs index f48b6c41..064996a8 100644 --- a/HSDRawViewer/MainForm.Designer.cs +++ b/HSDRawViewer/MainForm.Designer.cs @@ -34,9 +34,9 @@ private void InitializeComponent() this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.loadDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveAsUnoptimizedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.addRootFromFileToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); @@ -47,6 +47,7 @@ private void InitializeComponent() this.propertyViewToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.viewportToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.toolsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.diffToolToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.aJToolToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.sSMEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.sEMEditorToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -111,7 +112,7 @@ private void InitializeComponent() // this.openToolStripMenuItem.Name = "openToolStripMenuItem"; this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.openToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.openToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.openToolStripMenuItem.Text = "Open"; this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); // @@ -119,25 +120,25 @@ private void InitializeComponent() // this.saveToolStripMenuItem1.Name = "saveToolStripMenuItem1"; this.saveToolStripMenuItem1.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); - this.saveToolStripMenuItem1.Size = new System.Drawing.Size(192, 22); + this.saveToolStripMenuItem1.Size = new System.Drawing.Size(230, 22); this.saveToolStripMenuItem1.Text = "Save"; this.saveToolStripMenuItem1.Click += new System.EventHandler(this.saveToolStripMenuItem1_Click); // // saveDiffToolStripMenuItem // this.saveDiffToolStripMenuItem.Name = "saveDiffToolStripMenuItem"; - this.saveDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) + this.saveDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.D))); - this.saveDiffToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.saveDiffToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.saveDiffToolStripMenuItem.Text = "Save Diff To File"; this.saveDiffToolStripMenuItem.Click += new System.EventHandler(this.saveDiffToolStripMenuItem_Click); // // loadDiffToolStripMenuItem // this.loadDiffToolStripMenuItem.Name = "loadDiffToolStripMenuItem"; - this.loadDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) + this.loadDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.L))); - this.loadDiffToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.loadDiffToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.loadDiffToolStripMenuItem.Text = "Load Diff To File"; this.loadDiffToolStripMenuItem.Click += new System.EventHandler(this.loadDiffToolStripMenuItem_Click); // @@ -146,14 +147,14 @@ private void InitializeComponent() this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.S))); - this.saveToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.saveToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.saveToolStripMenuItem.Text = "Save As"; this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); // // saveAsUnoptimizedToolStripMenuItem // this.saveAsUnoptimizedToolStripMenuItem.Name = "saveAsUnoptimizedToolStripMenuItem"; - this.saveAsUnoptimizedToolStripMenuItem.Size = new System.Drawing.Size(192, 22); + this.saveAsUnoptimizedToolStripMenuItem.Size = new System.Drawing.Size(230, 22); this.saveAsUnoptimizedToolStripMenuItem.Text = "Save As (No Optimize)"; this.saveAsUnoptimizedToolStripMenuItem.Click += new System.EventHandler(this.saveAsUnoptimizedToolStripMenuItem_Click); // @@ -228,6 +229,7 @@ private void InitializeComponent() // toolsToolStripMenuItem // this.toolsToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.diffToolToolStripMenuItem, this.aJToolToolStripMenuItem, this.sSMEditorToolStripMenuItem, this.sEMEditorToolStripMenuItem}); @@ -235,24 +237,31 @@ private void InitializeComponent() this.toolsToolStripMenuItem.Size = new System.Drawing.Size(46, 20); this.toolsToolStripMenuItem.Text = "Tools"; // + // diffToolToolStripMenuItem + // + this.diffToolToolStripMenuItem.Name = "diffToolToolStripMenuItem"; + this.diffToolToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.diffToolToolStripMenuItem.Text = "Diff Tool"; + this.diffToolToolStripMenuItem.Click += new System.EventHandler(this.diffToolToolStripMenuItem_Click); + // // aJToolToolStripMenuItem // this.aJToolToolStripMenuItem.Name = "aJToolToolStripMenuItem"; - this.aJToolToolStripMenuItem.Size = new System.Drawing.Size(131, 22); + this.aJToolToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.aJToolToolStripMenuItem.Text = "AJ Tool"; this.aJToolToolStripMenuItem.Click += new System.EventHandler(this.aJToolToolStripMenuItem_Click); // // sSMEditorToolStripMenuItem // this.sSMEditorToolStripMenuItem.Name = "sSMEditorToolStripMenuItem"; - this.sSMEditorToolStripMenuItem.Size = new System.Drawing.Size(131, 22); + this.sSMEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.sSMEditorToolStripMenuItem.Text = "SSM Editor"; this.sSMEditorToolStripMenuItem.Click += new System.EventHandler(this.sSMEditorToolStripMenuItem_Click); // // sEMEditorToolStripMenuItem // this.sEMEditorToolStripMenuItem.Name = "sEMEditorToolStripMenuItem"; - this.sEMEditorToolStripMenuItem.Size = new System.Drawing.Size(131, 22); + this.sEMEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22); this.sEMEditorToolStripMenuItem.Text = "SEM Editor"; this.sEMEditorToolStripMenuItem.Click += new System.EventHandler(this.sEMEditorToolStripMenuItem_Click); // @@ -384,6 +393,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem optionsToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem selectAudioPlaybackDeviceToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem trimExcessDataToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem diffToolToolStripMenuItem; } } diff --git a/HSDRawViewer/MainForm.cs b/HSDRawViewer/MainForm.cs index 4cf44f07..e6707950 100644 --- a/HSDRawViewer/MainForm.cs +++ b/HSDRawViewer/MainForm.cs @@ -13,6 +13,7 @@ using VCDiff.Encoders; using VCDiff.Includes; using VCDiff.Decoders; +using HSDRawViewer.Tools; namespace HSDRawViewer { @@ -251,7 +252,7 @@ public void OpenFile(string filePath) /// private void openToolStripMenuItem_Click(object sender, EventArgs e) { - var f = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + var f = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); if (f != null) { if (f.ToLower().EndsWith(".sem")) @@ -302,9 +303,9 @@ private void saveToolStripMenuItem_Click(object sender, EventArgs e) private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) { - var originalFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); - var modifiedFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); - var diffFileName = Tools.FileIO.SaveFile("HSD Diff(*.dat.diff,*.usd.diff,*.ssm.diff,*.sem.diff)|*.dat.diff;*.usd.diff;*.ssm.diff;*.sem.diff"); + var originalFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + var modifiedFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + var diffFileName = Tools.FileIO.SaveFile(FileIO.DIFF_EXTENSIONS); if (originalFileName != null && modifiedFileName != null) { @@ -315,12 +316,7 @@ private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) using (FileStream diffStream = new FileStream(diffFileName, FileMode.Create, FileAccess.Write)) { - VCCoder coder = new VCCoder(origStream, modifiedStream, diffStream); - VCDiffResult result = coder.Encode(); //encodes with no checksum and not interleaved - if (result != VCDiffResult.SUCCESS) - { - //error was not able to encode properly - } + FileIO.SaveDiffToFile(origStream, modifiedStream, diffStream); } @@ -336,9 +332,9 @@ private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) private void loadDiffToolStripMenuItem_Click(object sender, EventArgs e) { - var originalFileName = Tools.FileIO.OpenFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); - var diffFileName = Tools.FileIO.OpenFile("HSD Diff(*.dat.diff,*.usd.diff,*.ssm.diff,*.sem.diff)|*.dat.diff;*.usd.diff;*.ssm.diff;*.sem.diff"); - var mergedFileName = Tools.FileIO.SaveFile("HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"); + var originalFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + var diffFileName = Tools.FileIO.OpenFile(FileIO.DIFF_EXTENSIONS); + var mergedFileName = Tools.FileIO.SaveFile(FileIO.NORMAL_EXTENSIONS); if (originalFileName != null && diffFileName != null) { @@ -349,21 +345,7 @@ private void loadDiffToolStripMenuItem_Click(object sender, EventArgs e) using (FileStream mergedStream = new FileStream(mergedFileName, FileMode.Create, FileAccess.Write)) { - VCDecoder decoder = new VCDecoder(origStream, modifiedStream, mergedStream); - VCDiffResult result = decoder.Start(); //encodes with no checksum and not interleaved - if (result != VCDiffResult.SUCCESS) - { - //error was not able to encode properly - } else - { - long bytesWritten = 0; - result = decoder.Decode(out bytesWritten); - - if(result != VCDiffResult.SUCCESS) - { - - } - } + FileIO.MergeDiffToDat(origStream, modifiedStream, mergedStream); } @@ -896,6 +878,14 @@ public HSDAccessor GetSymbol(string symbol) return null; } + + private void diffToolToolStripMenuItem_Click(object sender, EventArgs e) + { + using (DiffTool d = new DiffTool()) + { + d.ShowDialog(); + } + } } } diff --git a/HSDRawViewer/Tools/FileIO.cs b/HSDRawViewer/Tools/FileIO.cs index 2ba2484f..c0a00ddd 100644 --- a/HSDRawViewer/Tools/FileIO.cs +++ b/HSDRawViewer/Tools/FileIO.cs @@ -1,5 +1,9 @@ -using System.IO; +using System; +using System.IO; using System.Windows.Forms; +using VCDiff.Decoders; +using VCDiff.Encoders; +using VCDiff.Includes; namespace HSDRawViewer.Tools { @@ -8,7 +12,11 @@ public class FileIO private static string PrevSaveLocation = null; private static string PrevOpenLocation = null; - + + public static string NORMAL_EXTENSIONS = "HSD (*.dat,*.usd,*.ssm,*.sem)|*.dat;*.usd;*.ssm;*.sem"; + + public static string DIFF_EXTENSIONS = "HSD (*.dat.diff,*.usd.diff,*.ssm.diff,*.sem.diff, *.diff)|*.diff"; + /// /// /// @@ -72,6 +80,36 @@ public static string[] OpenFiles(string filter) return null; } + internal static void SaveDiffToFile(Stream origStream, Stream modifiedStream, Stream diffStream) + { + VCCoder coder = new VCCoder(origStream, modifiedStream, diffStream); + VCDiffResult result = coder.Encode(); //encodes with no checksum and not interleaved + if (result != VCDiffResult.SUCCESS) + { + //error was not able to encode properly + } + } + + internal static void MergeDiffToDat(Stream origStream, Stream modifiedStream, Stream mergedStream) + { + VCDecoder decoder = new VCDecoder(origStream, modifiedStream, mergedStream); + VCDiffResult result = decoder.Start(); //encodes with no checksum and not interleaved + if (result != VCDiffResult.SUCCESS) + { + //error was not able to encode properly + } + else + { + long bytesWritten = 0; + result = decoder.Decode(out bytesWritten); + + if (result != VCDiffResult.SUCCESS) + { + + } + } + } + /// /// /// From 38356bf35de14785ad99170547d2f2d1030df9ab Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Mon, 14 Dec 2020 19:49:54 -0400 Subject: [PATCH 3/6] Add support for importing and saving diffs. Also added Save Diff as when you can choose original file and target diff file name. --- HSDRaw/HSDRawFile.cs | 8 +- HSDRawViewer/GUI/Extra/DiffTool.cs | 1 + HSDRawViewer/MainForm.Designer.cs | 45 +++++++---- HSDRawViewer/MainForm.cs | 124 ++++++++++++++++++++--------- HSDRawViewer/Tools/FileIO.cs | 8 ++ 5 files changed, 134 insertions(+), 52 deletions(-) diff --git a/HSDRaw/HSDRawFile.cs b/HSDRaw/HSDRawFile.cs index 6f87652e..a12f564b 100644 --- a/HSDRaw/HSDRawFile.cs +++ b/HSDRaw/HSDRawFile.cs @@ -563,6 +563,8 @@ public void SetStructFlags() /// public Stream Save(Stream stream, bool bufferAlign = true, bool optimize = true, bool trim = false) { + MemoryStream result = new MemoryStream(); + if (Roots.Count > 0 && Roots[0].Data is MEX_Data) bufferAlign = false; @@ -611,6 +613,7 @@ public Stream Save(Stream stream, bool bufferAlign = true, bool optimize = true, if(optimize && Roots.Count > 0 && !(Roots[0].Data is SBM_FighterData) && !(Roots[0].Data is MEX_Data)) RemoveDuplicateBuffers(); + // build file -------------------------------------------------------------------------- using (BinaryWriterExt writer = new BinaryWriterExt(stream)) { @@ -712,8 +715,11 @@ public Stream Save(Stream stream, bool bufferAlign = true, bool optimize = true, writer.Write(References.Count); writer.Write(VersionChars); + stream.Position = 0; + stream.CopyTo(result); + } - return stream; + return result; } diff --git a/HSDRawViewer/GUI/Extra/DiffTool.cs b/HSDRawViewer/GUI/Extra/DiffTool.cs index ddd312fd..ca5ee25e 100644 --- a/HSDRawViewer/GUI/Extra/DiffTool.cs +++ b/HSDRawViewer/GUI/Extra/DiffTool.cs @@ -24,6 +24,7 @@ private void DiffTool_Load(object sender, EventArgs e) } + // TODO: I forgot to change the buttons reference names before double clicking them. oops? private void button1_Click(object sender, EventArgs e) { this.saveOriginalFileInput.Text = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); diff --git a/HSDRawViewer/MainForm.Designer.cs b/HSDRawViewer/MainForm.Designer.cs index 064996a8..f00feb79 100644 --- a/HSDRawViewer/MainForm.Designer.cs +++ b/HSDRawViewer/MainForm.Designer.cs @@ -35,6 +35,7 @@ private void InitializeComponent() this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.saveDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveDiffToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); this.loadDiffToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.saveAsUnoptimizedToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -100,10 +101,11 @@ private void InitializeComponent() this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.openToolStripMenuItem, this.saveToolStripMenuItem1, - this.saveDiffToolStripMenuItem, - this.loadDiffToolStripMenuItem, this.saveToolStripMenuItem, - this.saveAsUnoptimizedToolStripMenuItem}); + this.saveAsUnoptimizedToolStripMenuItem, + this.saveDiffToolStripMenuItem, + this.saveDiffToolStripMenuItem1, + this.loadDiffToolStripMenuItem}); this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); this.fileToolStripMenuItem.Text = "File"; @@ -112,7 +114,7 @@ private void InitializeComponent() // this.openToolStripMenuItem.Name = "openToolStripMenuItem"; this.openToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.O))); - this.openToolStripMenuItem.Size = new System.Drawing.Size(230, 22); + this.openToolStripMenuItem.Size = new System.Drawing.Size(204, 22); this.openToolStripMenuItem.Text = "Open"; this.openToolStripMenuItem.Click += new System.EventHandler(this.openToolStripMenuItem_Click); // @@ -120,7 +122,7 @@ private void InitializeComponent() // this.saveToolStripMenuItem1.Name = "saveToolStripMenuItem1"; this.saveToolStripMenuItem1.ShortcutKeys = ((System.Windows.Forms.Keys)((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.S))); - this.saveToolStripMenuItem1.Size = new System.Drawing.Size(230, 22); + this.saveToolStripMenuItem1.Size = new System.Drawing.Size(204, 22); this.saveToolStripMenuItem1.Text = "Save"; this.saveToolStripMenuItem1.Click += new System.EventHandler(this.saveToolStripMenuItem1_Click); // @@ -129,17 +131,29 @@ private void InitializeComponent() this.saveDiffToolStripMenuItem.Name = "saveDiffToolStripMenuItem"; this.saveDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.D))); - this.saveDiffToolStripMenuItem.Size = new System.Drawing.Size(230, 22); - this.saveDiffToolStripMenuItem.Text = "Save Diff To File"; + this.saveDiffToolStripMenuItem.Size = new System.Drawing.Size(204, 22); + this.saveDiffToolStripMenuItem.Text = "Save Diff"; + this.saveDiffToolStripMenuItem.ToolTipText = "Saves Diff changes into \"{filename}.diff\" (overwrites existing file)"; this.saveDiffToolStripMenuItem.Click += new System.EventHandler(this.saveDiffToolStripMenuItem_Click); // + // saveDiffToolStripMenuItem1 + // + this.saveDiffToolStripMenuItem1.AutoToolTip = true; + this.saveDiffToolStripMenuItem1.Name = "saveDiffToolStripMenuItem1"; + this.saveDiffToolStripMenuItem1.Size = new System.Drawing.Size(204, 22); + this.saveDiffToolStripMenuItem1.Text = "Save Diff..."; + this.saveDiffToolStripMenuItem1.ToolTipText = "Select Original File To Diff Against"; + this.saveDiffToolStripMenuItem1.Click += new System.EventHandler(this.saveDiffToolStripMenuItem1_Click); + // // loadDiffToolStripMenuItem // + this.loadDiffToolStripMenuItem.AutoToolTip = true; this.loadDiffToolStripMenuItem.Name = "loadDiffToolStripMenuItem"; this.loadDiffToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.L))); - this.loadDiffToolStripMenuItem.Size = new System.Drawing.Size(230, 22); - this.loadDiffToolStripMenuItem.Text = "Load Diff To File"; + this.loadDiffToolStripMenuItem.Size = new System.Drawing.Size(204, 22); + this.loadDiffToolStripMenuItem.Text = "Import Diff"; + this.loadDiffToolStripMenuItem.ToolTipText = "Merges Diff into current project"; this.loadDiffToolStripMenuItem.Click += new System.EventHandler(this.loadDiffToolStripMenuItem_Click); // // saveToolStripMenuItem @@ -147,14 +161,14 @@ private void InitializeComponent() this.saveToolStripMenuItem.Name = "saveToolStripMenuItem"; this.saveToolStripMenuItem.ShortcutKeys = ((System.Windows.Forms.Keys)(((System.Windows.Forms.Keys.Control | System.Windows.Forms.Keys.Shift) | System.Windows.Forms.Keys.S))); - this.saveToolStripMenuItem.Size = new System.Drawing.Size(230, 22); + this.saveToolStripMenuItem.Size = new System.Drawing.Size(204, 22); this.saveToolStripMenuItem.Text = "Save As"; this.saveToolStripMenuItem.Click += new System.EventHandler(this.saveToolStripMenuItem_Click); // // saveAsUnoptimizedToolStripMenuItem // this.saveAsUnoptimizedToolStripMenuItem.Name = "saveAsUnoptimizedToolStripMenuItem"; - this.saveAsUnoptimizedToolStripMenuItem.Size = new System.Drawing.Size(230, 22); + this.saveAsUnoptimizedToolStripMenuItem.Size = new System.Drawing.Size(204, 22); this.saveAsUnoptimizedToolStripMenuItem.Text = "Save As (No Optimize)"; this.saveAsUnoptimizedToolStripMenuItem.Click += new System.EventHandler(this.saveAsUnoptimizedToolStripMenuItem_Click); // @@ -240,28 +254,28 @@ private void InitializeComponent() // diffToolToolStripMenuItem // this.diffToolToolStripMenuItem.Name = "diffToolToolStripMenuItem"; - this.diffToolToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.diffToolToolStripMenuItem.Size = new System.Drawing.Size(131, 22); this.diffToolToolStripMenuItem.Text = "Diff Tool"; this.diffToolToolStripMenuItem.Click += new System.EventHandler(this.diffToolToolStripMenuItem_Click); // // aJToolToolStripMenuItem // this.aJToolToolStripMenuItem.Name = "aJToolToolStripMenuItem"; - this.aJToolToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.aJToolToolStripMenuItem.Size = new System.Drawing.Size(131, 22); this.aJToolToolStripMenuItem.Text = "AJ Tool"; this.aJToolToolStripMenuItem.Click += new System.EventHandler(this.aJToolToolStripMenuItem_Click); // // sSMEditorToolStripMenuItem // this.sSMEditorToolStripMenuItem.Name = "sSMEditorToolStripMenuItem"; - this.sSMEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.sSMEditorToolStripMenuItem.Size = new System.Drawing.Size(131, 22); this.sSMEditorToolStripMenuItem.Text = "SSM Editor"; this.sSMEditorToolStripMenuItem.Click += new System.EventHandler(this.sSMEditorToolStripMenuItem_Click); // // sEMEditorToolStripMenuItem // this.sEMEditorToolStripMenuItem.Name = "sEMEditorToolStripMenuItem"; - this.sEMEditorToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.sEMEditorToolStripMenuItem.Size = new System.Drawing.Size(131, 22); this.sEMEditorToolStripMenuItem.Text = "SEM Editor"; this.sEMEditorToolStripMenuItem.Click += new System.EventHandler(this.sEMEditorToolStripMenuItem_Click); // @@ -394,6 +408,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem selectAudioPlaybackDeviceToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem trimExcessDataToolStripMenuItem; private System.Windows.Forms.ToolStripMenuItem diffToolToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveDiffToolStripMenuItem1; } } diff --git a/HSDRawViewer/MainForm.cs b/HSDRawViewer/MainForm.cs index e6707950..b0a569ce 100644 --- a/HSDRawViewer/MainForm.cs +++ b/HSDRawViewer/MainForm.cs @@ -31,10 +31,14 @@ public partial class MainForm : DockContent public string FilePath { get; internal set; } + private HSDRawFile RawHSDFile = new HSDRawFile(); public static DataNode SelectedDataNode { get; internal set; } = null; + public Stream OpenFileStream; + public String OpenFilePath { get; private set; } + public static bool RefreshNode = false; private List Editors = new List(); @@ -213,6 +217,16 @@ public static void DeleteRoot(DataNode root) } } + public void OpenFile(Stream s) + { + this.OpenFileStream = new MemoryStream(); + s.CopyTo(this.OpenFileStream); + s.Position = 0; + RawHSDFile = new HSDRawFile(); + RawHSDFile.Open(s); + RefreshTree(); + } + /// /// /// @@ -220,11 +234,12 @@ public static void DeleteRoot(DataNode root) public void OpenFile(string filePath) { FilePath = filePath; + this.OpenFilePath = filePath.Clone().ToString(); - RawHSDFile = new HSDRawFile(); - RawHSDFile.Open(filePath); - RefreshTree(); + FileStream fs = new FileStream(filePath, FileMode.Open); + + OpenFile(fs); #if !DEBUG if(RawHSDFile.Roots.Count > 0 && RawHSDFile.Roots[0].Data is HSDRaw.MEX.MEX_Data) { @@ -241,8 +256,8 @@ public void OpenFile(string filePath) } } #endif - Text = "HSD DAT Browser - " + filePath; + } /// @@ -295,6 +310,24 @@ private void saveToolStripMenuItem_Click(object sender, EventArgs e) } + private void saveDiffToolStripMenuItem1_Click(object sender, EventArgs e) + { + if (this.OpenFileStream == null) return; + + var originalFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + if (originalFileName == null) return; + + var targetFileName = Tools.FileIO.SaveFile(FileIO.DIFF_EXTENSIONS); + if (targetFileName == null) return; + + var originalFileStream = new FileStream(originalFileName, FileMode.Open, FileAccess.Read); + var currentChangesStream = RawHSDFile.Save(new MemoryStream()); + FileStream diffStream = new FileStream(targetFileName, FileMode.Create, FileAccess.Write); + + saveDiffFile(originalFileStream, currentChangesStream, diffStream); + } + + /// /// /// @@ -302,28 +335,43 @@ private void saveToolStripMenuItem_Click(object sender, EventArgs e) /// private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) { + if (this.OpenFileStream == null) return; - var originalFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); - var modifiedFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); - var diffFileName = Tools.FileIO.SaveFile(FileIO.DIFF_EXTENSIONS); + var originalFileStream = this.OpenFileStream; + var currentChangesStream = RawHSDFile.Save(new MemoryStream()); + var outputPath = this.OpenFilePath + ".diff"; + FileStream diffStream = new FileStream(outputPath, FileMode.Create, FileAccess.Write); - if (originalFileName != null && modifiedFileName != null) + saveDiffFile(originalFileStream, currentChangesStream, diffStream); + } + + void saveDiffFile(Stream source, Stream changes, FileStream target) + { + if (source == null) return; + if (changes == null) return; + if (target == null) return; + + var bk = FileIO.ToUnwritableMemoryStream(this.OpenFileStream); + using (source) + using (changes) + using (target) { + source.Position = 0; + changes.Position = 0; + source = FileIO.ToUnwritableMemoryStream(source); + //source = FileIO.ToUnwritableMemoryStream(source); - using (FileStream origStream = new FileStream(originalFileName, FileMode.Open, FileAccess.Read)) - using (FileStream modifiedStream = new FileStream(modifiedFileName, FileMode.Open, FileAccess.Read)) - using (FileStream diffStream = new FileStream(diffFileName, FileMode.Create, FileAccess.Write)) + FileIO.SaveDiffToFile(source, changes, target); + MessageBox.Show("Diff file saved to: " + target.Name); + if (target.CanWrite) { - - FileIO.SaveDiffToFile(origStream, modifiedStream, diffStream); + target.Close(); } - - } + this.OpenFileStream = bk; } - /// /// /// @@ -331,25 +379,27 @@ private void saveDiffToolStripMenuItem_Click(object sender, EventArgs e) /// private void loadDiffToolStripMenuItem_Click(object sender, EventArgs e) { - - var originalFileName = Tools.FileIO.OpenFile(FileIO.NORMAL_EXTENSIONS); + if (this.OpenFileStream == null) return; + //MessageBox.Show("Select a Diff file to merge with the current open file. Saving your changes is recommended first."); var diffFileName = Tools.FileIO.OpenFile(FileIO.DIFF_EXTENSIONS); - var mergedFileName = Tools.FileIO.SaveFile(FileIO.NORMAL_EXTENSIONS); - if (originalFileName != null && diffFileName != null) - { + if (diffFileName == null) return; + byte[] buffer = new byte[this.OpenFileStream.Length]; - using (FileStream origStream = new FileStream(originalFileName, FileMode.Open, FileAccess.Read)) - using (FileStream modifiedStream = new FileStream(diffFileName, FileMode.Open, FileAccess.Read)) - using (FileStream mergedStream = new FileStream(mergedFileName, FileMode.Create, FileAccess.Write)) - { - - FileIO.MergeDiffToDat(origStream, modifiedStream, mergedStream); - } + this.OpenFileStream.Position = 0; + this.OpenFileStream.Read(buffer, 0, (int)this.OpenFileStream.Length); + using (MemoryStream originalFileStream = new MemoryStream(buffer, false)) + using (FileStream modifiedStream = new FileStream(diffFileName, FileMode.Open, FileAccess.Read)) + using (MemoryStream mergedFileStream = new MemoryStream()) + { + FileIO.MergeDiffToDat(originalFileStream, modifiedStream, mergedFileStream); + mergedFileStream.Position = 0; + OpenFile(mergedFileStream); } + } /// @@ -357,7 +407,7 @@ private void loadDiffToolStripMenuItem_Click(object sender, EventArgs e) /// public void SaveDAT() { - if(RawHSDFile != null) + if (RawHSDFile != null) RawHSDFile.Save(FilePath); } @@ -556,7 +606,8 @@ public void OpenEditor() { var editor = GetEditors(SelectedDataNode); editor[0].BringToFront(); - } else + } + else if (!IsChildOpened(SelectedDataNode.Accessor._s) && edit != null && edit is DockContent dc) @@ -769,7 +820,7 @@ private void addRootFromTypeToolStripMenuItem_Click(object sender, EventArgs e) var settings = new NewRootSettings(); using (HSDTypeDialog t = new HSDTypeDialog()) { - if(t.ShowDialog() == DialogResult.OK) + if (t.ShowDialog() == DialogResult.OK) { using (PropertyDialog d = new PropertyDialog("New Root", settings)) { @@ -798,12 +849,12 @@ private void addRootFromFileToolStripMenuItem1_Click(object sender, EventArgs e) { var f = Tools.FileIO.OpenFile("All Files |*.*"); - if(f != null) + if (f != null) { var root = new HSDRootNode(); root.Name = System.IO.Path.GetFileNameWithoutExtension(f); - root.Data = new HSDAccessor() ; + root.Data = new HSDAccessor(); root.Data._s.SetData(System.IO.File.ReadAllBytes(f)); RawHSDFile.Roots.Add(root); @@ -820,7 +871,7 @@ private void addRootFromFileToolStripMenuItem1_Click(object sender, EventArgs e) private void treeView1_BeforeLabelEdit(object sender, NodeLabelEditEventArgs e) { // Can only edit root node labels - if(!(e.Node is DataNode d && d.IsRootNode && !d.IsReferenceNode)) + if (!(e.Node is DataNode d && d.IsRootNode && !d.IsReferenceNode)) { e.CancelEdit = true; } @@ -857,9 +908,9 @@ private void selectAudioPlaybackDeviceToolStripMenuItem_Click(object sender, Eve private void trimExcessDataToolStripMenuItem_Click(object sender, EventArgs e) { var trimmed = 0; - foreach(DataNode d in treeView1.Nodes) + foreach (DataNode d in treeView1.Nodes) { - if(d.Accessor != null) + if (d.Accessor != null) trimmed += d.Accessor.Trim(); } MessageBox.Show($"Trimmed 0x{trimmed.ToString("X")} bytes", "Trimmed File"); @@ -886,6 +937,7 @@ private void diffToolToolStripMenuItem_Click(object sender, EventArgs e) d.ShowDialog(); } } + } } diff --git a/HSDRawViewer/Tools/FileIO.cs b/HSDRawViewer/Tools/FileIO.cs index c0a00ddd..8ea1f750 100644 --- a/HSDRawViewer/Tools/FileIO.cs +++ b/HSDRawViewer/Tools/FileIO.cs @@ -147,5 +147,13 @@ public static string SaveFile(string filter, string defaultName, string caption } return null; } + + internal static Stream ToUnwritableMemoryStream(Stream stream) + { + stream.Position = 0; + byte[] buffer = new byte[stream.Length]; + stream.Read(buffer, 0, (int)stream.Length); + return new MemoryStream(buffer, false); + } } } From 9e827d5881ff7d48cea9df1f446f24fba2514c90 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Mon, 14 Dec 2020 19:53:39 -0400 Subject: [PATCH 4/6] Update README.md --- README.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index dddf69e3..c205d7d8 100644 --- a/README.md +++ b/README.md @@ -45,4 +45,8 @@ This program is still a work-in-progress so expect bugs and incomplete features. * YamlDotNet * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Antoine Aubry and contributors -* MIT License: https://www.nuget.org/packages/YamlDotNet/8.1.0/license \ No newline at end of file +* MIT License: https://www.nuget.org/packages/YamlDotNet/8.1.0/license + +* VCDiff.Core +* Copyright (c) 2020 MatthiWare and contributors +* Apache License: https://github.com/MatthiWare/VCDiff.Core/blob/master/LICENSE From 8184c6c92c847de43fa71e6dae42fc7fe86348f7 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Mon, 14 Dec 2020 19:54:22 -0400 Subject: [PATCH 5/6] Add Apache LICENSE --- LICENSE | 203 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..005e65db --- /dev/null +++ b/LICENSE @@ -0,0 +1,203 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2008 The open-vcdiff Authors. + Copyright 2017 Metric (https://github.com/Metric) + Copyright 2018 MatthiWare (https://github.com/Matthiee) + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. From bddcbaa6991e141ddcf03df9c91fc3543d079066 Mon Sep 17 00:00:00 2001 From: Robert Peralta Date: Fri, 18 Dec 2020 18:31:15 -0400 Subject: [PATCH 6/6] Update Project build config --- HSDRawViewer/HSDRawViewer.csproj | 44 +++++++++++++++++++++++++++++- VCDiff/Properties/AssemblyInfo.cs | 6 ++-- VCDiff/VCDiff.csproj | 9 ++++-- VCDiff/VCDiff.dll | Bin 40960 -> 41472 bytes 4 files changed, 53 insertions(+), 6 deletions(-) diff --git a/HSDRawViewer/HSDRawViewer.csproj b/HSDRawViewer/HSDRawViewer.csproj index ce461a13..6ad5d7f5 100644 --- a/HSDRawViewer/HSDRawViewer.csproj +++ b/HSDRawViewer/HSDRawViewer.csproj @@ -14,6 +14,24 @@ + false + C:\Users\rapito\Desktop\test\ + true + Disk + false + Foreground + 7 + Days + false + false + true + publish.htm + true + 4 + 1.0.0.%2a + false + true + true AnyCPU @@ -39,6 +57,18 @@ dogg.ico + + D14E02651D7A0370A9CC7DD0474A519A2E7010A7 + + + HSDRawViewer_TemporaryKey.pfx + + + true + + + true + ..\packages\Be.Windows.Forms.HexBox.1.6.1\lib\net40\Be.Windows.Forms.HexBox.dll @@ -538,6 +568,7 @@ + @@ -813,7 +844,18 @@ VCDiff - + + + False + Microsoft .NET Framework 4.7.2 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 + false + + lib\ diff --git a/VCDiff/Properties/AssemblyInfo.cs b/VCDiff/Properties/AssemblyInfo.cs index 7bbd37ec..daca9fce 100644 --- a/VCDiff/Properties/AssemblyInfo.cs +++ b/VCDiff/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("vcdiff")] +[assembly: AssemblyTitle("VCDiff")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("vcdiff")] +[assembly: AssemblyProduct("VCDiff")] [assembly: AssemblyCopyright("Copyright © 2017")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] @@ -17,7 +17,7 @@ // Setting ComVisible to false makes the types in this assembly not visible // to COM components. If you need to access a type in this assembly from // COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] +[assembly: ComVisible(true)] // The following GUID is for the ID of the typelib if this project is exposed to COM [assembly: Guid("4d676f4e-e66d-4a41-861f-600f0ffcd052")] diff --git a/VCDiff/VCDiff.csproj b/VCDiff/VCDiff.csproj index 31c422a0..6b72d10e 100644 --- a/VCDiff/VCDiff.csproj +++ b/VCDiff/VCDiff.csproj @@ -14,7 +14,7 @@ true - full + portable false .\ DEBUG;TRACE @@ -29,7 +29,12 @@ TRACE prompt 4 - x64 + AnyCPU + 1701;1702 + NU1605 + + + true diff --git a/VCDiff/VCDiff.dll b/VCDiff/VCDiff.dll index 336cf01fba6d79a692d5dda470c46b728b5f1cf5..6958c313404ce4ef250aa1d80e0aa6b380dc0f8e 100644 GIT binary patch delta 391 zcmZoTz|?SrX+nphhW6cf1}J#IprpXfXu!ZQ3n;(_5&SlBWjN#F$&8HJj24p>nRF&c zGOBa&%m7L%0P(EJT|kLBleaOdGcK5Xk5P&7`esHZ<2;=|8yOg8GyK`az%Ym5&t?XO zxeP2@7#QX;uxw>un9snnje%hS1Iu;>hJ_3)J2od2?2!=I8JI0QHED{|7J+viX8^8p#ONNnwAz=d$PXcnF04T&D48-a{Tm!VSW-9ErOy4W!P+D#kst#G^DR#v|CpCo?G~%-JO~EiEQLDJ#Y$HK{ZmDxg=8 zk_1!^0+Z)YIyl*ZQNSeF!_mmpi~(wFs4LW1p!1l7^7iF9mPal%XeqvW8t4QVcse8KB?+gOUO_qX7fM44?oTL~zr@mEnvDlNlMc8D%CbGU-f? zWK`!mF&!wW0K_vUcL614P2R?+&Ny%KJw_$QDVrIYjPrDMY-C`V&9Gw=1H&AK9h(^# z<}w@rlJgi2Y++!S&v0NX1H%G_1KSuF7BU>zzB!>_kAy&(&+qfBH>Ub`AK2WYmuI_k zu}&vvILviSK)YlZ85mqP0P!RShM-`V;J9~=4huAeUG~ihx*7ZYa}rRR!PzP%v^ce> zIHoAEAhRSt#wEWzFDE}Sr8p+c*(EbAO*c2OxFoe`a{r{eyd6L%!oZ@*8zxILPMLgW ZvgYP*lVjL6^Ue9i%(TUDv*4_6i~uh|VtN1o