mirror of
https://github.com/fadden/6502bench.git
synced 2024-11-25 14:34:27 +00:00
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:
parent
7c2fbec773
commit
89413d11e4
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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() {
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
100
SourceGen/Tools/WpfGui/FileConcatenator.xaml
Normal file
100
SourceGen/Tools/WpfGui/FileConcatenator.xaml
Normal 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.

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>
|
244
SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs
Normal file
244
SourceGen/Tools/WpfGui/FileConcatenator.xaml.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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];
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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"/>
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user