Added read-only hex editor support for byte-array and int-array tags.

This commit is contained in:
Justin Aquadro 2012-03-13 01:19:48 -04:00
parent 5191805a22
commit afb61389bb
35 changed files with 8444 additions and 10 deletions

View file

@ -419,8 +419,6 @@ namespace NBTExplorer
_buttonDelete.Enabled = tag != null && node.Tag is TagNode;
_buttonEdit.Enabled = tag != null
&& node.Tag is TagNode
&& tag.GetTagType() != TagType.TAG_BYTE_ARRAY
&& tag.GetTagType() != TagType.TAG_INT_ARRAY
&& tag.GetTagType() != TagType.TAG_COMPOUND
&& tag.GetTagType() != TagType.TAG_LIST;
@ -889,19 +887,35 @@ namespace NBTExplorer
if (tag == null)
return;
if (tag.GetTagType() == TagType.TAG_BYTE_ARRAY ||
tag.GetTagType() == TagType.TAG_LIST ||
if (tag.GetTagType() == TagType.TAG_LIST ||
tag.GetTagType() == TagType.TAG_COMPOUND)
return;
EditValue form = new EditValue(tag);
if (form.ShowDialog() == DialogResult.OK) {
TreeNode baseNode = BaseNode(node);
if (baseNode != null) {
(baseNode.Tag as DataNode).Modified = true;
if (tag.GetTagType() == TagType.TAG_BYTE_ARRAY) {
HexEditor form = new HexEditor(GetTagNodeName(node), tag.ToTagByteArray().Data);
form.ShowDialog();
}
else if (tag.GetTagType() == TagType.TAG_INT_ARRAY) {
TagNodeIntArray iatag = tag.ToTagIntArray();
byte[] data = new byte[iatag.Length * 4];
for (int i = 0; i < iatag.Length; i++) {
byte[] buf = BitConverter.GetBytes(iatag.Data[i]);
Array.Copy(buf, 0, data, 4 * i, 4);
}
node.Text = GetNodeText(node);
HexEditor form = new HexEditor(GetTagNodeName(node), data);
form.ShowDialog();
}
else {
EditValue form = new EditValue(tag);
if (form.ShowDialog() == DialogResult.OK) {
TreeNode baseNode = BaseNode(node);
if (baseNode != null) {
(baseNode.Tag as DataNode).Modified = true;
}
node.Text = GetNodeText(node);
}
}
}

106
HexEditor.Designer.cs generated Normal file
View file

@ -0,0 +1,106 @@
namespace NBTExplorer
{
partial class HexEditor
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose (bool disposing)
{
if (disposing && (components != null)) {
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent ()
{
this.hexBox1 = new Be.Windows.Forms.HexBox();
this.statusStrip1 = new System.Windows.Forms.StatusStrip();
this._curPositionLabel = new System.Windows.Forms.ToolStripStatusLabel();
this._buttonClose = new System.Windows.Forms.Button();
this.statusStrip1.SuspendLayout();
this.SuspendLayout();
//
// hexBox1
//
this.hexBox1.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.hexBox1.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.hexBox1.LineInfoForeColor = System.Drawing.Color.Empty;
this.hexBox1.LineInfoVisible = true;
this.hexBox1.Location = new System.Drawing.Point(12, 12);
this.hexBox1.Name = "hexBox1";
this.hexBox1.ReadOnly = true;
this.hexBox1.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255)))));
this.hexBox1.Size = new System.Drawing.Size(492, 289);
this.hexBox1.TabIndex = 0;
this.hexBox1.VScrollBarVisible = true;
//
// statusStrip1
//
this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this._curPositionLabel});
this.statusStrip1.Location = new System.Drawing.Point(0, 333);
this.statusStrip1.Name = "statusStrip1";
this.statusStrip1.Size = new System.Drawing.Size(516, 22);
this.statusStrip1.TabIndex = 1;
this.statusStrip1.Text = "statusStrip1";
//
// _curPositionLabel
//
this._curPositionLabel.AutoSize = false;
this._curPositionLabel.Name = "_curPositionLabel";
this._curPositionLabel.Size = new System.Drawing.Size(100, 17);
this._curPositionLabel.Text = "0000";
//
// _buttonClose
//
this._buttonClose.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this._buttonClose.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this._buttonClose.Location = new System.Drawing.Point(429, 307);
this._buttonClose.Name = "_buttonClose";
this._buttonClose.Size = new System.Drawing.Size(75, 23);
this._buttonClose.TabIndex = 2;
this._buttonClose.Text = "Close";
this._buttonClose.UseVisualStyleBackColor = true;
//
// HexEditor
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this._buttonClose;
this.ClientSize = new System.Drawing.Size(516, 355);
this.Controls.Add(this._buttonClose);
this.Controls.Add(this.statusStrip1);
this.Controls.Add(this.hexBox1);
this.Name = "HexEditor";
this.Text = "HexEditor";
this.statusStrip1.ResumeLayout(false);
this.statusStrip1.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Be.Windows.Forms.HexBox hexBox1;
private System.Windows.Forms.StatusStrip statusStrip1;
private System.Windows.Forms.ToolStripStatusLabel _curPositionLabel;
private System.Windows.Forms.Button _buttonClose;
}
}

49
HexEditor.cs Normal file
View file

@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Be.Windows.Forms;
namespace NBTExplorer
{
public partial class HexEditor : Form
{
public HexEditor (string tagName, byte[] data)
{
InitializeComponent();
this.Text = "Editing: " + tagName + " (Read Only)";
hexBox1.ByteProvider = new DynamicByteProvider(data);
hexBox1.HorizontalByteCountChanged += HexBox_HorizontalByteCountChanged;
hexBox1.CurrentLineChanged += HexBox_CurrentLineChanged;
hexBox1.CurrentPositionInLineChanged += HexBox_CurrentPositionInLineChanged;
}
private void HexBox_HorizontalByteCountChanged (object sender, EventArgs e)
{
UpdatePosition();
}
private void HexBox_CurrentLineChanged (object sender, EventArgs e)
{
UpdatePosition();
}
private void HexBox_CurrentPositionInLineChanged (object sender, EventArgs e)
{
UpdatePosition();
}
private void UpdatePosition ()
{
long pos = (hexBox1.CurrentLine - 1) * hexBox1.HorizontalByteCount + hexBox1.CurrentPositionInLine;
_curPositionLabel.Text = pos.ToString();
}
}
}

123
HexEditor.resx Normal file
View file

@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

View file

@ -43,8 +43,10 @@
<HintPath>..\Substrate\SubstrateCS\bin\Release\NET2\Substrate.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="About.cs">
@ -71,6 +73,12 @@
<Compile Include="Form1.Designer.cs">
<DependentUpon>Form1.cs</DependentUpon>
</Compile>
<Compile Include="HexEditor.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="HexEditor.Designer.cs">
<DependentUpon>HexEditor.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="About.resx">
@ -86,6 +94,9 @@
<DependentUpon>Form1.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="HexEditor.resx">
<DependentUpon>HexEditor.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
@ -101,6 +112,12 @@
<ItemGroup>
<Content Include="dead_bush.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="Vendor\Be.Windows.Forms.HexBox\Be.Windows.Forms.HexBox.csproj">
<Project>{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}</Project>
<Name>Be.Windows.Forms.HexBox</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.

View file

@ -3,16 +3,38 @@ Microsoft Visual Studio Solution File, Format Version 11.00
# Visual C# Express 2010
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NBTExplorer", "NBTExplorer.csproj", "{8A458245-8176-4599-95CD-3CA39F2435CE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Be.Windows.Forms.HexBox", "Vendor\Be.Windows.Forms.HexBox\Be.Windows.Forms.HexBox.csproj", "{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|Mixed Platforms = Debug|Mixed Platforms
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|Mixed Platforms = Release|Mixed Platforms
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8A458245-8176-4599-95CD-3CA39F2435CE}.Debug|Any CPU.ActiveCfg = Debug|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Debug|Mixed Platforms.ActiveCfg = Debug|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Debug|Mixed Platforms.Build.0 = Debug|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Debug|x86.ActiveCfg = Debug|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Debug|x86.Build.0 = Debug|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Release|Any CPU.ActiveCfg = Release|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Release|Mixed Platforms.ActiveCfg = Release|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Release|Mixed Platforms.Build.0 = Release|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Release|x86.ActiveCfg = Release|x86
{8A458245-8176-4599-95CD-3CA39F2435CE}.Release|x86.Build.0 = Release|x86
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Debug|x86.ActiveCfg = Debug|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Release|Any CPU.Build.0 = Release|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Release|Mixed Platforms.Build.0 = Release|Any CPU
{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}.Release|x86.ActiveCfg = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

Binary file not shown.

After

Width:  |  Height:  |  Size: 612 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

BIN
Resources/edit-code-b.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 388 B

BIN
Resources/edit-code-i.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

BIN
Resources/edit-code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

View file

@ -0,0 +1,70 @@
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security.Permissions;
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("Be.Windows.Forms.HexBox")]
[assembly: AssemblyDescription("hex edit control (C# DOTNET)")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Be")]
[assembly: AssemblyProduct("Be.Windows.Forms.HexBox")]
[assembly: AssemblyCopyright("")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
//
// 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 Revision and Build Numbers
// by using the '*' as shown below:
[assembly: AssemblyVersion("1.4.7.*")]
//
// In order to sign your assembly you must specify a key to use. Refer to the
// Microsoft .NET Framework documentation for more information on assembly signing.
//
// Use the attributes below to control which key is used for signing.
//
// Notes:
// (*) If no key is specified, the assembly is not signed.
// (*) KeyName refers to a key that has been installed in the Crypto Service
// Provider (CSP) on your machine. KeyFile refers to a file which contains
// a key.
// (*) If the KeyFile and the KeyName values are both specified, the
// following processing occurs:
// (1) If the KeyName can be found in the CSP, that key is used.
// (2) If the KeyName does not exist and the KeyFile does exist, the key
// in the KeyFile is installed into the CSP and used.
// (*) In order to create a KeyFile, you can use the sn.exe (Strong Name) utility.
// When specifying the KeyFile, the location of the KeyFile should be
// relative to the project output directory which is
// %Project Directory%\obj\<configuration>. For example, if your KeyFile is
// located in the project directory, you would specify the AssemblyKeyFile
// attribute as [assembly: AssemblyKeyFile("..\\..\\mykey.snk")]
// (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
// documentation for more information on this.
//
[assembly: AssemblyDelaySign(false)]
//[assembly: AssemblyKeyFile("../../HexBox.snk")]
//[assembly: AssemblyKeyName("")]
//[assembly:IsolatedStorageFilePermission(SecurityAction.RequestRefuse, UserQuota=1048576)]
//[assembly:SecurityPermission(SecurityAction.RequestRefuse, UnmanagedCode=true)]
//[assembly:FileIOPermission(SecurityAction.RequestOptional, Unrestricted=true)]
[assembly:CLSCompliant(true)]
[assembly:ComVisible(false)]

View file

@ -0,0 +1,207 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
<PropertyGroup>
<ProjectType>Local</ProjectType>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{26C5F25F-B450-4CAF-AD8B-B8D11AE73457}</ProjectGuid>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ApplicationIcon>
</ApplicationIcon>
<AssemblyKeyContainerName>
</AssemblyKeyContainerName>
<AssemblyName>Be.Windows.Forms.HexBox</AssemblyName>
<AssemblyOriginatorKeyFile>HexBox.snk</AssemblyOriginatorKeyFile>
<DefaultClientScript>JScript</DefaultClientScript>
<DefaultHTMLPageLayout>Grid</DefaultHTMLPageLayout>
<DefaultTargetSchema>IE50</DefaultTargetSchema>
<DelaySign>false</DelaySign>
<OutputType>Library</OutputType>
<RootNamespace>Be.Windows.Forms</RootNamespace>
<RunPostBuildEvent>OnBuildSuccess</RunPostBuildEvent>
<StartupObject>
</StartupObject>
<FileUpgradeFlags>
</FileUpgradeFlags>
<UpgradeBackupLocation>
</UpgradeBackupLocation>
<OldToolsVersion>3.5</OldToolsVersion>
<SignAssembly>true</SignAssembly>
<IsWebBootstrapper>false</IsWebBootstrapper>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<PublishUrl>publish\</PublishUrl>
<Install>true</Install>
<InstallFrom>Disk</InstallFrom>
<UpdateEnabled>false</UpdateEnabled>
<UpdateMode>Foreground</UpdateMode>
<UpdateInterval>7</UpdateInterval>
<UpdateIntervalUnits>Days</UpdateIntervalUnits>
<UpdatePeriodically>false</UpdatePeriodically>
<UpdateRequired>false</UpdateRequired>
<MapFileExtensions>true</MapFileExtensions>
<ApplicationRevision>0</ApplicationRevision>
<ApplicationVersion>1.0.0.%2a</ApplicationVersion>
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<OutputPath>bin\Debug\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DocumentationFile>Be.Windows.Forms.HexBox.xml</DocumentationFile>
<DebugSymbols>true</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>false</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>full</DebugType>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>AllRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<OutputPath>bin\Release\</OutputPath>
<AllowUnsafeBlocks>false</AllowUnsafeBlocks>
<BaseAddress>285212672</BaseAddress>
<CheckForOverflowUnderflow>false</CheckForOverflowUnderflow>
<ConfigurationOverrideFile>
</ConfigurationOverrideFile>
<DefineConstants>TRACE</DefineConstants>
<DocumentationFile>
</DocumentationFile>
<DebugSymbols>false</DebugSymbols>
<FileAlignment>4096</FileAlignment>
<NoStdLib>false</NoStdLib>
<NoWarn>
</NoWarn>
<Optimize>true</Optimize>
<RegisterForComInterop>false</RegisterForComInterop>
<RemoveIntegerChecks>false</RemoveIntegerChecks>
<TreatWarningsAsErrors>false</TreatWarningsAsErrors>
<WarningLevel>4</WarningLevel>
<DebugType>none</DebugType>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>BasicCorrectnessRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
</Reference>
<Reference Include="System.Drawing.Design" />
<Reference Include="System.Windows.Forms">
<Name>System.Windows.Forms</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="BuiltInContextMenu.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="ByteCollection.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="BytePositionInfo.cs" />
<Compile Include="DataBlock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DataMap.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DynamicByteProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="DynamicFileByteProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FileByteProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="FileDataBlock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="HexBox.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="HexCasing.cs" />
<Compile Include="ByteCharConverters.cs" />
<Compile Include="IByteProvider.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="MemoryDataBlock.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="NativeMethods.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Util.cs" />
<EmbeddedResource Include="HexBox.bmp" />
<EmbeddedResource Include="HexBox.resx">
<DependentUpon>HexBox.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="HexBox.snk" />
</ItemGroup>
<ItemGroup>
<BootstrapperPackage Include="Microsoft.Net.Client.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1 Client Profile</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.2.0">
<Visible>False</Visible>
<ProductName>.NET Framework 2.0 %28x86%29</ProductName>
<Install>true</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.0">
<Visible>False</Visible>
<ProductName>.NET Framework 3.0 %28x86%29</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5</ProductName>
<Install>false</Install>
</BootstrapperPackage>
<BootstrapperPackage Include="Microsoft.Net.Framework.3.5.SP1">
<Visible>False</Visible>
<ProductName>.NET Framework 3.5 SP1</ProductName>
<Install>false</Install>
</BootstrapperPackage>
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PreBuildEvent>
</PreBuildEvent>
<PostBuildEvent>
</PostBuildEvent>
</PropertyGroup>
</Project>

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,220 @@
using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.ComponentModel;
using System.Windows.Forms;
namespace Be.Windows.Forms
{
/// <summary>
/// Defines a build-in ContextMenuStrip manager for HexBox control to show Copy, Cut, Paste menu in contextmenu of the control.
/// </summary>
[TypeConverterAttribute(typeof(ExpandableObjectConverter))]
public sealed class BuiltInContextMenu : Component
{
/// <summary>
/// Contains the HexBox control.
/// </summary>
HexBox _hexBox;
/// <summary>
/// Contains the ContextMenuStrip control.
/// </summary>
ContextMenuStrip _contextMenuStrip;
/// <summary>
/// Contains the "Cut"-ToolStripMenuItem object.
/// </summary>
ToolStripMenuItem _cutToolStripMenuItem;
/// <summary>
/// Contains the "Copy"-ToolStripMenuItem object.
/// </summary>
ToolStripMenuItem _copyToolStripMenuItem;
/// <summary>
/// Contains the "Paste"-ToolStripMenuItem object.
/// </summary>
ToolStripMenuItem _pasteToolStripMenuItem;
/// <summary>
/// Contains the "Select All"-ToolStripMenuItem object.
/// </summary>
ToolStripMenuItem _selectAllToolStripMenuItem;
/// <summary>
/// Initializes a new instance of BuildInContextMenu class.
/// </summary>
/// <param name="hexBox">the HexBox control</param>
internal BuiltInContextMenu(HexBox hexBox)
{
_hexBox = hexBox;
_hexBox.ByteProviderChanged += new EventHandler(HexBox_ByteProviderChanged);
}
/// <summary>
/// If ByteProvider
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void HexBox_ByteProviderChanged(object sender, EventArgs e)
{
CheckBuiltInContextMenu();
}
/// <summary>
/// Assigns the ContextMenuStrip control to the HexBox control.
/// </summary>
void CheckBuiltInContextMenu()
{
if (Util.DesignMode)
return;
if (this._contextMenuStrip == null)
{
ContextMenuStrip cms = new ContextMenuStrip();
_cutToolStripMenuItem = new ToolStripMenuItem(CutMenuItemTextInternal, CutMenuItemImage, new EventHandler(CutMenuItem_Click));
cms.Items.Add(_cutToolStripMenuItem);
_copyToolStripMenuItem = new ToolStripMenuItem(CopyMenuItemTextInternal, CopyMenuItemImage, new EventHandler(CopyMenuItem_Click));
cms.Items.Add(_copyToolStripMenuItem);
_pasteToolStripMenuItem = new ToolStripMenuItem(PasteMenuItemTextInternal, PasteMenuItemImage, new EventHandler(PasteMenuItem_Click));
cms.Items.Add(_pasteToolStripMenuItem);
cms.Items.Add(new ToolStripSeparator());
_selectAllToolStripMenuItem = new ToolStripMenuItem(SelectAllMenuItemTextInternal, SelectAllMenuItemImage, new EventHandler(SelectAllMenuItem_Click));
cms.Items.Add(_selectAllToolStripMenuItem);
cms.Opening += new CancelEventHandler(BuildInContextMenuStrip_Opening);
_contextMenuStrip = cms;
}
if (this._hexBox.ByteProvider == null && this._hexBox.ContextMenuStrip != null)
this._hexBox.ContextMenuStrip = null;
else if (this._hexBox.ByteProvider != null && this._hexBox.ContextMenuStrip == null)
this._hexBox.ContextMenuStrip = _contextMenuStrip;
}
/// <summary>
/// Before opening the ContextMenuStrip, we manage the availability of the items.
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void BuildInContextMenuStrip_Opening(object sender, CancelEventArgs e)
{
_cutToolStripMenuItem.Enabled = this._hexBox.CanCut();
_copyToolStripMenuItem.Enabled = this._hexBox.CanCopy();
_pasteToolStripMenuItem.Enabled = this._hexBox.CanPaste();
_selectAllToolStripMenuItem.Enabled = this._hexBox.CanSelectAll();
}
/// <summary>
/// The handler for the "Cut"-Click event
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void CutMenuItem_Click(object sender, EventArgs e) { this._hexBox.Cut(); }
/// <summary>
/// The handler for the "Copy"-Click event
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void CopyMenuItem_Click(object sender, EventArgs e) { this._hexBox.Copy(); }
/// <summary>
/// The handler for the "Paste"-Click event
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void PasteMenuItem_Click(object sender, EventArgs e) { this._hexBox.Paste(); }
/// <summary>
/// The handler for the "Select All"-Click event
/// </summary>
/// <param name="sender">the sender object</param>
/// <param name="e">the event data</param>
void SelectAllMenuItem_Click(object sender, EventArgs e) { this._hexBox.SelectAll(); }
/// <summary>
/// Gets or sets the custom text of the "Copy" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
public string CopyMenuItemText
{
get { return _copyMenuItemText; }
set { _copyMenuItemText = value; }
} string _copyMenuItemText;
/// <summary>
/// Gets or sets the custom text of the "Cut" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
public string CutMenuItemText
{
get { return _cutMenuItemText; }
set { _cutMenuItemText = value; }
} string _cutMenuItemText;
/// <summary>
/// Gets or sets the custom text of the "Paste" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
public string PasteMenuItemText
{
get { return _pasteMenuItemText; }
set { _pasteMenuItemText = value; }
} string _pasteMenuItemText;
/// <summary>
/// Gets or sets the custom text of the "Select All" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null), Localizable(true)]
public string SelectAllMenuItemText
{
get { return _selectAllMenuItemText; }
set { _selectAllMenuItemText = value; }
} string _selectAllMenuItemText = null;
/// <summary>
/// Gets the text of the "Cut" ContextMenuStrip item.
/// </summary>
internal string CutMenuItemTextInternal { get { return !string.IsNullOrEmpty(CutMenuItemText) ? CutMenuItemText : "Cut"; } }
/// <summary>
/// Gets the text of the "Copy" ContextMenuStrip item.
/// </summary>
internal string CopyMenuItemTextInternal { get { return !string.IsNullOrEmpty(CopyMenuItemText) ? CopyMenuItemText : "Copy"; } }
/// <summary>
/// Gets the text of the "Paste" ContextMenuStrip item.
/// </summary>
internal string PasteMenuItemTextInternal { get { return !string.IsNullOrEmpty(PasteMenuItemText) ? PasteMenuItemText : "Paste"; } }
/// <summary>
/// Gets the text of the "Select All" ContextMenuStrip item.
/// </summary>
internal string SelectAllMenuItemTextInternal { get { return !string.IsNullOrEmpty(SelectAllMenuItemText) ? SelectAllMenuItemText : "SelectAll"; } }
/// <summary>
/// Gets or sets the image of the "Cut" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
public Image CutMenuItemImage
{
get { return _cutMenuItemImage; }
set { _cutMenuItemImage = value; }
} Image _cutMenuItemImage = null;
/// <summary>
/// Gets or sets the image of the "Copy" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
public Image CopyMenuItemImage
{
get { return _copyMenuItemImage; }
set { _copyMenuItemImage = value; }
} Image _copyMenuItemImage = null;
/// <summary>
/// Gets or sets the image of the "Paste" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
public Image PasteMenuItemImage
{
get { return _pasteMenuItemImage; }
set { _pasteMenuItemImage = value; }
} Image _pasteMenuItemImage = null;
/// <summary>
/// Gets or sets the image of the "Select All" ContextMenuStrip item.
/// </summary>
[Category("BuiltIn-ContextMenu"), DefaultValue(null)]
public Image SelectAllMenuItemImage
{
get { return _selectAllMenuItemImage; }
set { _selectAllMenuItemImage = value; }
} Image _selectAllMenuItemImage = null;
}
}

View file

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Be.Windows.Forms
{
/// <summary>
/// The interface for objects that can translate between characters and bytes.
/// </summary>
public interface IByteCharConverter
{
/// <summary>
/// Returns the character to display for the byte passed across.
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
char ToChar(byte b);
/// <summary>
/// Returns the byte to use when the character passed across is entered during editing.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
byte ToByte(char c);
}
/// <summary>
/// The default <see cref="IByteCharConverter"/> implementation.
/// </summary>
public class DefaultByteCharConverter : IByteCharConverter
{
/// <summary>
/// Returns the character to display for the byte passed across.
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
public virtual char ToChar(byte b)
{
return b > 0x1F && !(b > 0x7E && b < 0xA0) ? (char)b : '.';
}
/// <summary>
/// Returns the byte to use for the character passed across.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public virtual byte ToByte(char c)
{
return (byte)c;
}
/// <summary>
/// Returns a description of the byte char provider.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "Default";
}
}
/// <summary>
/// A byte char provider that can translate bytes encoded in codepage 500 EBCDIC
/// </summary>
public class EbcdicByteCharProvider : IByteCharConverter
{
/// <summary>
/// The IBM EBCDIC code page 500 encoding. Note that this is not always supported by .NET,
/// the underlying platform has to provide support for it.
/// </summary>
private Encoding _ebcdicEncoding = Encoding.GetEncoding(500);
/// <summary>
/// Returns the EBCDIC character corresponding to the byte passed across.
/// </summary>
/// <param name="b"></param>
/// <returns></returns>
public virtual char ToChar(byte b)
{
string encoded = _ebcdicEncoding.GetString(new byte[] { b });
return encoded.Length > 0 ? encoded[0] : '.';
}
/// <summary>
/// Returns the byte corresponding to the EBCDIC character passed across.
/// </summary>
/// <param name="c"></param>
/// <returns></returns>
public virtual byte ToByte(char c)
{
byte[] decoded = _ebcdicEncoding.GetBytes(new char[] { c });
return decoded.Length > 0 ? decoded[0] : (byte)0;
}
/// <summary>
/// Returns a description of the byte char provider.
/// </summary>
/// <returns></returns>
public override string ToString()
{
return "EBCDIC (Code Page 500)";
}
}
}

View file

@ -0,0 +1,127 @@
using System;
using System.Collections;
namespace Be.Windows.Forms
{
/// <summary>
/// Represents a collection of bytes.
/// </summary>
public class ByteCollection : CollectionBase
{
/// <summary>
/// Initializes a new instance of ByteCollection class.
/// </summary>
public ByteCollection() { }
/// <summary>
/// Initializes a new instance of ByteCollection class.
/// </summary>
/// <param name="bs">an array of bytes to add to collection</param>
public ByteCollection(byte[] bs)
{ AddRange(bs); }
/// <summary>
/// Gets or sets the value of a byte
/// </summary>
public byte this[int index]
{
get { return (byte)List[index]; }
set { List[index] = value; }
}
/// <summary>
/// Adds a byte into the collection.
/// </summary>
/// <param name="b">the byte to add</param>
public void Add(byte b)
{ List.Add(b); }
/// <summary>
/// Adds a range of bytes to the collection.
/// </summary>
/// <param name="bs">the bytes to add</param>
public void AddRange(byte[] bs)
{ InnerList.AddRange(bs); }
/// <summary>
/// Removes a byte from the collection.
/// </summary>
/// <param name="b">the byte to remove</param>
public void Remove(byte b)
{ List.Remove(b); }
/// <summary>
/// Removes a range of bytes from the collection.
/// </summary>
/// <param name="index">the index of the start byte</param>
/// <param name="count">the count of the bytes to remove</param>
public void RemoveRange(int index, int count)
{ InnerList.RemoveRange(index, count); }
/// <summary>
/// Inserts a range of bytes to the collection.
/// </summary>
/// <param name="index">the index of start byte</param>
/// <param name="bs">an array of bytes to insert</param>
public void InsertRange(int index, byte[] bs)
{ InnerList.InsertRange(index, bs); }
/// <summary>
/// Gets all bytes in the array
/// </summary>
/// <returns>an array of bytes.</returns>
public byte[] GetBytes()
{
byte[] bytes = new byte[Count];
InnerList.CopyTo(0, bytes, 0, bytes.Length);
return bytes;
}
/// <summary>
/// Inserts a byte to the collection.
/// </summary>
/// <param name="index">the index</param>
/// <param name="b">a byte to insert</param>
public void Insert(int index, byte b)
{
InnerList.Insert(index, b);
}
/// <summary>
/// Returns the index of the given byte.
/// </summary>
public int IndexOf(byte b)
{
return InnerList.IndexOf(b);
}
/// <summary>
/// Returns true, if the byte exists in the collection.
/// </summary>
public bool Contains(byte b)
{
return InnerList.Contains(b);
}
/// <summary>
/// Copies the content of the collection into the given array.
/// </summary>
public void CopyTo(byte[] bs, int index)
{
InnerList.CopyTo(bs, index);
}
/// <summary>
/// Copies the content of the collection into an array.
/// </summary>
/// <returns>the array containing all bytes.</returns>
public byte[] ToArray()
{
byte[] data = new byte[this.Count];
this.CopyTo(data, 0);
return data;
}
}
}

View file

@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Be.Windows.Forms
{
/// <summary>
/// Represents a position in the HexBox control
/// </summary>
struct BytePositionInfo
{
public BytePositionInfo(long index, int characterPosition)
{
_index = index;
_characterPosition = characterPosition;
}
public int CharacterPosition
{
get { return _characterPosition; }
} int _characterPosition;
public long Index
{
get { return _index; }
} long _index;
}
}

View file

@ -0,0 +1,42 @@
using System;
namespace Be.Windows.Forms
{
internal abstract class DataBlock
{
internal DataMap _map;
internal DataBlock _nextBlock;
internal DataBlock _previousBlock;
public abstract long Length
{
get;
}
public DataMap Map
{
get
{
return _map;
}
}
public DataBlock NextBlock
{
get
{
return _nextBlock;
}
}
public DataBlock PreviousBlock
{
get
{
return _previousBlock;
}
}
public abstract void RemoveBytes(long position, long count);
}
}

View file

@ -0,0 +1,318 @@
using System;
using System.Collections;
using System.Text;
namespace Be.Windows.Forms
{
internal class DataMap : ICollection, IEnumerable
{
readonly object _syncRoot = new object();
internal int _count;
internal DataBlock _firstBlock;
internal int _version;
public DataMap()
{
}
public DataMap(IEnumerable collection)
{
if (collection == null)
{
throw new ArgumentNullException("collection");
}
foreach (DataBlock item in collection)
{
AddLast(item);
}
}
public DataBlock FirstBlock
{
get
{
return _firstBlock;
}
}
public void AddAfter(DataBlock block, DataBlock newBlock)
{
AddAfterInternal(block, newBlock);
}
public void AddBefore(DataBlock block, DataBlock newBlock)
{
AddBeforeInternal(block, newBlock);
}
public void AddFirst(DataBlock block)
{
if (_firstBlock == null)
{
AddBlockToEmptyMap(block);
}
else
{
AddBeforeInternal(_firstBlock, block);
}
}
public void AddLast(DataBlock block)
{
if (_firstBlock == null)
{
AddBlockToEmptyMap(block);
}
else
{
AddAfterInternal(GetLastBlock(), block);
}
}
public void Remove(DataBlock block)
{
RemoveInternal(block);
}
public void RemoveFirst()
{
if (_firstBlock == null)
{
throw new InvalidOperationException("The collection is empty.");
}
RemoveInternal(_firstBlock);
}
public void RemoveLast()
{
if (_firstBlock == null)
{
throw new InvalidOperationException("The collection is empty.");
}
RemoveInternal(GetLastBlock());
}
public DataBlock Replace(DataBlock block, DataBlock newBlock)
{
AddAfterInternal(block, newBlock);
RemoveInternal(block);
return newBlock;
}
public void Clear()
{
DataBlock block = FirstBlock;
while (block != null)
{
DataBlock nextBlock = block.NextBlock;
InvalidateBlock(block);
block = nextBlock;
}
_firstBlock = null;
_count = 0;
_version++;
}
void AddAfterInternal(DataBlock block, DataBlock newBlock)
{
newBlock._previousBlock = block;
newBlock._nextBlock = block._nextBlock;
newBlock._map = this;
if (block._nextBlock != null)
{
block._nextBlock._previousBlock = newBlock;
}
block._nextBlock = newBlock;
this._version++;
this._count++;
}
void AddBeforeInternal(DataBlock block, DataBlock newBlock)
{
newBlock._nextBlock = block;
newBlock._previousBlock = block._previousBlock;
newBlock._map = this;
if (block._previousBlock != null)
{
block._previousBlock._nextBlock = newBlock;
}
block._previousBlock = newBlock;
if (_firstBlock == block)
{
_firstBlock = newBlock;
}
this._version++;
this._count++;
}
void RemoveInternal(DataBlock block)
{
DataBlock previousBlock = block._previousBlock;
DataBlock nextBlock = block._nextBlock;
if (previousBlock != null)
{
previousBlock._nextBlock = nextBlock;
}
if (nextBlock != null)
{
nextBlock._previousBlock = previousBlock;
}
if (_firstBlock == block)
{
_firstBlock = nextBlock;
}
InvalidateBlock(block);
_count--;
_version++;
}
DataBlock GetLastBlock()
{
DataBlock lastBlock = null;
for (DataBlock block = FirstBlock; block != null; block = block.NextBlock)
{
lastBlock = block;
}
return lastBlock;
}
void InvalidateBlock(DataBlock block)
{
block._map = null;
block._nextBlock = null;
block._previousBlock = null;
}
void AddBlockToEmptyMap(DataBlock block)
{
block._map = this;
block._nextBlock = null;
block._previousBlock = null;
_firstBlock = block;
_version++;
_count++;
}
#region ICollection Members
public void CopyTo(Array array, int index)
{
DataBlock[] blockArray = array as DataBlock[];
for (DataBlock block = FirstBlock; block != null; block = block.NextBlock)
{
blockArray[index++] = block;
}
}
public int Count
{
get
{
return _count;
}
}
public bool IsSynchronized
{
get
{
return false;
}
}
public object SyncRoot
{
get
{
return _syncRoot;
}
}
#endregion
#region IEnumerable Members
public IEnumerator GetEnumerator()
{
return new Enumerator(this);
}
#endregion
#region Enumerator Nested Type
internal class Enumerator : IEnumerator, IDisposable
{
DataMap _map;
DataBlock _current;
int _index;
int _version;
internal Enumerator(DataMap map)
{
_map = map;
_version = map._version;
_current = null;
_index = -1;
}
object IEnumerator.Current
{
get
{
if (_index < 0 || _index > _map.Count)
{
throw new InvalidOperationException("Enumerator is positioned before the first element or after the last element of the collection.");
}
return _current;
}
}
public bool MoveNext()
{
if (this._version != _map._version)
{
throw new InvalidOperationException("Collection was modified after the enumerator was instantiated.");
}
if (_index >= _map.Count)
{
return false;
}
if (++_index == 0)
{
_current = _map.FirstBlock;
}
else
{
_current = _current.NextBlock;
}
return (_index < _map.Count);
}
void IEnumerator.Reset()
{
if (this._version != this._map._version)
{
throw new InvalidOperationException("Collection was modified after the enumerator was instantiated.");
}
this._index = -1;
this._current = null;
}
public void Dispose()
{
}
}
#endregion
}
}

View file

@ -0,0 +1,177 @@
using System;
using System.Collections.Generic;
namespace Be.Windows.Forms
{
/// <summary>
/// Byte provider for a small amount of data.
/// </summary>
public class DynamicByteProvider : IByteProvider
{
/// <summary>
/// Contains information about changes.
/// </summary>
bool _hasChanges;
/// <summary>
/// Contains a byte collection.
/// </summary>
List<byte> _bytes;
/// <summary>
/// Initializes a new instance of the DynamicByteProvider class.
/// </summary>
/// <param name="data"></param>
public DynamicByteProvider(byte[] data) : this(new List<Byte>(data))
{
}
/// <summary>
/// Initializes a new instance of the DynamicByteProvider class.
/// </summary>
/// <param name="bytes"></param>
public DynamicByteProvider(List<Byte> bytes)
{
_bytes = bytes;
}
/// <summary>
/// Raises the Changed event.
/// </summary>
void OnChanged(EventArgs e)
{
_hasChanges = true;
if(Changed != null)
Changed(this, e);
}
/// <summary>
/// Raises the LengthChanged event.
/// </summary>
void OnLengthChanged(EventArgs e)
{
if(LengthChanged != null)
LengthChanged(this, e);
}
/// <summary>
/// Gets the byte collection.
/// </summary>
public List<Byte> Bytes
{
get { return _bytes; }
}
#region IByteProvider Members
/// <summary>
/// True, when changes are done.
/// </summary>
public bool HasChanges()
{
return _hasChanges;
}
/// <summary>
/// Applies changes.
/// </summary>
public void ApplyChanges()
{
_hasChanges = false;
}
/// <summary>
/// Occurs, when the write buffer contains new changes.
/// </summary>
public event EventHandler Changed;
/// <summary>
/// Occurs, when InsertBytes or DeleteBytes method is called.
/// </summary>
public event EventHandler LengthChanged;
/// <summary>
/// Reads a byte from the byte collection.
/// </summary>
/// <param name="index">the index of the byte to read</param>
/// <returns>the byte</returns>
public byte ReadByte(long index)
{ return _bytes[(int)index]; }
/// <summary>
/// Write a byte into the byte collection.
/// </summary>
/// <param name="index">the index of the byte to write.</param>
/// <param name="value">the byte</param>
public void WriteByte(long index, byte value)
{
_bytes[(int)index] = value;
OnChanged(EventArgs.Empty);
}
/// <summary>
/// Deletes bytes from the byte collection.
/// </summary>
/// <param name="index">the start index of the bytes to delete.</param>
/// <param name="length">the length of bytes to delete.</param>
public void DeleteBytes(long index, long length)
{
int internal_index = (int)Math.Max(0, index);
int internal_length = (int)Math.Min((int)Length, length);
_bytes.RemoveRange(internal_index, internal_length);
OnLengthChanged(EventArgs.Empty);
OnChanged(EventArgs.Empty);
}
/// <summary>
/// Inserts byte into the byte collection.
/// </summary>
/// <param name="index">the start index of the bytes in the byte collection</param>
/// <param name="bs">the byte array to insert</param>
public void InsertBytes(long index, byte[] bs)
{
_bytes.InsertRange((int)index, bs);
OnLengthChanged(EventArgs.Empty);
OnChanged(EventArgs.Empty);
}
/// <summary>
/// Gets the length of the bytes in the byte collection.
/// </summary>
public long Length
{
get
{
return _bytes.Count;
}
}
/// <summary>
/// Returns true
/// </summary>
public bool SupportsWriteByte()
{
return true;
}
/// <summary>
/// Returns true
/// </summary>
public bool SupportsInsertBytes()
{
return true;
}
/// <summary>
/// Returns true
/// </summary>
public bool SupportsDeleteBytes()
{
return true;
}
#endregion
}
}

View file

@ -0,0 +1,569 @@
using System;
using System.Text;
using System.IO;
namespace Be.Windows.Forms
{
/// <summary>
/// Implements a fully editable byte provider for file data of any size.
/// </summary>
/// <remarks>
/// Only changes to the file are stored in memory with reads from the
/// original data occurring as required.
/// </remarks>
public sealed class DynamicFileByteProvider : IByteProvider, IDisposable
{
const int COPY_BLOCK_SIZE = 4096;
string _fileName;
Stream _stream;
DataMap _dataMap;
long _totalLength;
bool _readOnly;
/// <summary>
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
/// </summary>
/// <param name="fileName">The name of the file from which bytes should be provided.</param>
public DynamicFileByteProvider(string fileName) : this(fileName, false) { }
/// <summary>
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
/// </summary>
/// <param name="fileName">The name of the file from which bytes should be provided.</param>
/// <param name="readOnly">True, opens the file in read-only mode.</param>
public DynamicFileByteProvider(string fileName, bool readOnly)
{
_fileName = fileName;
if (!readOnly)
{
_stream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
}
else
{
_stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
}
_readOnly = readOnly;
ReInitialize();
}
/// <summary>
/// Constructs a new <see cref="DynamicFileByteProvider" /> instance.
/// </summary>
/// <param name="stream">the stream containing the data.</param>
/// <remarks>
/// The stream must supported seek operations.
/// </remarks>
public DynamicFileByteProvider(Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
if (!stream.CanSeek)
throw new ArgumentException("stream must supported seek operations(CanSeek)");
_stream = stream;
_readOnly = !stream.CanWrite;
ReInitialize();
}
#region IByteProvider Members
/// <summary>
/// See <see cref="IByteProvider.LengthChanged" /> for more information.
/// </summary>
public event EventHandler LengthChanged;
/// <summary>
/// See <see cref="IByteProvider.Changed" /> for more information.
/// </summary>
public event EventHandler Changed;
/// <summary>
/// See <see cref="IByteProvider.ReadByte" /> for more information.
/// </summary>
public byte ReadByte(long index)
{
long blockOffset;
DataBlock block = GetDataBlock(index, out blockOffset);
FileDataBlock fileBlock = block as FileDataBlock;
if (fileBlock != null)
{
return ReadByteFromFile(fileBlock.FileOffset + index - blockOffset);
}
else
{
MemoryDataBlock memoryBlock = (MemoryDataBlock)block;
return memoryBlock.Data[index - blockOffset];
}
}
/// <summary>
/// See <see cref="IByteProvider.WriteByte" /> for more information.
/// </summary>
public void WriteByte(long index, byte value)
{
try
{
// Find the block affected.
long blockOffset;
DataBlock block = GetDataBlock(index, out blockOffset);
// If the byte is already in a memory block, modify it.
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
if (memoryBlock != null)
{
memoryBlock.Data[index - blockOffset] = value;
return;
}
FileDataBlock fileBlock = (FileDataBlock)block;
// If the byte changing is the first byte in the block and the previous block is a memory block, extend that.
if (blockOffset == index && block.PreviousBlock != null)
{
MemoryDataBlock previousMemoryBlock = block.PreviousBlock as MemoryDataBlock;
if (previousMemoryBlock != null)
{
previousMemoryBlock.AddByteToEnd(value);
fileBlock.RemoveBytesFromStart(1);
if (fileBlock.Length == 0)
{
_dataMap.Remove(fileBlock);
}
return;
}
}
// If the byte changing is the last byte in the block and the next block is a memory block, extend that.
if (blockOffset + fileBlock.Length - 1 == index && block.NextBlock != null)
{
MemoryDataBlock nextMemoryBlock = block.NextBlock as MemoryDataBlock;
if (nextMemoryBlock != null)
{
nextMemoryBlock.AddByteToStart(value);
fileBlock.RemoveBytesFromEnd(1);
if (fileBlock.Length == 0)
{
_dataMap.Remove(fileBlock);
}
return;
}
}
// Split the block into a prefix and a suffix and place a memory block in-between.
FileDataBlock prefixBlock = null;
if (index > blockOffset)
{
prefixBlock = new FileDataBlock(fileBlock.FileOffset, index - blockOffset);
}
FileDataBlock suffixBlock = null;
if (index < blockOffset + fileBlock.Length - 1)
{
suffixBlock = new FileDataBlock(
fileBlock.FileOffset + index - blockOffset + 1,
fileBlock.Length - (index - blockOffset + 1));
}
block = _dataMap.Replace(block, new MemoryDataBlock(value));
if (prefixBlock != null)
{
_dataMap.AddBefore(block, prefixBlock);
}
if (suffixBlock != null)
{
_dataMap.AddAfter(block, suffixBlock);
}
}
finally
{
OnChanged(EventArgs.Empty);
}
}
/// <summary>
/// See <see cref="IByteProvider.InsertBytes" /> for more information.
/// </summary>
public void InsertBytes(long index, byte[] bs)
{
try
{
// Find the block affected.
long blockOffset;
DataBlock block = GetDataBlock(index, out blockOffset);
// If the insertion point is in a memory block, just insert it.
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
if (memoryBlock != null)
{
memoryBlock.InsertBytes(index - blockOffset, bs);
return;
}
FileDataBlock fileBlock = (FileDataBlock)block;
// If the insertion point is at the start of a file block, and the previous block is a memory block, append it to that block.
if (blockOffset == index && block.PreviousBlock != null)
{
MemoryDataBlock previousMemoryBlock = block.PreviousBlock as MemoryDataBlock;
if (previousMemoryBlock != null)
{
previousMemoryBlock.InsertBytes(previousMemoryBlock.Length, bs);
return;
}
}
// Split the block into a prefix and a suffix and place a memory block in-between.
FileDataBlock prefixBlock = null;
if (index > blockOffset)
{
prefixBlock = new FileDataBlock(fileBlock.FileOffset, index - blockOffset);
}
FileDataBlock suffixBlock = null;
if (index < blockOffset + fileBlock.Length)
{
suffixBlock = new FileDataBlock(
fileBlock.FileOffset + index - blockOffset,
fileBlock.Length - (index - blockOffset));
}
block = _dataMap.Replace(block, new MemoryDataBlock(bs));
if (prefixBlock != null)
{
_dataMap.AddBefore(block, prefixBlock);
}
if (suffixBlock != null)
{
_dataMap.AddAfter(block, suffixBlock);
}
}
finally
{
_totalLength += bs.Length;
OnLengthChanged(EventArgs.Empty);
OnChanged(EventArgs.Empty);
}
}
/// <summary>
/// See <see cref="IByteProvider.DeleteBytes" /> for more information.
/// </summary>
public void DeleteBytes(long index, long length)
{
try
{
long bytesToDelete = length;
// Find the first block affected.
long blockOffset;
DataBlock block = GetDataBlock(index, out blockOffset);
// Truncate or remove each block as necessary.
while ((bytesToDelete > 0) && (block != null))
{
long blockLength = block.Length;
DataBlock nextBlock = block.NextBlock;
// Delete the appropriate section from the block (this may result in two blocks or a zero length block).
long count = Math.Min(bytesToDelete, blockLength - (index - blockOffset));
block.RemoveBytes(index - blockOffset, count);
if (block.Length == 0)
{
_dataMap.Remove(block);
if (_dataMap.FirstBlock == null)
{
_dataMap.AddFirst(new MemoryDataBlock(new byte[0]));
}
}
bytesToDelete -= count;
blockOffset += block.Length;
block = (bytesToDelete > 0) ? nextBlock : null;
}
}
finally
{
_totalLength -= length;
OnLengthChanged(EventArgs.Empty);
OnChanged(EventArgs.Empty);
}
}
/// <summary>
/// See <see cref="IByteProvider.Length" /> for more information.
/// </summary>
public long Length
{
get
{
return _totalLength;
}
}
/// <summary>
/// See <see cref="IByteProvider.HasChanges" /> for more information.
/// </summary>
public bool HasChanges()
{
if (_readOnly)
return false;
if (_totalLength != _stream.Length)
{
return true;
}
long offset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
FileDataBlock fileBlock = block as FileDataBlock;
if (fileBlock == null)
{
return true;
}
if (fileBlock.FileOffset != offset)
{
return true;
}
offset += fileBlock.Length;
}
return (offset != _stream.Length);
}
/// <summary>
/// See <see cref="IByteProvider.ApplyChanges" /> for more information.
/// </summary>
public void ApplyChanges()
{
if (_readOnly)
throw new OperationCanceledException("File is in read-only mode");
// This method is implemented to efficiently save the changes to the same file stream opened for reading.
// Saving to a separate file would be a much simpler implementation.
// Firstly, extend the file length (if necessary) to ensure that there is enough disk space.
if (_totalLength > _stream.Length)
{
_stream.SetLength(_totalLength);
}
// Secondly, shift around any file sections that have moved.
long dataOffset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
FileDataBlock fileBlock = block as FileDataBlock;
if (fileBlock != null && fileBlock.FileOffset != dataOffset)
{
MoveFileBlock(fileBlock, dataOffset);
}
dataOffset += block.Length;
}
// Next, write in-memory changes.
dataOffset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
MemoryDataBlock memoryBlock = block as MemoryDataBlock;
if (memoryBlock != null)
{
_stream.Position = dataOffset;
for (int memoryOffset = 0; memoryOffset < memoryBlock.Length; memoryOffset += COPY_BLOCK_SIZE)
{
_stream.Write(memoryBlock.Data, memoryOffset, (int)Math.Min(COPY_BLOCK_SIZE, memoryBlock.Length - memoryOffset));
}
}
dataOffset += block.Length;
}
// Finally, if the file has shortened, truncate the stream.
_stream.SetLength(_totalLength);
ReInitialize();
}
/// <summary>
/// See <see cref="IByteProvider.SupportsWriteByte" /> for more information.
/// </summary>
public bool SupportsWriteByte()
{
return !_readOnly;
}
/// <summary>
/// See <see cref="IByteProvider.SupportsInsertBytes" /> for more information.
/// </summary>
public bool SupportsInsertBytes()
{
return !_readOnly;
}
/// <summary>
/// See <see cref="IByteProvider.SupportsDeleteBytes" /> for more information.
/// </summary>
public bool SupportsDeleteBytes()
{
return !_readOnly;
}
#endregion
#region IDisposable Members
/// <summary>
/// See <see cref="Object.Finalize" /> for more information.
/// </summary>
~DynamicFileByteProvider()
{
Dispose();
}
/// <summary>
/// See <see cref="IDisposable.Dispose" /> for more information.
/// </summary>
public void Dispose()
{
if (_stream != null)
{
_stream.Close();
_stream = null;
}
_fileName = null;
_dataMap = null;
GC.SuppressFinalize(this);
}
#endregion
/// <summary>
/// Gets a value, if the file is opened in read-only mode.
/// </summary>
public bool ReadOnly
{
get { return _readOnly; }
}
void OnLengthChanged(EventArgs e)
{
if (LengthChanged != null)
LengthChanged(this, e);
}
void OnChanged(EventArgs e)
{
if (Changed != null)
{
Changed(this, e);
}
}
DataBlock GetDataBlock(long findOffset, out long blockOffset)
{
if (findOffset < 0 || findOffset > _totalLength)
{
throw new ArgumentOutOfRangeException("findOffset");
}
// Iterate over the blocks until the block containing the required offset is encountered.
blockOffset = 0;
for (DataBlock block = _dataMap.FirstBlock; block != null; block = block.NextBlock)
{
if ((blockOffset <= findOffset && blockOffset + block.Length > findOffset) || block.NextBlock == null)
{
return block;
}
blockOffset += block.Length;
}
return null;
}
FileDataBlock GetNextFileDataBlock(DataBlock block, long dataOffset, out long nextDataOffset)
{
// Iterate over the remaining blocks until a file block is encountered.
nextDataOffset = dataOffset + block.Length;
block = block.NextBlock;
while (block != null)
{
FileDataBlock fileBlock = block as FileDataBlock;
if (fileBlock != null)
{
return fileBlock;
}
nextDataOffset += block.Length;
block = block.NextBlock;
}
return null;
}
byte ReadByteFromFile(long fileOffset)
{
// Move to the correct position and read the byte.
if (_stream.Position != fileOffset)
{
_stream.Position = fileOffset;
}
return (byte)_stream.ReadByte();
}
void MoveFileBlock(FileDataBlock fileBlock, long dataOffset)
{
// First, determine whether the next file block needs to move before this one.
long nextDataOffset;
FileDataBlock nextFileBlock = GetNextFileDataBlock(fileBlock, dataOffset, out nextDataOffset);
if (nextFileBlock != null && dataOffset + fileBlock.Length > nextFileBlock.FileOffset)
{
// The next block needs to move first, so do that now.
MoveFileBlock(nextFileBlock, nextDataOffset);
}
// Now, move the block.
if (fileBlock.FileOffset > dataOffset)
{
// Move the section to earlier in the file stream (done in chunks starting at the beginning of the section).
byte[] buffer = new byte[COPY_BLOCK_SIZE];
for (long relativeOffset = 0; relativeOffset < fileBlock.Length; relativeOffset += buffer.Length)
{
long readOffset = fileBlock.FileOffset + relativeOffset;
int bytesToRead = (int)Math.Min(buffer.Length, fileBlock.Length - relativeOffset);
_stream.Position = readOffset;
_stream.Read(buffer, 0, bytesToRead);
long writeOffset = dataOffset + relativeOffset;
_stream.Position = writeOffset;
_stream.Write(buffer, 0, bytesToRead);
}
}
else
{
// Move the section to later in the file stream (done in chunks starting at the end of the section).
byte[] buffer = new byte[COPY_BLOCK_SIZE];
for (long relativeOffset = 0; relativeOffset < fileBlock.Length; relativeOffset += buffer.Length)
{
int bytesToRead = (int)Math.Min(buffer.Length, fileBlock.Length - relativeOffset);
long readOffset = fileBlock.FileOffset + fileBlock.Length - relativeOffset - bytesToRead;
_stream.Position = readOffset;
_stream.Read(buffer, 0, bytesToRead);
long writeOffset = dataOffset + fileBlock.Length - relativeOffset - bytesToRead;
_stream.Position = writeOffset;
_stream.Write(buffer, 0, bytesToRead);
}
}
// This block now points to a different position in the file.
fileBlock.SetFileOffset(dataOffset);
}
void ReInitialize()
{
_dataMap = new DataMap();
_dataMap.AddFirst(new FileDataBlock(0, _stream.Length));
_totalLength = _stream.Length;
}
}
}

View file

@ -0,0 +1,271 @@
using System;
using System.IO;
using System.Collections;
namespace Be.Windows.Forms
{
/// <summary>
/// Byte provider for (big) files.
/// </summary>
public class FileByteProvider : IByteProvider, IDisposable
{
#region WriteCollection class
/// <summary>
/// Represents the write buffer class
/// </summary>
class WriteCollection : DictionaryBase
{
/// <summary>
/// Gets or sets a byte in the collection
/// </summary>
public byte this[long index]
{
get { return (byte)this.Dictionary[index]; }
set { Dictionary[index] = value; }
}
/// <summary>
/// Adds a byte into the collection
/// </summary>
/// <param name="index">the index of the byte</param>
/// <param name="value">the value of the byte</param>
public void Add(long index, byte value)
{ Dictionary.Add(index, value); }
/// <summary>
/// Determines if a byte with the given index exists.
/// </summary>
/// <param name="index">the index of the byte</param>
/// <returns>true, if the is in the collection</returns>
public bool Contains(long index)
{ return Dictionary.Contains(index); }
}
#endregion
/// <summary>
/// Occurs, when the write buffer contains new changes.
/// </summary>
public event EventHandler Changed;
/// <summary>
/// Contains all changes
/// </summary>
WriteCollection _writes = new WriteCollection();
/// <summary>
/// Contains the file name.
/// </summary>
string _fileName;
/// <summary>
/// Contains the file stream.
/// </summary>
FileStream _fileStream;
/// <summary>
/// Read-only access.
/// </summary>
bool _readOnly;
/// <summary>
/// Initializes a new instance of the FileByteProvider class.
/// </summary>
/// <param name="fileName"></param>
public FileByteProvider(string fileName)
{
_fileName = fileName;
try
{
// try to open in write mode
_fileStream = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
}
catch
{
// write mode failed, try to open in read-only and fileshare friendly mode.
try
{
_fileStream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
_readOnly = true;
}
catch
{
throw;
}
}
}
/// <summary>
/// Terminates the instance of the FileByteProvider class.
/// </summary>
~FileByteProvider()
{
Dispose();
}
/// <summary>
/// Raises the Changed event.
/// </summary>
/// <remarks>Never used.</remarks>
void OnChanged(EventArgs e)
{
if(Changed != null)
Changed(this, e);
}
/// <summary>
/// Gets the name of the file the byte provider is using.
/// </summary>
public string FileName
{
get { return _fileName; }
}
/// <summary>
/// Returns a value if there are some changes.
/// </summary>
/// <returns>true, if there are some changes</returns>
public bool HasChanges()
{
return (_writes.Count > 0);
}
/// <summary>
/// Updates the file with all changes the write buffer contains.
/// </summary>
public void ApplyChanges()
{
if (this._readOnly)
{
throw new Exception("File is in read-only mode.");
}
if(!HasChanges())
return;
IDictionaryEnumerator en = _writes.GetEnumerator();
while(en.MoveNext())
{
long index = (long)en.Key;
byte value = (byte)en.Value;
if(_fileStream.Position != index)
_fileStream.Position = index;
_fileStream.Write(new byte[] { value }, 0, 1);
}
_writes.Clear();
}
/// <summary>
/// Clears the write buffer and reject all changes made.
/// </summary>
public void RejectChanges()
{
_writes.Clear();
}
#region IByteProvider Members
/// <summary>
/// Never used.
/// </summary>
public event EventHandler LengthChanged;
/// <summary>
/// Reads a byte from the file.
/// </summary>
/// <param name="index">the index of the byte to read</param>
/// <returns>the byte</returns>
public byte ReadByte(long index)
{
if(_writes.Contains(index))
return _writes[index];
if(_fileStream.Position != index)
_fileStream.Position = index;
byte res = (byte)_fileStream.ReadByte();
return res;
}
/// <summary>
/// Gets the length of the file.
/// </summary>
public long Length
{
get
{
return _fileStream.Length;
}
}
/// <summary>
/// Writes a byte into write buffer
/// </summary>
public void WriteByte(long index, byte value)
{
if(_writes.Contains(index))
_writes[index] = value;
else
_writes.Add(index, value);
OnChanged(EventArgs.Empty);
}
/// <summary>
/// Not supported
/// </summary>
public void DeleteBytes(long index, long length)
{
throw new NotSupportedException("FileByteProvider.DeleteBytes");
}
/// <summary>
/// Not supported
/// </summary>
public void InsertBytes(long index, byte[] bs)
{
throw new NotSupportedException("FileByteProvider.InsertBytes");
}
/// <summary>
/// Returns true
/// </summary>
public bool SupportsWriteByte()
{
return !_readOnly;
}
/// <summary>
/// Returns false
/// </summary>
public bool SupportsInsertBytes()
{
return false;
}
/// <summary>
/// Returns false
/// </summary>
public bool SupportsDeleteBytes()
{
return false;
}
#endregion
#region IDisposable Members
/// <summary>
/// Releases the file handle used by the FileByteProvider.
/// </summary>
public void Dispose()
{
if(_fileStream != null)
{
_fileName = null;
_fileStream.Close();
_fileStream = null;
}
GC.SuppressFinalize(this);
}
#endregion
}
}

View file

@ -0,0 +1,96 @@
using System;
namespace Be.Windows.Forms
{
internal sealed class FileDataBlock : DataBlock
{
long _length;
long _fileOffset;
public FileDataBlock(long fileOffset, long length)
{
_fileOffset = fileOffset;
_length = length;
}
public long FileOffset
{
get
{
return _fileOffset;
}
}
public override long Length
{
get
{
return _length;
}
}
public void SetFileOffset(long value)
{
_fileOffset = value;
}
public void RemoveBytesFromEnd(long count)
{
if (count > _length)
{
throw new ArgumentOutOfRangeException("count");
}
_length -= count;
}
public void RemoveBytesFromStart(long count)
{
if (count > _length)
{
throw new ArgumentOutOfRangeException("count");
}
_fileOffset += count;
_length -= count;
}
public override void RemoveBytes(long position, long count)
{
if (position > _length)
{
throw new ArgumentOutOfRangeException("position");
}
if (position + count > _length)
{
throw new ArgumentOutOfRangeException("count");
}
long prefixLength = position;
long prefixFileOffset = _fileOffset;
long suffixLength = _length - count - prefixLength;
long suffixFileOffset = _fileOffset + position + count;
if (prefixLength > 0 && suffixLength > 0)
{
_fileOffset = prefixFileOffset;
_length = prefixLength;
_map.AddAfter(this, new FileDataBlock(suffixFileOffset, suffixLength));
return;
}
if (prefixLength > 0)
{
_fileOffset = prefixFileOffset;
_length = prefixLength;
}
else
{
_fileOffset = suffixFileOffset;
_length = suffixLength;
}
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 B

3701
Vendor/Be.Windows.Forms.HexBox/HexBox.cs vendored Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="contextMenuStrip.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

Binary file not shown.

View file

@ -0,0 +1,21 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Be.Windows.Forms
{
/// <summary>
/// Specifies the case of hex characters in the HexBox control
/// </summary>
public enum HexCasing
{
/// <summary>
/// Converts all characters to uppercase.
/// </summary>
Upper = 0,
/// <summary>
/// Converts all characters to lowercase.
/// </summary>
Lower = 1
}
}

View file

@ -0,0 +1,75 @@
using System;
namespace Be.Windows.Forms
{
/// <summary>
/// Defines a byte provider for HexBox control
/// </summary>
public interface IByteProvider
{
/// <summary>
/// Reads a byte from the provider
/// </summary>
/// <param name="index">the index of the byte to read</param>
/// <returns>the byte to read</returns>
byte ReadByte(long index);
/// <summary>
/// Writes a byte into the provider
/// </summary>
/// <param name="index">the index of the byte to write</param>
/// <param name="value">the byte to write</param>
void WriteByte(long index, byte value);
/// <summary>
/// Inserts bytes into the provider
/// </summary>
/// <param name="index"></param>
/// <param name="bs"></param>
/// <remarks>This method must raise the LengthChanged event.</remarks>
void InsertBytes(long index, byte[] bs);
/// <summary>
/// Deletes bytes from the provider
/// </summary>
/// <param name="index">the start index of the bytes to delete</param>
/// <param name="length">the length of the bytes to delete</param>
/// <remarks>This method must raise the LengthChanged event.</remarks>
void DeleteBytes(long index, long length);
/// <summary>
/// Returns the total length of bytes the byte provider is providing.
/// </summary>
long Length { get; }
/// <summary>
/// Occurs, when the Length property changed.
/// </summary>
event EventHandler LengthChanged;
/// <summary>
/// True, when changes are done.
/// </summary>
bool HasChanges();
/// <summary>
/// Applies changes.
/// </summary>
void ApplyChanges();
/// <summary>
/// Occurs, when bytes are changed.
/// </summary>
event EventHandler Changed;
/// <summary>
/// Returns a value if the WriteByte methods is supported by the provider.
/// </summary>
/// <returns>True, when it´s supported.</returns>
bool SupportsWriteByte();
/// <summary>
/// Returns a value if the InsertBytes methods is supported by the provider.
/// </summary>
/// <returns>True, when it´s supported.</returns>
bool SupportsInsertBytes();
/// <summary>
/// Returns a value if the DeleteBytes methods is supported by the provider.
/// </summary>
/// <returns>True, when it´s supported.</returns>
bool SupportsDeleteBytes();
}
}

View file

@ -0,0 +1,87 @@
using System;
namespace Be.Windows.Forms
{
internal sealed class MemoryDataBlock : DataBlock
{
byte[] _data;
public MemoryDataBlock(byte data)
{
_data = new byte[] { data };
}
public MemoryDataBlock(byte[] data)
{
if (data == null)
{
throw new ArgumentNullException("data");
}
_data = (byte[])data.Clone();
}
public override long Length
{
get
{
return _data.LongLength;
}
}
public byte[] Data
{
get
{
return _data;
}
}
public void AddByteToEnd(byte value)
{
byte[] newData = new byte[_data.LongLength + 1];
_data.CopyTo(newData, 0);
newData[newData.LongLength - 1] = value;
_data = newData;
}
public void AddByteToStart(byte value)
{
byte[] newData = new byte[_data.LongLength + 1];
newData[0] = value;
_data.CopyTo(newData, 1);
_data = newData;
}
public void InsertBytes(long position, byte[] data)
{
byte[] newData = new byte[_data.LongLength + data.LongLength];
if (position > 0)
{
Array.Copy(_data, 0, newData, 0, position);
}
Array.Copy(data, 0, newData, position, data.LongLength);
if (position < _data.LongLength)
{
Array.Copy(_data, position, newData, position + data.LongLength, _data.LongLength - position);
}
_data = newData;
}
public override void RemoveBytes(long position, long count)
{
byte[] newData = new byte[_data.LongLength - count];
if (position > 0)
{
Array.Copy(_data, 0, newData, 0, position);
}
if (position + count < _data.LongLength)
{
Array.Copy(_data, position + count, newData, position, newData.LongLength - position);
}
_data = newData;
}
}
}

View file

@ -0,0 +1,27 @@
using System;
using System.Drawing;
using System.Runtime.InteropServices;
namespace Be.Windows.Forms
{
internal static class NativeMethods
{
// Caret definitions
[DllImport("user32.dll", SetLastError=true)]
public static extern bool CreateCaret(IntPtr hWnd, IntPtr hBitmap, int nWidth, int nHeight);
[DllImport("user32.dll", SetLastError=true)]
public static extern bool ShowCaret(IntPtr hWnd);
[DllImport("user32.dll", SetLastError=true)]
public static extern bool DestroyCaret();
[DllImport("user32.dll", SetLastError=true)]
public static extern bool SetCaretPos(int X, int Y);
// Key definitions
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_CHAR = 0x102;
}
}

View file

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.1
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Be.Windows.Forms.Properties {
using System;
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// 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", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Be.Windows.Forms.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
}
}

View file

@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

38
Vendor/Be.Windows.Forms.HexBox/Util.cs vendored Normal file
View file

@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Be.Windows.Forms
{
static class Util
{
/// <summary>
/// Contains true, if we are in design mode of Visual Studio
/// </summary>
private static bool _designMode;
/// <summary>
/// Initializes an instance of Util class
/// </summary>
static Util()
{
_designMode = (System.Diagnostics.Process.GetCurrentProcess().ProcessName.ToLower() == "devenv");
}
/// <summary>
/// Gets true, if we are in design mode of Visual Studio
/// </summary>
/// <remarks>
/// In Visual Studio 2008 SP1 the designer is crashing sometimes on windows forms.
/// The DesignMode property of Control class is buggy and cannot be used, so use our own implementation instead.
/// </remarks>
public static bool DesignMode
{
get
{
return _designMode;
}
}
}
}