Add file concatenation tool

Select a list of files and save it.  File lengths and CRCs are
shown for reference.
This commit is contained in:
Andy McFadden 2019-12-28 17:14:29 -08:00
parent 7c2fbec773
commit 89413d11e4
14 changed files with 492 additions and 113 deletions

View File

@ -47,13 +47,13 @@ namespace CommonUtil {
}
/// <summary>
/// Computes a CRC on part of a buffer of data.
/// Computes a CRC-32 on part of a buffer of data.
/// </summary>
/// <param name="crc">Previously computed CRC value. Initially zero.</param>
/// <param name="crc">Previously computed CRC value. Use zero as initial value.</param>
/// <param name="buffer">Data to compute CRC on.</param>
/// <param name="offset">Start offset within buffer.</param>
/// <param name="count">Number of bytes to process.</param>
/// <returns>New CRC value.</returns>
/// <returns>Computed CRC value.</returns>
public static uint OnBuffer(uint crc, byte[] buffer, int offset, int count) {
crc = crc ^ INVERT;
while (count-- != 0) {
@ -64,45 +64,37 @@ namespace CommonUtil {
}
/// <summary>
/// Computes a CRC on a buffer of data.
/// Computes a CRC-32 on a buffer of data.
/// </summary>
/// <param name="crc">Previously computed CRC value. Initially zero.</param>
/// <param name="buffer">Data to compute CRC on.</param>
/// <returns>New CRC value.</returns>
/// <returns>Computed CRC value.</returns>
public static uint OnWholeBuffer(uint crc, byte[] buffer) {
return OnBuffer(crc, buffer, 0, buffer.Length);
}
/// <summary>
/// Computes a CRC on an entire file.
/// Computes a CRC-32 on an entire file. An exception will be thrown on file errors.
/// </summary>
/// <param name="pathName">Full path to file to open.</param>
/// <param name="ocrc">Receives the CRC.</param>
/// <returns>True on success, false on file error.</returns>
public static bool OnWholeFile(string pathName, out uint ocrc) {
try {
using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[8192];
uint crc = 0;
long remain = fs.Length;
while (remain != 0) {
int toRead = (remain < buffer.Length) ? (int)remain : buffer.Length;
int actual = fs.Read(buffer, 0, toRead);
if (toRead != actual) {
throw new IOException("Expected " + toRead + ", got " + actual);
}
crc = OnBuffer(crc, buffer, 0, toRead);
remain -= toRead;
/// <returns>Computed CRC value.</returns>
public static uint OnWholeFile(string pathName) {
using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) {
byte[] buffer = new byte[8192];
uint crc = 0;
long remain = fs.Length;
while (remain != 0) {
int toRead = (remain < buffer.Length) ? (int)remain : buffer.Length;
int actual = fs.Read(buffer, 0, toRead);
if (toRead != actual) {
throw new IOException("Expected " + toRead + ", got " + actual);
}
ocrc = crc;
return true;
crc = OnBuffer(crc, buffer, 0, toRead);
remain -= toRead;
}
} catch (IOException ioe) {
Debug.WriteLine("Fail: " + ioe);
ocrc = 0;
return false;
return crc;
}
}
}

View File

@ -125,7 +125,7 @@ namespace CommonUtil {
/// <param name="dstFile">File of interest.</param>
/// <param name="srcFile">File to compare dates with.</param>
/// <returns>True if dstFile is missing or older than srcFile.</returns>
public static bool FileMissingOrOlder(string dstFile, string srcFile) {
public static bool IsFileMissingOrOlder(string dstFile, string srcFile) {
FileInfo fid = new FileInfo(dstFile);
if (!fid.Exists) {
return true; // not there

View File

@ -3117,44 +3117,6 @@ namespace SourceGen {
return -1;
}
public void ShowFileHexDump() {
OpenFileDialog fileDlg = new OpenFileDialog() {
Filter = Res.Strings.FILE_FILTER_ALL,
FilterIndex = 1
};
if (fileDlg.ShowDialog() != true) {
return;
}
string fileName = fileDlg.FileName;
FileInfo fi = new FileInfo(fileName);
if (fi.Length > Tools.WpfGui.HexDumpViewer.MAX_LENGTH) {
string msg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT,
fi.Length / 1024, Tools.WpfGui.HexDumpViewer.MAX_LENGTH / 1024);
MessageBox.Show(msg, Res.Strings.OPEN_DATA_FAIL_CAPTION,
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
byte[] data;
try {
data = File.ReadAllBytes(fileName);
} catch (Exception ex) {
// not expecting this to happen
MessageBox.Show(ex.Message);
return;
}
// Create the dialog without an owner, and add it to the "unowned" list.
Tools.WpfGui.HexDumpViewer dlg = new Tools.WpfGui.HexDumpViewer(null,
data, mOutputFormatter);
dlg.SetFileName(Path.GetFileName(fileName));
dlg.Closing += (sender, e) => {
Debug.WriteLine("Window " + dlg + " closed, removing from unowned list");
mUnownedWindows.Remove(dlg);
};
mUnownedWindows.Add(dlg);
dlg.Show();
}
public void ShowHexDump() {
if (mHexDumpDialog == null) {
// Create and show modeless dialog. This one is "always on top" by default,
@ -3283,34 +3245,6 @@ namespace SourceGen {
dlg.ShowDialog();
}
public void ToggleAsciiChart() {
if (mAsciiChartDialog == null) {
// Create without owner so it doesn't have to be in front of main window.
mAsciiChartDialog = new Tools.WpfGui.AsciiChart(null);
mAsciiChartDialog.Closing += (sender, e) => {
Debug.WriteLine("ASCII chart closed");
mAsciiChartDialog = null;
};
mAsciiChartDialog.Show();
} else {
mAsciiChartDialog.Close();
}
}
public void ToggleInstructionChart() {
if (mInstructionChartDialog == null) {
// Create without owner so it doesn't have to be in front of main window.
mInstructionChartDialog = new Tools.WpfGui.InstructionChart(null,mOutputFormatter);
mInstructionChartDialog.Closing += (sender, e) => {
Debug.WriteLine("Instruction chart closed");
mInstructionChartDialog = null;
};
mInstructionChartDialog.Show();
} else {
mInstructionChartDialog.Close();
}
}
public void ToggleDataScan() {
ProjectProperties oldProps = mProject.ProjectProps;
ProjectProperties newProps = new ProjectProperties(oldProps);
@ -4017,6 +3951,82 @@ namespace SourceGen {
#endregion Info panel
#region Tools
public void ToggleAsciiChart() {
if (mAsciiChartDialog == null) {
// Create without owner so it doesn't have to be in front of main window.
mAsciiChartDialog = new Tools.WpfGui.AsciiChart(null);
mAsciiChartDialog.Closing += (sender, e) => {
Debug.WriteLine("ASCII chart closed");
mAsciiChartDialog = null;
};
mAsciiChartDialog.Show();
} else {
mAsciiChartDialog.Close();
}
}
public void ToggleInstructionChart() {
if (mInstructionChartDialog == null) {
// Create without owner so it doesn't have to be in front of main window.
mInstructionChartDialog = new Tools.WpfGui.InstructionChart(null, mOutputFormatter);
mInstructionChartDialog.Closing += (sender, e) => {
Debug.WriteLine("Instruction chart closed");
mInstructionChartDialog = null;
};
mInstructionChartDialog.Show();
} else {
mInstructionChartDialog.Close();
}
}
public void ShowFileHexDump() {
OpenFileDialog fileDlg = new OpenFileDialog() {
Filter = Res.Strings.FILE_FILTER_ALL,
FilterIndex = 1
};
if (fileDlg.ShowDialog() != true) {
return;
}
string fileName = fileDlg.FileName;
FileInfo fi = new FileInfo(fileName);
if (fi.Length > Tools.WpfGui.HexDumpViewer.MAX_LENGTH) {
string msg = string.Format(Res.Strings.OPEN_DATA_TOO_LARGE_FMT,
fi.Length / 1024, Tools.WpfGui.HexDumpViewer.MAX_LENGTH / 1024);
MessageBox.Show(msg, Res.Strings.OPEN_DATA_FAIL_CAPTION,
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
byte[] data;
try {
data = File.ReadAllBytes(fileName);
} catch (Exception ex) {
// not expecting this to happen
MessageBox.Show(ex.Message);
return;
}
// Create the dialog without an owner, and add it to the "unowned" list.
Tools.WpfGui.HexDumpViewer dlg = new Tools.WpfGui.HexDumpViewer(null,
data, mOutputFormatter);
dlg.SetFileName(Path.GetFileName(fileName));
dlg.Closing += (sender, e) => {
Debug.WriteLine("Window " + dlg + " closed, removing from unowned list");
mUnownedWindows.Remove(dlg);
};
mUnownedWindows.Add(dlg);
dlg.Show();
}
public void ConcatenateFiles() {
Tools.WpfGui.FileConcatenator concat =
new Tools.WpfGui.FileConcatenator(this.mMainWin);
concat.ShowDialog();
}
#endregion Tools
#region Debug features
public void Debug_ExtensionScriptInfo() {

View File

@ -138,6 +138,7 @@ and 65816 code. The official web site is
<li><a href="tools.html">Tools</a>
<ul>
<li><a href="tools.html#hexdump">Hex Dump Viewer</a></li>
<li><a href="tools.html#file-concat">File Concatenator</a></li>
<li><a href="tools.html#ascii-chart">ASCII Chart</a></li>
<li><a href="tools.html#instruction-chart">Instruction Chart</a></li>
</ul></li>
@ -175,6 +176,7 @@ and 65816 code. The official web site is
<li><a href="tutorials.html#advanced-features">Tutorial #2: Advanced Features</a></li>
<li><a href="tutorials.html#address-tables">Tutorial #3: Address Table Formatting</a></li>
<li><a href="tutorials.html#extension-scripts">Tutorial #4: Extension Scripts</a></li>
<li><a href="tutorials.html#visualizations">Tutorial #5: Visualizations</a></li>
</ul></li>
</ul>

View File

@ -49,6 +49,13 @@ default this box is checked when displaying project data, and not checked for
external files.</p>
<h2><a name="file-concat">File Concatenator</a></h2>
<p>The File Concatenator combines multiple files into a single file.
Select the files to add, arrange them in the proper order, then hit
"Save".</p>
<h2><a name="ascii-chart">ASCII Chart</a></h2>
<p>This opens a window with the ASCII character set. Each character is

View File

@ -104,7 +104,7 @@ namespace SourceGen.Sandbox {
/// <param name="dstDir">Destination directory.</param>
private static void CopyIfNewer(string srcDll, string dstDir) {
string dstFile = Path.Combine(dstDir, Path.GetFileName(srcDll));
if (FileUtil.FileMissingOrOlder(dstFile, srcDll)) {
if (FileUtil.IsFileMissingOrOlder(dstFile, srcDll)) {
Debug.WriteLine("Copying " + srcDll + " to " + dstFile);
File.Copy(srcDll, dstFile, true);
}
@ -155,10 +155,10 @@ namespace SourceGen.Sandbox {
// the DLLs the app uses, not the copy the plugins use, but earlier we made sure
// that they were the same. This test doesn't handle the case where the DLLs
// get rolled back, but that's probably not interesting for us.)
bool needCompile = FileUtil.FileMissingOrOlder(destPathName, srcPathName) ||
FileUtil.FileMissingOrOlder(destPathName,
bool needCompile = FileUtil.IsFileMissingOrOlder(destPathName, srcPathName) ||
FileUtil.IsFileMissingOrOlder(destPathName,
typeof(PluginCommon.PluginManager).Assembly.Location) ||
FileUtil.FileMissingOrOlder(destPathName,
FileUtil.IsFileMissingOrOlder(destPathName,
typeof(CommonUtil.CRC32).Assembly.Location);
if (needCompile) {
Debug.WriteLine("Compiling " + srcPathName + " to " + destPathName);

View File

@ -86,6 +86,9 @@
</Compile>
<Compile Include="Tools\ApplesoftToHtml.cs" />
<Compile Include="Tools\VirtualHexDump.cs" />
<Compile Include="Tools\WpfGui\FileConcatenator.xaml.cs">
<DependentUpon>FileConcatenator.xaml</DependentUpon>
</Compile>
<Compile Include="Tools\WpfGui\InstructionChart.xaml.cs">
<DependentUpon>InstructionChart.xaml</DependentUpon>
</Compile>
@ -262,6 +265,10 @@
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Tools\WpfGui\FileConcatenator.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Tools\WpfGui\InstructionChart.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>

View File

@ -0,0 +1,100 @@
<!--
Copyright 2019 faddenSoft
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.
-->
<Window x:Class="SourceGen.Tools.WpfGui.FileConcatenator"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:SourceGen.Tools.WpfGui"
xmlns:system="clr-namespace:System;assembly=mscorlib"
mc:Ignorable="d"
Title="File Concatenator"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner">
<Window.Resources>
<system:String x:Key="str_FileAccessFailedCaption">File Access Error</system:String>
<system:String x:Key="str_FileAccessFailedFmt">Unable to access file: {0}</system:String>
<system:String x:Key="str_FileCrcFailed">CRC failed</system:String>
<system:String x:Key="str_SelectFileTitle">Select File</system:String>
<system:String x:Key="str_SuccessCaption">Success</system:String>
<system:String x:Key="str_SuccessMsgFmt">All files were concatenated successfully.&#x0d;&#x0d;Total length is {0}, overall CRC-32 is {1}.</system:String>
</Window.Resources>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="0" Text="Select files:"/>
<DataGrid Name="concatGrid" Grid.Row="1" Height="300" Width="480"
IsReadOnly="True"
ItemsSource="{Binding ConcatItems}"
FontFamily="{StaticResource GeneralMonoFont}"
SnapsToDevicePixels="True"
GridLinesVisibility="All"
VerticalGridLinesBrush="#FF7F7F7F"
HorizontalGridLinesBrush="#FFE2E2E2"
AutoGenerateColumns="False"
HeadersVisibility="Column"
CanUserReorderColumns="False"
CanUserSortColumns="False"
SelectionMode="Single"
VerticalScrollBarVisibility="Visible"
SelectionChanged="ConcatGrid_SelectionChanged">
<DataGrid.Resources>
<!-- make the no-focus color the same as the in-focus color -->
<!-- thanks: https://stackoverflow.com/a/13053511/294248 -->
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightBrushKey}"
Color="{x:Static SystemColors.HighlightColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.InactiveSelectionHighlightTextBrushKey}"
Color="{x:Static SystemColors.HighlightTextColor}"/>
</DataGrid.Resources>
<DataGrid.Columns>
<DataGridTextColumn Header="Filename" Width="298" Binding="{Binding FileName}"/>
<DataGridTextColumn Header="Length" Width="102" Binding="{Binding Length}"/>
<DataGridTextColumn Header="CRC-32" Width="60" Binding="{Binding Crc32}"/>
</DataGrid.Columns>
</DataGrid>
<StackPanel Grid.Row="1" Grid.Column="1" Margin="4,0,0,0">
<Button Width="90" Content="Add File..." Click="AddButton_Click"/>
<Button Width="90" Margin="0,4,0,0" Content="Remove File"
IsEnabled="{Binding IsRemoveEnabled}" Click="RemoveButton_Click"/>
<Button Width="90" Margin="0,12,0,0" Content="Up"
IsEnabled="{Binding IsUpEnabled}" Click="UpButton_Click"/>
<Button Width="90" Margin="0,4,0,0" Content="Down"
IsEnabled="{Binding IsDownEnabled}" Click="DownButton_Click"/>
</StackPanel>
<StackPanel Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
Orientation="Horizontal" HorizontalAlignment="Right" Margin="0,10,0,0">
<Button Content="Save..." IsDefault="True" Width="70"
IsEnabled="{Binding IsSaveEnabled}" Click="SaveButton_Click"/>
<Button Content="Cancel" IsCancel="True" Width="70" Margin="4,0,0,0"/>
</StackPanel>
</Grid>
</Window>

View File

@ -0,0 +1,244 @@
/*
* Copyright 2019 faddenSoft
*
* 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.
*/
using Microsoft.Win32;
using System;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Runtime.CompilerServices;
using System.Windows;
using CommonUtil;
using System.Windows.Controls;
namespace SourceGen.Tools.WpfGui {
/// <summary>
/// File concatenation tool.
/// </summary>
public partial class FileConcatenator : Window, INotifyPropertyChanged {
//
// Every action causes a selection change, so we don't explicitly call an "update
// controls" function.
//
public bool IsRemoveEnabled {
get { return mIsRemoveEnabled; }
set { mIsRemoveEnabled = value; OnPropertyChanged(); }
}
private bool mIsRemoveEnabled;
public bool IsUpEnabled {
get { return mIsUpEnabled; }
set { mIsUpEnabled = value; OnPropertyChanged(); }
}
private bool mIsUpEnabled;
public bool IsDownEnabled {
get { return mIsDownEnabled; }
set { mIsDownEnabled = value; OnPropertyChanged(); }
}
private bool mIsDownEnabled;
public bool IsSaveEnabled {
get { return mIsSaveEnabled; }
set { mIsSaveEnabled = value; OnPropertyChanged(); }
}
private bool mIsSaveEnabled;
/// <summary>
/// Item for main list.
/// </summary>
public class ConcatItem {
public string PathName { get; private set; }
public string FileName { get; private set; }
public long Length { get; private set; }
public string Crc32 { get; private set; }
public ConcatItem(string pathName, long length, uint crc32) {
PathName = pathName;
Length = length;
Crc32 = crc32.ToString("x8");
FileName = Path.GetFileName(pathName);
}
}
public ObservableCollection<ConcatItem> ConcatItems { get; private set; } =
new ObservableCollection<ConcatItem>();
// INotifyPropertyChanged implementation
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="owner">Parent window.</param>
public FileConcatenator(Window owner) {
InitializeComponent();
Owner = owner;
DataContext = this;
// all "enabled" flags are initially false
}
private void ConcatGrid_SelectionChanged(object sender, SelectionChangedEventArgs e) {
bool isItemSelected = (concatGrid.SelectedItem != null);
IsRemoveEnabled = isItemSelected;
IsUpEnabled = isItemSelected && concatGrid.SelectedIndex != 0;
IsDownEnabled = isItemSelected && concatGrid.SelectedIndex != ConcatItems.Count - 1;
IsSaveEnabled = ConcatItems.Count != 0;
}
private void AddButton_Click(object sender, RoutedEventArgs e) {
OpenFileDialog fileDlg = new OpenFileDialog() {
Filter = Res.Strings.FILE_FILTER_ALL,
FilterIndex = 0,
Title = (string)FindResource("str_SelectFileTitle")
};
if (fileDlg.ShowDialog() != true) {
return;
}
try {
string pathName = Path.GetFullPath(fileDlg.FileName);
long length = new FileInfo(pathName).Length;
uint crc32 = CRC32.OnWholeFile(pathName);
ConcatItems.Add(new ConcatItem(pathName, length, crc32));
concatGrid.SelectedIndex = ConcatItems.Count - 1;
} catch (Exception ex) {
string caption = (string)FindResource("str_FileAccessFailedCaption");
string fmt = (string)FindResource("str_FileAccessFailedFmt");
MessageBox.Show(string.Format(fmt, ex.Message), caption,
MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
int index = concatGrid.SelectedIndex;
ConcatItems.RemoveAt(index);
// Keep selection at same index, unless we just removed the item at the end.
if (index == ConcatItems.Count) {
index--;
}
if (index >= 0) {
concatGrid.SelectedIndex = index;
}
}
private void UpButton_Click(object sender, RoutedEventArgs e) {
ConcatItem item = (ConcatItem)concatGrid.SelectedItem;
int index = concatGrid.SelectedIndex;
Debug.Assert(index > 0);
ConcatItems.RemoveAt(index);
ConcatItems.Insert(index - 1, item);
concatGrid.SelectedIndex = index - 1;
}
private void DownButton_Click(object sender, RoutedEventArgs e) {
ConcatItem item = (ConcatItem)concatGrid.SelectedItem;
int index = concatGrid.SelectedIndex;
Debug.Assert(index >= 0 && index < ConcatItems.Count - 1);
ConcatItems.RemoveAt(index);
ConcatItems.Insert(index + 1, item);
concatGrid.SelectedIndex = index + 1;
}
private void SaveButton_Click(object sender, RoutedEventArgs e) {
SaveFileDialog fileDlg = new SaveFileDialog() {
Filter = Res.Strings.FILE_FILTER_ALL,
FilterIndex = 0,
ValidateNames = true,
FileName = "concat.bin"
};
if (fileDlg.ShowDialog() != true) {
return;
}
string pathName = Path.GetFullPath(fileDlg.FileName);
Debug.WriteLine("OUTPUT TO " + pathName);
// Create the new file, and copy each file to it in order. We compute a total
// CRC for the file contents as we go. (It's possible to combine the CRCs we already
// have with some math; see e.g. https://stackoverflow.com/a/44061990/294248 .)
uint totalCrc;
long totalLen;
try {
using (FileStream stream = new FileStream(pathName, FileMode.Create)) {
totalCrc = CopyAllFiles(stream);
}
totalLen = new FileInfo(pathName).Length;
} catch (Exception ex) {
string ecaption = (string)FindResource("str_FileAccessFailedCaption");
string efmt = (string)FindResource("str_FileAccessFailedFmt");
MessageBox.Show(string.Format(efmt, ex.Message), ecaption,
MessageBoxButton.OK, MessageBoxImage.Error);
return;
}
// TODO(someday): make all data available on the clipboard
string caption = (string)FindResource("str_SuccessCaption");
string fmt = (string)FindResource("str_SuccessMsgFmt");
MessageBox.Show(string.Format(fmt, totalLen, "0x" + totalCrc.ToString("x8")),
caption, MessageBoxButton.OK, MessageBoxImage.None);
DialogResult = true;
}
private uint CopyAllFiles(Stream outStream) {
byte[] buffer = new byte[256 * 1024];
uint crc32 = 0;
foreach (ConcatItem item in ConcatItems) {
CopyFileToStreamWithCrc32(item.PathName, outStream, buffer, ref crc32);
}
return crc32;
}
/// <summary>
/// Copies a file on disk to a Stream, computing a CRC-32 on the data. Throws an
/// exception on file error.
/// </summary>
/// <param name="inputPath">Full path to input file.</param>
/// <param name="outStream">Output data stream.</param>
/// <param name="buffer">Data buffer, to avoid repeated allocations.</param>
/// <param name="crc">CRC-32, initially zero.</param>
public static void CopyFileToStreamWithCrc32(string inputPath, Stream outStream,
byte[] buffer, ref uint crc) {
using (FileStream inStream = new FileStream(inputPath, FileMode.Open,
FileAccess.Read)) {
while (true) {
int actual = inStream.Read(buffer, 0, buffer.Length);
if (actual == 0) {
// end of stream
return;
}
crc = CRC32.OnBuffer(crc, buffer, 0, actual);
outStream.Write(buffer, 0, actual);
}
}
}
}
}

View File

@ -87,6 +87,8 @@ namespace SourceGen.Tools.WpfGui {
/// <summary>
/// Constructor.
/// </summary>
/// <param name="owner">Parent window.</param>
/// <param name="formatter">Text formatter.</param>
public InstructionChart(Window owner, Formatter formatter) {
InitializeComponent();
Owner = owner;

View File

@ -831,7 +831,7 @@ namespace SourceGen.WpfGui {
}
}
private void SymbolFileUpButton_Click(object sender, EventArgs e) {
private void SymbolFileUpButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count == 1);
int selIndex = symbolFilesListBox.SelectedIndex;
Debug.Assert(selIndex > 0);
@ -839,7 +839,7 @@ namespace SourceGen.WpfGui {
MoveSingleItem(selIndex, symbolFilesListBox.SelectedItem, -1);
}
private void SymbolFileDownButton_Click(object sender, EventArgs e) {
private void SymbolFileDownButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count == 1);
int selIndex = symbolFilesListBox.SelectedIndex;
Debug.Assert(selIndex < symbolFilesListBox.Items.Count - 1);
@ -862,7 +862,7 @@ namespace SourceGen.WpfGui {
UpdateControls();
}
private void SymbolFileRemoveButton_Click(object sender, EventArgs e) {
private void SymbolFileRemoveButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(symbolFilesListBox.SelectedItems.Count > 0);
for (int i = symbolFilesListBox.SelectedItems.Count - 1; i >= 0; i--) {
string selItem = (string)symbolFilesListBox.SelectedItems[i];

View File

@ -71,6 +71,11 @@ namespace SourceGen.WpfGui {
// this can't change while the dialog is open, so don't need OnPropertyChanged
}
//
// Every action causes a selection change, so we don't explicitly call an "update
// controls" function.
//
public bool IsEditEnabled {
get { return mIsEditEnabled; }
set { mIsEditEnabled = value; OnPropertyChanged(); }
@ -229,8 +234,10 @@ namespace SourceGen.WpfGui {
private void RemoveButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
int index = VisualizationList.IndexOf(item);
VisualizationList.Remove(item);
int index = visualizationGrid.SelectedIndex;
VisualizationList.RemoveAt(index);
// Keep selection at same index, unless we just removed the item at the end.
if (index == VisualizationList.Count) {
index--;
}
@ -271,18 +278,18 @@ namespace SourceGen.WpfGui {
private void UpButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
int index = VisualizationList.IndexOf(item);
int index = visualizationGrid.SelectedIndex;
Debug.Assert(index > 0);
VisualizationList.Remove(item);
VisualizationList.RemoveAt(index);
VisualizationList.Insert(index - 1, item);
visualizationGrid.SelectedIndex = index - 1;
}
private void DownButton_Click(object sender, RoutedEventArgs e) {
Visualization item = (Visualization)visualizationGrid.SelectedItem;
int index = VisualizationList.IndexOf(item);
int index = visualizationGrid.SelectedIndex;
Debug.Assert(index >= 0 && index < VisualizationList.Count - 1);
VisualizationList.Remove(item);
VisualizationList.RemoveAt(index);
VisualizationList.Insert(index + 1, item);
visualizationGrid.SelectedIndex = index + 1;
}

View File

@ -63,6 +63,7 @@ limitations under the License.
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="CloseCmd" Text="Close"/>
<RoutedUICommand x:Key="ConcatenateFilesCmd" Text="Concatenate Files..."/>
<RoutedUICommand x:Key="CreateLocalVariableTableCmd" Text="Create Local Variable Table..."/>
<RoutedUICommand x:Key="DeleteMlcCmd" Text="Delete Note/Long Comment">
<RoutedUICommand.InputGestures>
@ -161,7 +162,7 @@ limitations under the License.
<KeyGesture>Ctrl+A</KeyGesture>
</RoutedUICommand.InputGestures>
</RoutedUICommand>
<RoutedUICommand x:Key="ShowFileHexDumpCmd" Text="Hex Dump..."/>
<RoutedUICommand x:Key="ShowFileHexDumpCmd" Text="File Hex Dump..."/>
<RoutedUICommand x:Key="ShowHexDumpCmd" Text="Show Hex Dump"/>
<RoutedUICommand x:Key="ToggleAsciiChartCmd" Text="ASCII Chart"/>
<RoutedUICommand x:Key="ToggleDataScanCmd" Text="Toggle Data Scan">
@ -203,6 +204,8 @@ limitations under the License.
CanExecute="IsProjectOpen" Executed="AssembleCmd_Executed"/>
<CommandBinding Command="{StaticResource CloseCmd}"
CanExecute="IsProjectOpen" Executed="CloseCmd_Executed"/>
<CommandBinding Command="{StaticResource ConcatenateFilesCmd}"
Executed="ConcatenateFilesCmd_Executed"/>
<CommandBinding Command="{StaticResource CreateLocalVariableTableCmd}"
CanExecute="CanCreateLocalVariableTable" Executed="CreateLocalVariableTableCmd_Executed"/>
<CommandBinding Command="{StaticResource DeleteMlcCmd}"
@ -393,11 +396,12 @@ limitations under the License.
<MenuItem Command="{StaticResource ShowHexDumpCmd}"/>
</MenuItem>
<MenuItem Header="_Tools" SubmenuOpened="ToolsMenu_SubmenuOpened">
<MenuItem Command="{StaticResource ShowFileHexDumpCmd}"/>
<MenuItem Name="toggleAsciiChartMenuItem"
Command="{StaticResource ToggleAsciiChartCmd}" IsCheckable="True"/>
<MenuItem Name="toggleInstructionChartMenuItem"
Command="{StaticResource ToggleInstructionChartCmd}" IsCheckable="True"/>
<MenuItem Name="toggleAsciiChartMenuItem"
Command="{StaticResource ToggleAsciiChartCmd}" IsCheckable="True"/>
<MenuItem Command="{StaticResource ShowFileHexDumpCmd}"/>
<MenuItem Command="{StaticResource ConcatenateFilesCmd}"/>
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Command="Help"/>

View File

@ -1131,6 +1131,10 @@ namespace SourceGen.WpfGui {
}
}
private void ConcatenateFilesCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.ConcatenateFiles();
}
private void CreateLocalVariableTableCmd_Executed(object sender, ExecutedRoutedEventArgs e) {
mMainCtrl.CreateLocalVariableTable();
}