Initial file commit

This commit is contained in:
Andy McFadden 2018-09-28 10:05:11 -07:00
parent 469eb49c4a
commit 2c6212404d
370 changed files with 72418 additions and 2 deletions

312
.gitignore vendored Normal file
View File

@ -0,0 +1,312 @@
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
# 6502bench-specific stuff
SourceGen/SourceGen-settings
SourceGen/PluginDll
DIST_Debug
DIST_release
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# Benchmark Results
BenchmarkDotNet.Artifacts/
# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/
**/Properties/launchSettings.json
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# Visual Studio Trace Files
*.e2e
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json
# Visual Studio code coverage results
*.coverage
*.coveragexml
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
*.ndf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/
# Typescript v1 declaration files
typings/
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config
# Tabs Studio
*.tss
# Telerik's JustMock configuration file
*.jmconfig
# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs
# OpenCover UI analysis results
OpenCover/

92
Asm65/Address.cs Normal file
View File

@ -0,0 +1,92 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace Asm65 {
public static class Address {
/// <summary>
/// Converts a 16- or 24-bit address to a string.
/// </summary>
/// <param name="addr"></param>
/// <returns></returns>
public static string AddressToString(int addr, bool always24) {
if (!always24 && addr < 65536) {
return addr.ToString("x4");
} else {
return (addr >> 16).ToString("x2") + "/" + (addr & 0xffff).ToString("x4");
}
}
/// <summary>
/// Parses and validates a 16- or 24-bit address, expressed in hexadecimal. Bits
/// 16-23 may be specified with a slash.
///
/// The following all evaluate to the same thing: 1000, $1000, 0x1000, 00/1000.
/// </summary>
/// <param name="addrStr">String to validate.</param>
/// <param name="max">Maximum valid address value.</param>
/// <param name="addr">Integer form.</param>
/// <returns>True if the address is valid.</returns>
public static bool ParseAddress(string addrStr, int max, out int addr) {
string trimStr = addrStr.Trim(); // strip whitespace
if (trimStr.Length < 1) {
addr = -1;
return false;
}
if (trimStr[0] == '$') {
trimStr = trimStr.Remove(0, 1);
}
int slashIndex = trimStr.IndexOf('/');
try {
if (slashIndex < 0) {
addr = Convert.ToInt32(trimStr, 16);
if (addr < 0 || addr > max) {
Debug.WriteLine("Simple value out of range");
addr = -1;
return false;
}
} else {
string[] splitStr = trimStr.Split('/');
if (splitStr.Length == 2) {
int addr1 = Convert.ToInt32(splitStr[0], 16);
int addr2 = Convert.ToInt32(splitStr[1], 16);
addr = (addr1 << 16) | addr2;
// Check components separately to catch overflow.
if (addr1 < 0 || addr1 > 255 || addr2 < 0 || addr2 > 65535 ||
addr > max) {
Debug.WriteLine("Slash value out of range");
addr = -1;
return false;
}
} else {
addr = -1;
}
}
} catch (Exception) {
// Thrown from Convert.ToInt32
//Debug.WriteLine("ValidateAddress: conversion of '" + addrStr + "' failed: " +
// ex.Message);
addr = -1;
return false;
}
//Debug.WriteLine("Conv " + addrStr + " --> " + addr.ToString("x6"));
return true;
}
}
}

26
Asm65/Asm65.csproj Normal file
View File

@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CommonUtil\CommonUtil.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

1184
Asm65/CpuDef.cs Normal file

File diff suppressed because it is too large Load Diff

819
Asm65/Formatter.cs Normal file
View File

@ -0,0 +1,819 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using AddressMode = Asm65.OpDef.AddressMode;
namespace Asm65 {
/// <summary>
/// Functions used for formatting bits of 65xx code into human-readable form.
///
/// There are a variety of ways to format a given thing, based on personal preference
/// (e.g. whether opcodes are upper- or lower-case) and assembler syntax requirements.
///
/// The functions in this class serve two purposes: (1) produce consistent output
/// throughout the program; (2) cache format strings and other components to reduce
/// string manipulation overhead. Note the caching is per-Formatter, so it's best to
/// create just one and share it around.
///
/// The configuration of a Formatter may not be altered once created. This is important
/// in situations where we compute output size in one pass and generate it in another,
/// because it guarantees that a given Formatter object will produce the same number of
/// lines of output.
///
/// NOTE: if the CpuDef changes, the cached values in the Formatter will become invalid.
/// Discard the Formatter and create a new one. (This could be fixed by keying off of
/// the OpDef instead of OpDef.Opcode, but that's less convenient.)
/// </summary>
public class Formatter {
/// <summary>
/// Various format configuration options. Fill one of these out and pass it to
/// the Formatter constructor.
/// </summary>
public struct FormatConfig {
// alpha case for some case-insensitive items
public bool mUpperHexDigits; // display hex values in upper case?
public bool mUpperOpcodes; // display opcodes in upper case?
public bool mUpperPseudoOpcodes; // display pseudo-opcodes in upper case?
public bool mUpperOperandA; // display acc operand in upper case?
public bool mUpperOperandS; // display stack operand in upper case?
public bool mUpperOperandXY; // display index register operand in upper case?
public bool mAddSpaceLongComment; // insert space after delimiter for long comments?
// functional changes to assembly output
public bool mSuppressHexNotation; // omit '$' before hex digits
public bool mAllowHighAsciiCharConst; // can we do high-ASCII character constants?
// (this might need to be generalized)
public string mForceAbsOpcodeSuffix; // these may be null or empty
public string mForceAbsOperandPrefix;
public string mForceLongOpcodeSuffix;
public string mForceLongOperandPrefix;
public string mEndOfLineCommentDelimiter; // usually ';'
public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space
public string mBoxLineCommentDelimiter; // usually blank or ';'
// miscellaneous
public bool mHexDumpAsciiOnly; // disallow non-ASCII chars in hex dumps?
public enum CharConvMode { Unknown = 0, PlainAscii, HighLowAscii };
public CharConvMode mHexDumpCharConvMode; // character conversion mode for dumps
public enum ExpressionMode { Unknown = 0, Simple, Merlin };
public ExpressionMode mExpressionMode; // symbol rendering mode
// Deserialization helper.
public static ExpressionMode ParseExpressionMode(string str) {
ExpressionMode em = ExpressionMode.Simple;
if (!string.IsNullOrEmpty(str)) {
if (Enum.TryParse<ExpressionMode>(str, out ExpressionMode pem)) {
em = pem;
}
}
return em;
}
}
private static readonly char[] sHexCharsLower = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
};
private static readonly char[] sHexCharsUpper = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
/// <summary>
/// Formatter configuration options. Fixed at construction time.
/// </summary>
private FormatConfig mFormatConfig;
/// <summary>
/// Get a copy of the format config.
/// </summary>
public FormatConfig Config { get { return mFormatConfig; } }
// Bits and pieces.
char mHexFmtChar;
string mHexPrefix;
char mAccChar;
char mXregChar;
char mYregChar;
char mSregChar;
// Format string for offsets.
private string mOffset20Format;
// Format strings for addresses.
private string mAddrFormatNoBank;
private string mAddrFormatWithBank;
// Generated opcode strings. The index is the bitwise OR of the opcode value and
// the disambiguation value. In most cases this just helps us avoid calling
// ToUpper incessantly.
private Dictionary<int, string> mOpcodeStrings = new Dictionary<int, string>();
// Generated pseudo-opcode strings.
private Dictionary<string, string> mPseudoOpStrings = new Dictionary<string, string>();
// Generated format strings for operands. The index is the bitwise OR of the
// address mode and the disambiguation value.
private Dictionary<int, string> mOperandFormats = new Dictionary<int, string>();
// Generated format strings for bytes.
private const int MAX_BYTE_DUMP = 4;
private string[] mByteDumpFormats = new string[MAX_BYTE_DUMP];
// Generated format strings for hex values.
private string[] mHexValueFormats = new string[4];
private string mFullLineCommentDelimiterPlus;
// Buffer to use when generating hex dump lines.
private char[] mHexDumpBuffer;
/// <summary>
/// A 16-character array with 0-9a-f, for hex conversions. The letters will be
/// upper or lower case, per the format config.
/// </summary>
public char[] HexDigits {
get {
return mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower;
}
}
/// <summary>
/// String to put between the operand and the end-of-line comment.
/// </summary>
public string EndOfLineCommentDelimiter {
get { return mFormatConfig.mEndOfLineCommentDelimiter; }
}
/// <summary>
/// String to put at the start of a line with a full-line comment.
/// </summary>
public string FullLineCommentDelimiter {
get { return mFullLineCommentDelimiterPlus; }
}
/// <summary>
/// String to put at the start of a line that has a box comment. This is usually
/// blank, as it's only needed if the assembler doesn't recognize the box character
/// as a comment.
/// </summary>
public string BoxLineCommentDelimiter {
get { return mFormatConfig.mBoxLineCommentDelimiter; }
}
/// <summary>
/// When formatting a symbol with an offset, if this flag is set, generate code that
/// assumes the assembler applies the adjustment, then shifts the result. If not,
/// assume the assembler shifts the operand before applying the adjustment.
/// </summary>
public FormatConfig.ExpressionMode ExpressionMode {
get { return mFormatConfig.mExpressionMode; }
}
public Formatter(FormatConfig config) {
mFormatConfig = config;
if (mFormatConfig.mEndOfLineCommentDelimiter == null) {
mFormatConfig.mEndOfLineCommentDelimiter = string.Empty;
}
if (mFormatConfig.mFullLineCommentDelimiterBase == null) {
mFormatConfig.mFullLineCommentDelimiterBase = string.Empty;
}
if (mFormatConfig.mBoxLineCommentDelimiter == null) {
mFormatConfig.mBoxLineCommentDelimiter = string.Empty;
}
if (mFormatConfig.mAddSpaceLongComment) {
mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase + " ";
} else {
mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase;
}
Reset();
// Prep the static parts of the hex dump buffer.
mHexDumpBuffer = new char[73];
for (int i = 0; i < mHexDumpBuffer.Length; i++) {
mHexDumpBuffer[i] = ' ';
}
mHexDumpBuffer[6] = ':';
}
/// <summary>
/// Resets the pieces we use to build format strings.
/// </summary>
private void Reset() {
// Clear old data. (No longer needed.)
//mAddrFormatNoBank = mAddrFormatWithBank = null;
//mOffset20Format = null;
//mOpcodeStrings.Clear();
//mPseudoOpStrings.Clear();
//mOperandFormats.Clear();
//for (int i = 0; i < MAX_BYTE_DUMP; i++) {
// mByteDumpFormats[i] = null;
//}
if (mFormatConfig.mUpperHexDigits) {
mHexFmtChar = 'X';
} else {
mHexFmtChar = 'x';
}
if (mFormatConfig.mSuppressHexNotation) {
mHexPrefix = "";
} else {
mHexPrefix = "$";
}
if (mFormatConfig.mUpperOperandA) {
mAccChar = 'A';
} else {
mAccChar = 'a';
}
if (mFormatConfig.mUpperOperandXY) {
mXregChar = 'X';
mYregChar = 'Y';
} else {
mXregChar = 'x';
mYregChar = 'y';
}
if (mFormatConfig.mUpperOperandS) {
mSregChar = 'S';
} else {
mSregChar = 's';
}
}
/// <summary>
/// Formats a 24-bit offset value as hex.
/// </summary>
/// <param name="offset">Offset to format.</param>
/// <returns>Formatted string.</returns>
public string FormatOffset24(int offset) {
if (string.IsNullOrEmpty(mOffset20Format)) {
mOffset20Format = "+{0:" + mHexFmtChar + "6}";
}
return string.Format(mOffset20Format, offset & 0x0fffff);
}
/// <summary>
/// Formats a value in hexadecimal. The width is padded with zeroes to make the
/// length even (so it'll be $00, $0100, $010000, etc.) If minDigits is nonzero,
/// additional zeroes may be added.
/// </summary>
/// <param name="value">Value to format, up to 32 bits.</param>
/// <param name="minDigits">Minimum width, in printed digits (e.g. 4 is "0000").</param>
/// <returns>Formatted string.</returns>
public string FormatHexValue(int value, int minDigits) {
int width = minDigits > 2 ? minDigits : 2;
if (width < 8 && value > 0xffffff) {
width = 8;
} else if (width < 6 && value > 0xffff) {
width = 6;
} else if (width < 4 && value > 0xff) {
width = 4;
}
int index = (width / 2) - 1;
if (mHexValueFormats[index] == null) {
mHexValueFormats[index] = mHexFmtChar + width.ToString();
}
return mHexPrefix + value.ToString(mHexValueFormats[index]);
}
/// <summary>
/// Format a value as a number in the specified base.
/// </summary>
/// <param name="value">Value to format.</param>
/// <param name="numBase">Numeric base (2, 10, or 16).</param>
/// <returns>Formatted string.</returns>
public string FormatValueInBase(int value, int numBase) {
switch (numBase) {
case 2:
return FormatBinaryValue(value, 8);
case 10:
return FormatDecimalValue(value);
case 16:
return FormatHexValue(value, 2);
default:
Debug.Assert(false);
return "???";
}
}
/// <summary>
/// Formats a value as decimal.
/// </summary>
/// <param name="value">Value to convert.</param>
/// <returns>Formatted string.</returns>
public string FormatDecimalValue(int value) {
return value.ToString();
}
/// <summary>
/// Formats a value in binary, padding with zeroes so the length is a multiple of 8.
/// </summary>
/// <param name="value">Value to convert.</param>
/// <param name="minDigits">Minimum width, in printed digits. Will be rounded up to
/// a multiple of 8.</param>
/// <returns>Formatted string.</returns>
public string FormatBinaryValue(int value, int minDigits) {
string binaryStr = Convert.ToString(value, 2);
int desiredWidth = ((binaryStr.Length + 7) / 8) * 8;
if (desiredWidth < minDigits) {
desiredWidth = ((minDigits + 7) / 8) * 8;
}
return '%' + binaryStr.PadLeft(desiredWidth, '0');
}
/// <summary>
/// Formats a value as an ASCII character, surrounded by quotes. Must be a valid
/// low- or high-ASCII value.
/// </summary>
/// <param name="value">Value to format.</param>
/// <returns>Formatted string.</returns>
private string FormatAsciiChar(int value) {
Debug.Assert(CommonUtil.TextUtil.IsHiLoAscii(value));
char ch = (char)(value & 0x7f);
bool hiAscii = ((value & 0x80) != 0);
StringBuilder sb;
int method = -1;
switch (method) {
case 0:
default:
// Convention is from Merlin: single quote for low-ASCII, double-quote
// for high-ASCII. Add a backslash if we're quoting the delimiter.
sb = new StringBuilder(4);
char quoteChar = ((value & 0x80) == 0) ? '\'' : '"';
sb.Append(quoteChar);
if (quoteChar == ch) {
sb.Append('\\');
}
sb.Append(ch);
sb.Append(quoteChar);
break;
case 1:
// Convention is similar to Merlin, but with curly-quotes so it doesn't
// look weird when quoting ' or ".
sb = new StringBuilder(3);
sb.Append(hiAscii ? '\u201c' : '\u2018');
sb.Append(ch);
sb.Append(hiAscii ? '\u201d' : '\u2019');
break;
case 2:
// Always use apostrophe, but follow it with an up-arrow to indicate
// that it's high-ASCII.
sb = new StringBuilder(4);
sb.Append("'");
sb.Append(ch);
sb.Append("'");
if (hiAscii) {
sb.Append('\u21e1'); // UPWARDS DASHED ARROW
//sb.Append('\u2912'); // UPWARDS ARROW TO BAR
}
break;
}
return sb.ToString();
}
/// <summary>
/// Formats a value as an ASCII character, if possible, or as a hex value.
/// </summary>
/// <param name="value">Value to format.</param>
/// <returns>Formatted string.</returns>
public string FormatAsciiOrHex(int value) {
bool hiAscii = ((value & 0x80) != 0);
if (hiAscii && !mFormatConfig.mAllowHighAsciiCharConst) {
return FormatHexValue(value, 2);
} else if (CommonUtil.TextUtil.IsHiLoAscii(value)) {
return FormatAsciiChar(value);
} else {
return FormatHexValue(value, 2);
}
}
/// <summary>
/// Formats a 16- or 24-bit address value. This is intended for the left column
/// of something (hex dump, code listing), not as an operand.
/// </summary>
/// <param name="address">Address to format.</param>
/// <param name="showBank">Set to true for CPUs with 24-bit address spaces.</param>
/// <returns>Formatted string.</returns>
public string FormatAddress(int address, bool showBank) {
if (mAddrFormatNoBank == null) {
mAddrFormatNoBank = "{0:" + mHexFmtChar + "4}";
mAddrFormatWithBank = "{0:" + mHexFmtChar + "2}/{1:" + mHexFmtChar + "4}";
}
if (showBank) {
return string.Format(mAddrFormatWithBank, address >> 16, address & 0xffff);
} else {
return string.Format(mAddrFormatNoBank, address & 0xffff);
}
}
/// <summary>
/// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment
/// is required, an empty string is returned.
/// </summary>
/// <param name="adjValue">Adjustment value.</param>
/// <returns>Formatted string.</returns>
public string FormatAdjustment(int adjValue) {
if (adjValue == 0) {
return string.Empty;
}
// This formats in decimal with a leading '+' or '-'. To avoid adding a plus
// on zero, we'd use "+#;-#;0", but we took care of the zero case above.
return adjValue.ToString("+0;-#");
}
/// <summary>
/// Formats the instruction opcode mnemonic, and caches the result.
///
/// It may be necessary to modify the mnemonic for some assemblers, e.g. LDA from a
/// 24-bit address might need to be LDAL, even if the high byte is nonzero.
/// </summary>
/// <param name="op">Opcode to format</param>
/// <param name="wdis">Width disambiguation specifier.</param>
/// <returns>Formatted string.</returns>
public string FormatOpcode(OpDef op, OpDef.WidthDisambiguation wdis) {
// TODO(someday): using op.Opcode as the key is a bad idea, as the operation may
// not be the same on different CPUs. We currently rely on the caller to discard
// the Formatter when the CPU definition changes. We'd be better off keying off of
// the OpDef object and factoring wdis in some other way.
int key = op.Opcode | ((int)wdis << 8);
if (!mOpcodeStrings.TryGetValue(key, out string opcodeStr)) {
// Not found, generate value.
opcodeStr = FormatMnemonic(op.Mnemonic, wdis);
// Memoize.
mOpcodeStrings[key] = opcodeStr;
}
return opcodeStr;
}
/// <summary>
/// Formats the string as an opcode mnemonic.
///
/// It may be necessary to modify the mnemonic for some assemblers, e.g. LDA from a
/// 24-bit address might need to be LDAL, even if the high byte is nonzero.
/// </summary>
/// <param name="mnemonic">Instruction mnemonic string.</param>
/// <param name="wdis">Width disambiguation specifier.</param>
/// <returns></returns>
public string FormatMnemonic(string mnemonic, OpDef.WidthDisambiguation wdis) {
string opcodeStr = mnemonic;
if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOpcodeSuffix)) {
opcodeStr += mFormatConfig.mForceAbsOpcodeSuffix;
}
} else if (wdis == OpDef.WidthDisambiguation.ForceLong ||
wdis == OpDef.WidthDisambiguation.ForceLongMaybe) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceLongOpcodeSuffix)) {
opcodeStr += mFormatConfig.mForceLongOpcodeSuffix;
}
} else {
Debug.Assert(wdis == OpDef.WidthDisambiguation.None);
}
if (mFormatConfig.mUpperOpcodes) {
opcodeStr = opcodeStr.ToUpperInvariant();
}
return opcodeStr;
}
/// <summary>
/// Generates an operand format.
/// </summary>
/// <param name="addrMode">Addressing mode.</param>
/// <returns>Format string.</returns>
private string GenerateOperandFormat(OpDef.AddressMode addrMode,
OpDef.WidthDisambiguation wdis) {
string fmt;
string wdisStr = string.Empty;
if (wdis == OpDef.WidthDisambiguation.ForceAbs) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceAbsOperandPrefix)) {
wdisStr = mFormatConfig.mForceAbsOperandPrefix;
}
} else if (wdis == OpDef.WidthDisambiguation.ForceLong) {
if (!string.IsNullOrEmpty(mFormatConfig.mForceLongOperandPrefix)) {
wdisStr = mFormatConfig.mForceLongOperandPrefix;
}
} else if (wdis == OpDef.WidthDisambiguation.ForceLongMaybe) {
// Don't add a width disambiguator to an operand that is unambiguously long.
} else {
Debug.Assert(wdis == OpDef.WidthDisambiguation.None);
}
switch (addrMode) {
case AddressMode.Abs:
case AddressMode.AbsLong:
case AddressMode.BlockMove:
case AddressMode.StackAbs:
case AddressMode.DP:
case AddressMode.PCRel:
case AddressMode.PCRelLong: // BRL
case AddressMode.StackInt: // BRK/COP
case AddressMode.StackPCRelLong: // PER
case AddressMode.WDM:
fmt = wdisStr + "{0}";
break;
case AddressMode.AbsIndexX:
case AddressMode.AbsIndexXLong:
case AddressMode.DPIndexX:
fmt = wdisStr + "{0}," + mXregChar;
break;
case AddressMode.DPIndexY:
case AddressMode.AbsIndexY:
fmt = wdisStr + "{0}," + mYregChar;
break;
case AddressMode.AbsIndexXInd:
case AddressMode.DPIndexXInd:
fmt = wdisStr + "({0}," + mXregChar + ")";
break;
case AddressMode.AbsInd:
case AddressMode.DPInd:
case AddressMode.StackDPInd: // PEI
fmt = "({0})";
break;
case AddressMode.AbsIndLong:
case AddressMode.DPIndLong:
// IIgs monitor uses "()" for AbsIndLong, E&L says "[]". Assemblers
// seem to expect the latter.
fmt = "[{0}]";
break;
case AddressMode.Acc:
fmt = "" + mAccChar;
break;
case AddressMode.DPIndIndexY:
fmt = "({0})," + mYregChar;
break;
case AddressMode.DPIndIndexYLong:
fmt = "[{0}]," + mYregChar;
break;
case AddressMode.Imm:
case AddressMode.ImmLongA:
case AddressMode.ImmLongXY:
fmt = "#{0}";
break;
case AddressMode.Implied:
case AddressMode.StackPull:
case AddressMode.StackPush:
case AddressMode.StackRTI:
case AddressMode.StackRTL:
case AddressMode.StackRTS:
fmt = "";
break;
case AddressMode.StackRel:
fmt = "{0}," + mSregChar;
break;
case AddressMode.StackRelIndIndexY:
fmt = "({0}," + mSregChar + ")," + mYregChar;
break;
case AddressMode.Unknown:
default:
Debug.Assert(false);
fmt = "???";
break;
}
return fmt;
}
/// <summary>
/// Formats a pseudo-opcode.
/// </summary>
/// <param name="opstr">Pseudo-op string to format.</param>
/// <returns>Formatted string.</returns>
public string FormatPseudoOp(string opstr) {
if (!mPseudoOpStrings.TryGetValue(opstr, out string result)) {
if (mFormatConfig.mUpperPseudoOpcodes) {
result = mPseudoOpStrings[opstr] = opstr.ToUpperInvariant();
} else {
result = mPseudoOpStrings[opstr] = opstr;
}
}
return result;
}
/// <summary>
/// Formats the instruction operand.
/// </summary>
/// <param name="op">Opcode definition (needed for address mode).</param>
/// <param name="contents">Label or numeric operand value.</param>
/// <param name="wdis">Width disambiguation value.</param>
/// <returns>Formatted string.</returns>
public string FormatOperand(OpDef op, string contents, OpDef.WidthDisambiguation wdis) {
Debug.Assert(((int)op.AddrMode & 0xff) == (int) op.AddrMode);
int key = (int) op.AddrMode | ((int)wdis << 8);
if (!mOperandFormats.TryGetValue(key, out string format)) {
format = mOperandFormats[key] = GenerateOperandFormat(op.AddrMode, wdis);
}
return string.Format(format, contents);
}
/// <summary>
/// Generates a format string for N hex bytes.
/// </summary>
/// <param name="len">Number of bytes to handle in the format.</param>
private void GenerateByteFormat(int len) {
Debug.Assert(len <= MAX_BYTE_DUMP);
StringBuilder sb = new StringBuilder(len * 6);
for (int i = 0; i < len; i++) {
//. e.g. "{0:x2}"
sb.Append("{" + i + ":" + mHexFmtChar + "2}");
}
mByteDumpFormats[len - 1] = sb.ToString();
}
/// <summary>
/// Formats 1-4 bytes as hex values.
/// </summary>
/// <param name="data">Data source.</param>
/// <param name="offset">Start offset within data array.</param>
/// <param name="length">Number of bytes to print. Fewer than this many may
/// actually appear.</param>
/// <returns>Formatted data string.</returns>
public string FormatBytes(byte[] data, int offset, int length) {
Debug.Assert(length > 0);
int printLen = length < MAX_BYTE_DUMP ? length : MAX_BYTE_DUMP;
if (string.IsNullOrEmpty(mByteDumpFormats[printLen - 1])) {
GenerateByteFormat(printLen);
}
string format = mByteDumpFormats[printLen - 1];
string result;
// The alternative is to allocate a temporary object[] and copy the integers
// into it, which requires boxing. We know we're only printing 1-4 bytes, so
// it's easier to just handle each case individually.
switch (printLen) {
case 1:
result = string.Format(format, data[offset]);
break;
case 2:
result = string.Format(format, data[offset], data[offset + 1]);
break;
case 3:
result = string.Format(format,
data[offset], data[offset + 1], data[offset + 2]);
break;
case 4:
result = string.Format(format,
data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
break;
default:
result = "INTERNAL ERROR";
break;
}
if (length > printLen) {
result += "...";
}
return result;
}
/// <summary>
/// Formats an end-of-line comment, prepending an end-of-line comment delimiter.
/// </summary>
/// <param name="comment">Comment string; may be empty.</param>
/// <returns>Formatted string.</returns>
public string FormatEolComment(string comment) {
if (string.IsNullOrEmpty(comment) ||
string.IsNullOrEmpty(mFormatConfig.mEndOfLineCommentDelimiter)) {
return comment;
} else {
return mFormatConfig.mEndOfLineCommentDelimiter + comment;
}
}
/// <summary>
/// Formats a collection of bytes as a dense hex string.
/// </summary>
/// <param name="data">Data source.</param>
/// <param name="offset">Start offset within data array.</param>
/// <param name="length">Number of bytes to print.</param>
/// <returns>Formatted data string.</returns>
public string FormatDenseHex(byte[] data, int offset, int length) {
char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower;
char[] text = new char[length * 2];
for (int i = 0; i < length; i++) {
byte val = data[offset + i];
text[i * 2] = hexChars[val >> 4];
text[i * 2 + 1] = hexChars[val & 0x0f];
}
return new string(text);
}
/// <summary>
/// Formats up to 16 bytes of data into a single line hex dump, in this format:
/// <pre>012345: 00 11 22 33 44 55 66 77 88 99 aa bb cc dd ee ff 0123456789abcdef</pre>
/// </summary>
/// <param name="data">Reference to data.</param>
/// <param name="offset">Start offset.</param>
/// <returns>Formatted string.</returns>
public string FormatHexDump(byte[] data, int offset) {
FormatHexDumpCommon(data, offset);
// this is the only allocation
return new string(mHexDumpBuffer);
}
/// <summary>
/// Formats up to 16 bytes of data into a single line hex dump. The output is
/// appended to the StringBuilder.
/// </summary>
/// <param name="data">Reference to data.</param>
/// <param name="offset">Start offset.</param>
/// <param name="sb">StringBuilder that receives output.</param>
public void FormatHexDump(byte[] data, int offset, StringBuilder sb) {
FormatHexDumpCommon(data, offset);
sb.Append(mHexDumpBuffer);
}
/// <summary>
/// Formats up to 16 bytes of data into mHexDumpBuffer.
/// </summary>
private void FormatHexDumpCommon(byte[] data, int offset) {
Debug.Assert(offset >= 0 && offset < data.Length);
Debug.Assert(data.Length < (1 << 24));
const int dataCol = 8;
const int asciiCol = 57;
char[] hexChars = mFormatConfig.mUpperHexDigits ? sHexCharsUpper : sHexCharsLower;
char[] outBuf = mHexDumpBuffer;
// address field
int addr = offset;
for (int i = 5; i >= 0; i--) {
outBuf[i] = hexChars[addr & 0x0f];
addr >>= 4;
}
// hex digits and characters
int length = Math.Min(16, data.Length - offset);
int index;
for (index = 0; index < length; index++) {
byte val = data[offset + index];
outBuf[dataCol + index * 3] = hexChars[val >> 4];
outBuf[dataCol + index * 3 + 1] = hexChars[val & 0x0f];
outBuf[asciiCol + index] = CharConv(val);
}
// for partial line, clear out previous contents
for (; index < 16; index++) {
outBuf[dataCol + index * 3] =
outBuf[dataCol + index * 3 + 1] =
outBuf[asciiCol + index] = ' ';
}
}
/// <summary>
/// Converts a byte into printable form according to the current hex dump
/// character conversion mode.
/// </summary>
/// <param name="val">Value to convert.</param>
/// <returns>Printable character.</returns>
private char CharConv(byte val) {
char ch;
if (mFormatConfig.mHexDumpCharConvMode == FormatConfig.CharConvMode.HighLowAscii) {
ch = (char)(val & 0x7f);
} else {
ch = (char)val;
}
if (CommonUtil.TextUtil.IsPrintableAscii(ch)) {
return ch;
} else if (mFormatConfig.mHexDumpAsciiOnly) {
return '.';
} else {
// These values makes the hex dump ListView freak out.
//if (ch < 0x20) {
// return (char)(ch + '\u2400'); // Unicode "control pictures" block
//}
//return '\ufffd'; // Unicode "replacement character"
//return '\u00bf'; // INVERTED QUESTION MARK
return '\u00b7'; // MIDDLE DOT
}
}
}
}

56
Asm65/Helper.cs Normal file
View File

@ -0,0 +1,56 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace Asm65 {
/// <summary>
/// Small utility functions.
/// </summary>
public static class Helper {
/// <summary>
/// Computes the target address of an 8-bit relative branch instruction.
/// </summary>
/// <param name="addr">24-bit address of branch instruction opcode.</param>
/// <param name="branchOffset">Branch operand.</param>
/// <returns>Target address.</returns>
public static int RelOffset8(int addr, sbyte branchOffset) {
// Branch is relative to the start of the following instruction, so add 2.
// Branches wrap around the current bank (in both directions), and the target
// is in the same bank as source addr.
Debug.Assert(addr >= 0 && addr <= 0xffffff);
int target = (addr + 2 + branchOffset) & 0xffff;
target |= addr & 0x7fff0000;
return target;
}
/// <summary>
/// Computes the target address of a 16-bit relative branch instruction.
/// </summary>
/// <param name="addr">24-bit address of branch instruction opcode.</param>
/// <param name="branchOffset">Branch operand.</param>
/// <returns>Target address.</returns>
public static int RelOffset16(int addr, short branchOffset) {
// Branch is relative to the start of the following instruction, so add 3.
// Branches wrap around the current bank (in both directions), and the target
// is in the same bank as source addr.
Debug.Assert(addr >= 0 && addr <= 0xffffff);
int target = (addr + 3 + branchOffset) & 0xffff;
target |= addr & 0x7fff0000;
return target;
}
}
}

78
Asm65/Label.cs Normal file
View File

@ -0,0 +1,78 @@
/*
* Copyright 2018 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 System;
using System.Text.RegularExpressions;
namespace Asm65 {
/// <summary>
/// Utility classes for working with labels.
///
/// The decision of whether to treat labels as case-sensitive or case-insensitive is
/// encapsulated here. All code should be case-preserving, but the comparison method
/// and "normal form" are defined here.
/// </summary>
public class Label {
// Arbitrary choice for SourceGen. Different assemblers have different limits.
public const int MAX_LABEL_LEN = 32;
public const bool LABELS_CASE_SENSITIVE = true;
/// <summary>
/// String comparer to use when comparing labels.
///
/// We may want case-insensitive string compares, and we want the "invariant culture"
/// version for consistent results across users in multiple locales. (The labels are
/// expected to be ASCII strings, so the latter isn't crucial unless we change the
/// allowed set.)
/// </summary>
public static readonly StringComparer LABEL_COMPARER = LABELS_CASE_SENSITIVE ?
StringComparer.InvariantCulture :
StringComparer.InvariantCultureIgnoreCase;
/// <summary>
/// Regex pattern for a valid label.
///
/// ASCII-only, starts with letter or underscore, followed by at least
/// one alphanumeric or underscore. Some assemblers may allow single-letter
/// labels, but I don't want to risk confusion with A/S/X/Y. So either we
/// reserve those, or we just mandate a two-character minimum.
/// </summary>
private static string sValidLabelPattern = @"^[a-zA-Z_][a-zA-Z0-9_]+$";
private static Regex sValidLabelCharRegex = new Regex(sValidLabelPattern);
/// <summary>
/// Validates a label, confirming that it is correctly formed.
/// </summary>
/// <param name="label">Label to validate.</param>
/// <returns>True if the label is correctly formed.</returns>
public static bool ValidateLabel(string label) {
if (label.Length > MAX_LABEL_LEN) {
return false;
}
MatchCollection matches = sValidLabelCharRegex.Matches(label);
return matches.Count == 1;
}
/// <summary>
/// Returns "normal form" of label. This matches LABEL_COMPARER behavior.
/// </summary>
/// <param name="label">Label to transform.</param>
/// <returns>Transformed label.</returns>
public static string ToNormal(string label) {
return LABELS_CASE_SENSITIVE ? label : label.ToUpperInvariant();
}
}
}

60
Asm65/Number.cs Normal file
View File

@ -0,0 +1,60 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace Asm65 {
public class Number {
/// <summary>
/// Parses an integer in a variety of formats (hex, decimal, binary).
///
/// Trim whitespace before calling here.
/// </summary>
/// <param name="str">String to parse.</param>
/// <param name="val">Integer value of string.</param>
/// <param name="intBase">What base the string was in (2, 10, or 16).</param>
/// <returns>True if the parsing was successful.</returns>
public static bool TryParseInt(string str, out int val, out int intBase) {
if (string.IsNullOrEmpty(str)) {
val = intBase = 0;
return false;
}
if (str[0] == '$') {
intBase = 16;
str = str.Substring(1); // Convert functions don't like '$'
} else if (str.Length > 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) {
intBase = 16;
} else if (str[0] == '%') {
intBase = 2;
str = str.Substring(1); // Convert functions don't like '%'
} else {
intBase = 10; // try it as decimal
}
try {
val = Convert.ToInt32(str, intBase);
//Debug.WriteLine("GOT " + val + " - " + intBase);
} catch (Exception) {
//Debug.WriteLine("TryParseInt failed on '" + str + "': " + ex.Message);
val = 0;
return false;
}
return true;
}
}
}

3225
Asm65/OpDef.cs Normal file

File diff suppressed because it is too large Load Diff

738
Asm65/OpDescription.cs Normal file
View File

@ -0,0 +1,738 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Text;
namespace Asm65 {
/// <summary>
/// Human-readable text describing instructions.
///
/// The expectation is that the long description will include information about all
/// address modes and any differences in behavior between CPUs. So there will be one
/// entry per instruction mnemonic, and one global table for all CPUs, rather than one
/// entry per opcode and one instance per CpuDef.
///
/// There may, however, be different instances for different Cultures. Also, the 65816
/// traditionally splits JSR/JSL and JMP/JML, so in that case there will be two entries
/// for the same instruction.
/// </summary>
public class OpDescription {
private Dictionary<string, string> mShortDescriptions;
private Dictionary<string, string> mLongDescriptions;
private Dictionary<OpDef.AddressMode, string> mAddressModeDescriptions;
private Dictionary<OpDef.CycleMod, string> mCycleModDescriptions;
private OpDescription(Dictionary<string, string> sd, Dictionary<string, string> ld,
Dictionary<OpDef.AddressMode, string> am, Dictionary<OpDef.CycleMod, string> cm) {
mShortDescriptions = sd;
mLongDescriptions = ld;
mAddressModeDescriptions = am;
mCycleModDescriptions = cm;
}
/// <summary>
/// Returns an OpDescription instance for the requested region.
/// </summary>
/// <param name="region">TBD</param>
public static OpDescription GetOpDescription(string region) {
// ignoring region for now
return new OpDescription(sShort_enUS, sLong_enUS, sAddrMode_enUS, sCycleMod_enUS);
}
/// <summary>
/// Short description of instruction, e.g. "Load Accumulator".
/// </summary>
/// <param name="mnemonic">Instruction mnemonic.</param>
/// <returns>Short description string, or empty string if not found.</returns>
public string GetShortDescription(string mnemonic) {
if (mShortDescriptions.TryGetValue(mnemonic, out string desc)) {
return desc;
} else {
return string.Empty;
}
}
/// <summary>
/// Long description of instruction. May span multiple lines, with embedded CRLF at
/// paragraph breaks.
/// </summary>
/// <param name="mnemonic">Instruction mnemonic.</param>
/// <returns>Long description string, or empty string if not found.</returns>
public string GetLongDescription(string mnemonic) {
if (mLongDescriptions.TryGetValue(mnemonic, out string desc)) {
return desc;
} else {
return string.Empty;
}
}
/// <summary>
/// Address mode short description.
/// </summary>
/// <param name="addrMode">Address mode to look up.</param>
/// <returns>Description string, or an empty string for instructions
/// with implied address modes.</returns>
public string GetAddressModeDescription(OpDef.AddressMode addrMode) {
if (mAddressModeDescriptions.TryGetValue(addrMode, out string desc)) {
return desc;
} else {
return string.Empty;
}
}
/// <summary>
/// Cycle modifier description.
/// </summary>
/// <param name="modBit">A single-bit item from the CycleMod enum.</param>
/// <returns>Description string, or question marks if not found.</returns>
public string GetCycleModDescription(OpDef.CycleMod modBit) {
if (mCycleModDescriptions.TryGetValue(modBit, out string desc)) {
return desc;
} else {
return "???";
}
}
/// <summary>
/// Short descriptions, USA English.
///
/// Text is adapted from instruction summaries in Eyes & Lichty, which are slightly
/// shorter than those in the CPU data sheet.
/// </summary>
private static Dictionary<string, string> sShort_enUS = new Dictionary<string, string>() {
{ OpName.ADC, "Add With Carry" },
{ OpName.AND, "AND Accumulator With Memory" },
{ OpName.ASL, "Shift Memory or Accumulator Left" },
{ OpName.BCC, "Branch If Carry Clear" },
{ OpName.BCS, "Branch If Carry Set" },
{ OpName.BEQ, "Branch If Equal" },
{ OpName.BIT, "Test Memory Against Accumulator" },
{ OpName.BMI, "Branch If Minus" },
{ OpName.BNE, "Branch If Not Equal" },
{ OpName.BPL, "Branch If Plus" },
{ OpName.BRA, "Branch Always" },
{ OpName.BRK, "Software Break" },
{ OpName.BRL, "Branch Always Long" },
{ OpName.BVC, "Branch If Overflow Clear" },
{ OpName.BVS, "Branch If Overflow Set" },
{ OpName.CLC, "Clear Carry Flag" },
{ OpName.CLD, "Clear Decimal Flag" },
{ OpName.CLI, "Clear Interrupt Disable Flag" },
{ OpName.CLV, "Clear Overflow Flag" },
{ OpName.CMP, "Compare Accumulator With Memory" },
{ OpName.COP, "Co-Processor" },
{ OpName.CPX, "Compare Index X With Memory" },
{ OpName.CPY, "Compare Index Y With Memory" },
{ OpName.DEC, "Decrement Accumulator" },
{ OpName.DEX, "Decrement Index X" },
{ OpName.DEY, "Decrement Index Y" },
{ OpName.EOR, "XOR Accumulator With Memory" },
{ OpName.INC, "Increment Accumulator" },
{ OpName.INX, "Increment Index X" },
{ OpName.INY, "Increment Index Y" },
{ OpName.JMP, "Jump" },
{ OpName.JSL, "Jump to Subroutine Long" },
{ OpName.JSR, "Jump to Subroutine" },
{ OpName.LDA, "Load Accumulator from Memory" },
{ OpName.LDX, "Load Index X from Memory" },
{ OpName.LDY, "Load Index Y from Memory" },
{ OpName.LSR, "Logical Shift Memory or Accumulator Right" },
{ OpName.MVN, "Block Move Next" },
{ OpName.MVP, "Block Move Previous" },
{ OpName.NOP, "No Operation" },
{ OpName.ORA, "OR Accumulator With Memory" },
{ OpName.PEA, "Push Effective Absolute Address" },
{ OpName.PEI, "Push Effective Indirect Address" },
{ OpName.PER, "Push Effective Relative Indirect Address" },
{ OpName.PHA, "Push Accumulator" },
{ OpName.PHB, "Push Data Bank Register" },
{ OpName.PHD, "Push Direct Page Register" },
{ OpName.PHK, "Push Program Bank Register" },
{ OpName.PHP, "Push Processor Status Register" },
{ OpName.PHX, "Push Index Register X" },
{ OpName.PHY, "Push Index Register Y" },
{ OpName.PLA, "Pull Accumulator" },
{ OpName.PLB, "Pull Data Bank Register" },
{ OpName.PLD, "Pull Direct Page Register" },
{ OpName.PLP, "Pull Processor Status Register" },
{ OpName.PLX, "Pull Index Register X" },
{ OpName.PLY, "Pull Index Register Y" },
{ OpName.REP, "Reset Status Bits" },
{ OpName.ROL, "Rotate Memory or Accumulator Left" },
{ OpName.ROR, "Rotate Memory or Accumulator Right" },
{ OpName.RTI, "Return from Interrupt" },
{ OpName.RTL, "Return from Subroutine Long" },
{ OpName.RTS, "Return from Subroutine" },
{ OpName.SBC, "Subtract With Borrow" },
{ OpName.SEC, "Set Carry Flag" },
{ OpName.SED, "Set Decimal Flag" },
{ OpName.SEI, "Set Interrupt Disable Flag" },
{ OpName.SEP, "Set Status Bits" },
{ OpName.STA, "Store Accumulator to Memory" },
{ OpName.STP, "Stop Processor" },
{ OpName.STX, "Store Index X to Memory" },
{ OpName.STY, "Store Index Y to Memory" },
{ OpName.STZ, "Store Zero to Memory" },
{ OpName.TAX, "Transfer Accumulator to Index X" },
{ OpName.TAY, "Transfer Accumulator to Index Y" },
{ OpName.TCD, "Transfer 16-Bit Accumulator to Direct Page Register" },
{ OpName.TCS, "Transfer Accumulator to Stack Pointer" },
{ OpName.TDC, "Transfer Direct Page Register to 16-Bit Accumulator" },
{ OpName.TRB, "Test and Reset Memory Bits" },
{ OpName.TSB, "Test and Set Memory Bits" },
{ OpName.TSC, "Transfer Stack Pointer to 16-Bit Accumulator" },
{ OpName.TSX, "Transfer Stack Pointer to Index X" },
{ OpName.TXA, "Transfer Index X to Accumulator" },
{ OpName.TXS, "Transfer Index X to Stack Pointer" },
{ OpName.TXY, "Transfer Index X to Index Y" },
{ OpName.TYA, "Transfer Index Y to Accumulator" },
{ OpName.TYX, "Transfer Index Y to Index X" },
{ OpName.WAI, "Wait for Interrupt" },
{ OpName.WDM, "Future Expansion" },
{ OpName.XBA, "Exchange Accumulator B and A" },
{ OpName.XCE, "Exchange Carry and Emulation Bits" },
// MOS 6502 undocumented ops
{ OpName.ANC, "AND Accumulator With Value and Set Carry" },
{ OpName.ANE, "Transfer Index X to Accumulator and AND" },
{ OpName.ARR, "AND and Rotate Right" },
{ OpName.ASR, "AND and Shift Right" },
{ OpName.DCP, "Decrement and Compare" },
{ OpName.DOP, "Double-Byte NOP" },
{ OpName.HLT, "Halt CPU" },
{ OpName.ISB, "Increment and Subtract" },
{ OpName.LAE, "Load Acc, X, and Stack Pointer with Memory AND Stack Pointer" },
{ OpName.LAX, "Load Accumulator and Index X" },
{ OpName.LXA, "OR, AND, and Transfer to X" },
{ OpName.RLA, "Rotate Left and AND" },
{ OpName.RRA, "Rotate Right and Add" },
{ OpName.SAX, "Store Accumulator AND Index X" }, // AXS
{ OpName.SBX, "AND Acc With Index X, Subtract, and Store in X" }, // SAX
{ OpName.SHA, "AND Acc With Index X and High Byte, and Store" }, // AXA
{ OpName.SHS, "AND Acc with Index X, Transfer to Stack, AND High Byte" }, // TAS
{ OpName.SHX, "AND Acc With Index X and High Byte, and Store" }, // XAS
{ OpName.SHY, "AND Acc With Index Y and High Byte, and Store" }, // SAY
{ OpName.SLO, "Shift Left and OR" },
{ OpName.SRE, "Shift right and EOR" },
{ OpName.TOP, "Triple-Byte NOP" },
// WDC 65C02 undocumented
{ OpName.LDD, "Load and Discard" },
};
/// <summary>
/// Long descriptions, USA English.
/// </summary>
private static Dictionary<string, string> sLong_enUS = new Dictionary<string, string>() {
{ OpName.ADC,
"Adds the accumulator and a value in memory, storing the result in the " +
"accumulator. Adds one if the carry is set."
},
{ OpName.AND,
"Performs a bitwise AND of the accumulator with a value in memory, storing " +
"the result in the accumulator."
},
{ OpName.ASL,
"Shifts memory or the accumulator one bit left. The low bit is set to zero, " +
"and the carry flag receives the high bit."
},
{ OpName.BCC,
"Branches to a relative address if the processor carry flag (C) is zero. " +
"Sometimes referred to as Branch If Less Than, or BLT."
},
{ OpName.BCS,
"Branches to a relative address if the processor carry flag (C) is one. " +
"Sometimes referred to as Branch If Greater Than or Equal, or BGE."
},
{ OpName.BEQ,
"Branches to a relative address if the processor zero flag (Z) is one."
},
{ OpName.BIT,
"Sets processor flags based on the result of two operations. The N and V flags " +
"are set according to bits 7 and 6, and the Z flag is set based on an AND of " +
"the accumulator and memory. However, when used with immediate addressing, " +
"the N and V flags are not affected."
},
{ OpName.BMI,
"Branches to a relative address if the processor negative flag (N) is one."
},
{ OpName.BNE,
"Branches to a relative address if the processor zero flag (Z) is zero."
},
{ OpName.BPL,
"Branches to a relative address if the processor negative flag (N) is zero."
},
{ OpName.BRA,
"Branches to a relative address."
},
{ OpName.BRK,
"Pushes state onto the stack, and jumps to the software break vector at " +
"$fffe-ffff. While this is technically a single-byte instruction, the " +
"program counter pushed onto the stack is incremented by two."
},
{ OpName.BRL,
"Branches to a long relative address."
},
{ OpName.BVC,
"Branches to a relative address if the processor overflow flag (V) is zero."
},
{ OpName.BVS,
"Branches to a relative address if the processor overflow flag (V) is one."
},
{ OpName.CLC,
"Sets the processor carry flag (C) to zero."
},
{ OpName.CLD,
"Sets the processor decimal flag (D) to zero."
},
{ OpName.CLI,
"Sets the processor interrupt disable flag (I) to zero."
},
{ OpName.CLV,
"Sets the processor overflow flag (V) to zero."
},
{ OpName.CMP,
"Subtracts the value specified by the operand from the contents of the " +
"accumulator. Sets the carry, zero, and negative flags, but does not alter " +
"memory or the accumulator."
},
{ OpName.COP,
"Pushes state onto the stack, and jumps to the software interrupt vector at " +
"$fff4-fff5."
},
{ OpName.CPX,
"Subtracts the value specified by the operand from the contents of the " +
"X register. Sets the carry, zero, and negative flags, but does not alter " +
"memory or the X register."
},
{ OpName.CPY,
"Subtracts the value specified by the operand from the contents of the " +
"Y register. Sets the carry, zero, and negative flags, but does not alter " +
"memory or the Y register."
},
{ OpName.DEC,
"Decrements the contents of the location specified by the operand by one."
},
{ OpName.DEX,
"Decrements the X register by one."
},
{ OpName.DEY,
"Decrements the Y register by one."
},
{ OpName.EOR,
"Performs a bitwise EOR of the accumulator with a value in memory, storing " +
"the result in the accumulator."
},
{ OpName.INC,
"Increments the contents of the location specified by the operand by one."
},
{ OpName.INX,
"Increments the X register by one."
},
{ OpName.INY,
"Increments the Y register by one."
},
{ OpName.JML,
"Branches to a long absolute address."
},
{ OpName.JMP,
"Branches to an absolute address."
},
{ OpName.JSL,
"Branches to a long absolute address after pushing the current address onto " +
"the stack. The value pushed is the address of the last operand byte."
},
{ OpName.JSR,
"Branches to an absolute address after pushing the current address onto " +
"the stack. The value pushed is the address of the last operand byte."
},
{ OpName.LDA,
"Loads the accumulator from memory."
},
{ OpName.LDX,
"Loads the X register from memory."
},
{ OpName.LDY,
"Loads the Y register from memory."
},
{ OpName.LSR,
"Shifts memory or the accumulator one bit right. The high bit is set to zero, " +
"and the carry flag receives the low bit."
},
{ OpName.MVN,
"Moves a block of memory, starting from a low address and incrementing. " +
"The source and destination addresses are in the X and Y registers, " +
"respectively. The accumulator holds the number of bytes to move minus 1, " +
"and the source and destination banks are specified by the operands."
},
{ OpName.MVP,
"Moves a block of memory, starting from a high address and decrementing. " +
"The source and destination addresses are in the X and Y registers, " +
"respectively. The accumulator holds the number of bytes to move minus 1, " +
"and the source and destination banks are specified by the operands."
},
{ OpName.NOP,
"No operation."
},
{ OpName.ORA,
"Performs a bitwise OR of the accumulator with a value in memory, storing " +
"the result in the accumulator."
},
{ OpName.PEA,
"Pushes the 16-bit operand onto the stack. This always pushes two bytes, " +
"regardless of the M/X processor flags."
},
{ OpName.PEI,
"Pushes a 16-bit value from the direct page onto the stack."
},
{ OpName.PER,
"Converts a relative offset to an absolute address, and pushes it onto the stack."
},
{ OpName.PHA,
"Pushes the accumulator onto the stack."
},
{ OpName.PHB,
"Pushes the data bank register onto the stack."
},
{ OpName.PHD,
"Pushes the direct page register onto the stack."
},
{ OpName.PHK,
"Pushes the program bank register onto the stack."
},
{ OpName.PHP,
"Pushes the processor status register onto the stack."
},
{ OpName.PHX,
"Pushes the X register onto the stack."
},
{ OpName.PHY,
"Pushes the Y register onto the stack."
},
{ OpName.PLA,
"Pulls the accumulator off of the stack."
},
{ OpName.PLB,
"Pulls the data bank register off of the stack."
},
{ OpName.PLD,
"Pulls the direct page register off of the stack."
},
{ OpName.PLP,
"Pulls the processor status register off of the stack."
},
{ OpName.PLX,
"Pulls the X register off of the stack."
},
{ OpName.PLY,
"Pulls the Y register off of the stack."
},
{ OpName.REP,
"Sets specific bits in the processor status register to zero."
},
{ OpName.ROL,
"Rotates memory or the accumulator one bit left. The low bit is set to the " +
"carry flag, and the carry flag receives the high bit."
},
{ OpName.ROR,
"Rotates memory or the accumulator one bit right. The high bit is set to the " +
"carry flag, and the carry flag receives the low bit."
},
{ OpName.RTI,
"Pulls the status register and return address from the stack, and jumps " +
"to the exact address pulled (note this is different from RTL/RTS)."
},
{ OpName.RTL,
"Pulls the 24-bit return address from the stack, increments it, and jumps to it."
},
{ OpName.RTS,
"Pulls the 16-bit return address from the stack, increments it, and jumps to it."
},
{ OpName.SBC,
"Subtracts the value specified by the operand from the contents of the " +
"accumulator, and leaves the result in the accumulator. Sets the carry, " +
"zero, and negative flags."
},
{ OpName.SEC,
"Sets the processor carry flag (C) to one."
},
{ OpName.SED,
"Sets the processor decimal flag (D) to one."
},
{ OpName.SEI,
"Sets the processor interrupt disable flag (I) to one."
},
{ OpName.SEP,
"Sets specific bits in the processor status register to one."
},
{ OpName.STA,
"Stores the value in the accumulator into memory."
},
{ OpName.STP,
"Stops the processor until a CPU reset occurs."
},
{ OpName.STX,
"Stores the value in the X register into memory."
},
{ OpName.STY,
"Stores the value in the Y register into memory."
},
{ OpName.STZ,
"Stores zero into memory."
},
{ OpName.TAX,
"Transfers the contents of the accumulator to the X register."
},
{ OpName.TAY,
"Transfers the contents of the accumulator to the Y register."
},
{ OpName.TCD,
"Transfers the 16-bit accumulator to the direct page register."
},
{ OpName.TCS,
"Transfers the 16-bit accumulator to the stack pointer register."
},
{ OpName.TDC,
"Transfers the direct page register to the 16-bit accumulator."
},
{ OpName.TRB,
"Logically ANDs the complement of the value in the accumulator with a value " +
"in memory, and stores it in memory. This can be used to clear specific bits " +
"in memory."
},
{ OpName.TSB,
"Logically ORs the value in the accumulator with a value in memory, and " +
"stores it in memory. This can be used to set specific bits in memory."
},
{ OpName.TSC,
"Transfers the stack pointer register to the 16-bit accumulator."
},
{ OpName.TSX,
"Transfers the stack pointer register to the X register."
},
{ OpName.TXA,
"Transfers the X register to the accumulator."
},
{ OpName.TXS,
"Transfers the X register to the stack pointer register."
},
{ OpName.TXY,
"Transfers the X register to the Y register."
},
{ OpName.TYA,
"Transfers the Y register to the accumulator."
},
{ OpName.TYX,
"Transfers the Y register to the X register."
},
{ OpName.WAI,
"Stalls the processor until an interrupt is received. If the interrupt " +
"disable flag (I) is set to one, execution will continue with the next " +
"instruction rather than calling through an interrupt vector."
},
{ OpName.WDM,
"Reserved for future expansion. (Behaves as a two-byte NOP.)"
},
{ OpName.XBA,
"Swaps the high and low bytes in the 16-bit accumulator. Sometimes referred " +
"to as SWA."
},
{ OpName.XCE,
"Exchanges carry and emulation bits."
},
//
// 6502 undocumented instructions.
//
// References:
// http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
// http://nesdev.com/undocumented_opcodes.txt
//
{ OpName.ANC,
"AND byte with accumulator. If result is negative then carry is set." +
"\r\n\r\nAlt mnemonic: AAC"
},
{ OpName.ANE,
"Transfer X register to accumulator, then AND accumulator with value." +
"\r\n\r\nAlt mnemonic: XAA"
},
{ OpName.ARR,
"AND byte with accumulator, then rotate one bit right. Equivalent to " +
"AND + ROR."
},
{ OpName.ASR,
"AND byte with accumulator, then shift right one bit. Equivalent to AND + LSR." +
"\r\n\r\nAlt mnemonic: ALR"
},
{ OpName.DCP,
"Decrement memory location, then compare result to accumulator. Equivalent " +
"to DEC + CMP." +
"\r\n\r\nAlt mnemonic: DCM"
},
{ OpName.DOP,
"Double-byte no-operation." +
"\r\n\r\nAlt mnemonic: NOP / SKB"
},
{ OpName.HLT,
"Crash the CPU, halting execution and ignoring interrupts." +
"\r\n\r\nAlt mnemonic: KIL / JAM"
},
{ OpName.ISB,
"Increment memory, then subtract memory from accumulator with borrow. " +
"Equivalent to INC + SBC." +
"\r\n\r\nAlt mnemonic: ISC / INS"
},
{ OpName.LAE,
"AND memory with stack pointer, then transfer result to accumulator, " +
"X register, and stack pointer. (Note: possibly unreliable.)" +
"\r\n\r\nAlt mnemonic: LAR / LAS"
},
{ OpName.LAX,
"Load accumulator and X register from memory. Equivalent to LDA + LDX."
},
{ OpName.LXA,
"ORs accumulator with a value, ANDs result with immediate value, then stores " +
"the result in accumulator and X register." +
"Equivalent to ORA + AND + TAX." +
"\r\n\r\nAlt mnemonic: ATX / OAL"
},
{ OpName.RLA,
"Rotate memory one bit left, then AND accumulator with memory. Equivalent " +
"to ROL + AND."
},
{ OpName.RRA,
"Rotate memory one bit right, then add accumulator to memory with carry. " +
"Equivalent to ROR + ADC."
},
{ OpName.SAX,
"AND X register with accumulator, without changing the contents of either " +
"register, subtract an immediate value, then store result in X register." +
"\r\n\r\nAlt mnemonic: AAX / AXS"
},
{ OpName.SBX,
"AND X register with accumulator and transfer to X register, then " +
"subtract byte from X register without borrow." +
"\r\n\r\nAlt mnemonic: AXS / SAX"
},
{ OpName.SHA,
"AND X register with accumulator, then AND result with 7 and store." +
"\r\n\r\nAlt mnemonic: AXA"
},
{ OpName.SHS,
"AND X register with accumulator, without changing the contents of either" +
"register, and transfer to stack pointer. Then " +
"AND stack pointer with high byte of operand + 1." +
"\r\n\r\nAlt mnemonic: XAS / TAS"
},
{ OpName.SHX,
"AND X register with the high byte of the argument + 1, and store the result." +
"\r\n\r\nAlt mnemonic: SXA / XAS"
},
{ OpName.SHY,
"AND Y register with the high byte of the argument + 1, and store the result." +
"\r\n\r\nAlt mnemonic: SYA / SAY"
},
{ OpName.SLO,
"Shift memory left one bit, then OR accumulator with memory. Equivalent to " +
"ASL + ORA." +
"\r\n\r\nAlt mnemonic: ASO"
},
{ OpName.SRE,
"Shift memory right one bit, then EOR accumulator with memory. Equivalent to " +
"LSR + EOR." +
"\r\n\r\nAlt mnemonic: LSE"
},
{ OpName.TOP,
"Triple-byte no-operation. This actually performs a load." +
"\r\n\r\nAlt mnemonic: NOP / SKW"
},
//
// 65C02 undocumented instructions.
//
{ OpName.LDD,
"Load and Discard. Usually a no-op, but the activity on the address bus " +
"can affect memory-mapped I/O."
},
};
/// <summary>
/// Address mode short descriptions, USA English.
/// </summary>
private static Dictionary<OpDef.AddressMode, string> sAddrMode_enUS =
new Dictionary<OpDef.AddressMode, string>() {
{ OpDef.AddressMode.Abs, "Absolute" },
{ OpDef.AddressMode.AbsInd, "Absolute Indirect" },
{ OpDef.AddressMode.AbsIndLong, "Absolute Indirect Long" },
{ OpDef.AddressMode.AbsIndexX, "Absolute Indexed X" },
{ OpDef.AddressMode.AbsIndexXInd, "Absolute Indexed X Indirect" },
{ OpDef.AddressMode.AbsIndexXLong, "Absolute Indexed X Long" },
{ OpDef.AddressMode.AbsIndexY, "Absolute Indexed Y" },
{ OpDef.AddressMode.AbsLong, "Absolute Long" },
{ OpDef.AddressMode.Acc, "Accumulator" },
{ OpDef.AddressMode.BlockMove, "Block Move" },
{ OpDef.AddressMode.DP, "Direct Page" },
{ OpDef.AddressMode.DPInd, "Direct Page Indirect" },
{ OpDef.AddressMode.DPIndIndexY, "Direct Page Indirect Indexed Y" },
{ OpDef.AddressMode.DPIndIndexYLong, "Direct Page Indirect Indexed Y Long" },
{ OpDef.AddressMode.DPIndLong, "Direct Page Indirect Long" },
{ OpDef.AddressMode.DPIndexX, "Direct Page Indexed X" },
{ OpDef.AddressMode.DPIndexXInd, "Direct Page Indexed X Indirect" },
{ OpDef.AddressMode.DPIndexY, "Direct Page Indexed Y" },
{ OpDef.AddressMode.Imm, "Immediate" },
{ OpDef.AddressMode.ImmLongA, "Immediate" },
{ OpDef.AddressMode.ImmLongXY, "Immediate" },
{ OpDef.AddressMode.Implied, "" },
{ OpDef.AddressMode.PCRel, "PC Relative" },
{ OpDef.AddressMode.PCRelLong, "PC Relative Long" },
{ OpDef.AddressMode.StackAbs, "Stack Absolute" },
{ OpDef.AddressMode.StackDPInd, "Stack Direct Page Indirect" },
{ OpDef.AddressMode.StackInt, "" },
{ OpDef.AddressMode.StackPCRelLong, "Stack PC Relative Long" },
{ OpDef.AddressMode.StackPull, "" },
{ OpDef.AddressMode.StackPush, "" },
{ OpDef.AddressMode.StackRTI, "" },
{ OpDef.AddressMode.StackRTL, "" },
{ OpDef.AddressMode.StackRTS, "" },
{ OpDef.AddressMode.StackRel, "" },
{ OpDef.AddressMode.StackRelIndIndexY, "Stack Relative Indirect Index Y" },
{ OpDef.AddressMode.WDM, "" }
};
/// <summary>
/// Cycle modifier descriptions. These are intended to be very terse.
/// </summary>
private static Dictionary<OpDef.CycleMod, string> sCycleMod_enUS =
new Dictionary<OpDef.CycleMod, string>() {
{ OpDef.CycleMod.OneIfM0, "+1 if M=0" },
{ OpDef.CycleMod.TwoIfM0, "+2 if M=0" },
{ OpDef.CycleMod.OneIfX0, "+1 if X=0" },
{ OpDef.CycleMod.OneIfDpNonzero, "+1 if DL != 0" },
{ OpDef.CycleMod.OneIfIndexPage, "+1 if index across page" },
{ OpDef.CycleMod.OneIfD1, "+1 if D=1 on 65C02" },
{ OpDef.CycleMod.OneIfBranchTaken, "+1 if branch taken" },
{ OpDef.CycleMod.OneIfBranchPage, "+1 if branch across page unless E=0" },
{ OpDef.CycleMod.OneIfE0, "+1 if E=0" },
{ OpDef.CycleMod.OneIf65C02, "+1 if 65C02" },
{ OpDef.CycleMod.MinusOneIfNoPage, "-1 if 65C02 and not across page" },
{ OpDef.CycleMod.BlockMove, "+7 per byte" },
};
}
}

148
Asm65/OpName.cs Normal file
View File

@ -0,0 +1,148 @@
/*
* Copyright 2018 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 System;
namespace Asm65 {
/// <summary>
/// String constants for opcodes. These are not (and should not be) localized. They
/// must be lower-case.
/// </summary>
public static class OpName {
// NOTE: these all happen to be three characters, but I don't think we want to
// guarantee that. On the 65816 some mnemonics are extended (e.g. LDAL for LDA with
// a 24-bit operand), but that's assembler-specific and handled elsewhere.
public const string Unknown = "???";
public const string ADC = "adc";
public const string AND = "and";
public const string ASL = "asl";
public const string BCC = "bcc";
public const string BCS = "bcs";
public const string BEQ = "beq";
public const string BIT = "bit";
public const string BMI = "bmi";
public const string BNE = "bne";
public const string BPL = "bpl";
public const string BRA = "bra";
public const string BRK = "brk";
public const string BRL = "brl";
public const string BVC = "bvc";
public const string BVS = "bvs";
public const string CLC = "clc";
public const string CLD = "cld";
public const string CLI = "cli";
public const string CLV = "clv";
public const string CMP = "cmp";
public const string COP = "cop";
public const string CPX = "cpx";
public const string CPY = "cpy";
public const string DEC = "dec";
public const string DEX = "dex";
public const string DEY = "dey";
public const string EOR = "eor";
public const string INC = "inc";
public const string INX = "inx";
public const string INY = "iny";
public const string JML = "jml";
public const string JMP = "jmp";
public const string JSL = "jsl";
public const string JSR = "jsr";
public const string LDA = "lda";
public const string LDX = "ldx";
public const string LDY = "ldy";
public const string LSR = "lsr";
public const string MVN = "mvn";
public const string MVP = "mvp";
public const string NOP = "nop";
public const string ORA = "ora";
public const string PEA = "pea";
public const string PEI = "pei";
public const string PER = "per";
public const string PHA = "pha";
public const string PHB = "phb";
public const string PHD = "phd";
public const string PHK = "phk";
public const string PHP = "php";
public const string PHX = "phx";
public const string PHY = "phy";
public const string PLA = "pla";
public const string PLB = "plb";
public const string PLD = "pld";
public const string PLP = "plp";
public const string PLX = "plx";
public const string PLY = "ply";
public const string REP = "rep";
public const string ROL = "rol";
public const string ROR = "ror";
public const string RTI = "rti";
public const string RTL = "rtl";
public const string RTS = "rts";
public const string SBC = "sbc";
public const string SEC = "sec";
public const string SED = "sed";
public const string SEI = "sei";
public const string SEP = "sep";
public const string STA = "sta";
public const string STP = "stp";
public const string STX = "stx";
public const string STY = "sty";
public const string STZ = "stz";
public const string TAX = "tax";
public const string TAY = "tay";
public const string TCD = "tcd";
public const string TCS = "tcs";
public const string TDC = "tdc";
public const string TRB = "trb";
public const string TSB = "tsb";
public const string TSC = "tsc";
public const string TSX = "tsx";
public const string TXA = "txa";
public const string TXS = "txs";
public const string TXY = "txy";
public const string TYA = "tya";
public const string TYX = "tyx";
public const string WAI = "wai";
public const string WDM = "wdm";
public const string XBA = "xba";
public const string XCE = "xce";
// Undocumented 6502 instructions.
public const string ANC = "anc";
public const string ANE = "ane";
public const string ARR = "arr";
public const string ASR = "asr";
public const string DCP = "dcp";
public const string DOP = "dop";
public const string HLT = "hlt";
public const string ISB = "isb";
public const string LAE = "lae";
public const string LAX = "lax";
public const string LXA = "lxa";
public const string RLA = "rla";
public const string RRA = "rra";
public const string SAX = "sax";
public const string SBX = "sbx";
public const string SHA = "sha";
public const string SHS = "shs";
public const string SHX = "shx";
public const string SHY = "shy";
public const string SLO = "slo";
public const string SRE = "sre";
public const string TOP = "top";
// Undocumented 65C02 instructions.
public const string LDD = "ldd";
}
}

73
Asm65/Properties/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,73 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Asm65.Properties {
using System;
using System.Reflection;
/// <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", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public 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)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Asm65.Properties.Resources", typeof(Resources).GetTypeInfo().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)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to Fourth time.
/// </summary>
public static string TEST_STRING {
get {
return ResourceManager.GetString("TEST_STRING", resourceCulture);
}
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="TEST_STRING" xml:space="preserve">
<value>Fourth time</value>
</data>
</root>

328
Asm65/StatusFlags.cs Normal file
View File

@ -0,0 +1,328 @@
/*
* Copyright 2018 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 System;
using System.Text;
namespace Asm65 {
/// <summary>
/// Status flag holder. Each flag may be known to be zero, known to be one, or
/// hold an indeterminate value (represented as a negative number).
///
/// For the 65802/65816, we also keep track of the E flag (emulation bit), even though
/// that's not actually held in the P register.
///
/// Note this is a value type, not a reference type.
///
/// The default value is UNSPECIFIED for all bits.
/// </summary>
public struct StatusFlags {
private TriState16 mState;
/// <summary>
/// Flag bits, from processor status register definition. The 'e' (emulation)
/// flag from the 65816 is tacked onto the end.
///
/// The enumerated value matches the bit number in the P register.
/// </summary>
public enum FlagBits {
C = 0,
Z = 1,
I = 2,
D = 3,
B = 4, // all CPUs except 65802/65816 in native mode
X = 4, // 65802/65816 in native mode
M = 5, // 65802/65816 in native mode (always 1 on other CPUs)
V = 6,
N = 7,
E = 8 // not actually part of P-reg; accessible only through XCE
}
/// <summary>
/// Default value (all flags UNSPECIFIED). A newly-created array of StatusFlags will
/// all have this value.
/// </summary>
public static readonly StatusFlags DefaultValue =
new StatusFlags { mState = new TriState16(0, 0) };
/// <summary>
/// All flags are INDETERMINATE.
/// </summary>
public static readonly StatusFlags AllIndeterminate =
new StatusFlags() { mState = new TriState16(0x01ff, 0x01ff) };
public int C {
get {
return mState.GetBit((int)FlagBits.C);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.C);
} else if (value == 1) {
mState.SetOne((int)FlagBits.C);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.C);
} else {
mState.SetIndeterminate((int)FlagBits.C);
}
}
}
public int Z {
get {
return mState.GetBit((int)FlagBits.Z);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.Z);
} else if (value == 1) {
mState.SetOne((int)FlagBits.Z);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.Z);
} else {
mState.SetIndeterminate((int)FlagBits.Z);
}
}
}
public int I {
get {
return mState.GetBit((int)FlagBits.I);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.I);
} else if (value == 1) {
mState.SetOne((int)FlagBits.I);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.I);
} else {
mState.SetIndeterminate((int)FlagBits.I);
}
}
}
public int D {
get {
return mState.GetBit((int)FlagBits.D);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.D);
} else if (value == 1) {
mState.SetOne((int)FlagBits.D);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.D);
} else {
mState.SetIndeterminate((int)FlagBits.D);
}
}
}
public int X {
get {
return mState.GetBit((int)FlagBits.X);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.X);
} else if (value == 1) {
mState.SetOne((int)FlagBits.X);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.X);
} else {
mState.SetIndeterminate((int)FlagBits.X);
}
}
}
public int M {
get {
return mState.GetBit((int)FlagBits.M);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.M);
} else if (value == 1) {
mState.SetOne((int)FlagBits.M);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.M);
} else {
mState.SetIndeterminate((int)FlagBits.M);
}
}
}
public int V {
get {
return mState.GetBit((int)FlagBits.V);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.V);
} else if (value == 1) {
mState.SetOne((int)FlagBits.V);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.V);
} else {
mState.SetIndeterminate((int)FlagBits.V);
}
}
}
public int N {
get {
return mState.GetBit((int)FlagBits.N);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.N);
} else if (value == 1) {
mState.SetOne((int)FlagBits.N);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.N);
} else {
mState.SetIndeterminate((int)FlagBits.N);
}
}
}
public int E {
get {
return mState.GetBit((int)FlagBits.E);
}
set {
if (value == 0) {
mState.SetZero((int)FlagBits.E);
} else if (value == 1) {
mState.SetOne((int)FlagBits.E);
} else if (value == TriState16.UNSPECIFIED) {
mState.SetUnspecified((int)FlagBits.E);
} else {
mState.SetIndeterminate((int)FlagBits.E);
}
}
}
public int GetBit(FlagBits index) {
return mState.GetBit((int) index);
}
/// <summary>
/// Returns true if the current processor status flags are configured for a short
/// (8-bit) accumulator.
/// </summary>
public bool ShortM {
get {
// E==1 --> true (we're in emulation mode)
// E==0 || E==? : native / assumed native
// M==1 || M==? --> true (native mode, configured short or assumed short)
// M==0 --> false (native mode, configured long)
return (E == 1) || (M != 0);
}
}
/// <summary>
/// Returns true if the current processor status flags are configured for short
/// (8-bit) X/Y registers.
/// </summary>
public bool ShortX {
get {
// (same logic as ShortM)
return (E == 1) || (X != 0);
}
}
/// <summary>
/// Access the value as a single integer. Used for serialization.
/// </summary>
public int AsInt {
get {
return mState.AsInt;
}
}
/// <summary>
/// Set the value from an integer. Used for serialization.
/// </summary>
public static StatusFlags FromInt(int value) {
if ((value & ~0x01ff01ff) != 0) {
throw new InvalidOperationException("Bad StatusFlags value " +
value.ToString("x8"));
}
StatusFlags newFlags = new StatusFlags();
newFlags.mState.AsInt = value;
return newFlags;
}
/// <summary>
/// Merge a set of status flags into this one.
/// </summary>
public void Merge(StatusFlags other) {
mState.Merge(other.mState);
}
/// <summary>
/// Applies flags, overwriting existing values. This will set one or more flags
/// to 0, 1, or indeterminate. Unspecified (0/0) values have no effect.
///
/// This is useful when merging "overrides" in.
/// </summary>
public void Apply(StatusFlags overrides) {
mState.Apply(overrides.mState);
}
/// <summary>
/// Returns a string representation of the flags.
/// </summary>
/// <param name="showMXE">If set, include the 'E' flag, and show M/X.</param>
public string ToString(bool showMXE) {
StringBuilder sb = new StringBuilder(showMXE ? 10 : 8);
sb.Append("-?nN"[N + 2]);
sb.Append("-?vV"[V + 2]);
sb.Append(showMXE ? "-?mM"[M + 2] : '-');
sb.Append(showMXE ? "-?xX"[X + 2] : '-');
sb.Append("-?dD"[D + 2]);
sb.Append("-?iI"[I + 2]);
sb.Append("-?zZ"[Z + 2]);
sb.Append("-?cC"[C + 2]);
if (showMXE) {
sb.Append(' ');
sb.Append("-?eE"[E + 2]);
}
return sb.ToString();
}
public static bool operator ==(StatusFlags a, StatusFlags b) {
return a.mState == b.mState;
}
public static bool operator !=(StatusFlags a, StatusFlags b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is StatusFlags && this == (StatusFlags)obj;
}
public override int GetHashCode() {
return mState.GetHashCode();
}
public override string ToString() {
return ToString(true);
// + " [" + mState.ToString() + "]"
}
}
}

172
Asm65/TriState16.cs Normal file
View File

@ -0,0 +1,172 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace Asm65 {
/// <summary>
/// A value with 16 tri-state bits.
/// </summary>
public struct TriState16 {
/// <summary>
/// Two 16-bit values. The low 16 bits indicate that bit N is zero, the high
/// 16 bits indicate that bit N is one. If neither or both are set, the value
/// is undetermined and could be either.
///
/// While 0/0 and 1/1 both represent an indeterminate value, they're not equivalent.
/// When two values are merged, a 0/0 bit has no effect on the result, while
/// a 1/1 bit will force the merged bit to be indeterminate. We use UNSPECIFIED for
/// 0/0 and INDETERMINATE for 1/1.
///
/// The default value for new instances is UNSPECIFIED for all bits.
/// </summary>
private ushort mZero, mOne;
public const int INDETERMINATE = -1;
public const int UNSPECIFIED = -2;
/// <summary>
/// Constructor; sets initial zero/one values.
/// </summary>
/// <param name="zeroes">16-bit value, with bits set for each known-zero.</param>
/// <param name="ones">16-bit value, with bits set for each known-one.</param>
public TriState16(ushort zeroes, ushort ones) {
mZero = zeroes;
mOne = ones;
}
/// <summary>
/// Access the value as a single integer. Used for serialization.
/// </summary>
public int AsInt {
get {
return (int)mZero | ((int)mOne << 16);
}
set {
mZero = (ushort) value;
mOne = (ushort) (value >> 16);
}
}
/// <summary>
/// Sets bit N to zero.
/// </summary>
public void SetZero(int bit) {
Debug.Assert(bit >= 0 && bit < 16);
// clear 1-flag, set 0-flag
//mValue = (mValue & ~(1U << (bit + 16))) | (1U << bit);
mZero |= (ushort) (1U << bit);
mOne &= (ushort) ~(1U << bit);
}
/// <summary>
/// Sets bit N to one.
/// </summary>
public void SetOne(int bit) {
Debug.Assert(bit >= 0 && bit < 16);
// clear 0-flag, set 1-flag
//mValue = (mValue & ~(1U << bit)) | (1U << (bit + 16));
mZero &= (ushort)~(1U << bit);
mOne |= (ushort)(1U << bit);
}
/// <summary>
/// Sets bit N to indeterminate.
/// </summary>
public void SetIndeterminate(int bit) {
Debug.Assert(bit >= 0 && bit < 16);
// set both flags
mZero |= (ushort)(1U << bit);
mOne |= (ushort)(1U << bit);
}
/// <summary>
/// Sets bit N to unspecified.
/// </summary>
public void SetUnspecified(int bit) {
Debug.Assert(bit >= 0 && bit < 16);
// clear both flags
mZero &= (ushort)~(1U << bit);
mOne &= (ushort)~(1U << bit);
}
/// <summary>
/// Merges bit states.
/// </summary>
/// <param name="other">Value to merge in.</param>
public void Merge(TriState16 other) {
//mValue |= other.mValue;
mZero |= other.mZero;
mOne |= other.mOne;
}
/// <summary>
/// Applies a set of bits to an existing set, overriding any bits that aren't set
/// to "unspecified" in the input.
/// </summary>
public void Apply(TriState16 overrides) {
ushort mask = (ushort) ~(overrides.mZero | overrides.mOne);
mZero = (ushort)((mZero & mask) | overrides.mZero);
mOne = (ushort)((mOne & mask) | overrides.mOne);
}
/// <summary>
/// Returns 0, 1, -1, or -2 depending on whether the specified bit is 0, 1,
/// indeterminate, or unspecified.
/// </summary>
public int GetBit(int bit) {
bool zero = ((mZero >> bit) & 0x01) != 0;
bool one = ((mOne >> bit) & 0x01) != 0;
if (zero ^ one) {
// Only one of the bits is set.
if (one) {
return 1;
} else {
return 0;
}
} else {
// Both or neither are set.
if (zero) {
return INDETERMINATE;
} else {
return UNSPECIFIED;
}
}
}
public static bool operator ==(TriState16 a, TriState16 b) {
return a.mZero == b.mZero && a.mOne == b.mOne;
}
public static bool operator !=(TriState16 a, TriState16 b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is TriState16 && this == (TriState16)obj;
}
public override int GetHashCode() {
return (mOne << 16) | mZero;
}
public override string ToString() {
return mZero.ToString("x4") + "-" + mOne.ToString("x4");
}
}
}

6
CodeLab/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

84
CodeLab/CodeLab.csproj Normal file
View File

@ -0,0 +1,84 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{3B66ABD8-7129-4D2B-B48E-B03FEC835CE1}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>CodeLab</RootNamespace>
<AssemblyName>CodeLab</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainWindow.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MainWindow.Designer.cs">
<DependentUpon>MainWindow.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="MainWindow.resx">
<DependentUpon>MainWindow.cs</DependentUpon>
<SubType>Designer</SubType>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

142
CodeLab/Form1.resx Normal file
View File

@ -0,0 +1,142 @@
<?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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="menuStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="statusStrip1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>132, 17</value>
</metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="SuperSplitButton.Image" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8
YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAIDSURBVDhPpZLrS5NhGMb3j4SWh0oRQVExD4gonkDpg4hG
YKxG6WBogkMZKgPNCEVJFBGdGETEvgwyO9DJE5syZw3PIlPEE9pgBCLZ5XvdMB8Ew8gXbl54nuf63dd9
0OGSnwCahxbPRNPAPMw9Xpg6ZmF46kZZ0xSKzJPIrhpDWsVnpBhGkKx3nAX8Pv7z1zg8OoY/cITdn4fw
bf/C0kYAN3Ma/w3gWfZL5kzTKBxjWyK2DftwI9tyMYCZKXbNHaD91bLYJrDXsYbrWfUKwJrPE9M2M1Oc
VzOOpHI7Jr376Hi9ogHqFIANO0/MmmmbmSmm9a8ze+I4MrNWAdjtoJgWcx+PSzg166yZZ8xM8XvXDix9
c4jIqFYAjoriBV9AhEPv1mH/sonogha0afbZMMZz+yreTGyhpusHwtNNCsA5U1zS4BLxzJIfg299qO32
Ir7UJtZfftyATqeT+8o2D8JSjQrAJblrncYL7ZJ2+bfaFnC/1S1NjL3diRat7qrO7wLRP3HjWsojBeCo
mDEo5mNjuweFGvjWg2EBhCbpkW78htSHHwRyNdmgAFzPEee2iFkzayy2OLXzT4gr6UdUnlXrullsxxQ+
kx0g8BTA3aZlButjSTyjODq/WcQcW/B/Je4OQhLvKQDnzN1mp0nnkvAhR8VuMzNrpm1mpjgkoVwB/v8D
TgDQASA1MVpwzwAAAABJRU5ErkJggg==
</value>
</data>
</root>

46
CodeLab/MainWindow.Designer.cs generated Normal file
View File

@ -0,0 +1,46 @@
namespace WorkBench
{
partial class MainWindow
{
/// <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.SuspendLayout();
//
// MainWindow
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(284, 261);
this.Name = "MainWindow";
this.Text = "6502bench";
this.ResumeLayout(false);
}
#endregion
}
}

20
CodeLab/MainWindow.cs Normal file
View File

@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorkBench
{
public partial class MainWindow : Form
{
public MainWindow()
{
InitializeComponent();
}
}
}

120
CodeLab/MainWindow.resx Normal file
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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

22
CodeLab/Program.cs Normal file
View File

@ -0,0 +1,22 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WorkBench
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MainWindow());
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WorkBench")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("WorkBench")]
[assembly: AssemblyCopyright("Copyright © 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("3b66abd8-7129-4d2b-b48e-b03fec835ce1")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

63
CodeLab/Properties/Resources.Designer.cs generated Normal file
View File

@ -0,0 +1,63 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CodeLab.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", "15.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("CodeLab.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,117 @@
<?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.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: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" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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>

26
CodeLab/Properties/Settings.Designer.cs generated Normal file
View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CodeLab.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.6.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

1
CodeLab/README.md Normal file
View File

@ -0,0 +1 @@
This is just a place-holder. Nothing to see here.

109
CommonUtil/CRC32.cs Normal file
View File

@ -0,0 +1,109 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.IO;
namespace CommonUtil {
/// <summary>
/// Compute a standard CRC-32 (polynomial 0xedb88320, or
/// x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1).
/// </summary>
public static class CRC32 {
private static readonly uint[] sTable = ComputeTable();
private const uint INVERT = 0xffffffff;
/// <summary>
/// Generates 256-entry CRC table.
/// </summary>
/// <returns>Table.</returns>
private static uint[] ComputeTable() {
uint[] table = new uint[256];
uint poly = 0xedb88320;
for (int i = 0; i < 256; i++) {
uint val = (uint) i;
for (int j = 0; j < 8; j++) {
val = (val & 1) != 0 ? poly ^ (val >> 1) : val >> 1;
}
table[i] = val;
}
return table;
}
/// <summary>
/// Computes a CRC on part of a buffer of data.
/// </summary>
/// <param name="crc">Previously computed CRC value. Initially zero.</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>
public static uint OnBuffer(uint crc, byte[] buffer, int offset, int count) {
crc = crc ^ INVERT;
while (count-- != 0) {
crc = sTable[(crc ^ buffer[offset]) & 0xff] ^ (crc >> 8);
offset++;
}
return crc ^ INVERT;
}
/// <summary>
/// Computes a CRC 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>
public static uint OnWholeBuffer(uint crc, byte[] buffer) {
return OnBuffer(crc, buffer, 0, buffer.Length);
}
/// <summary>
/// Computes a CRC on an entire file.
/// </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;
}
ocrc = crc;
return true;
}
} catch (IOException ioe) {
Debug.WriteLine("Fail: " + ioe);
ocrc = 0;
return false;
}
}
}
}

View File

@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<Compile Update="Properties\Resources.Designer.cs">
<DesignTime>True</DesignTime>
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Update="Properties\Resources.resx">
<Generator>PublicResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
</ItemGroup>
</Project>

40
CommonUtil/Container.cs Normal file
View File

@ -0,0 +1,40 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Linq;
namespace CommonUtil {
public class Container {
/// <summary>
/// Compares two lists of strings to see if their contents are equal. The lists
/// must contain the same strings, in the same order.
/// </summary>
/// <param name="l1">List #1.</param>
/// <param name="l2">List #2.</param>
/// <param name="comparer">String comparer (e.g. StringComparer.InvariantCulture). If
/// null, the default string comparer is used.</param>
/// <returns>True if the lists are equal.</returns>
public static bool StringListEquals(IList<string> l1, IList<string>l2,
StringComparer comparer) {
// Quick check for reference equality.
if (l1 == l2) {
return true;
}
return Enumerable.SequenceEqual<string>(l1, l2, comparer);
}
}
}

233
CommonUtil/DebugLog.cs Normal file
View File

@ -0,0 +1,233 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace CommonUtil {
/// <summary>
/// Debug log facility, with priority levels and time stamps.
///
/// The logs are held in memory. The internal storage expands as logs are added
/// until the maximum size is reached, then switches to circular buffering. This
/// minimizes overhead for small logs while avoiding infinite expansion.
/// </summary>
public class DebugLog {
/// <summary>
/// Log priority levels, in ascending order. "Silent" is only used as an argument
/// when setting the minimum priority level.
/// </summary>
public enum Priority {
Verbose = 0, Debug, Info, Warning, Error, Silent
}
private static char[] sSingleLetter = { 'V', 'D', 'I', 'W', 'E', 'S' };
/// <summary>
/// Holds a single log entry.
/// </summary>
private struct LogEntry {
public DateTime mWhen;
public Priority mPriority;
public string mText;
public LogEntry(Priority prio, string msg) {
mWhen = DateTime.Now;
mPriority = prio;
mText = msg;
}
}
/// <summary>
/// Log collection.
/// </summary>
private List<LogEntry> mEntries = new List<LogEntry>();
private int mTopEntry = 0;
/// <summary>
/// Date/time when the log object was created. Used for relative time display mode.
/// </summary>
private DateTime mStartWhen;
/// <summary>
/// If set, display time stamps as relative time rather than absolute.
/// </summary>
private bool mShowRelTime = false;
/// <summary>
/// Minimum priority level. Anything below this is ignored.
/// </summary>
private Priority mMinPriority = Priority.Debug;
/// <summary>
/// Maximum number of lines we'll hold in memory. This is a simple measure
/// to keep the process from expanding without bound.
/// </summary>
private int mMaxLines = 100000;
/// <summary>
/// Constructor. Configures min priority to Info.
/// </summary>
public DebugLog() : this(Priority.Info) { }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="prio">Minimum log priority level.</param>
public DebugLog(Priority prio) {
mMinPriority = prio;
mStartWhen = DateTime.Now;
LogI("Log started at " + DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss zzz"));
}
/// <summary>
/// Sets the message priority threshold. Messages below the specified priority
/// will be ignored.
/// </summary>
/// <param name="prio">Minimum priority value.</param>
public void SetMinPriority(Priority prio) {
mMinPriority = prio;
}
/// <summary>
/// Sets the "show relative time" flag. If set, the timestamp in the log is
/// relative to when the log object was created, instead of wall-clock time.
/// </summary>
/// <param name="showRelTime"></param>
public void SetShowRelTime(bool showRelTime) {
mShowRelTime = showRelTime;
}
/// <summary>
/// Returns true if a message logged at the specified priority would be accepted.
/// </summary>
/// <param name="prio"></param>
/// <returns></returns>
public bool IsLoggable(Priority prio) {
return prio >= mMinPriority;
}
/// <summary>
/// Clears all entries.
/// </summary>
public void Clear() {
mEntries.Clear();
}
/// <summary>
/// Adds a message to the log buffer.
/// </summary>
/// <param name="prio">Log priority.</param>
/// <param name="message">Message to log.</param>
public void Log(Priority prio, string message) {
if (prio < mMinPriority) {
return;
}
LogEntry ent = new LogEntry(prio, message);
if (mEntries.Count < mMaxLines) {
// Still growing.
mEntries.Add(ent);
} else {
// Circular replacement. Adding to the end then removing [0] has
// significant performance issues.
mEntries[mTopEntry++] = ent;
if (mTopEntry == mMaxLines) {
mTopEntry = 0;
}
}
}
public void LogV(string message) {
Log(Priority.Verbose, message);
}
public void LogD(string message) {
Log(Priority.Debug, message);
}
public void LogI(string message) {
Log(Priority.Info, message);
}
public void LogW(string message) {
Log(Priority.Warning, message);
}
public void LogE(string message) {
Log(Priority.Error, message);
}
/// <summary>
/// Dumps the contents of the log to a file.
/// </summary>
/// <param name="pathName">Full or partial pathname.</param>
public void WriteToFile(string pathName) {
StringBuilder sb = new StringBuilder();
using (StreamWriter sw = new StreamWriter(pathName, false, Encoding.UTF8)) {
for (int i = mTopEntry; i < mEntries.Count; i++) {
WriteEntry(sw, mEntries[i], sb);
}
for (int i = 0; i < mTopEntry; i++) {
WriteEntry(sw, mEntries[i], sb);
}
}
}
/// <summary>
/// Writes a single entry to a file. Pass in a StringBuilder so we don't have
/// to create a new one every time.
/// </summary>
private void WriteEntry(StreamWriter sw, LogEntry ent, StringBuilder sb) {
sb.Clear();
FormatEntry(ent, sb);
sw.WriteLine(sb.ToString());
}
/// <summary>
/// Formats an entry, appending the text to the provided StringBuilder.
/// </summary>
private void FormatEntry(LogEntry ent, StringBuilder sb) {
if (mShowRelTime) {
sb.Append((ent.mWhen - mStartWhen).ToString(@"mm\:ss\.fff"));
} else {
sb.Append(ent.mWhen.ToString(@"hh\:mm\:ss\.fff"));
}
sb.Append(' ');
sb.Append(sSingleLetter[(int)ent.mPriority]);
sb.Append(' ');
sb.Append(ent.mText);
}
/// <summary>
/// Dumps the contents of the log to a string. This is intended for display in a
/// text box, so lines are separated with CRLF.
/// </summary>
/// <returns></returns>
public string WriteToString() {
StringBuilder sb = new StringBuilder();
for (int i = mTopEntry; i < mEntries.Count; i++) {
FormatEntry(mEntries[i], sb);
sb.Append("\r\n");
}
for (int i = 0; i < mTopEntry; i++) {
FormatEntry(mEntries[i], sb);
sb.Append("\r\n");
}
return sb.ToString();
}
public override string ToString() {
return "DebugLog has " + mEntries.Count + " entries";
}
}
}

View File

@ -0,0 +1,136 @@
/*
* Copyright 2018 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 System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
namespace CommonUtil {
/// <summary>
/// File load item, identifying the location, severity, and details of the issue.
/// </summary>
public class FileLoadItem {
public const int NO_LINE = -1;
public const int NO_COLUMN = -1;
public enum Type {
Unknown = 0,
Notice,
Warning,
Error
}
public int Line { get; private set; }
public int Column { get; private set; }
public Type MsgType { get; private set; }
public string Message { get; private set; }
public FileLoadItem(int line, int col, Type msgType, string msg) {
Line = line;
Column = col;
MsgType = msgType; ;
Message = msg;
}
}
/// <summary>
/// A structured collection of errors and warnings generated when reading data from a file.
/// </summary>
public class FileLoadReport : IEnumerable<FileLoadItem> {
// List of items. Currently unsorted; items will appear in the order they were added.
private List<FileLoadItem> mItems = new List<FileLoadItem>();
public string FileName { get; private set; }
public bool HasWarnings { get; private set; }
public bool HasErrors { get; private set; }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="fileName">Name of file being loaded. This is just for output; this
/// class doesn't actually try to access the file.</param>
public FileLoadReport(string fileName) {
FileName = fileName;
}
public IEnumerator<FileLoadItem> GetEnumerator() {
return mItems.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return mItems.GetEnumerator();
}
public int Count { get { return mItems.Count; } }
/// <summary>
/// Adds a new message to the report.
/// </summary>
/// <param name="isWarn">Is this a warning or an error?</param>
/// <param name="msg">Human-readable message.</param>
public void Add(FileLoadItem.Type msgType, string msg) {
Add(FileLoadItem.NO_LINE, FileLoadItem.NO_COLUMN, msgType, msg);
}
/// <summary>
/// Adds a new message to the report.
/// </summary>
/// <param name="line">Line where the issue was seen.</param>
/// <param name="col">Column where the problem starts, or NO_COLUMN.</param>
/// <param name="isWarn">Is this a warning or an error?</param>
/// <param name="msg">Human-readable message.</param>
public void Add(int line, int col, FileLoadItem.Type msgType, string msg) {
mItems.Add(new FileLoadItem(line, col, msgType, msg));
switch (msgType) {
case FileLoadItem.Type.Warning:
HasWarnings = true;
break;
case FileLoadItem.Type.Error:
HasErrors = true;
break;
}
}
/// <summary>
/// Formats the entire collection into a single multi-line string.
/// </summary>
/// <returns>Formatted string.</returns>
public string Format() {
StringBuilder sb = new StringBuilder();
if (mItems.Count > 0) {
sb.AppendFormat("File {0}:\r\n", FileName);
}
foreach (FileLoadItem item in mItems) {
if (item.Line != FileLoadItem.NO_LINE && item.Column != FileLoadItem.NO_COLUMN) {
sb.AppendFormat(" Line {0}.{1}: {2}: {3}\r\n", item.Line, item.Column,
item.MsgType.ToString().ToLower(), item.Message);
} else if (item.Line != FileLoadItem.NO_LINE) {
sb.AppendFormat(" Line {0}: {1}: {2}\r\n", item.Line,
item.MsgType.ToString().ToLower(), item.Message);
} else {
// Capitalized form looks nicer here.
sb.AppendFormat(" {0}: {1}\r\n", item.MsgType.ToString(), item.Message);
}
}
return sb.ToString();
}
public override string ToString() {
return "FileLoadReport: count=" + mItems.Count + " hasWarn=" +
HasWarnings + " hasErr=" + HasErrors;
}
}
}

141
CommonUtil/FileUtil.cs Normal file
View File

@ -0,0 +1,141 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.IO;
using System.Text;
namespace CommonUtil {
public static class FileUtil {
/// <summary>
/// Compares the contents of a file against a byte array.
/// </summary>
/// <param name="expected">Expected data values.</param>
/// <param name="pathName">Pathname of file to inspect.</param>
/// <param name="badOffset">Offset of first mismatch. If this is equal to expected.Length
/// or File(pathName).Length, then the contents were equal but one source stopped
/// early. The badFileVal parameter will be zero.</param>
/// <param name="badFileVal">Mismatched value, from the file.</param>
/// <returns>True if the contents match, false if not.</returns>
public static bool CompareBinaryFile(byte[] expected, string pathName,
out int badOffset, out byte badFileVal) {
badOffset = -1;
badFileVal = 0;
int chunkOffset = 0;
byte[] buffer = new byte[65536];
using (FileStream fs = File.Open(pathName, FileMode.Open, FileAccess.Read)) {
//if (fs.Length != expected.Length) {
// return false;
//}
int fileRemain = (int)fs.Length;
while (fileRemain != 0) {
int toRead = Math.Min(buffer.Length, fileRemain);
int actual = fs.Read(buffer, 0, toRead);
if (actual != toRead) {
// File I/O problem; unexpected.
return false;
}
for (int i = 0; i < toRead; i++) {
if (chunkOffset + i >= expected.Length) {
// File on disk was too long.
Debug.Assert(fs.Length > expected.Length);
badOffset = chunkOffset + i;
return false;
}
if (expected[chunkOffset + i] != buffer[i]) {
badOffset = chunkOffset + i;
badFileVal = buffer[i];
return false;
}
}
fileRemain -= toRead;
chunkOffset += toRead;
}
if (fs.Length != expected.Length) {
Debug.Assert(fs.Length < expected.Length);
badOffset = (int) fs.Length;
return false;
}
}
return true;
}
/// <summary>
/// Compares two text files line-by-line. Ignores line termination characters.
/// Assumes UTF-8 encoding.
/// </summary>
/// <param name="pathName1">Full path of first file.</param>
/// <param name="pathName2">Full path of second file.</param>
/// <param name="firstDiffLine">Line number of first differing line.</param>
/// <param name="line1">Differing line from first file.</param>
/// <param name="line2">Differing line from second file.</param>
/// <returns>True if the files are equal.</returns>
public static bool CompareTextFiles(string pathName1, string pathName2,
out int firstDiffLine, out string line1, out string line2) {
int line = 0;
using (StreamReader sr1 = new StreamReader(pathName1, Encoding.UTF8)) {
using (StreamReader sr2 = new StreamReader(pathName2, Encoding.UTF8)) {
while (true) {
// ReadLine strips the EOL char(s)
string str1 = sr1.ReadLine();
string str2 = sr2.ReadLine();
line++;
if (str1 != str2) {
firstDiffLine = line;
line1 = str1;
line2 = str2;
return false;
}
if (str1 == null) {
Debug.Assert(str2 == null);
break;
}
}
}
}
firstDiffLine = -1;
line1 = line2 = null;
return true;
}
/// <summary>
/// Determines whether the destination file is missing or older than the source file.
/// This can be used do decide whether it's necessary to copy srcFile over.
/// </summary>
/// <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) {
FileInfo fid = new FileInfo(dstFile);
if (!fid.Exists) {
return true; // not there
}
FileInfo fis = new FileInfo(srcFile);
if (!fis.Exists) {
return false; // nothing to compare against
}
return (fid.LastWriteTimeUtc < fis.LastWriteTimeUtc);
}
}
}

32
CommonUtil/Misc.cs Normal file
View File

@ -0,0 +1,32 @@
/*
* Copyright 2018 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 System;
using System.Linq;
namespace CommonUtil {
public static class Misc {
// Given a type, dump all namespaces found in the same assembly.
// https://stackoverflow.com/a/1549216/294248
public static void DumpNamespacesInAssembly(Type type) {
Console.WriteLine("Assembly: " + type.Assembly.Location);
Type[] typeList = type.Assembly.GetTypes();
var namespaces = typeList.Select(t => t.Namespace).Distinct();
foreach (string ns in namespaces) {
Console.WriteLine(" " + ns);
}
}
}
}

View File

@ -0,0 +1,108 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace CommonUtil.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", "15.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
public 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)]
public static global::System.Resources.ResourceManager ResourceManager {
get {
if (object.ReferenceEquals(resourceMan, null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("CommonUtil.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)]
public static global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to File not found.
/// </summary>
public static string ERR_FILE_NOT_FOUND {
get {
return ResourceManager.GetString("ERR_FILE_NOT_FOUND", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid address.
/// </summary>
public static string ERR_INVALID_ADDRESS {
get {
return ResourceManager.GetString("ERR_INVALID_ADDRESS", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Invalid numeric constant.
/// </summary>
public static string ERR_INVALID_NUMERIC_CONSTANT {
get {
return ResourceManager.GetString("ERR_INVALID_NUMERIC_CONSTANT", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Syntax error.
/// </summary>
public static string ERR_SYNTAX {
get {
return ResourceManager.GetString("ERR_SYNTAX", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to -.-.-.
/// </summary>
public static string NO_VERSION {
get {
return ResourceManager.GetString("NO_VERSION", resourceCulture);
}
}
}
}

View File

@ -0,0 +1,135 @@
<?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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="ERR_FILE_NOT_FOUND" xml:space="preserve">
<value>File not found</value>
</data>
<data name="ERR_INVALID_ADDRESS" xml:space="preserve">
<value>Invalid address</value>
</data>
<data name="ERR_INVALID_NUMERIC_CONSTANT" xml:space="preserve">
<value>Invalid numeric constant</value>
</data>
<data name="ERR_SYNTAX" xml:space="preserve">
<value>Syntax error</value>
</data>
<data name="NO_VERSION" xml:space="preserve">
<value>-.-.-</value>
</data>
</root>

426
CommonUtil/RangeSet.cs Normal file
View File

@ -0,0 +1,426 @@
/*
* Copyright 2018 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 System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace CommonUtil {
/// <summary>
/// Compact representation of a set of integers that tend to be adjacent.
///
/// <para>The default enumeration is a series of integers, not a series of ranges. Use
/// RangeListIterator to get the latter.</para>
///
/// <para>Most operations operate in log(N) time, where N is the number of
/// regions.</para>
/// </summary>
public class RangeSet : IEnumerable<int> {
/// <summary>
/// List of ranges, in sorted order.
/// </summary>
private List<Range> mRangeList = new List<Range>();
/// <summary>
/// Number of values in the set.
/// </summary>
public int Count { get; private set; }
/// <summary>
/// For unit tests: return the number of Range elements in the list.
/// </summary>
public int DebugRangeCount { get { return mRangeList.Count; } }
/// <summary>
/// Represents a contiguous range of values.
/// </summary>
public struct Range {
/// <summary>
/// Lowest value (inclusive).
/// </summary>
public int Low { get; set; }
/// <summary>
/// Highest value (inclusive).
/// </summary>
public int High { get; set; }
public Range(int low, int high) {
Debug.Assert(low <= high);
Low = low;
High = high;
}
/// <summary>
/// Returns true if the specified value falls in this range.
/// </summary>
public bool Contains(int val) {
return (val >= Low && val <= High);
}
}
/// <summary>
/// Iterator definition.
/// </summary>
private class RangeSetIterator : IEnumerator {
/// <summary>
/// The RangeSet we're iterating over.
/// </summary>
private RangeSet mSet;
// Index of current Range element in mSet.mRangeList.
private int mListIndex = -1;
// Current range, extracted from mRangeList.
private Range mCurrentRange;
// Current value in mCurrentRange.
private int mCurrentVal;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="set">RangeSet to iterate over.</param>
public RangeSetIterator(RangeSet set) {
mSet = set;
Reset();
}
// IEnumerator: current element
public object Current {
get {
if (mListIndex < 0) {
// not started
return null;
}
return mCurrentVal;
}
}
/// <summary>
/// Puts the next range in the list in mCurrentRange.
/// </summary>
/// <returns>True on success, false if we reached the end of the list.</returns>
private bool GetNextRange() {
mListIndex++; // increments to 0 on first invocation
if (mListIndex == mSet.mRangeList.Count) {
// no more ranges
return false;
}
mCurrentRange = mSet.mRangeList[mListIndex];
mCurrentVal = mCurrentRange.Low;
return true;
}
// IEnumerator: move to the next element, returning false if there isn't one
public bool MoveNext() {
if (mListIndex < 0) {
// just started
return GetNextRange();
} else {
// iterating within range object
mCurrentVal++;
if (mCurrentVal > mCurrentRange.High) {
// finished with this one, move on to the next
return GetNextRange();
} else {
return true;
}
}
}
// IEnumerator: reset state
public void Reset() {
mListIndex = -1;
}
}
/// <summary>
/// General constructor. Creates an empty set.
/// </summary>
public RangeSet() {
Count = 0;
}
/// <summary>
/// Constructs set from an iterator.
/// </summary>
/// <param name="iter">Iterator that generates a set of integers in ascending order.</param>
public RangeSet(IEnumerator iter) : this() {
if (!iter.MoveNext()) {
return;
}
int first = (int) iter.Current;
Count++;
Range curRange = new Range(first, first);
while (iter.MoveNext()) {
int val = (int) iter.Current;
Count++;
if (val == curRange.High + 1) {
// Add to current range.
curRange.High = val;
} else {
// Not contiguous, create new range.
mRangeList.Add(curRange);
curRange = new Range(val, val);
}
}
mRangeList.Add(curRange);
}
/// <summary>
/// Returns an enumerator that iterates through the range list, returning Range objects.
/// </summary>
public IEnumerator<Range> RangeListIterator {
get { return mRangeList.GetEnumerator(); }
}
/// <summary>
/// Removes all values from the set.
/// </summary>
public void Clear() {
mRangeList.Clear();
Count = 0;
}
// IEnumerable: get an enumerator instance that returns integer values
public IEnumerator GetEnumerator() {
return new RangeSetIterator(this);
}
// IEnumerable<int>
IEnumerator<int> IEnumerable<int>.GetEnumerator() {
return (IEnumerator<int>)GetEnumerator();
}
/// <summary>
/// Finds the range that contains "val", or an appropriate place in the list to
/// insert a new range.
/// </summary>
/// <param name="val">Value to find.</param>
/// <returns>The index of the matching element, or a negative value indicating
/// the index to insert at. 2C doesn't support negative 0, so the insertion
/// index will be incremented before negation.</returns>
private int FindValue(int val) {
int low = 0;
int high = mRangeList.Count - 1;
while (low <= high) {
int mid = (low + high) / 2;
Range midRange = mRangeList[mid];
if (midRange.Contains(val)) {
// found it
return mid;
} else if (val < midRange.Low) {
// too big, move the high end in
high = mid - 1;
} else if (val > midRange.High) {
// too small, move the low end in
low = mid + 1;
} else {
// WTF... list not sorted?
throw new Exception("Bad binary search");
}
}
// Not found, insert before "low".
return -(low + 1);
}
/// <summary>
/// Determines whether val is a member of the set.
/// </summary>
/// <param name="val">Value to check.</param>
/// <returns>True if the value is a member of the set.</returns>
public bool Contains(int val) {
return (FindValue(val) >= 0);
}
/// <summary>
/// Adds a value to the set. If the value is already present, nothing changes.
/// </summary>
/// <param name="val">Value to add.</param>
public void Add(int val) {
int listIndex = FindValue(val);
if (listIndex >= 0) {
// Already present in set.
return;
}
Count++;
if (mRangeList.Count == 0) {
// Empty list, skip the gymnastics.
mRangeList.Add(new Range(val, val));
return;
}
// Negate and decrement to get insertion index. This value may == Count if
// the value is higher than all current members.
listIndex = -listIndex - 1;
if (listIndex > 0 && mRangeList[listIndex - 1].High == val - 1) {
// Expand prior range. Check to see if it blends into next.
if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1) {
// Combine ranges.
Range prior = mRangeList[listIndex - 1];
Range next = mRangeList[listIndex];
Debug.Assert(prior.High + 2 == next.Low);
prior.High = next.High;
mRangeList[listIndex - 1] = prior;
mRangeList.RemoveAt(listIndex);
} else {
// Nope, just expand the prior range.
Range prior = mRangeList[listIndex - 1];
Debug.Assert(prior.High == val - 1);
prior.High = val;
mRangeList[listIndex - 1] = prior;
}
} else if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1) {
// Expand next range.
Range next = mRangeList[listIndex];
Debug.Assert(next.Low == val + 1);
next.Low = val;
mRangeList[listIndex] = next;
} else {
// Add a new single-entry element.
mRangeList.Insert(listIndex, new Range(val, val));
}
}
/// <summary>
/// Adds a range of contiguous values to the set.
/// </summary>
/// <param name="low">Lowest value (inclusive).</param>
/// <param name="high">Highest value (inclusive).</param>
public void AddRange(int low, int high) {
// There's probably some very efficient way to do this. Keeping it simple for now.
for (int i = low; i <= high; i++) {
Add(i);
}
}
/// <summary>
/// Removes a value from the set. If the value is not present, nothing changes.
/// </summary>
/// <param name="val">Value to remove.</param>
public void Remove(int val) {
int listIndex = FindValue(val);
if (listIndex < 0) {
// not found
return;
}
Count--;
Range rng = mRangeList[listIndex];
if (rng.Low == val && rng.High == val) {
// Single-value range. Remove.
mRangeList.RemoveAt(listIndex);
} else if (rng.Low == val) {
// We're at the low end, reduce range.
rng.Low = val + 1;
mRangeList[listIndex] = rng;
} else if (rng.High == val) {
// We're at the high end, reduce range.
rng.High = val - 1;
mRangeList[listIndex] = rng;
} else {
// We're in the middle, split the range.
Range next = new Range(val + 1, rng.High);
rng.High = val - 1;
mRangeList[listIndex] = rng;
mRangeList.Insert(listIndex + 1, next);
}
}
/// <summary>
/// Internal test function.
/// </summary>
private static bool CheckRangeSet(RangeSet set, int expectedRanges, int[] expected) {
if (set.DebugRangeCount != expectedRanges) {
Debug.WriteLine("Expected " + expectedRanges + " ranges, got " +
set.DebugRangeCount);
return false;
}
// Compare actual vs. expected. If we have more actual than expected we'll
// throw on the array access.
int expIndex = 0;
foreach (int val in set) {
if (val != expected[expIndex]) {
Debug.WriteLine("Expected " + expected[expIndex] + ", got " + val);
return false;
}
expIndex++;
}
// See if we have more expected than actual.
if (expIndex != expected.Length) {
Debug.WriteLine("Expected " + expected.Length + " elements, found " + expIndex);
return false;
}
// The count is maintained separately, so check it.
if (set.Count != expected.Length) {
Debug.WriteLine("Expected Count=" + expected.Length + ", got " + set.Count);
return false;
}
return true;
}
/// <summary>
/// Executes unit tests.
/// </summary>
/// <returns>True if all goes well.</returns>
public static bool Test() {
bool result = true;
RangeSet one = new RangeSet();
one.Add(7);
one.Add(5);
one.Add(3);
one.Add(9);
one.Add(7);
one.Add(8);
one.Add(2);
one.Add(4);
result &= CheckRangeSet(one, 2, new int[] { 2, 3, 4, 5, 7, 8, 9 });
one.Remove(2);
one.Remove(9);
one.Remove(4);
result &= CheckRangeSet(one, 3, new int[] { 3, 5, 7, 8 });
one.Clear();
one.AddRange(10, 15);
result &= CheckRangeSet(one, 1, new int[] { 10, 11, 12, 13, 14, 15 });
one.Add(-1);
one.Add(0);
one.Add(-2);
result &= CheckRangeSet(one, 2, new int[] { -2, -1, 0, 10, 11, 12, 13, 14, 15 });
Debug.WriteLine("RangeSet: test complete (ok=" + result + ")");
return result;
}
}
}

63
CommonUtil/RawData.cs Normal file
View File

@ -0,0 +1,63 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace CommonUtil {
public class RawData {
/// <summary>
/// Extracts an integer from the data stream.
/// </summary>
/// <param name="data">Raw data stream.</param>
/// <param name="offset">Start offset.</param>
/// <param name="width">Word width, which may be 1-4 bytes.</param>
/// <param name="isBigEndian">True if word is in big-endian order.</param>
/// <returns>Value found.</returns>
public static int GetWord(byte[] data, int offset, int width, bool isBigEndian) {
if (width < 1 || width > 4 || offset + width > data.Length) {
throw new ArgumentOutOfRangeException("GetWord(offset=" + offset + " width=" +
width + "), data.Length=" + data.Length);
}
if (isBigEndian) {
switch (width) {
case 1:
return data[offset];
case 2:
return (data[offset] << 8) | data[offset + 1];
case 3:
return (data[offset] << 16) | (data[offset + 1] << 8) | data[offset + 2];
case 4:
return (data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3];
}
} else {
switch (width) {
case 1:
return data[offset];
case 2:
return data[offset] | (data[offset + 1] << 8);
case 3:
return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16);
case 4:
return data[offset] | (data[offset + 1] << 8) | (data[offset + 2] << 16) |
data[offset + 3] << 24;
}
}
throw new Exception("GetWord(): should not be here");
}
}
}

222
CommonUtil/ShellCommand.cs Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Runtime.InteropServices;
namespace CommonUtil {
/// <summary>
/// Execute a shell command and return stdout/stderr.
///
/// Returning stdout/stderr separately loses the interleave, but it's unclear whether
/// that gets lost anyway with output buffering and the asynchronous I/O facility.
/// </summary>
public class ShellCommand {
// These were handy:
// https://stackoverflow.com/a/32872174/294248
// https://stackoverflow.com/a/18616369/294248
// https://stackoverflow.com/a/7334029/294248
// https://stackoverflow.com/a/5187715/294248
private const int CMD_TIMEOUT_MS = 10000; // 10 sec
private bool USE_CMD_EXE = false;
/// <summary>
/// Filename of shell command to execute.
/// </summary>
public string CommandFileName { get; private set; }
/// <summary>
/// Arguments to pass to command. Individual arguments are separated by spaces.
/// If an argument may contain spaces (e.g. it's a filename), surround it with
/// double quotes (").
/// </summary>
public string Arguments { get; private set; }
/// <summary>
/// Working directory for command. The directory will be changed for the
/// command only.
/// </summary>
public string WorkDirectory { get; private set; }
public Dictionary<string, string> EnvVars { get; private set; }
/// <summary>
/// The full command line, for display purposes. This is just CommandFileName + Arguments
/// unless some funny business is going on under the hood.
/// </summary>
public string FullCommandLine { get; private set; }
/// <summary>
/// Output from stdout.
/// </summary>
public string Stdout { get; private set; }
/// <summary>
/// Output from stderr.
/// </summary>
public string Stderr { get; private set; }
/// <summary>
/// Command exit code. Will be 0 on success.
/// </summary>
public int ExitCode { get; private set; }
/// <summary>
/// Buffers for gathering stdout/stderr.
/// </summary>
private StringBuilder mStdout, mStderr;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="commandFileName">Filename of command to execute.</param>
/// <param name="arguments">Command arguments, separated by spaces. Surround args with
/// embedded spaces with double quotes. Pass empty string if no args.</param>
/// <param name="workDir">Working directory for command. Pass an empty string if you
/// want to use the default.</param>
/// <param name="env">Dictionary of values to set in the shell environment.</param>
public ShellCommand(string commandFileName, string arguments, string workDir,
Dictionary<string, string> env) {
Debug.Assert(commandFileName != null);
Debug.Assert(arguments != null);
Debug.Assert(workDir != null);
CommandFileName = commandFileName;
Arguments = arguments;
WorkDirectory = workDir;
EnvVars = env;
ExitCode = -100;
mStdout = new StringBuilder();
mStderr = new StringBuilder();
}
/// <summary>
/// Execute the command.
/// </summary>
/// <returns>Process exit code. 0 on success.</returns>
public void Execute() {
// Works for full paths to command, but not for shell stuff like "dir".
FileInfo fi = new FileInfo(CommandFileName);
if (!fi.Exists) {
Debug.WriteLine("Warning: file '" + CommandFileName + "' does not exist");
}
ProcessStartInfo psi = new ProcessStartInfo();
if (USE_CMD_EXE) {
// Run inside cmd.exe on Windows.
psi.FileName = "cmd.exe";
psi.Arguments = "/C " + CommandFileName +
(string.IsNullOrEmpty(Arguments) ? "" : " " + Arguments);
} else {
psi.FileName = CommandFileName;
psi.Arguments = Arguments;
}
FullCommandLine = psi.FileName + " " + psi.Arguments;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false; // required for stdin/stdout redirect
if (!string.IsNullOrEmpty(WorkDirectory)) {
psi.WorkingDirectory = WorkDirectory;
}
if (EnvVars != null) {
foreach (KeyValuePair<string, string> kvp in EnvVars) {
Debug.WriteLine("ENV: " + kvp.Key + "=" + kvp.Value);
psi.Environment.Add(kvp);
}
}
try {
using (Process process = Process.Start(psi)) {
process.OutputDataReceived += (sendingProcess, outLine) =>
mStdout.AppendLine(outLine.Data);
process.ErrorDataReceived += (sendingProcess, errLine) =>
mStderr.AppendLine(errLine.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
// Close stdin so interactive programs don't stall.
process.StandardInput.Close();
// I'm calling with a (fairly long) timeout, just in case.
process.WaitForExit(CMD_TIMEOUT_MS);
if (!process.HasExited) {
Debug.WriteLine("Process stalled, killing");
process.Kill();
}
// WaitForExit(timeout) can return before the async stdout/stderr stuff
// has completed. Calling it without a timeout ensures correct behavior.
process.WaitForExit();
// This will be zero on success. I've seen 1 when the command wasn't
// found, and -1 when the process was killed.
ExitCode = process.ExitCode;
}
} catch (Exception ex) {
// This can happen if the command doesn't exist.
ExitCode = -2000;
Stdout = string.Empty;
Stderr = "Failed to execute command: " + FullCommandLine + "\r\n" + ex.ToString();
return;
}
Stdout = mStdout.ToString();
Stderr = mStderr.ToString();
}
/// <summary>
/// Opens a tab in the system web browser for the specified URL.
///
/// NOTE: on Windows 10, as of 2018/09/02, this loses the anchor (the "#thing" at the
/// end of the URL). Chasing through various stackoverflow posts, it appears the
/// only way around this is to invoke the specific browser (which you dredge out of
/// the Registry).
/// </summary>
public static void OpenUrl(string url) {
// See https://stackoverflow.com/a/43232486/294248
// The idea is to see if Start() will just do it, and if it doesn't then fall
// back on something platform-specific. I don't know if this is actually
// necessary -- I suspect Mono will do the right thing.
try {
Process.Start(url);
} catch {
// hack because of this: https://github.com/dotnet/corefx/issues/10361
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
url = url.Replace("&", "^&");
Process.Start(new ProcessStartInfo("cmd", $"/c start {url}") {
CreateNoWindow = true
});
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
Process.Start("xdg-open", url);
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
Process.Start("open", url);
} else {
throw;
}
}
}
}
}

199
CommonUtil/TaskTimer.cs Normal file
View File

@ -0,0 +1,199 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
namespace CommonUtil {
/// <summary>
/// Collects timestamps from a series of events. Events may be nested, but may not overlap.
/// A summary of task durations can be written to a log.
/// </summary>
public class TaskTimer {
// TODO(maybe): create a start/end pair that works with a "using" directive to ensure
// that a given item is always closed.
/// <summary>
/// Timed task info.
/// </summary>
private class TimedItem {
public string mTag;
public int mIndentLevel;
public DateTime mStartWhen;
public DateTime mEndWhen;
public TimedItem(string tag, int indentLevel) {
mTag = tag;
mIndentLevel = indentLevel;
mStartWhen = DateTime.Now;
}
}
/// <summary>
/// List of items. They are ordered by when the tasks ended.
/// </summary>
private List<TimedItem> mItems = new List<TimedItem>();
/// <summary>
/// Indentation level. Cosmetic.
/// </summary>
private int mIndentLevel = 0;
/// <summary>
/// Place where next record is to be inserted.
///
/// We keep inserting records ahead of whatever we inserted last, only advancing
/// the insertion point when we close a record. Essentially a stack that moves
/// forward when you pop() instead of removing the element. This lets us handle
/// nested tasks correctly.
/// </summary>
private int mInsertPoint = 0;
/// <summary>
/// Resets object to initial state.
/// </summary>
public void Clear() {
mItems.Clear();
mIndentLevel = mInsertPoint = 0;
}
/// <summary>
/// Adds a start record for a task.
/// </summary>
/// <param name="tag">Task tag.</param>
public void StartTask(string tag) {
TimedItem ti = new TimedItem(tag, mIndentLevel);
mItems.Insert(mInsertPoint, ti);
mIndentLevel++;
}
/// <summary>
/// Closes out a record. The tag must match the most recently started task.
/// </summary>
/// <param name="tag">Task tag.</param>
public void EndTask(string tag) {
TimedItem lastItem = mItems[mInsertPoint];
if (lastItem.mTag != tag) {
Debug.WriteLine("ERROR: tag mismatch: " + tag + " vs. " + lastItem.mTag);
Debug.Assert(false);
return;
}
lastItem.mEndWhen = DateTime.Now;
mIndentLevel--;
mInsertPoint++;
Debug.Assert(mIndentLevel >= 0);
}
/// <summary>
/// Prints the timing data into a log object.
/// </summary>
/// <param name="log">Output destination.</param>
/// <param name="msg">Header message.</param>
public void DumpTimes(string msg, DebugLog log) {
if (mItems.Count == 0) {
return;
}
if (!string.IsNullOrEmpty(msg)) {
log.LogI(msg);
}
StringBuilder sb = new StringBuilder();
int lastIndent = 0;
foreach (TimedItem ti in mItems) {
sb.Clear();
FormatItem(ti, ref lastIndent, sb);
log.LogI(sb.ToString());
}
//DateTime firstStart = mItems[0].mStartWhen;
//DateTime lastEnd = mItems[mItems.Count - 1].mEndWhen;
//log.LogI(" Total: " + (lastEnd - firstStart).TotalMilliseconds + " ms");
}
/// <summary>
/// Prints the timing data into the debug log.
/// </summary>
/// <param name="msg">Header message.</param>
public void DumpTimes(string msg) {
if (mItems.Count == 0) {
return;
}
if (!string.IsNullOrEmpty(msg)) {
Debug.WriteLine(msg);
}
StringBuilder sb = new StringBuilder();
int lastIndent = 0;
foreach (TimedItem ti in mItems) {
sb.Clear();
FormatItem(ti, ref lastIndent, sb);
Debug.WriteLine(sb.ToString());
}
}
/// <summary>
/// Prints the timing data into a string with newlines.
/// </summary>
/// <param name="log">Output destination.</param>
/// <param name="msg">Header message.</param>
public string DumpToString(string msg) {
if (mItems.Count == 0) {
return msg;
}
StringBuilder sb = new StringBuilder();
#if DEBUG
sb.Append("[NOTE: debug build -- assertions and extra checks are enabled]\r\n\r\n");
#endif
if (!string.IsNullOrEmpty(msg)) {
sb.Append(msg);
sb.Append("\r\n\r\n");
}
int lastIndent = 0;
foreach (TimedItem ti in mItems) {
FormatItem(ti, ref lastIndent, sb);
sb.Append("\r\n");
}
return sb.ToString();
}
/// <summary>
/// Formats the specified item, appending it to the StringBuilder.
/// </summary>
/// <param name="ti">Item to format.</param>
/// <param name="lastIndent">Previous indentation level.</param>
/// <param name="sb">StringBuilder to append to.</param>
private void FormatItem(TimedItem ti, ref int lastIndent, StringBuilder sb) {
for (int i = 0; i <= ti.mIndentLevel - 1; i++) {
sb.Append("| ");
}
if (lastIndent < ti.mIndentLevel) {
//sb.Append("/-");
sb.Append("/ ");
} else /*if (lastIndent == ti.mIndentLevel)*/ {
sb.Append("| ");
}
sb.Append(ti.mTag);
sb.Append(": ");
sb.Append((ti.mEndWhen - ti.mStartWhen).TotalMilliseconds.ToString());
sb.Append(" ms");
lastIndent = ti.mIndentLevel;
}
}
}

241
CommonUtil/TextUtil.cs Normal file
View File

@ -0,0 +1,241 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace CommonUtil {
/// <summary>
/// Text utility functions.
/// </summary>
public static class TextUtil {
// 100 spaces, useful when padding things out.
private const string SPACES = " " +
" ";
private static readonly char[] CSV_ESCAPE_CHARS = { ',', '"' };
private const string NonPrintableAsciiPattern = @"[^\u0020-\u007e]";
private static Regex sNonPrintableAsciiRegex = new Regex(NonPrintableAsciiPattern);
/// <summary>
/// Converts a string to an ASCII-only string, replacing iso-latin characters
/// with their ASCII equivalents. This may change the length of the string.
///
/// For example, "¿Dónde está Über bären?" becomes "Donde esta Uber baren?".
/// </summary>
/// <param name="inString"></param>
/// <returns>Converted string.</returns>
public static string LatinToAscii(string inString) {
// https://stackoverflow.com/questions/140422/
var newStringBuilder = new StringBuilder();
newStringBuilder.Append(inString.Normalize(NormalizationForm.FormKD)
.Where(x => x < 128)
.ToArray());
return newStringBuilder.ToString();
// Alternatively?
// System.Text.Encoding.ASCII.GetString(
// System.Text.Encoding.GetEncoding(1251).GetBytes(text))
}
/// <summary>
/// Returns true if the value is valid high- or low-ASCII.
/// </summary>
/// <param name="val">Value to test.</param>
/// <returns>True if val is valid ASCII.</returns>
public static bool IsHiLoAscii(int val) {
return (val >= 0x20 && val < 0x7f) || (val >= 0xa0 && val < 0xff);
}
/// <summary>
/// Determines whether the character is printable ASCII.
/// </summary>
/// <param name="ch">Character to evaluate.</param>
/// <returns>True if the character is printable ASCII.</returns>
public static bool IsPrintableAscii(char ch) {
return ch >= 0x20 && ch < 0x7f;
}
/// <summary>
/// Determines whether the string has nothing but printable ASCII characters in it.
/// </summary>
/// <param name="str">String to evaluate.</param>
/// <returns>True if all characters are printable ASCII.</returns>
public static bool IsPrintableAscii(string str) {
// Linq version: return str.Any(c => c < 0x20 || c > 0x7e);
MatchCollection matches = sNonPrintableAsciiRegex.Matches(str);
return matches.Count == 0;
}
/// <summary>
/// Converts high-ASCII bytes to a string.
/// </summary>
/// <param name="data">Array of bytes with ASCII data.</param>
/// <param name="offset">Start offset.</param>
/// <param name="length">String length.</param>
/// <returns>Converted string.</returns>
public static string HighAsciiToString(byte[] data, int offset, int length) {
StringBuilder sb = new StringBuilder(length);
for (int i = offset; i < offset + length; i++) {
sb.Append((char)(data[i] & 0x7f));
}
return sb.ToString();
}
/// <summary>
/// Trims whitespace off the end of a StringBuilder.
/// </summary>
/// <param name="sb">StringBuilder reference.</param>
public static void TrimEnd(StringBuilder sb) {
const string WSPC = " \t\r\n";
int len = sb.Length;
while (WSPC.IndexOf(sb[len - 1]) >= 0) {
len--;
}
sb.Length = len;
}
/// <summary>
/// Replaces all occurrences of a string with another string, but only if they appear
/// outside quoted text. Useful for replacing structural bits of a JSON string without
/// messing with the quoted items. Assumes that quoted quotes (backslash-quote) only
/// appear inside quoted text.
/// </summary>
/// <param name="inStr"></param>
/// <param name="findStr"></param>
/// <param name="repStr"></param>
public static string NonQuoteReplace(string inStr, string findStr, string repStr) {
// There's probably a better way to do this...
StringBuilder sb = new StringBuilder(inStr.Length + inStr.Length / 20);
int cmpLen = findStr.Length;
bool findStrQuote = findStr.Contains('\"'); // find/rep str switches to in-quote
Debug.Assert(findStrQuote == repStr.Contains('\"'));
bool inQuote = false;
for (int i = 0; i < inStr.Length; i++) {
char ch = inStr[i];
if (inQuote) {
// Check to see if the double-quote is quoted. It's safe to back up
// one because we don't start in-quote.
if (ch == '\"' && inStr[i-1] != '\\') {
inQuote = false;
} else {
// in quoted text, keep going
}
sb.Append(ch);
} else {
if (string.Compare(inStr, i, findStr, 0, cmpLen) == 0) {
sb.Append(repStr);
i += cmpLen - 1;
inQuote = findStrQuote;
} else {
if (ch == '"') {
// There are no quoted-quotes outside of quotes, so we don't need
// to check for a '\'.
inQuote = true;
}
sb.Append(ch);
}
}
}
return sb.ToString();
}
/// <summary>
/// Pads a string with trailing spaces so that the total length of the line, including
/// previous contents, is the length specified. One trailing space will be added even
/// if the string's length is >= toLen.
///
/// You must begin with an empty StringBuilder at the start of each line.
/// </summary>
/// <param name="sb">StringBuilder to append to.</param>
/// <param name="str">String to add.</param>
/// <param name="toLen">Total line width to pad to.</param>
public static void AppendPaddedString(StringBuilder sb, string str, int toLen) {
if (str == null) {
str = string.Empty;
}
int newLen = sb.Length + str.Length;
if (newLen >= toLen) {
sb.Append(str);
sb.Append(' ');
} else {
sb.Append(str);
// would be nice to avoid this allocation/copy
sb.Append(SPACES.Substring(0, toLen - newLen));
}
}
/// <summary>
/// Escapes a string for CSV.
/// </summary>
/// <param name="str">String to process.</param>
/// <returns>Escaped string, or an empty string if the input was null.</returns>
public static string EscapeCSV(string str) {
if (str == null) {
return string.Empty;
}
bool needQuote = (str.IndexOfAny(CSV_ESCAPE_CHARS) >= 0);
if (needQuote) {
return '"' + str.Replace("\"", "\"\"") + '"';
} else {
return str;
}
}
/// <summary>
/// Serializes an integer array into a string.
/// </summary>
/// <param name="values">Array to serialize.</param>
/// <returns>Serialized data.</returns>
public static string SerializeIntArray(int[] values) {
StringBuilder sb = new StringBuilder(64);
sb.Append("int[]");
for (int i = 0; i < values.Length; i++) {
sb.Append(',');
sb.Append(values[i]);
}
return sb.ToString();
}
/// <summary>
/// Deserializes an integer array from a string.
/// </summary>
/// <param name="cereal"></param>
/// <returns></returns>
public static int[] DeserializeIntArray(string cereal) {
string[] splitted = cereal.Split(',');
if (splitted.Length == 0) {
throw new Exception("Bad serialized int[]");
}
if (splitted[0] != "int[]") {
throw new Exception("Bad serialized int[], started with " + splitted[0]);
}
int[] arr = new int[splitted.Length - 1];
try {
for (int i = 1; i < splitted.Length; i++) {
arr[i - 1] = int.Parse(splitted[i]);
}
} catch (Exception ex) {
throw new Exception("Bad serialized int[]: " + ex.Message);
}
return arr;
}
}
}

511
CommonUtil/TypedRangeSet.cs Normal file
View File

@ -0,0 +1,511 @@
/*
* Copyright 2018 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 System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace CommonUtil {
/// <summary>
/// Compact representation of a set of typed integers that tend to be adjacent.
/// We expect there to be relatively few different types of things.
///
/// <para>The default enumeration is a series of integers, not a series of ranges. Use
/// RangeListIterator to get the latter.</para>
///
/// <para>Most operations operate in log(N) time, where N is the number of
/// regions.</para>
/// </summary>
public class TypedRangeSet : IEnumerable<TypedRangeSet.Tuple> {
/// <summary>
/// List of ranges, in sorted order.
/// </summary>
private List<TypedRange> mRangeList = new List<TypedRange>();
/// <summary>
/// Number of values in the set.
/// </summary>
public int Count { get; private set; }
/// <summary>
/// Returns the number of Range elements in the list.
/// </summary>
public int RangeCount { get { return mRangeList.Count; } }
/// <summary>
/// Represents a contiguous range of values.
/// </summary>
public struct TypedRange {
/// <summary>
/// Lowest value (inclusive).
/// </summary>
public int Low { get; set; }
/// <summary>
/// Highest value (inclusive).
/// </summary>
public int High { get; set; }
/// <summary>
/// Value type in this range.
/// </summary>
public int Type { get; set; }
public TypedRange(int low, int high, int type) {
Debug.Assert(low <= high);
Low = low;
High = high;
Type = type;
}
public bool Contains(int val) {
return (val >= Low && val <= High);
}
}
/// <summary>
/// Value + type pair. Returned from foreach enumerator.
/// </summary>
public struct Tuple {
public int Value;
public int Type;
public Tuple(int value, int type) {
Value = value;
Type = type;
}
public static bool operator ==(Tuple a, Tuple b) {
return a.Value == b.Value && a.Type == b.Type;
}
public static bool operator !=(Tuple a, Tuple b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is Tuple && this == (Tuple)obj;
}
public override int GetHashCode() {
return Value ^ Type;
}
public override string ToString() {
return Value + " (" + Type + ")";
}
}
/// <summary>
/// Iterator definition.
/// </summary>
private class TypedRangeSetIterator : IEnumerator<Tuple> {
/// <summary>
/// The TypedRangeSet we're iterating over.
/// </summary>
private TypedRangeSet mSet;
// Index of current Range element in mSet.mRangeList.
private int mListIndex = -1;
// Current range, extracted from mRangeList.
private TypedRange mCurrentRange;
// Current value in mCurrentRange.
private int mCurrentVal;
/// <summary>
/// Constructor.
/// </summary>
/// <param name="set">TypedRangeSet to iterate over.</param>
public TypedRangeSetIterator(TypedRangeSet set) {
mSet = set;
Reset();
}
// IEnumerator: current element
public object Current {
get {
if (mListIndex < 0) {
// not started
return null;
}
return new Tuple(mCurrentVal, mCurrentRange.Type);
}
}
// IEnumerator<Tuple>
Tuple IEnumerator<Tuple>.Current {
get {
return (Tuple)Current;
}
}
/// <summary>
/// Puts the next range in the list in mCurrentRange.
/// </summary>
/// <returns>True on success, false if we reached the end of the list.</returns>
private bool GetNextRange() {
mListIndex++; // increments to 0 on first invocation
if (mListIndex == mSet.mRangeList.Count) {
// no more ranges
return false;
}
mCurrentRange = mSet.mRangeList[mListIndex];
mCurrentVal = mCurrentRange.Low;
return true;
}
// IEnumerator: move to the next element, returning false if there isn't one
public bool MoveNext() {
if (mListIndex < 0) {
// just started
return GetNextRange();
} else {
// iterating within range object
mCurrentVal++;
if (mCurrentVal > mCurrentRange.High) {
// finished with this one, move on to the next
return GetNextRange();
} else {
return true;
}
}
}
// IEnumerator: reset state
public void Reset() {
mListIndex = -1;
}
// IEnumerator<Tuple>
public void Dispose() {
mSet = null;
}
}
/// <summary>
/// Constructor. Creates an empty set.
/// </summary>
public TypedRangeSet() {
Count = 0;
}
/// <summary>
/// Returns an enumerator that iterates through the range list, returning Range objects.
/// </summary>
public IEnumerator<TypedRange> RangeListIterator {
get { return mRangeList.GetEnumerator(); }
}
/// <summary>
/// Removes all values from the set.
/// </summary>
public void Clear() {
mRangeList.Clear();
Count = 0;
}
// IEnumerable: get an enumerator instance that returns integer values
public IEnumerator GetEnumerator() {
return new TypedRangeSetIterator(this);
}
// IEnumerable<Tuple>
IEnumerator<Tuple> IEnumerable<Tuple>.GetEnumerator() {
return (IEnumerator<Tuple>)GetEnumerator();
}
/// <summary>
/// Finds the range that contains "val", or an appropriate place in the list to
/// insert a new range.
/// </summary>
/// <param name="val">Value to find.</param>
/// <returns>The index of the matching element, or a negative value indicating
/// the index to insert at. 2C doesn't support negative 0, so the insertion
/// index will be incremented before negation.</returns>
private int FindValue(int val) {
int low = 0;
int high = mRangeList.Count - 1;
while (low <= high) {
int mid = (low + high) / 2;
TypedRange midRange = mRangeList[mid];
if (midRange.Contains(val)) {
// found it
return mid;
} else if (val < midRange.Low) {
// too big, move the high end in
high = mid - 1;
} else if (val > midRange.High) {
// too small, move the low end in
low = mid + 1;
} else {
// WTF... list not sorted?
throw new Exception("Bad binary search");
}
}
// Not found, insert before "low".
return -(low + 1);
}
/// <summary>
/// Determines whether val is a member of the set.
/// </summary>
/// <param name="val">Value to check.</param>
/// <returns>True if the value is a member of the set.</returns>
public bool Contains(int val) {
return (FindValue(val) >= 0);
}
/// <summary>
/// Gets the type of the specified value.
/// </summary>
/// <param name="val">Value to query.</param>
/// <param name="type">Receives the type, or -1 if the value is not in the set.</param>
/// <returns>True if the value is in the set.</returns>
public bool GetType(int val, out int type) {
int listIndex = FindValue(val);
if (listIndex >= 0) {
type = mRangeList[listIndex].Type;
return true;
} else {
type = -1;
return false;
}
}
/// <summary>
/// Adds or changes a value to the set. If the value is already present and has
/// a matching type, nothing changes.
/// </summary>
/// <param name="val">Value to add.</param>
/// <param name="type">Value's type.</param>
public void Add(int val, int type) {
int listIndex = FindValue(val);
if (listIndex >= 0) {
// Value is present in set, check type.
if (mRangeList[listIndex].Type == type) {
// It's a match, do nothing.
return;
}
// Wrong type. Remove previous entry, then fall through to add new.
Remove(val);
listIndex = FindValue(val); // get insertion point
}
Count++;
if (mRangeList.Count == 0) {
// Empty list, skip the gymnastics.
mRangeList.Add(new TypedRange(val, val, type));
return;
}
// Negate and decrement to get insertion index. This value may == Count if
// the value is higher than all current members.
listIndex = -listIndex - 1;
if (listIndex > 0 && mRangeList[listIndex - 1].High == val - 1 &&
mRangeList[listIndex - 1].Type == type) {
// Expand prior range. Check to see if it blends into next as well.
if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1 &&
mRangeList[listIndex].Type == type) {
// Combine ranges.
TypedRange prior = mRangeList[listIndex - 1];
TypedRange next = mRangeList[listIndex];
Debug.Assert(prior.High + 2 == next.Low);
prior.High = next.High;
mRangeList[listIndex - 1] = prior;
mRangeList.RemoveAt(listIndex);
} else {
// Nope, just expand the prior range.
TypedRange prior = mRangeList[listIndex - 1];
Debug.Assert(prior.High == val - 1);
prior.High = val;
mRangeList[listIndex - 1] = prior;
}
} else if (listIndex < mRangeList.Count && mRangeList[listIndex].Low == val + 1 &&
mRangeList[listIndex].Type == type) {
// Expand next range.
TypedRange next = mRangeList[listIndex];
Debug.Assert(next.Low == val + 1);
next.Low = val;
mRangeList[listIndex] = next;
} else {
// Nothing adjacent, add a new single-entry element.
mRangeList.Insert(listIndex, new TypedRange(val, val, type));
}
}
/// <summary>
/// Adds a range of contiguous values to the set.
/// </summary>
/// <param name="low">Lowest value (inclusive).</param>
/// <param name="high">Highest value (inclusive).</param>
/// <param name="high">Value type.</param>
public void AddRange(int low, int high, int type) {
// There's probably some very efficient way to do this. Keeping it simple for now.
for (int i = low; i <= high; i++) {
Add(i, type);
}
}
/// <summary>
/// Removes a value from the set. If the value is not present, nothing changes.
/// </summary>
/// <param name="val">Value to remove.</param>
public void Remove(int val) {
int listIndex = FindValue(val);
if (listIndex < 0) {
// not found
return;
}
Count--;
TypedRange rng = mRangeList[listIndex];
if (rng.Low == val && rng.High == val) {
// Single-value range. Remove.
mRangeList.RemoveAt(listIndex);
} else if (rng.Low == val) {
// We're at the low end, reduce range.
rng.Low = val + 1;
mRangeList[listIndex] = rng;
} else if (rng.High == val) {
// We're at the high end, reduce range.
rng.High = val - 1;
mRangeList[listIndex] = rng;
} else {
// We're in the middle, split the range.
TypedRange next = new TypedRange(val + 1, rng.High, rng.Type);
rng.High = val - 1;
mRangeList[listIndex] = rng;
mRangeList.Insert(listIndex + 1, next);
}
}
/// <summary>
/// Internal test function.
/// </summary>
private static bool CheckTypedRangeSet(TypedRangeSet set, int expectedRanges,
Tuple[] expected) {
if (set.RangeCount != expectedRanges) {
Debug.WriteLine("Expected " + expectedRanges + " ranges, got " +
set.RangeCount);
return false;
}
// Compare actual vs. expected. If we have more actual than expected we'll
// throw on the array access.
int expIndex = 0;
foreach (TypedRangeSet.Tuple val in set) {
if (val != expected[expIndex]) {
Debug.WriteLine("Expected " + expected[expIndex] + ", got " + val);
return false;
}
expIndex++;
}
// See if we have more expected than actual.
if (expIndex != expected.Length) {
Debug.WriteLine("Expected " + expected.Length + " elements, found " + expIndex);
return false;
}
// The count is maintained separately, so check it.
if (set.Count != expected.Length) {
Debug.WriteLine("Expected Count=" + expected.Length + ", got " + set.Count);
return false;
}
return true;
}
/// <summary>
/// Executes unit tests.
/// </summary>
/// <returns>True if all goes well.</returns>
public static bool Test() {
bool result = true;
TypedRangeSet one = new TypedRangeSet();
one.Add(7, 100);
one.Add(5, 100);
one.Add(3, 100);
one.Add(9, 100);
one.Add(7, 100);
one.Add(8, 100);
one.Add(2, 100);
one.Add(4, 100);
result &= CheckTypedRangeSet(one, 2, new Tuple[] {
new Tuple(2, 100),
new Tuple(3, 100),
new Tuple(4, 100),
new Tuple(5, 100),
new Tuple(7, 100),
new Tuple(8, 100),
new Tuple(9, 100) });
one.Remove(2);
one.Remove(9);
one.Remove(4);
result &= CheckTypedRangeSet(one, 3, new Tuple[] {
new Tuple(3, 100),
new Tuple(5, 100),
new Tuple(7, 100),
new Tuple(8, 100) });
one.Clear();
one.Add(1, 200);
one.Add(3, 100);
one.Add(7, 100);
one.Add(5, 100);
one.Add(9, 100);
one.Add(6, 100);
one.Add(8, 100);
one.Add(6, 200);
one.Add(2, 200);
one.Add(4, 300);
one.Add(4, 100);
result &= CheckTypedRangeSet(one, 4, new Tuple[] {
new Tuple(1, 200),
new Tuple(2, 200),
new Tuple(3, 100),
new Tuple(4, 100),
new Tuple(5, 100),
new Tuple(6, 200),
new Tuple(7, 100),
new Tuple(8, 100),
new Tuple(9, 100) });
one.Add(6, 100);
result &= CheckTypedRangeSet(one, 2, new Tuple[] {
new Tuple(1, 200),
new Tuple(2, 200),
new Tuple(3, 100),
new Tuple(4, 100),
new Tuple(5, 100),
new Tuple(6, 100),
new Tuple(7, 100),
new Tuple(8, 100),
new Tuple(9, 100) });
Debug.WriteLine("TypedRangeSet: test complete (ok=" + result + ")");
return result;
}
}
}

192
CommonUtil/Version.cs Normal file
View File

@ -0,0 +1,192 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
namespace CommonUtil {
/// <summary>
/// Version number container. Instances are immutable.
///
/// See https://semver.org/ for explanation of system.
/// </summary>
public struct Version {
// Must be in ascending order, e.g. Alpha release comes before Beta.
public enum PreRelType { Dev, Alpha, Beta, Final };
/// <summary>
/// Major version number.
/// </summary>
public int Major { get; private set; }
/// <summary>
/// Minor version number.
/// </summary>
public int Minor { get; private set; }
/// <summary>
/// Bug fix release number.
/// </summary>
public int Patch { get; private set; }
/// <summary>
/// Software grade, for pre-release versions.
/// </summary>
public PreRelType PreReleaseType { get; private set; }
/// <summary>
/// Pre-release version.
/// </summary>
public int PreRelease { get; private set; }
/// <summary>
/// Version instance to use when no version information is available. This will
/// always compare as less than a "real" version.
/// </summary>
public static readonly Version NO_VERSION = new Version(-1, -1, -1);
/// <summary>
/// Shortcut for comparing vs. NO_VERSION.
/// </summary>
public bool IsValid {
get {
return this != NO_VERSION;
}
}
public Version(int major, int minor) :
this(major, minor, 0, PreRelType.Final, 0) { }
public Version(int major, int minor, int patch) :
this(major, minor, patch, PreRelType.Final, 0) { }
public Version(int major, int minor, int patch, PreRelType preRelType, int preRel) {
Debug.Assert(preRelType != PreRelType.Final || preRel == 0);
Major = major;
Minor = minor;
Patch = patch;
PreReleaseType = preRelType;
PreRelease = preRel;
}
/// <summary>
/// Attempts to parse the argument into version components.
/// </summary>
/// <param name="str">Version string.</param>
/// <returns>New Version object, or NO_VERSION on parsing failure.</returns>
public static Version Parse(string str) {
try {
int major, minor, patch;
major = minor = patch = 0;
string[] parts = str.Split(new char[] { '.', '-' });
major = int.Parse(parts[0]);
if (parts.Length > 1) {
minor = int.Parse(parts[1]);
}
if (parts.Length > 2) {
patch = int.Parse(parts[2]);
}
// parse the preRel thing someday
return new Version(major, minor, patch);
} catch (Exception ex) {
Debug.WriteLine("Version parse failed: '" + str + "': " + ex.Message);
return NO_VERSION;
}
}
public static bool operator ==(Version a, Version b) {
return a.Major == b.Major && a.Minor == b.Minor && a.Patch == b.Patch &&
a.PreReleaseType == b.PreReleaseType && a.PreRelease == b.PreRelease;
}
public static bool operator !=(Version a, Version b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is Version && this == (Version)obj;
}
public override int GetHashCode() {
return Major * 10000 + Minor * 1000 + Patch * 100 +
(int)PreReleaseType * 10 + PreRelease;
}
public static bool operator <(Version a, Version b) {
if (a.Major != b.Major) {
return a.Major < b.Major;
}
if (a.Minor != b.Minor) {
return a.Minor < b.Minor;
}
if (a.Patch != b.Patch) {
return a.Patch < b.Patch;
}
if (a.PreReleaseType != b.PreReleaseType) {
return (int)a.PreReleaseType < (int)b.PreReleaseType;
}
if (a.PreRelease != b.PreRelease) {
return a.PreRelease < b.PreRelease;
}
Debug.Assert(a == b);
return false;
}
public static bool operator >(Version a, Version b) {
return b < a;
}
public static bool operator <=(Version a, Version b) {
return a == b || a < b;
}
public static bool operator >=(Version a, Version b) {
return a == b || a > b;
}
public override string ToString() {
if (this == NO_VERSION) {
return Properties.Resources.NO_VERSION;
} else if (PreReleaseType == PreRelType.Final) {
return string.Format("{0}.{1}.{2}", Major, Minor, Patch);
} else {
return string.Format("{0}.{1}.{2}-{3}{4}", Major, Minor, Patch,
PreReleaseType.ToString().ToLower(), PreRelease);
}
}
/// <summary>
/// Simple unit test.
/// </summary>
public static bool Test() {
bool ok = true;
Version checkVers = new Version(1, 2, 3, PreRelType.Beta, 4);
Version sameVers = new Version(1, 2, 3, PreRelType.Beta, 4);
ok &= (checkVers == sameVers);
ok &= (checkVers <= sameVers);
ok &= (checkVers >= sameVers);
ok &= (!(checkVers < sameVers));
ok &= (!(checkVers > sameVers));
ok &= (checkVers != new Version(1, 2, 3));
ok &= (checkVers < new Version(1, 2, 3));
ok &= (checkVers > new Version(1, 2, 3, PreRelType.Beta, 3));
ok &= (checkVers < new Version(2, 0));
ok &= (checkVers > new Version(1, 2, 2));
ok &= (checkVers < new Version(1, 3, 1));
Debug.WriteLine("Version struct test complete (ok=" + ok + ")");
return ok;
}
}
}

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{08EC328D-078E-4236-B574-BE6B3FD85915}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>CommonWinForms</RootNamespace>
<AssemblyName>CommonWinForms</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG;BUILD_FOR_WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE;BUILD_FOR_WINDOWS</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="NativeMethods.cs" />
<Compile Include="WinFormsExtensions.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -0,0 +1,85 @@
/*
* Copyright 2018 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 System;
using System.Runtime.InteropServices;
using System.Windows.Forms;
#if BUILD_FOR_WINDOWS // I don't know if other platforms will emulate the LVITEM stuff
namespace CommonWinForms {
// From https://stackoverflow.com/questions/1019388/
/// <summary>
/// Unpleasant hackery to make "select all" faster. With 500K items it takes about 24
/// seconds to select everything individually, and there isn't a better way. With this
/// it only takes a few milliseconds.
/// </summary>
public class NativeMethods {
private const int LVM_FIRST = 0x1000;
private const int LVM_SETITEMSTATE = LVM_FIRST + 43;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public struct LVITEM {
public int mask;
public int iItem;
public int iSubItem;
public int state;
public int stateMask;
[MarshalAs(UnmanagedType.LPTStr)]
public string pszText;
public int cchTextMax;
public int iImage;
public IntPtr lParam;
public int iIndent;
public int iGroupId;
public int cColumns;
public IntPtr puColumns;
};
[DllImport("user32.dll", EntryPoint = "SendMessage", CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageLVItem(IntPtr hWnd, int msg, int wParam, ref LVITEM lvi);
/// <summary>
/// Select all rows on the given listview
/// </summary>
/// <param name="list">The listview whose items are to be selected</param>
public static void SelectAllItems(ListView list) {
NativeMethods.SetItemState(list, -1, 2, 2);
}
/// <summary>
/// Deselect all rows on the given listview
/// </summary>
/// <param name="list">The listview whose items are to be deselected</param>
public static void DeselectAllItems(ListView list) {
NativeMethods.SetItemState(list, -1, 2, 0);
}
/// <summary>
/// Set the item state on the given item
/// </summary>
/// <param name="list">The listview whose item's state is to be changed</param>
/// <param name="itemIndex">The index of the item to be changed</param>
/// <param name="mask">Which bits of the value are to be set?</param>
/// <param name="value">The value to be set</param>
public static void SetItemState(ListView list, int itemIndex, int mask, int value) {
LVITEM lvItem = new LVITEM();
lvItem.stateMask = mask;
lvItem.state = value;
SendMessageLVItem(list.Handle, LVM_SETITEMSTATE, itemIndex, ref lvItem);
}
}
}
#endif //BUILD_FOR_WINDOWS

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("CommonWinForms")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CommonWinForms")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("08ec328d-078e-4236-b574-be6b3fd85915")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,121 @@
/*
* Copyright 2018 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 System;
using System.Drawing;
using System.Windows.Forms;
namespace CommonWinForms {
/// <summary>
/// Overload RichTextBox.AppendText() with a version that takes a color as an argument.
///
/// From https://stackoverflow.com/a/1926822/294248
/// </summary>
public static class RichTextBoxExtensions {
public static void AppendText(this RichTextBox box, string text, Color color) {
box.SelectionStart = box.TextLength;
box.SelectionLength = 0;
box.SelectionColor = color;
box.AppendText(text);
box.SelectionColor = box.ForeColor;
}
}
/// <summary>
/// Add functions to select and deselect all items.
/// </summary>
public static class ListViewExtensions {
/// <summary>
/// Selects all items in the list view.
/// </summary>
public static void SelectAll(this ListView listView) {
// Neither I nor the Internet can figure out how to do this efficiently for
// large lists without P/Invoke interop. With 554253 lines, it takes 24.3 seconds
// to select each item individually, but only 4 milliseconds to do it through an
// LVITEM. The latter causes a single VirtualItemsSelectionRangeChanged event
// instead of 554K ItemSelectionChanged events.
//
// https://stackoverflow.com/questions/9039989/
// https://stackoverflow.com/questions/1019388/
#if BUILD_FOR_WINDOWS // defined in our project properties
NativeMethods.SelectAllItems(listView);
#else
try {
Application.UseWaitCursor = true;
Cursor.Current = Cursors.WaitCursor;
listView.BeginUpdate();
int max = listView.VirtualListSize;
for (int i = 0; i < max; i++) {
//codeListView.Items[i].Selected = true;
listView.SelectedIndices.Add(i);
}
} finally {
listView.EndUpdate();
Application.UseWaitCursor = false;
}
#endif
}
/// <summary>
/// Deselects all items in the list view.
/// </summary>
public static void DeselectAll(this ListView listView) {
// This is as fast as the native DeselectAllItems(), so just use it.
listView.SelectedIndices.Clear();
}
/// <summary>
/// Sets the double-buffered status of the list view.
/// </summary>
public static void SetDoubleBuffered(this ListView listView, bool enable) {
WinFormsUtil.SetDoubleBuffered(listView, enable);
}
/// <summary>
/// Determines whether the specified item is visible in the list view.
/// </summary>
public static bool IsItemVisible(this ListView listView, ListViewItem item) {
Rectangle lvBounds = listView.ClientRectangle;
if (listView.HeaderStyle != ColumnHeaderStyle.None) {
// Need to factor the header height out. There's no easy way to do that,
// but the header should be (almost) the same height as an item.
// https://stackoverflow.com/q/538906/294248
int headerHeight = item.Bounds.Height + 5; // 5 is magic, will probably break
lvBounds = new Rectangle(lvBounds.X, lvBounds.Y + headerHeight,
lvBounds.Width, lvBounds.Height - headerHeight);
}
//Console.WriteLine("IsVis LV: " + lvBounds + " IT: " +
// item.GetBounds(ItemBoundsPortion.Entire));
return lvBounds.IntersectsWith(item.GetBounds(ItemBoundsPortion.Entire));
}
}
public static class WinFormsUtil {
/// <summary>
/// Sets the "DoubleBuffered" property on a Control. For some reason the
/// property is defined as "protected", but I don't want to subclass a ListView
/// just so I can enable double-buffering.
/// </summary>
/// <param name="ctrl">Control to update.</param>
/// <param name="enable">New state.</param>
public static void SetDoubleBuffered(Control ctrl, bool enable) {
System.Reflection.PropertyInfo prop = ctrl.GetType().GetProperty("DoubleBuffered",
System.Reflection.BindingFlags.Instance |
System.Reflection.BindingFlags.NonPublic);
prop.SetValue(ctrl, enable, null);
}
}
}

BIN
ImageSrc/SourceGenIcon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 KiB

BIN
ImageSrc/SourceGenIcon.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

BIN
ImageSrc/left-arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

BIN
ImageSrc/left-arrow.xcf Normal file

Binary file not shown.

BIN
ImageSrc/right-arrow.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 B

6
MakeDist/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

103
MakeDist/CopyProgress.Designer.cs generated Normal file
View File

@ -0,0 +1,103 @@
/*
* Copyright 2018 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.
*/
namespace MakeDist {
partial class CopyProgress {
/// <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.progressRichTextBox = new System.Windows.Forms.RichTextBox();
this.cancelButton = new System.Windows.Forms.Button();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
this.SuspendLayout();
//
// progressRichTextBox
//
this.progressRichTextBox.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.progressRichTextBox.Location = new System.Drawing.Point(13, 13);
this.progressRichTextBox.Name = "progressRichTextBox";
this.progressRichTextBox.ReadOnly = true;
this.progressRichTextBox.Size = new System.Drawing.Size(459, 507);
this.progressRichTextBox.TabIndex = 0;
this.progressRichTextBox.Text = "";
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(397, 526);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 1;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
//
// backgroundWorker1
//
this.backgroundWorker1.WorkerReportsProgress = true;
this.backgroundWorker1.WorkerSupportsCancellation = true;
this.backgroundWorker1.DoWork += new System.ComponentModel.DoWorkEventHandler(this.backgroundWorker1_DoWork);
this.backgroundWorker1.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.backgroundWorker1_ProgressChanged);
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
//
// CopyProgress
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(484, 561);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.progressRichTextBox);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "CopyProgress";
this.ShowInTaskbar = false;
this.Text = "CopyDistFiles";
this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.CopyProgress_FormClosing);
this.Load += new System.EventHandler(this.CopyProgress_Load);
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.RichTextBox progressRichTextBox;
private System.Windows.Forms.Button cancelButton;
private System.ComponentModel.BackgroundWorker backgroundWorker1;
}
}

134
MakeDist/CopyProgress.cs Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright 2018 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 System;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using CommonWinForms;
namespace MakeDist {
public partial class CopyProgress : Form {
/// <summary>
/// Progress message, with colorful text. This is generated by the worker thread and
/// passed to the UI thread.
/// </summary>
public class ProgressMessage {
public string Text { get; private set; }
public Color Color { get; private set; }
public bool HasColor { get { return Color.A != 0; } }
public ProgressMessage(string msg) : this(msg, Color.FromArgb(0, 0, 0, 0)) { }
public ProgressMessage(string msg, Color color) {
Text = msg;
Color = color;
}
}
private bool mClosedWhileRunning;
// Copy parameters.
private FileCopier.BuildType mBuildType;
private bool mCopyTestFiles;
public CopyProgress(FileCopier.BuildType buildType, bool copyTestFiles) {
InitializeComponent();
this.Size = new Size(1200, 600);
mBuildType = buildType;
mCopyTestFiles = copyTestFiles;
}
private void CopyProgress_Load(object sender, EventArgs e) {
backgroundWorker1.RunWorkerAsync();
}
private void CopyProgress_FormClosing(object sender, FormClosingEventArgs e) {
if (backgroundWorker1.IsBusy) {
backgroundWorker1.CancelAsync();
DialogResult = DialogResult.Cancel;
// First close attempt is converted to a cancel.
if (!mClosedWhileRunning) {
e.Cancel = true;
mClosedWhileRunning = true;
}
} else {
DialogResult = DialogResult.OK;
}
}
private void cancelButton_Click(object sender, EventArgs e) {
if (backgroundWorker1.IsBusy) {
backgroundWorker1.CancelAsync();
} else {
Close();
}
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) {
BackgroundWorker worker = sender as BackgroundWorker;
FileCopier copier = new FileCopier(mBuildType, mCopyTestFiles);
e.Result = copier.CopyAllFiles(worker);
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) {
if (e.UserState is ProgressMessage) {
ProgressMessage msg = e.UserState as ProgressMessage;
if (msg.HasColor) {
progressRichTextBox.AppendText(msg.Text, msg.Color);
} else {
// plain foreground text color
progressRichTextBox.AppendText(msg.Text);
}
progressRichTextBox.SelectionStart = progressRichTextBox.Text.Length;
progressRichTextBox.ScrollToCaret();
} else {
if (!string.IsNullOrEmpty((string)e.UserState)) {
Debug.WriteLine("Weird progress: " + e.UserState);
}
}
}
private void backgroundWorker1_RunWorkerCompleted(object sender,
RunWorkerCompletedEventArgs e) {
if (e.Cancelled) {
Debug.WriteLine("Test halted -- user cancellation");
} else if (e.Error != null) {
// test harness shouldn't be throwing errors like this
Debug.WriteLine("Test failed: " + e.Error.ToString());
progressRichTextBox.AppendText("\r\n");
progressRichTextBox.AppendText(e.Error.ToString());
progressRichTextBox.SelectionStart = progressRichTextBox.Text.Length;
progressRichTextBox.ScrollToCaret();
} else {
bool ok = (bool)e.Result;
Debug.WriteLine("Tests complete, success=" + ok);
}
if (mClosedWhileRunning) {
Close();
}
cancelButton.Text = "Close";
}
}
}

123
MakeDist/CopyProgress.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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="backgroundWorker1.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
</root>

316
MakeDist/FileCopier.cs Normal file
View File

@ -0,0 +1,316 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
namespace MakeDist {
public class FileCopier {
private const string SOURCEGEN_DIRNAME = "SourceGen";
/// <summary>
/// Type of build to gather files for.
/// </summary>
public enum BuildType { Unknown, Release, Debug };
private enum SourceFileSpec {
Unknown = 0,
All,
List,
RegressionTests,
AsmSources,
NotBins,
}
private class CopySpec {
public string SourceDir { get; private set; }
public string DestDir { get; private set; }
public SourceFileSpec FileSpec { get; private set; }
public bool IsRecursive { get; private set; }
public string[] FileList { get; private set; }
public CopySpec(string srcDir, string dstDir, SourceFileSpec spec, bool recursive,
string[] fileList) {
SourceDir = srcDir;
DestDir = dstDir;
FileSpec = spec;
IsRecursive = recursive;
FileList = fileList;
}
}
private static CopySpec[] sMainSpec = {
new CopySpec(".", ".",
SourceFileSpec.List, false, new string[] { "README.md" }),
new CopySpec("Asm65/bin/{BUILD_TYPE}/netstandard2.0/", ".",
SourceFileSpec.List, false, new string[] { "Asm65.dll" }),
new CopySpec("CommonUtil/bin/{BUILD_TYPE}/netstandard2.0/", ".",
SourceFileSpec.List, false, new string[] { "CommonUtil.dll" }),
new CopySpec("CommonWinForms/bin/{BUILD_TYPE}/", ".",
SourceFileSpec.List, false, new string[] { "CommonWinForms.dll" }),
new CopySpec("PluginCommon/bin/{BUILD_TYPE}/netstandard2.0/", ".",
SourceFileSpec.List, false, new string[] { "PluginCommon.dll" }),
new CopySpec("SourceGen/bin/{BUILD_TYPE}/", ".",
SourceFileSpec.List, false, new string[] { "SourceGen.exe" }),
new CopySpec("SourceGen/RuntimeData", "RuntimeData",
SourceFileSpec.NotBins, true, null),
new CopySpec("SourceGen/Examples", "Examples",
SourceFileSpec.All, true, null),
};
private static CopySpec[] sTestSpec = {
new CopySpec("SourceGen/SGTestData", "SGTestData",
SourceFileSpec.RegressionTests, false, null),
new CopySpec("SourceGen/SGTestData", "SGTestData",
SourceFileSpec.List, false, new string[] { "README.md" }),
new CopySpec("SourceGen/SGTestData/Expected", "SGTestData/Expected",
SourceFileSpec.AsmSources, false, null),
new CopySpec("SourceGen/SGTestData/Source", "SGTestData/Source",
SourceFileSpec.AsmSources, false, null),
new CopySpec("SourceGen/SGTestData/FunkyProjects", "SGTestData/FunkyProjects",
SourceFileSpec.All, false, null),
};
private static string sBasePath;
// We want all of the regression test binaries, plus the .sym65, .dis65, and .cs,
// but nothing with an underscore in the part before the extension.
private const string TestCasePattern = @"^\d\d\d\d-[A-Za-z0-9-]+(\..*)?$";
private static Regex sTestCaseRegex = new Regex(TestCasePattern);
private BuildType mBuildType;
private bool mCopyTestFiles;
private BackgroundWorker mWorker;
public FileCopier(BuildType buildType, bool copyTestFiles) {
mBuildType = buildType;
mCopyTestFiles = copyTestFiles;
}
private void ReportProgress(string msg) {
mWorker.ReportProgress(0, new CopyProgress.ProgressMessage(msg + "\r\n"));
}
private void ReportProgress(string msg, Color color) {
mWorker.ReportProgress(0, new CopyProgress.ProgressMessage(msg + "\r\n", color));
}
private void ReportErrMsg(string msg) {
ReportProgress(msg, Color.Red);
}
/// <summary>
/// Main entry point.
/// </summary>
/// <param name="worker">Background task interface object.</param>
/// <returns>True on success.</returns>
public bool CopyAllFiles(BackgroundWorker worker) {
mWorker = worker;
ReportProgress("Preparing... build type is " + mBuildType + ", test files are " +
(mCopyTestFiles ? "" : "NOT ") + "included.");
string buildStr = mBuildType.ToString();
string basePath = FindBasePath();
Debug.Assert(basePath != null);
string distPath = Path.Combine(basePath, "DIST_" + buildStr);
// TODO(maybe): recursively delete distPath
if (!CopySpecList(sMainSpec, basePath, distPath, buildStr)) {
return false;
}
if (mCopyTestFiles) {
if (!CopySpecList(sTestSpec, basePath, distPath, buildStr)) {
return false;
}
}
return true;
}
private bool CopySpecList(CopySpec[] specList, string basePath, string distPath,
string buildStr) {
foreach (CopySpec cs in specList) {
string srcDir = Path.GetFullPath(Path.Combine(basePath,
cs.SourceDir.Replace("{BUILD_TYPE}", buildStr)));
string dstDir = Path.GetFullPath(Path.Combine(distPath, cs.DestDir));
ReportProgress("Scanning [" + cs.FileSpec + "] " + srcDir);
if (!CopyBySpec(srcDir, dstDir, cs.FileSpec, cs.FileList, cs.IsRecursive)) {
return false;
}
}
return true;
}
private bool CopyBySpec(string srcDir, string dstDir, SourceFileSpec sfspec,
string[] specFileList, bool isRecursive) {
if (!EnsureDirectoryExists(dstDir)) {
return false;
}
string[] fileList;
if (sfspec == SourceFileSpec.List) {
fileList = specFileList;
} else {
fileList = Directory.GetFiles(srcDir);
}
foreach (string str in fileList) {
// Spec list is filenames, GetFiles is paths; convert to simple filename.
string fileName = Path.GetFileName(str);
switch (sfspec) {
case SourceFileSpec.All:
case SourceFileSpec.List:
// keep all
break;
case SourceFileSpec.NotBins:
// Mostly this means "skip obj and bin dirs", which happens later.
// Rather than specify everything we do want, just omit this one thing.
if (fileName == "RuntimeData.csproj") {
continue;
}
break;
case SourceFileSpec.AsmSources:
if (!fileName.ToUpperInvariant().EndsWith(".S")) {
continue;
}
break;
case SourceFileSpec.RegressionTests:
MatchCollection matches = sTestCaseRegex.Matches(fileName);
if (matches.Count != 1) {
continue;
}
// Could probably do this with regex... but why.
if (fileName.StartsWith("1") && fileName.EndsWith(".dis65")) {
continue;
}
break;
default:
throw new Exception("Unsupported spec " + sfspec);
}
string srcPath = Path.Combine(srcDir, fileName);
string dstPath = Path.Combine(dstDir, fileName);
if (!CopyFile(srcPath, dstPath)) {
return false;
}
}
if (isRecursive) {
string[] dirList = Directory.GetDirectories(srcDir);
foreach (string str in dirList) {
string dirFileName = Path.GetFileName(str);
if (sfspec == SourceFileSpec.NotBins &&
(dirFileName == "obj" || dirFileName == "bin")) {
continue;
}
if (!CopyBySpec(Path.Combine(srcDir, dirFileName),
Path.Combine(dstDir, dirFileName),
sfspec, specFileList, isRecursive)) {
return false;
}
}
}
return true;
}
private bool EnsureDirectoryExists(string dirPath) {
if (Directory.Exists(dirPath)) {
return true;
}
if (File.Exists(dirPath)) {
ReportErrMsg("File exists and is not directory: " + dirPath);
return false;
}
try {
Directory.CreateDirectory(dirPath);
ReportProgress(" Created " + dirPath);
} catch (Exception ex) {
ReportErrMsg("Failed creating directory " + dirPath + ": " + ex.Message);
return false;
}
return true;
}
private bool CopyFile(string srcPath, string dstPath) {
// Poll cancel button.
if (mWorker.CancellationPending) {
ReportErrMsg("Cancel\r\n");
return false;
}
ReportProgress(" Copy " + srcPath + " --> " + dstPath);
try {
File.Copy(srcPath, dstPath, true);
} catch (Exception ex) {
ReportErrMsg("Failed: " + ex.Message);
return false;
}
return true;
}
/// <summary>
/// Returns the base directory of the 6502bench installation.
/// </summary>
/// <returns></returns>
private static string FindBasePath() {
if (sBasePath != null) {
return sBasePath;
}
string exeName = Process.GetCurrentProcess().MainModule.FileName;
string baseDir = Path.GetDirectoryName(exeName);
if (string.IsNullOrEmpty(baseDir)) {
return null;
}
string tryPath;
// Use the SourceGen directory as a sentinel.
tryPath = Path.Combine(baseDir, SOURCEGEN_DIRNAME);
if (Directory.Exists(tryPath)) {
sBasePath = Path.GetFullPath(tryPath);
return sBasePath;
}
string upThree = Path.GetDirectoryName(
Path.GetDirectoryName(Path.GetDirectoryName(baseDir)));
tryPath = Path.Combine(upThree, SOURCEGEN_DIRNAME);
if (Directory.Exists(tryPath)) {
sBasePath = Path.GetFullPath(upThree);
return sBasePath;
}
Debug.WriteLine("Unable to find RuntimeData dir near " + exeName);
return null;
}
}
}

162
MakeDist/MakeDist.Designer.cs generated Normal file
View File

@ -0,0 +1,162 @@
/*
* Copyright 2018 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.
*/
namespace MakeDist {
partial class MakeDist {
/// <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.descriptionLabel = new System.Windows.Forms.Label();
this.distributionTypeGroupBox = new System.Windows.Forms.GroupBox();
this.releaseDistRadio = new System.Windows.Forms.RadioButton();
this.debugDistRadio = new System.Windows.Forms.RadioButton();
this.includeTestsCheckBox = new System.Windows.Forms.CheckBox();
this.goButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.distributionTypeGroupBox.SuspendLayout();
this.SuspendLayout();
//
// descriptionLabel
//
this.descriptionLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.descriptionLabel.Location = new System.Drawing.Point(13, 13);
this.descriptionLabel.Name = "descriptionLabel";
this.descriptionLabel.Size = new System.Drawing.Size(371, 35);
this.descriptionLabel.TabIndex = 0;
this.descriptionLabel.Text = "This program gathers up all the files needed for a 6502bench distribution. A full" +
" debug or release build should be performed before running this.";
//
// distributionTypeGroupBox
//
this.distributionTypeGroupBox.Controls.Add(this.releaseDistRadio);
this.distributionTypeGroupBox.Controls.Add(this.debugDistRadio);
this.distributionTypeGroupBox.Location = new System.Drawing.Point(16, 51);
this.distributionTypeGroupBox.Name = "distributionTypeGroupBox";
this.distributionTypeGroupBox.Size = new System.Drawing.Size(120, 67);
this.distributionTypeGroupBox.TabIndex = 1;
this.distributionTypeGroupBox.TabStop = false;
this.distributionTypeGroupBox.Text = "Distribution Type";
//
// releaseDistRadio
//
this.releaseDistRadio.AutoSize = true;
this.releaseDistRadio.Location = new System.Drawing.Point(7, 20);
this.releaseDistRadio.Name = "releaseDistRadio";
this.releaseDistRadio.Size = new System.Drawing.Size(64, 17);
this.releaseDistRadio.TabIndex = 0;
this.releaseDistRadio.TabStop = true;
this.releaseDistRadio.Text = "Release";
this.releaseDistRadio.UseVisualStyleBackColor = true;
//
// debugDistRadio
//
this.debugDistRadio.AutoSize = true;
this.debugDistRadio.Location = new System.Drawing.Point(7, 43);
this.debugDistRadio.Name = "debugDistRadio";
this.debugDistRadio.Size = new System.Drawing.Size(57, 17);
this.debugDistRadio.TabIndex = 1;
this.debugDistRadio.TabStop = true;
this.debugDistRadio.Text = "Debug";
this.debugDistRadio.UseVisualStyleBackColor = true;
//
// includeTestsCheckBox
//
this.includeTestsCheckBox.AutoSize = true;
this.includeTestsCheckBox.Location = new System.Drawing.Point(16, 125);
this.includeTestsCheckBox.Name = "includeTestsCheckBox";
this.includeTestsCheckBox.Size = new System.Drawing.Size(153, 17);
this.includeTestsCheckBox.TabIndex = 2;
this.includeTestsCheckBox.Text = "Include regression test files";
this.includeTestsCheckBox.UseVisualStyleBackColor = true;
//
// goButton
//
this.goButton.Location = new System.Drawing.Point(192, 59);
this.goButton.Name = "goButton";
this.goButton.Size = new System.Drawing.Size(88, 47);
this.goButton.TabIndex = 3;
this.goButton.Text = "BUILD";
this.goButton.UseVisualStyleBackColor = true;
this.goButton.Click += new System.EventHandler(this.goButton_Click);
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(309, 120);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 4;
this.cancelButton.Text = "Close";
this.cancelButton.UseVisualStyleBackColor = true;
this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click);
//
// MakeDist
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(396, 155);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.goButton);
this.Controls.Add(this.includeTestsCheckBox);
this.Controls.Add(this.distributionTypeGroupBox);
this.Controls.Add(this.descriptionLabel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "MakeDist";
this.Text = "6502bench Distribution Maker";
this.Load += new System.EventHandler(this.MakeDist_Load);
this.distributionTypeGroupBox.ResumeLayout(false);
this.distributionTypeGroupBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label descriptionLabel;
private System.Windows.Forms.GroupBox distributionTypeGroupBox;
private System.Windows.Forms.RadioButton releaseDistRadio;
private System.Windows.Forms.RadioButton debugDistRadio;
private System.Windows.Forms.CheckBox includeTestsCheckBox;
private System.Windows.Forms.Button goButton;
private System.Windows.Forms.Button cancelButton;
}
}

47
MakeDist/MakeDist.cs Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright 2018 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 System;
using System.Windows.Forms;
namespace MakeDist {
public partial class MakeDist : Form {
public MakeDist() {
InitializeComponent();
}
private void MakeDist_Load(object sender, EventArgs e) {
releaseDistRadio.Checked = true;
}
private void goButton_Click(object sender, EventArgs e) {
FileCopier.BuildType buildType;
if (releaseDistRadio.Checked) {
buildType = FileCopier.BuildType.Release;
} else {
buildType = FileCopier.BuildType.Debug;
}
bool copyTestFiles = includeTestsCheckBox.Checked;
CopyProgress dlg = new CopyProgress(buildType, copyTestFiles);
dlg.ShowDialog();
dlg.Dispose();
}
private void cancelButton_Click(object sender, EventArgs e) {
Close();
}
}
}

99
MakeDist/MakeDist.csproj Normal file
View File

@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{2415F337-2CE2-42E0-A8A7-4127FEEC94C4}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>MakeDist</RootNamespace>
<AssemblyName>MakeDist</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="CopyProgress.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="CopyProgress.Designer.cs">
<DependentUpon>CopyProgress.cs</DependentUpon>
</Compile>
<Compile Include="FileCopier.cs" />
<Compile Include="MakeDist.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="MakeDist.Designer.cs">
<DependentUpon>MakeDist.cs</DependentUpon>
</Compile>
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="CopyProgress.resx">
<DependentUpon>CopyProgress.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="MakeDist.resx">
<DependentUpon>MakeDist.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
<SubType>Designer</SubType>
</EmbeddedResource>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\CommonWinForms\CommonWinForms.csproj">
<Project>{08ec328d-078e-4236-b574-be6b3fd85915}</Project>
<Name>CommonWinForms</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

120
MakeDist/MakeDist.resx Normal file
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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

31
MakeDist/Program.cs Normal file
View File

@ -0,0 +1,31 @@
/*
* Copyright 2018 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 System;
using System.Windows.Forms;
namespace MakeDist {
static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new MakeDist());
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MakeDist")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MakeDist")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("2415f337-2ce2-42e0-a8a7-4127feec94c4")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,62 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MakeDist.Properties {
/// <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 ((resourceMan == null)) {
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MakeDist.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,117 @@
<?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.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: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" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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>

26
MakeDist/Properties/Settings.Designer.cs generated Normal file
View File

@ -0,0 +1,26 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MakeDist.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default {
get {
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

134
PluginCommon/Interfaces.cs Normal file
View File

@ -0,0 +1,134 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
namespace PluginCommon {
/// <summary>
/// Script "plugins" must implement this interface.
/// </summary>
public interface IPlugin {
/// <summary>
/// Identification string. Contents are arbitrary, but should briefly identify the
/// purpose of the plugin, e.g. "Apple II ProDOS 8 MLI call handler". It may
/// contain version information, but should not be expected to be machine-readable.
/// </summary>
string Identifier { get; }
/// <summary>
/// Returns true if this plugin checks JSR/JSL for inline data.
/// </summary>
//bool HasInlineDataAnalyzer { get; }
/// <summary>
/// Initializes the plugin with an application reference and a buffer with file
/// data. Called before each analysis pass.
///
/// In the current implementation, the file data will be the same every time,
/// because plugins are discarded when a project is closed. However, this may
/// change if we add a descramble feature.
/// </summary>
/// <param name="appRef">Reference to application interface.</param>
/// <param name="fileData">65xx code and data.</param>
/// <param name="platSyms">Platform symbols, in no particular order.</param>
void Prepare(IApplication appRef, byte[] fileData, List<PlatSym> platSyms);
/// <summary>
/// Checks to see if code/data near a JSR instruction should be formatted.
///
/// The file data is guaranteed to hold the JSR (offset + 2).
/// </summary>
/// <param name="offset">Offset of the JSR instruction.</param>
/// <param name="noContinue">Set to true if the JSR doesn't actually return.</param>
void CheckJsr(int offset, out bool noContinue);
/// <summary>
/// Checks to see if code/data near a JSL instruction should be formatted.
///
/// The file data is guaranteed to hold the JSL (offset + 3).
/// </summary>
/// <param name="offset">Offset of the JSL instruction.</param>
/// <param name="noContinue">Set to true if the JSL doesn't actually return.</param>
void CheckJsl(int offset, out bool noContinue);
}
/// <summary>
/// Interfaces provided by the application for use by plugins.
/// </summary>
public interface IApplication {
/// <summary>
/// Sends a debug message to the application. This can be useful when debugging scripts.
/// (Use DEBUG > Show Analyzer Output to view it.)
/// </summary>
/// <param name="msg">Message to send.</param>
void DebugLog(string msg);
/// <summary>
/// Specifies operand formatting.
/// </summary>
/// <param name="offset">File offset of opcode.</param>
/// <param name="subType">Sub-type. Must be appropriate for NumericLE.</param>
/// <param name="label">Optional symbolic label.</param>
/// <returns>True if the change was made, false if it was rejected.</returns>
bool SetOperandFormat(int offset, DataSubType subType, string label);
/// <summary>
/// Formats file data as inline data.
/// </summary>
/// <param name="offset">File offset.</param>
/// <param name="length">Length of item.</param>
/// <param name="type">Type of item. Must be NumericLE, NumericBE, or Dense.</param>
/// <param name="subType">Sub-type. Must be appropriate for type.</param>
/// <param name="label">Optional symbolic label.</param>
/// <returns>True if the change was made, false if it was rejected.</returns>
bool SetInlineDataFormat(int offset, int length, DataType type,
DataSubType subType, string label);
// Might want to add:
// int AddressToOffset(int address) // returns 24-bit offset, or -1 if outside file
// int OffsetToAddress(int offset) // returns 24-bit address
// (although we could also just pass the address map in at Prepare() -- more efficient
// if this gets called frequently)
}
/// <summary>
/// Data format type.
/// </summary>
public enum DataType {
Unknown = 0,
NumericLE,
NumericBE,
String,
Dense,
Fill
}
/// <summary>
/// Data format sub-type.
/// </summary>
public enum DataSubType {
// No sub-type specified.
None = 0,
// For NumericLE/BE
Hex,
Decimal,
Binary,
Ascii,
Address,
Symbol
}
}

84
PluginCommon/PlatSym.cs Normal file
View File

@ -0,0 +1,84 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Text;
namespace PluginCommon {
/// <summary>
/// Symbols loaded from platform symbol files, for use in extension scripts.
///
/// Instances are immutable.
/// </summary>
[Serializable]
public class PlatSym {
public string Label { get; private set; }
public int Value { get; private set; }
public string Tag { get; private set; }
/// <summary>
/// Nullary constructor, for deserialization.
/// </summary>
private PlatSym() { }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="label">Symbol label.</param>
/// <param name="value">Symbol value.</param>
/// <param name="tag">Symbol group tag.</param>
public PlatSym(string label, int value, string tag) {
Label = label;
Value = value;
Tag = tag;
}
/// <summary>
/// Generates a dictionary of platform symbols, keyed by value. Only symbols with
/// a matching tag are included. If more than one symbol has the same value, only
/// one will be included; which one it will be is undefined.
/// </summary>
/// <param name="platSyms">List of platform symbols to select from.</param>
/// <param name="tag">Tag to match, or null to collect all symbols.</param>
/// <param name="appRef">Application reference, for debug log output.</param>
/// <returns></returns>
public static Dictionary<int, PlatSym> GenerateValueList(List<PlatSym> platSyms,
string tag, IApplication appRef) {
Dictionary<int, PlatSym> dict = new Dictionary<int, PlatSym>();
foreach (PlatSym ps in platSyms) {
if (tag == null || tag == ps.Tag) {
try {
dict.Add(ps.Value, ps);
} catch (ArgumentException) {
appRef.DebugLog("WARNING: GenerateValueList: multiple entries with " +
"value " + ps.Value.ToString("x4"));
}
}
}
if (dict.Count == 0) {
appRef.DebugLog("PlatSym: no symbols found for tag=" + tag);
}
return dict;
}
public override string ToString() {
return Label + "=" + Value.ToString("x4") + " [" + Tag + "]";
}
}
}

View File

@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\CommonUtil\CommonUtil.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,205 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Threading;
namespace PluginCommon {
/// <summary>
/// Manages loaded plugins, in the "remote" AppDomain.
/// </summary>
public sealed class PluginManager : MarshalByRefObject {
/// <summary>
/// Collection of instances of active plugins, keyed by script identifier. Other
/// plugin assemblies may be present in the AppDomain, but have not been identified
/// by the application as being of interest.
/// </summary>
private Dictionary<string, IPlugin> mActivePlugins = new Dictionary<string, IPlugin>();
/// <summary>
/// Reference to file data.
/// </summary>
private byte[] mFileData;
/// <summary>
/// Constructor, invoked from CreateInstanceAndUnwrap().
/// </summary>
public PluginManager() {
Debug.WriteLine("PluginManager ctor (id=" + AppDomain.CurrentDomain.Id + ")");
// Seems to require [SecurityCritical]
//Type lsc = Type.GetType("System.Runtime.Remoting.Lifetime.LifetimeServices");
//PropertyInfo prop = lsc.GetProperty("LeaseTime");
//prop.SetValue(null, TimeSpan.FromSeconds(30));
}
~PluginManager() {
Debug.WriteLine("~PluginManager (id=" + AppDomain.CurrentDomain.Id + ")");
}
/// <summary>
/// Sets the file data to use for all plugins.
///
/// The file data argument will be an AppDomain-local copy of the data, made by the
/// argument marshalling code. So plugins can scribble all over it without trashing
/// the original. We want to store it in PluginManager so we don't make a new copy
/// for each individual plugin.
/// </summary>
/// <param name="fileData">65xx code and data.</param>
public void SetFileData(byte[] fileData) {
mFileData = fileData;
}
/// <summary>
/// Tests simple round-trip communication. This may be called from an arbitrary thread.
/// </summary>
public int Ping(int val) {
Debug.WriteLine("PluginManager Ping tid=" + Thread.CurrentThread.ManagedThreadId +
" (id=" + AppDomain.CurrentDomain.Id + "): " + val);
return val + 1;
}
/// <summary>
/// Creates a plugin instance from a compiled assembly. Pass in the script identifier
/// for future lookups. If the plugin has already been instantiated, that object
/// will be returned.
/// </summary>
/// <param name="dllPath">Full path to compiled assembly.</param>
/// <param name="scriptIdent">Identifier to use in e.g. GetPlugin().</param>
/// <returns>Reference to plugin instance.</returns>
public IPlugin LoadPlugin(string dllPath, string scriptIdent) {
if (mActivePlugins.TryGetValue(dllPath, out IPlugin ip)) {
Debug.WriteLine("PM: returning cached plugin for " + dllPath);
return ip;
}
Assembly asm = Assembly.LoadFile(dllPath);
foreach (Type type in asm.GetExportedTypes()) {
// Using a System.Linq extension method.
if (type.IsClass && !type.IsAbstract &&
type.GetInterfaces().Contains(typeof(IPlugin))) {
ConstructorInfo ctor = type.GetConstructor(Type.EmptyTypes);
IPlugin iplugin = (IPlugin)ctor.Invoke(null);
Debug.WriteLine("PM created instance: " + iplugin);
mActivePlugins.Add(scriptIdent, iplugin);
return iplugin;
}
}
throw new Exception("No IPlugin class found in " + dllPath);
}
/// <summary>
/// Gets an instance of a previously-loaded plugin.
/// </summary>
/// <param name="scriptIdent">Script identifier that was passed to LoadPlugin().</param>
/// <returns>Reference to instance of plugin.</returns>
public IPlugin GetPlugin(string scriptIdent) {
if (mActivePlugins.TryGetValue(scriptIdent, out IPlugin plugin)) {
return plugin;
}
return null;
}
/// <summary>
/// Returns a string with the assembly's location.
/// </summary>
public string GetPluginAssemblyLocation(IPlugin plugin) {
return plugin.GetType().Assembly.Location;
}
/// <summary>
/// Generates a list of references to instances of active plugins.
/// </summary>
/// <returns>Newly-created list of plugin references.</returns>
public List<IPlugin> GetActivePlugins() {
List<IPlugin> list = new List<IPlugin>(mActivePlugins.Count);
foreach (KeyValuePair<string, IPlugin> kvp in mActivePlugins) {
list.Add(kvp.Value);
}
Debug.WriteLine("PluginManager: returning " + list.Count + " plugins (id=" +
AppDomain.CurrentDomain.Id + ")");
return list;
}
/// <summary>
/// Clears the list of loaded plugins. This does not unload the assemblies from
/// the AppDomain.
/// </summary>
public void ClearPluginList() {
mActivePlugins.Clear();
}
/// <summary>
/// Invokes the Prepare() method on all active plugins.
/// </summary>
/// <param name="appRef">Reference to host object providing app services.</param>
public void PreparePlugins(IApplication appRef, List<PlatSym> platSyms) {
foreach (KeyValuePair<string, IPlugin> kvp in mActivePlugins) {
kvp.Value.Prepare(appRef, mFileData, platSyms);
}
}
#if false
/// <summary>
/// DEBUG ONLY: establish a fast lease timeout. Normally the lease
/// is five minutes; this reduces it to a few seconds. (The actual time is
/// also affected by LifetimeServices.LeaseManagerPollTime, which defaults
/// to 10 seconds.)
///
/// Unfortunately this must be tagged [SecurityCritical] to match the method being
/// overridden, but in a partially-trusted sandbox that's not allowed. You have
/// to relax security entirely for this to work.
/// </summary>
//[SecurityPermissionAttribute(SecurityAction.Demand,
// Flags = SecurityPermissionFlag.Infrastructure)]
[System.Security.SecurityCritical]
public override object InitializeLifetimeService() {
object lease = base.InitializeLifetimeService();
// netstandard2.0 doesn't have System.Runtime.Remoting.Lifetime, so use reflection
PropertyInfo leaseState = lease.GetType().GetProperty("CurrentState");
PropertyInfo initialLeaseTime = lease.GetType().GetProperty("InitialLeaseTime");
PropertyInfo sponsorshipTimeout = lease.GetType().GetProperty("SponsorshipTimeout");
PropertyInfo renewOnCallTime = lease.GetType().GetProperty("RenewOnCallTime");
Console.WriteLine("Default lease: ini=" +
initialLeaseTime.GetValue(lease) + " spon=" +
sponsorshipTimeout.GetValue(lease) + " renOC=" +
renewOnCallTime.GetValue(lease));
if ((int)leaseState.GetValue(lease) == 1 /*LeaseState.Initial*/) {
// Initial lease duration.
initialLeaseTime.SetValue(lease, TimeSpan.FromSeconds(8));
// How long we will wait for the sponsor to respond
// with a lease renewal time.
sponsorshipTimeout.SetValue(lease, TimeSpan.FromSeconds(5));
// Each call to the remote object extends the lease so that
// it has at least this much time left.
renewOnCallTime.SetValue(lease, TimeSpan.FromSeconds(2));
}
return lease;
}
#endif
}
}

50
PluginCommon/Util.cs Normal file
View File

@ -0,0 +1,50 @@
/*
* Copyright 2018 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 System;
using CommonUtil;
namespace PluginCommon {
/// <summary>
/// Utility functions available for plugins to use.
///
/// The idea is to make CommonUtil functions available to plugins while isolating
/// them from changes to the library. Anything here is guaranteed to keep working,
/// while other classes and functions in CommonUtil may change between releases.
/// </summary>
public static class Util {
/// <summary>
/// Extracts an integer from the data stream.
/// </summary>
/// <param name="data">Raw data stream.</param>
/// <param name="offset">Start offset.</param>
/// <param name="width">Word width, which may be 1-4 bytes.</param>
/// <param name="isBigEndian">True if word is in big-endian order.</param>
/// <returns>Value found.</returns>
public static int GetWord(byte[] data, int offset, int width, bool isBigEndian) {
return RawData.GetWord(data, offset, width, isBigEndian);
}
/// <summary>
/// Compute a standard CRC-32 (polynomial 0xedb88320) on a buffer of data.
/// </summary>
/// <param name="data">Buffer to process.</param>
/// <returns>CRC value.</returns>
public static uint ComputeBufferCRC(byte[] data) {
return CRC32.OnBuffer(0, data, 0, data.Length);
}
}
}

107
README.md
View File

@ -1,2 +1,105 @@
# 6502bench
A workbench for developing 6502 code
# 6502bench #
6502bench is a code development "workbench" for 6502, 65C02, and 65802/65816
code. It currently features one tool, the SourceGen disassembler.
You can download the source code and build it yourself, or click the
"Releases" tab near the top of the github page for pre-built downloads.
## SourceGen ##
SourceGen converts machine-language programs to assembly-language source
code. It has most of the features you will find in other 6502 disassemblers,
as well as many less-common ones.
A demo video is available: https://youtu.be/dalISyBPQq8
#### Features ####
Analysis:
- Support for 6502 (including undocumented opcodes), 65C02, and 65816.
- Code flow analyzer traces execution to find all reachable
instructions. Hinting mechanism allows manual specification of
entry points.
- Processor status flags are tracked, allowing automatic detection
of branch-always and branch-never, as well as register widths on
16-bit CPUs. The analyzer tracks these across subroutine calls and
branches. Cycle counts factor these in automatically.
- Editable labels are generated for every branch and data target.
- Automatic detection and classification of ASCII strings and runs of
identical bytes.
- All target-platform-specific stuff is stored in plain text files
loaded at runtime. This includes symbols for ROM entry points
and standard zero-page locations. Additional symbols and overrides
can be specified at the project level.
- Extension scripts can be used to reformat code and identify inline
data that follows JSR/JSL. Code is compiled at run time and
executes in a sandbox.
UI:
- Fully interactive point-and-click GUI. Add labels and multi-line
comments, change addresses, and see the changes immediately.
- Instruction operand formats (hex, decimal, etc) can be set for
individual lines. Symbols that don't match the operands are automatically
offset, allowing simple expressions like "address - 1".
- Full-line comments are automatically word-wrapped, and can be
"boxed" for an authentic retro feel.
- Data areas can be formatted as bytes, words, addresses, and more.
Several types of strings are recognized (null-terminated, length
prefixed, etc).
- "Infinite" undo/redo of all actions.
- Notes can be added that aren't included in generated output. Very
useful for marking up a work in progress.
- Cross-reference tables are generated for every branch and data
target address, as well as for external platform symbols.
- Instruction summaries, including cpu cycles and flags modified, are
shown along with a description of the opcode function.
- Display is configurable for upper/lower case, choice of pseudo-op
names, expression formats, and more.
Output:
- Assembly source can be generated for multiple assemblers (currently
cc65 and Merlin 32).
- Cross-assemblers can be launched directly from the generation GUI to
verify output correctness.
- Optional automatic conversion of labels from global to local.
- Symbols may be exported from one project and imported into another
to facilitate multi-binary disassembly.
Misc:
- Preset project attributes (CPU type, platform symbol file sets) are defined
for a variety of platforms.
- Project file is stored in a text format, and only holds metadata. None
of the original file is included, allowing the project file to be shared
without violating copyrights (note: may vary depending on local laws).
There are a couple of significant areas where support is currently lacking:
- Poor support for multi-bank 65816 files (IIgs OMF, SNES).
- No support for alternate character sets (e.g. PETSCII).
## About the Code ##
All of the code is written in C# .NET, using the (free to download) Visual
Studio Community 2017 IDE as the primary development environment. The user
interface uses the WinForms API. Efforts have been made to avoid doing
anything Windows-specific, in the hope that the applications will be
straightforward to port to other platforms.
The solution is called "WorkBench.sln" rather than "6502bench.sln" because
some things in Visual Studio got weird when it didn't start with a letter.
The code style is closer to what Android uses than "standard" C#. Lines
are folded to fit 100 columns.
The source code is licensed under Apache 2.0
(http://www.apache.org/licenses/LICENSE-2.0), which makes it free for use in
both open-source programs and closed-source commercial software. The license
terms are similar to BSD or MIT, but with some additional constraints on
patent licensing. (This is the same license Google uses for the Android
Open Source Project.)
Images are licensed under Creative Commons ShareAlike 4.0 International
(https://creativecommons.org/licenses/by-sa/4.0/).

284
SourceGen/AddressMap.cs Normal file
View File

@ -0,0 +1,284 @@
/*
* Copyright 2018 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 System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace SourceGen {
/// <summary>
/// Map file offsets to 65xx addresses and vice-versa. Useful for sources with
/// multiple ORG directives.
///
/// It's possible to generate code that would overlap once relocated at run time,
/// which means a given address could map to multiple offsets. For this reason
/// it's useful to know the offset of the referring code when evaluating a
/// reference, so that a "local" match can take priority.
/// </summary>
public class AddressMap : IEnumerable<AddressMap.AddressMapEntry> {
/// <summary>
/// Code starting at the specified offset will have the specified address.
///
/// The entries are held in the list in order, sorted by offset, with no gaps.
/// This makes the "length" field redundant, as it can be computed by
/// (entry[N+1].mOffset - entry[N].mOffset), with a special case for the last
/// entry in the list. It's convenient to maintain it explicitly however, as
/// the list is read far more often than it is updated.
///
/// Entries are mutable, but must only be altered by AddressMap. Don't retain
/// instances of this across other activity.
/// </summary>
public class AddressMapEntry {
public int Offset { get; set; }
public int Addr { get; set; }
public int Length { get; set; }
public AddressMapEntry(int offset, int addr, int len) {
Offset = offset;
Addr = addr;
Length = len;
}
}
/// <summary>
/// Total length, in bytes, spanned by this map.
/// </summary>
private int mTotalLength;
/// <summary>
/// List of definitions, in sorted order.
/// </summary>
private List<AddressMapEntry> mAddrList = new List<AddressMapEntry>();
/// <summary>
/// Constructor.
/// </summary>
/// <param name="length">Total length, in bytes, spanned by this map.</param>
public AddressMap(int length) {
/// There must always be at least one entry, defining the target address
/// for file offset 0. This can be changed, but can't be removed.
mTotalLength = length;
mAddrList.Add(new AddressMapEntry(0, 0, length));
}
// IEnumerable
public IEnumerator<AddressMapEntry> GetEnumerator() {
return ((IEnumerable<AddressMapEntry>)mAddrList).GetEnumerator();
}
// IEnumerable
IEnumerator IEnumerable.GetEnumerator() {
return ((IEnumerable<AddressMapEntry>)mAddrList).GetEnumerator();
}
/// <summary>
/// Returns the address map entry index associated with the specified offset, or -1
/// if there is no address map entry there.
/// </summary>
public int Get(int offset) {
foreach (AddressMapEntry ad in mAddrList) {
if (ad.Offset == offset) {
return ad.Addr;
}
}
return -1;
}
/// <summary>
/// Adds, updates, or removes a map entry.
/// </summary>
/// <param name="offset">File offset at which the address changes.</param>
/// <param name="addr">24-bit address.</param>
public void Set(int offset, int addr) {
Debug.Assert(offset >= 0);
if (addr == -1) {
if (offset != 0) { // ignore attempts to remove entry at offset zero
Remove(offset);
}
return;
}
Debug.Assert(addr >= 0 && addr < 0x01000000); // 24-bit address space
int i;
for (i = 0; i < mAddrList.Count; i++) {
AddressMapEntry ad = mAddrList[i];
if (ad.Offset == offset) {
// update existing
ad.Addr = addr;
mAddrList[i] = ad;
return;
} else if (ad.Offset > offset) {
// The i'th entry is one past the interesting part.
break;
}
}
// Carve a chunk out of the previous entry.
AddressMapEntry prev = mAddrList[i - 1];
int prevOldLen = prev.Length;
int prevNewLen = offset - prev.Offset;
prev.Length = prevNewLen;
mAddrList[i - 1] = prev;
mAddrList.Insert(i,
new AddressMapEntry(offset, addr, prevOldLen - prevNewLen));
DebugValidate();
}
/// <summary>
/// Removes an entry from the set.
/// </summary>
/// <param name="offset">The initial offset of the mapping to remove. This
/// must be the initial value, not a mid-range value.</param>
/// <returns>True if something was removed.</returns>
public bool Remove(int offset) {
if (offset == 0) {
throw new Exception("Not allowed to remove entry 0");
}
for (int i = 1; i < mAddrList.Count; i++) {
if (mAddrList[i].Offset == offset) {
// Add the length to the previous entry.
AddressMapEntry prev = mAddrList[i - 1];
prev.Length += mAddrList[i].Length;
mAddrList[i - 1] = prev;
mAddrList.RemoveAt(i);
DebugValidate();
return true;
}
}
return false;
}
/// <summary>
/// Returns the index of the address map entry that contains the given offset.
/// We assume the offset is valid.
/// </summary>
private int IndexForOffset(int offset) {
for (int i = 1; i < mAddrList.Count; i++) {
if (mAddrList[i].Offset > offset) {
return i - 1;
}
}
return mAddrList.Count - 1;
}
/// <summary>
/// Returns true if the given address falls into the range spanned by the
/// address map entry.
/// </summary>
/// <param name="index">Address map entry index.</param>
/// <param name="addr">Address to check.</param>
/// <returns></returns>
private bool IndexContainsAddress(int index, int addr) {
return addr >= mAddrList[index].Addr &&
addr < mAddrList[index].Addr + mAddrList[index].Length;
}
/// <summary>
/// Determines the file offset that best contains the specified target address.
/// </summary>
/// <param name="srcOffset">Offset of the address reference.</param>
/// <param name="targetAddr">Address to look up.</param>
/// <returns>The file offset, or -1 if the address falls outside the file.</returns>
public int AddressToOffset(int srcOffset, int targetAddr) {
if (mAddrList.Count == 1) {
// Trivial case.
if (IndexContainsAddress(0, targetAddr)) {
Debug.Assert(targetAddr >= mAddrList[0].Addr);
return targetAddr - mAddrList[0].Addr;
} else {
return -1;
}
}
// We have multiple, potentially overlapping address ranges. Start by
// looking for a match in the srcOffset range; if that fails, scan
// forward from the start.
int srcOffIndex = IndexForOffset(srcOffset);
if (IndexContainsAddress(srcOffIndex, targetAddr)) {
Debug.Assert(targetAddr >= mAddrList[srcOffIndex].Addr);
return (targetAddr - mAddrList[srcOffIndex].Addr) + mAddrList[srcOffIndex].Offset;
}
for (int i = 0; i < mAddrList.Count; i++) {
if (i == srcOffIndex) {
// optimization -- we already checked this one
continue;
}
if (IndexContainsAddress(i, targetAddr)) {
Debug.Assert(targetAddr >= mAddrList[i].Addr);
return (targetAddr - mAddrList[i].Addr) + mAddrList[i].Offset;
}
}
return -1;
}
/// <summary>
/// Converts a file offset to an address.
/// </summary>
/// <param name="offset">File offset.</param>
/// <returns>24-bit address.</returns>
public int OffsetToAddress(int offset) {
int srcOffIndex = IndexForOffset(offset);
return mAddrList[srcOffIndex].Addr + (offset - mAddrList[srcOffIndex].Offset);
}
/// <summary>
/// Internal consistency checks.
/// </summary>
private void DebugValidate() {
if (mAddrList.Count < 1) {
throw new Exception("AddressMap: empty");
}
if (mAddrList[0].Offset != 0) {
throw new Exception("AddressMap: bad offset 0");
}
if (mAddrList.Count == 1) {
if (mAddrList[0].Length != mTotalLength) {
throw new Exception("AddressMap: single entry len bad");
}
} else {
int totalLen = 0;
for (int i = 0; i < mAddrList.Count; i++) {
AddressMapEntry ent = mAddrList[i];
if (i != 0) {
if (ent.Offset != mAddrList[i - 1].Offset + mAddrList[i - 1].Length) {
throw new Exception("Bad offset step to " + i);
}
}
totalLen += ent.Length;
}
if (totalLen != mTotalLength) {
throw new Exception("AddressMap: bad length sum (" + totalLen + " vs " +
mTotalLength + ")");
}
}
}
public override string ToString() {
return "[AddressMap: " + mAddrList.Count + " entries]";
}
}
}

355
SourceGen/Anattrib.cs Normal file
View File

@ -0,0 +1,355 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.Text;
using Asm65;
namespace SourceGen {
/// <summary>
/// Analyzer attribute holder. Contains the output of the instruction and data analyzers.
/// Every byte in the input file has one of these associated with it.
///
/// (Yes, it's a mutable struct. Yes, that fact has bitten me a few times. The array
/// of these may have millions of elements, so the reduction in overhead seems worthwhile.)
/// </summary>
public struct Anattrib {
[FlagsAttribute]
private enum AttribFlags {
InstrStart = 1 << 0, // byte is first of an instruction
Instruction = 1 << 1, // byte is part of an instruction or inline data
InlineData = 1 << 2, // byte is inline data
Data = 1 << 3, // byte is data
EntryPoint = 1 << 8, // external code branches here
BranchTarget = 1 << 9, // internal code branches here
ExternalBranch = 1 << 10, // this abs/rel branch lands outside input file
NoContinue = 1 << 12, // execution does not continue to following instruction
Visited = 1 << 16, // has the analyzer visited this byte?
Changed = 1 << 17, // set/cleared as the analyzer works
Hinted = 1 << 18, // was this byte affected by a type hint?
}
// Flags indicating what type of data is here. Use the following Is* properties
// to set/clear.
private AttribFlags mAttribFlags;
public bool IsInstructionStart {
get {
return (mAttribFlags & AttribFlags.InstrStart) != 0;
}
set {
IsInstruction = value;
if (value) {
mAttribFlags |= AttribFlags.InstrStart;
} else {
mAttribFlags &= ~AttribFlags.InstrStart;
}
}
}
public bool IsInstruction {
get {
return (mAttribFlags & AttribFlags.Instruction) != 0;
}
set {
Debug.Assert(value == false ||
(mAttribFlags & (AttribFlags.InlineData | AttribFlags.Data)) == 0);
if (value) {
mAttribFlags |= AttribFlags.Instruction;
} else {
mAttribFlags &= ~AttribFlags.Instruction;
}
}
}
public bool IsInlineData {
get {
return (mAttribFlags & AttribFlags.InlineData) != 0;
}
set {
Debug.Assert(value == false ||
(mAttribFlags & (AttribFlags.Instruction | AttribFlags.Data)) == 0);
if (value) {
mAttribFlags |= AttribFlags.InlineData;
} else {
mAttribFlags &= ~AttribFlags.InlineData;
}
}
}
public bool IsData {
get {
return (mAttribFlags & AttribFlags.Data) != 0;
}
set {
Debug.Assert(value == false ||
(mAttribFlags & (AttribFlags.InlineData | AttribFlags.Instruction)) == 0);
if (value) {
mAttribFlags |= AttribFlags.Data;
} else {
mAttribFlags &= ~AttribFlags.Data;
}
}
}
public bool IsStart {
get {
return IsInstructionStart || IsDataStart || IsInlineDataStart;
}
}
public bool IsEntryPoint {
get {
return (mAttribFlags & AttribFlags.EntryPoint) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.EntryPoint;
} else {
mAttribFlags &= ~AttribFlags.EntryPoint;
}
}
}
public bool IsBranchTarget {
get {
return (mAttribFlags & AttribFlags.BranchTarget) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.BranchTarget;
} else {
mAttribFlags &= ~AttribFlags.BranchTarget;
}
}
}
public bool IsExternalBranch {
get {
return (mAttribFlags & AttribFlags.ExternalBranch) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.ExternalBranch;
} else {
mAttribFlags &= ~AttribFlags.ExternalBranch;
}
}
}
public bool DoesNotContinue {
get {
return (mAttribFlags & AttribFlags.NoContinue) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.NoContinue;
} else {
mAttribFlags &= ~AttribFlags.NoContinue;
}
}
}
public bool DoesNotBranch {
get {
return (BranchTaken == OpDef.BranchTaken.Never);
}
}
public bool IsVisited {
get {
return (mAttribFlags & AttribFlags.Visited) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.Visited;
} else {
mAttribFlags &= ~AttribFlags.Visited;
}
}
}
public bool IsChanged {
get {
return (mAttribFlags & AttribFlags.Changed) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.Changed;
} else {
mAttribFlags &= ~AttribFlags.Changed;
}
}
}
public bool IsHinted {
get {
return (mAttribFlags & AttribFlags.Hinted) != 0;
}
set {
if (value) {
mAttribFlags |= AttribFlags.Hinted;
} else {
mAttribFlags &= ~AttribFlags.Hinted;
}
}
}
public bool IsDataStart {
get {
return IsData && DataDescriptor != null;
}
}
public bool IsInlineDataStart {
get {
return IsInlineData && DataDescriptor != null;
}
}
/// <summary>
/// Get the target memory address for this byte.
/// </summary>
public int Address { get; set; }
/// <summary>
/// Instructions: length of the instruction (for InstrStart). If a FormatDescriptor
/// is assigned, the length must match.
/// Inline data: FormatDescriptor length, or zero if no descriptor is defined.
/// Data: FormatDescriptor length, or zero if no descriptor is defined.
///
/// This field should only be set by CodeAnalysis methods, although the "get" value
/// can be changed for data/inline-data by setting the DataDescriptor field.
/// </summary>
public int Length {
get {
// For data we don't even use the field; this ensures that we're always
// using the FormatDescriptor's length.
if (IsData || IsInlineData) {
Debug.Assert(mLength == 0);
if (DataDescriptor != null) {
return DataDescriptor.Length;
} else {
return 0;
}
}
return mLength;
}
set {
Debug.Assert(!IsData);
mLength = value;
}
}
private int mLength;
/// <summary>
/// Instructions only: processor status flags.
///
/// Note this returns a copy of a struct, so modifications to the returned value
/// (including calls to Merge and Apply) are not permanent.
/// </summary>
public StatusFlags StatusFlags {
get { return mStatusFlags; }
set { mStatusFlags = value; }
}
private StatusFlags mStatusFlags;
public void MergeStatusFlags(StatusFlags other) {
mStatusFlags.Merge(other);
}
public void ApplyStatusFlags(StatusFlags other) {
mStatusFlags.Apply(other);
}
/// <summary>
/// Branch instructions only: outcome of branch.
/// </summary>
public OpDef.BranchTaken BranchTaken { get; set; }
/// <summary>
/// Instructions only: decoded operand address value. Will be -1 if not
/// yet computed or not applicable. For a relative branch instruction,
/// this will have the absolute branch target address. On the 65816, this
/// will be a 24-bit address.
/// </summary>
public int OperandAddress {
get { return mOperandAddressSet ? mOperandAddress : -1; }
set {
Debug.Assert(mOperandAddress >= -1);
mOperandAddress = value;
mOperandAddressSet = (value >= 0);
}
}
private int mOperandAddress;
private bool mOperandAddressSet;
/// <summary>
/// Instructions only: offset referenced by OperandAddress. Will be -1 if not
/// yet computed, not applicable, or if OperandAddress refers to a location
/// outside the scope of the file.
/// </summary>
public int OperandOffset {
get { return mOperandOffsetSet ? mOperandOffset : -1; }
set {
Debug.Assert(mOperandOffset >= -1);
mOperandOffset = value;
mOperandOffsetSet = (value >= 0);
}
}
private int mOperandOffset;
private bool mOperandOffsetSet;
/// <summary>
/// Instructions only: is OperandOffset a direct target offset? (This is used when
/// tracing jump instructions, to know if we should add the offset to the scan list.
/// It's determined by the opcode, e.g. "JMP addr" -> true, "JMP (addr,X)" -> false.)
/// </summary>
public bool IsOperandOffsetDirect { get; set; }
/// <summary>
/// Symbol defined as the label for this offset. All offsets that are instruction
/// or data target offsets will have one of these defined. Users can define additional
/// symbols as well.
///
/// Will be null if no label is defined for this offset.
/// </summary>
public Symbol Symbol { get; set; }
/// <summary>
/// Format descriptor for operands and data items. Will be null if no descriptor
/// is defined for this offset.
/// </summary>
public FormatDescriptor DataDescriptor { get; set; }
/// <summary>
/// Is this an instruction with an operand (i.e. not impl/acc)?
/// </summary>
public bool IsInstructionWithOperand {
get {
if (!IsInstructionStart) {
return false;
}
return Length != 1;
}
}
/// <summary>
/// Returns a fixed-width string with indicators for items of interest.
/// </summary>
public string ToAttrString() {
StringBuilder sb = new StringBuilder(5);
char blank = '.';
sb.Append(IsEntryPoint ? '@' : blank);
sb.Append(IsHinted ? 'H' : blank);
sb.Append(DoesNotBranch ? '!' : blank);
sb.Append(DoesNotContinue ? '#' : blank);
sb.Append(IsBranchTarget ? '>' : blank);
return sb.ToString();
}
}
}

6
SourceGen/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

187
SourceGen/AppForms/AboutBox.Designer.cs generated Normal file
View File

@ -0,0 +1,187 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class AboutBox {
/// <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.boardPictureBox = new System.Windows.Forms.PictureBox();
this.sourceGenLabel = new System.Windows.Forms.Label();
this.versionLabel = new System.Windows.Forms.Label();
this.createdLabel = new System.Windows.Forms.Label();
this.okButton = new System.Windows.Forms.Button();
this.legalStuffTextBox = new System.Windows.Forms.TextBox();
this.label1 = new System.Windows.Forms.Label();
this.osPlatformLabel = new System.Windows.Forms.Label();
this.debugEnabledLabel = new System.Windows.Forms.Label();
((System.ComponentModel.ISupportInitialize)(this.boardPictureBox)).BeginInit();
this.SuspendLayout();
//
// boardPictureBox
//
this.boardPictureBox.Location = new System.Drawing.Point(13, 13);
this.boardPictureBox.Name = "boardPictureBox";
this.boardPictureBox.Size = new System.Drawing.Size(320, 236);
this.boardPictureBox.SizeMode = System.Windows.Forms.PictureBoxSizeMode.Zoom;
this.boardPictureBox.TabIndex = 0;
this.boardPictureBox.TabStop = false;
//
// sourceGenLabel
//
this.sourceGenLabel.AutoSize = true;
this.sourceGenLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 24F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.sourceGenLabel.Location = new System.Drawing.Point(340, 13);
this.sourceGenLabel.Name = "sourceGenLabel";
this.sourceGenLabel.Size = new System.Drawing.Size(349, 37);
this.sourceGenLabel.TabIndex = 1;
this.sourceGenLabel.Text = "6502bench SourceGen";
//
// versionLabel
//
this.versionLabel.AutoSize = true;
this.versionLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 20.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.versionLabel.Location = new System.Drawing.Point(340, 60);
this.versionLabel.Name = "versionLabel";
this.versionLabel.Size = new System.Drawing.Size(273, 31);
this.versionLabel.TabIndex = 2;
this.versionLabel.Text = "Version X.Y.Z Alpha1";
//
// createdLabel
//
this.createdLabel.AutoSize = true;
this.createdLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.createdLabel.Location = new System.Drawing.Point(407, 142);
this.createdLabel.Name = "createdLabel";
this.createdLabel.Size = new System.Drawing.Size(206, 40);
this.createdLabel.TabIndex = 3;
this.createdLabel.Text = "Copyright 2018 faddenSoft\r\nCreated by Andy McFadden\r\n";
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(617, 526);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 0;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
//
// legalStuffTextBox
//
this.legalStuffTextBox.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.legalStuffTextBox.Location = new System.Drawing.Point(12, 305);
this.legalStuffTextBox.MaxLength = 0;
this.legalStuffTextBox.Multiline = true;
this.legalStuffTextBox.Name = "legalStuffTextBox";
this.legalStuffTextBox.ReadOnly = true;
this.legalStuffTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
this.legalStuffTextBox.Size = new System.Drawing.Size(680, 215);
this.legalStuffTextBox.TabIndex = 4;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 289);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(59, 13);
this.label1.TabIndex = 5;
this.label1.Text = "Legal stuff:";
//
// osPlatformLabel
//
this.osPlatformLabel.AutoSize = true;
this.osPlatformLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.osPlatformLabel.Location = new System.Drawing.Point(12, 263);
this.osPlatformLabel.Name = "osPlatformLabel";
this.osPlatformLabel.Size = new System.Drawing.Size(86, 16);
this.osPlatformLabel.TabIndex = 6;
this.osPlatformLabel.Text = "[OS platform]";
//
// debugEnabledLabel
//
this.debugEnabledLabel.AutoSize = true;
this.debugEnabledLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.debugEnabledLabel.Location = new System.Drawing.Point(340, 233);
this.debugEnabledLabel.Name = "debugEnabledLabel";
this.debugEnabledLabel.Size = new System.Drawing.Size(293, 16);
this.debugEnabledLabel.TabIndex = 7;
this.debugEnabledLabel.Text = "Assertions and extended validation are enabled";
this.debugEnabledLabel.Visible = false;
//
// AboutBox
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(704, 561);
this.Controls.Add(this.debugEnabledLabel);
this.Controls.Add(this.osPlatformLabel);
this.Controls.Add(this.label1);
this.Controls.Add(this.legalStuffTextBox);
this.Controls.Add(this.okButton);
this.Controls.Add(this.createdLabel);
this.Controls.Add(this.versionLabel);
this.Controls.Add(this.sourceGenLabel);
this.Controls.Add(this.boardPictureBox);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "AboutBox";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "AboutBox";
this.Load += new System.EventHandler(this.AboutBox_Load);
((System.ComponentModel.ISupportInitialize)(this.boardPictureBox)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.PictureBox boardPictureBox;
private System.Windows.Forms.Label sourceGenLabel;
private System.Windows.Forms.Label versionLabel;
private System.Windows.Forms.Label createdLabel;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.TextBox legalStuffTextBox;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.Label osPlatformLabel;
private System.Windows.Forms.Label debugEnabledLabel;
}
}

View File

@ -0,0 +1,51 @@
/*
* Copyright 2018 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 System;
using System.IO;
using System.Windows.Forms;
namespace SourceGen.AppForms {
public partial class AboutBox : Form {
private const string IMAGE_FILE_NAME = "AboutImage.png";
private const string LEGAL_STUFF_FILE_NAME = "LegalStuff.txt";
public AboutBox() {
InitializeComponent();
boardPictureBox.ImageLocation = RuntimeDataAccess.GetPathName(IMAGE_FILE_NAME);
versionLabel.Text = string.Format(Properties.Resources.VERSION_FMT,
Program.ProgramVersion);
osPlatformLabel.Text = "OS: " +
System.Runtime.InteropServices.RuntimeInformation.OSDescription;
#if DEBUG
debugEnabledLabel.Visible = true;
#endif
}
private void AboutBox_Load(object sender, EventArgs e) {
string text;
string pathName = RuntimeDataAccess.GetPathName(LEGAL_STUFF_FILE_NAME);
try {
text = File.ReadAllText(pathName);
} catch (Exception ex) {
text = ex.ToString();
}
legalStuffTextBox.Text = text;
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,143 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class DataFileLoadIssue {
/// <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.problemWithFileLabel = new System.Windows.Forms.Label();
this.pathNameTextBox = new System.Windows.Forms.TextBox();
this.problemLabel = new System.Windows.Forms.Label();
this.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.doYouWantLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// problemWithFileLabel
//
this.problemWithFileLabel.AutoSize = true;
this.problemWithFileLabel.Location = new System.Drawing.Point(13, 13);
this.problemWithFileLabel.Name = "problemWithFileLabel";
this.problemWithFileLabel.Size = new System.Drawing.Size(221, 13);
this.problemWithFileLabel.TabIndex = 2;
this.problemWithFileLabel.Text = "There was an error while loading the data file:";
//
// pathNameTextBox
//
this.pathNameTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.pathNameTextBox.Location = new System.Drawing.Point(13, 39);
this.pathNameTextBox.Name = "pathNameTextBox";
this.pathNameTextBox.ReadOnly = true;
this.pathNameTextBox.Size = new System.Drawing.Size(488, 20);
this.pathNameTextBox.TabIndex = 3;
//
// problemLabel
//
this.problemLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.problemLabel.Location = new System.Drawing.Point(13, 73);
this.problemLabel.Name = "problemLabel";
this.problemLabel.Size = new System.Drawing.Size(488, 31);
this.problemLabel.TabIndex = 4;
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(428, 113);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 1;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(347, 113);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 0;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
//
// doYouWantLabel
//
this.doYouWantLabel.AutoSize = true;
this.doYouWantLabel.Location = new System.Drawing.Point(13, 117);
this.doYouWantLabel.Name = "doYouWantLabel";
this.doYouWantLabel.Size = new System.Drawing.Size(175, 13);
this.doYouWantLabel.TabIndex = 5;
this.doYouWantLabel.Text = "Do you want to locate the data file?\r\n";
//
// DataFileLoadIssue
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(515, 148);
this.Controls.Add(this.doYouWantLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.problemLabel);
this.Controls.Add(this.pathNameTextBox);
this.Controls.Add(this.problemWithFileLabel);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "DataFileLoadIssue";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Data File Load Issue";
this.Load += new System.EventHandler(this.DataFileLoadIssue_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Label problemWithFileLabel;
private System.Windows.Forms.TextBox pathNameTextBox;
private System.Windows.Forms.Label problemLabel;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Label doYouWantLabel;
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2018 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 System;
using System.Windows.Forms;
namespace SourceGen.AppForms {
public partial class DataFileLoadIssue : Form {
/// <summary>
/// Path name of problematic file.
/// </summary>
public string PathName { get; set; }
/// <summary>
/// Message to show in the dialog.
/// </summary>
public string Message { get; set; }
public DataFileLoadIssue() {
InitializeComponent();
}
private void DataFileLoadIssue_Load(object sender, EventArgs e) {
pathNameTextBox.Text = PathName;
problemLabel.Text = Message;
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

140
SourceGen/AppForms/EditAddress.Designer.cs generated Normal file
View File

@ -0,0 +1,140 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class EditAddress {
/// <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() {
System.Windows.Forms.Label addressLabel;
this.okButton = new System.Windows.Forms.Button();
this.cancelButton = new System.Windows.Forms.Button();
this.textBox1 = new System.Windows.Forms.TextBox();
this.instructionLabel1 = new System.Windows.Forms.Label();
this.instructionLabel2 = new System.Windows.Forms.Label();
addressLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// addressLabel
//
addressLabel.AutoSize = true;
addressLabel.Location = new System.Drawing.Point(12, 16);
addressLabel.Name = "addressLabel";
addressLabel.Size = new System.Drawing.Size(48, 13);
addressLabel.TabIndex = 1;
addressLabel.Text = "Address:";
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(190, 91);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 3;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(271, 91);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 4;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(68, 13);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(275, 20);
this.textBox1.TabIndex = 0;
//
// instructionLabel1
//
this.instructionLabel1.AutoSize = true;
this.instructionLabel1.Location = new System.Drawing.Point(12, 36);
this.instructionLabel1.Name = "instructionLabel1";
this.instructionLabel1.Size = new System.Drawing.Size(331, 13);
this.instructionLabel1.TabIndex = 2;
this.instructionLabel1.Text = "Enter 16-bit or 24-bit address in hexadecimal, e.g. $1000 or 00/be00.\r\n";
//
// instructionLabel2
//
this.instructionLabel2.AutoSize = true;
this.instructionLabel2.Location = new System.Drawing.Point(12, 58);
this.instructionLabel2.Name = "instructionLabel2";
this.instructionLabel2.Size = new System.Drawing.Size(258, 13);
this.instructionLabel2.TabIndex = 5;
this.instructionLabel2.Text = "Leave the field blank to remove the address override.";
//
// EditAddress
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(358, 126);
this.Controls.Add(this.instructionLabel2);
this.Controls.Add(this.instructionLabel1);
this.Controls.Add(this.textBox1);
this.Controls.Add(addressLabel);
this.Controls.Add(this.cancelButton);
this.Controls.Add(this.okButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditAddress";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Address";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label instructionLabel1;
private System.Windows.Forms.Label instructionLabel2;
}
}

View File

@ -0,0 +1,124 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.Windows.Forms;
namespace SourceGen.AppForms {
public partial class EditAddress : Form {
#if false
private bool mAllowLastChar;
#endif
/// <summary>
/// Maximum allowed address value.
/// </summary>
public int MaxAddressValue { get; set; }
/// <summary>
/// Address typed by user. Only valid after the dialog returns OK. Will be set to -1
/// if the user is attempting to delete the address.
/// </summary>
public int Address { get; private set; }
public EditAddress() {
InitializeComponent();
Address = -2;
MaxAddressValue = (1 << 24) - 1;
#if false
// This is probably not all that useful. We're not preventing
// invalid inputs, e.g. excessively large values or "$/$/$/", by restricting
// the keys that can be typed.
textBox1.KeyDown += textBox1_KeyDown;
textBox1.KeyPress += textBox1_KeyPress;
#endif
// Update the OK button based on current contents.
textBox1.TextChanged += textBox1_TextChanged;
}
public void SetInitialAddress(int addr) {
textBox1.Text = Asm65.Address.AddressToString(addr, false);
textBox1.SelectAll();
}
/// <summary>
/// Handles a click on the OK button by setting the Address property to the
/// decoded value from the text field.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void okButton_Click(object sender, EventArgs e) {
if (textBox1.Text.Length == 0) {
Address = -1;
} else {
Asm65.Address.ParseAddress(textBox1.Text, MaxAddressValue, out int addr);
Address = addr;
}
}
/// <summary>
/// Enables or disables the OK button depending on whether the current input is
/// valid. We allow valid addresses and an empty box.
/// </summary>
private void UpdateOkEnabled() {
string text = textBox1.Text;
okButton.Enabled = (text.Length == 0) ||
Asm65.Address.ParseAddress(text, MaxAddressValue, out int unused);
}
#if false
/// <summary>
/// Limits characters to [A-F][a-f][0-9][/].
/// </summary>
private void textBox1_KeyDown(object sender, KeyEventArgs e) {
bool allow = false;
if (e.KeyCode == Keys.D4 && e.Modifiers == Keys.Shift) {
allow = true; // allow '$'; not sure this works on non-US keyboards?
} else if (e.KeyCode >= Keys.A && e.KeyCode <= Keys.F) {
allow = !(e.Alt || e.Control); // allow shift
} else if ((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) ||
e.KeyCode == Keys.OemQuestion) {
allow = (e.Modifiers == 0);
} else if (e.KeyCode == Keys.Back || e.KeyCode == Keys.Delete) {
allow = true;
}
mAllowLastChar = allow;
//Debug.WriteLine("DOWN " + e.KeyCode + " allow=" + allow);
}
/// <summary>
/// Rejects invalid characters.
/// </summary>
private void textBox1_KeyPress(object sender, System.Windows.Forms.KeyPressEventArgs e) {
//Debug.WriteLine("PRESS " + e.KeyChar + " : " + mAllowLastChar);
if (!mAllowLastChar) {
e.Handled = true;
}
}
#endif
/// <summary>
/// Updates the OK button whenever the text changes. This works for all change sources,
/// including programmatic.
/// </summary>
private void textBox1_TextChanged(object sender, EventArgs e) {
UpdateOkEnabled();
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="addressLabel.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,617 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Reflection;
using System.Text;
using System.Windows.Forms;
namespace SourceGen.AppForms {
public partial class EditAppSettings : Form {
/// <summary>
/// Tab page enumeration. Numbers must match page indices in designer.
/// </summary>
public enum Tab {
Unknown = -1,
CodeList = 0,
Assembler = 1,
AsmFormat = 2,
PseudoOp = 3
}
/// <summary>
/// ProjectView reference. When the user hits Apply, the object's ApplyAppSettings
/// method will be invoked.
/// </summary>
private ProjectView mProjectView;
/// <summary>
/// Copy of settings that we make changes to. On "Apply" or "OK", this is pushed
/// into the global settings object, and applied to the ProjectView.
/// </summary>
private AppSettings mSettings;
/// <summary>
/// Dirty flag, set when anything in mSettings changes. Don't modify this directly. Use
/// the SetDirty() call so that the Apply button's enabled status gets updated.
/// </summary>
private bool mDirty;
/// <summary>
/// Tab to show when dialog is first opened.
/// </summary>
private Tab mInitialTab;
// Map buttons to column show/hide buttons.
private const int NUM_COLUMNS = ProjectView.CodeListColumnWidths.NUM_COLUMNS;
private string[] mColumnFormats = new string[NUM_COLUMNS];
private Button[] mColButtons;
// Map pseudo-op text entry fields to PseudoOpName properties.
private struct TextBoxPropertyMap {
public TextBox TextBox { get; private set; }
public PropertyInfo PropInfo { get; private set; }
public TextBoxPropertyMap(TextBox textBox, string propName) {
TextBox = textBox;
PropInfo = typeof(PseudoOp.PseudoOpNames).GetProperty(propName);
}
}
private TextBoxPropertyMap[] mPseudoNameMap;
public EditAppSettings(ProjectView projectView, Tab initialTab) {
InitializeComponent();
mProjectView = projectView;
mInitialTab = initialTab;
// Make a work copy, so we can discard changes if the user cancels out of the dialog.
projectView.SaveCodeListColumnWidths();
mSettings = AppSettings.Global.GetCopy();
// Put buttons in an array.
mColButtons = new Button[] {
showCol0, showCol1, showCol2, showCol3, showCol4,
showCol5, showCol6, showCol7, showCol8 };
Debug.Assert(NUM_COLUMNS == 9);
// Extract formats from button labels.
for (int i = 0; i < NUM_COLUMNS; i++) {
mColButtons[i].Click += ColumnVisibilityButtonClick;
mColumnFormats[i] = mColButtons[i].Text;
}
// Map text boxes to PseudoOpName fields.
mPseudoNameMap = new TextBoxPropertyMap[] {
new TextBoxPropertyMap(equDirectiveTextBox, "EquDirective"),
new TextBoxPropertyMap(orgDirectiveTextBox, "OrgDirective"),
new TextBoxPropertyMap(regWidthDirectiveTextBox, "RegWidthDirective"),
new TextBoxPropertyMap(defineData1TextBox, "DefineData1"),
new TextBoxPropertyMap(defineData2TextBox, "DefineData2"),
new TextBoxPropertyMap(defineData3TextBox, "DefineData3"),
new TextBoxPropertyMap(defineData4TextBox, "DefineData4"),
new TextBoxPropertyMap(defineBigData2TextBox, "DefineBigData2"),
new TextBoxPropertyMap(fillTextBox, "Fill"),
new TextBoxPropertyMap(denseTextBox, "Dense"),
new TextBoxPropertyMap(strGenericTextBox, "StrGeneric"),
new TextBoxPropertyMap(strGenericHiTextBox, "StrGenericHi"),
new TextBoxPropertyMap(strReverseTextBox, "StrReverse"),
new TextBoxPropertyMap(strReverseHiTextBox, "StrReverseHi"),
new TextBoxPropertyMap(strLen8TextBox, "StrLen8"),
new TextBoxPropertyMap(strLen8HiTextBox, "StrLen8Hi"),
new TextBoxPropertyMap(strLen16TextBox, "StrLen16"),
new TextBoxPropertyMap(strLen16HiTextBox, "StrLen16Hi"),
new TextBoxPropertyMap(strNullTermTextBox, "StrNullTerm"),
new TextBoxPropertyMap(strNullTermHiTextBox, "StrNullTermHi"),
new TextBoxPropertyMap(strDciTextBox, "StrDci"),
new TextBoxPropertyMap(strDciHiTextBox, "StrDciHi"),
};
}
private void EditAppSettings_Load(object sender, EventArgs e) {
// Column widths. We called SaveCodeListColumnWidths() earlier, so this
// should always be a valid serialized string.
string widthStr = mSettings.GetString(AppSettings.CDLV_COL_WIDTHS, null);
Debug.Assert(!string.IsNullOrEmpty(widthStr));
ProjectView.CodeListColumnWidths widths =
ProjectView.CodeListColumnWidths.Deserialize(widthStr);
Debug.Assert(widths != null);
for (int i = 0; i < NUM_COLUMNS; i++) {
SetShowHideButton(i, widths.Width[i]);
}
// Display localized font string.
FontConverter cvt = new FontConverter();
currentFontDisplayLabel.Text = cvt.ConvertToString(mProjectView.CodeListViewFont);
// Upper-case formatting.
upperHexCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_HEX_DIGITS, false);
upperOpcodeCheckBox.Checked = mSettings.GetBool(
AppSettings.FMT_UPPER_OP_MNEMONIC, false);
upperPseudoOpCheckBox.Checked = mSettings.GetBool(
AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC, false);
upperACheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_A, false);
upperSCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_S, false);
upperXYCheckBox.Checked = mSettings.GetBool(AppSettings.FMT_UPPER_OPERAND_XY, false);
int clipIndex = mSettings.GetInt(AppSettings.CLIP_LINE_FORMAT, 0);
if (clipIndex >= 0 && clipIndex < clipboardFormatComboBox.Items.Count) {
clipboardFormatComboBox.SelectedIndex = clipIndex;
}
enableDebugCheckBox.Checked = mSettings.GetBool(AppSettings.DEBUG_MENU_ENABLED, false);
// Assemblers.
cc65PathTextBox.Text =
mSettings.GetString(AppSettings.ASM_CC65_EXECUTABLE, string.Empty);
merlin32PathTextBox.Text =
mSettings.GetString(AppSettings.ASM_MERLIN32_EXECUTABLE, string.Empty);
showAsmIdentCheckBox.Checked =
mSettings.GetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, false);
disableLabelLocalizationCheckBox.Checked =
mSettings.GetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION, false);
longLabelNewLineCheckBox.Checked =
mSettings.GetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE, false);
showCycleCountsCheckBox.Checked =
mSettings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false);
// Pseudo ops.
string opStrCereal = mSettings.GetString(AppSettings.FMT_PSEUDO_OP_NAMES, null);
if (!string.IsNullOrEmpty(opStrCereal)) {
PseudoOp.PseudoOpNames opNames = PseudoOp.PseudoOpNames.Deserialize(opStrCereal);
ImportPseudoOpNames(opNames);
} else {
// no data available, populate with blanks
//PseudoOp.PseudoOpNames opNames = PseudoOp.sDefaultPseudoOpNames;
ImportPseudoOpNames(new PseudoOp.PseudoOpNames());
}
PopulateWidthDisamSettings();
string exprMode = mSettings.GetString(AppSettings.FMT_EXPRESSION_MODE, string.Empty);
useMerlinExpressions.Checked =
(Asm65.Formatter.FormatConfig.ParseExpressionMode(exprMode) ==
Asm65.Formatter.FormatConfig.ExpressionMode.Merlin);
if (mInitialTab != Tab.Unknown) {
settingsTabControl.SelectTab((int)mInitialTab);
}
mDirty = false;
UpdateControls();
}
/// <summary>
/// Updates controls.
/// </summary>
private void UpdateControls() {
applyButton.Enabled = mDirty;
clearMerlin32Button.Enabled = !string.IsNullOrEmpty(merlin32PathTextBox.Text);
clearCc65Button.Enabled = !string.IsNullOrEmpty(cc65PathTextBox.Text);
}
/// <summary>
/// Sets the dirty flag and updates the controls.
/// </summary>
/// <param name="dirty">New value for dirty flag.</param>
private void SetDirty(bool dirty) {
mDirty = dirty;
UpdateControls();
}
private void okButton_Click(object sender, EventArgs e) {
ApplySettings();
}
private void applyButton_Click(object sender, EventArgs e) {
ApplySettings();
}
private void ApplySettings() {
PseudoOp.PseudoOpNames opNames = ExportPseudoOpNames();
string pseudoCereal = opNames.Serialize();
mSettings.SetString(AppSettings.FMT_PSEUDO_OP_NAMES, pseudoCereal);
mProjectView.SetAppSettings(mSettings);
AsmGen.AssemblerVersionCache.QueryVersions();
SetDirty(false);
}
#region Code View
/// <summary>
/// Updates the text on a show/hide column button.
/// </summary>
/// <param name="index">Column index.</param>
/// <param name="width">New width.</param>
private void SetShowHideButton(int index, int width) {
Button button = mColButtons[index];
string fmt = mColumnFormats[index];
string show = Properties.Resources.SHOW_COL;
string hide = Properties.Resources.HIDE_COL;
button.Text = string.Format(fmt, (width == 0) ? show : hide);
}
/// <summary>
/// Handler for all show/hide column buttons.
/// </summary>
/// <param name="sender">Identifies the button that was clicked.</param>
/// <param name="e">Stuff.</param>
private void ColumnVisibilityButtonClick(object sender, EventArgs e) {
int index = -1;
for (int i = 0; i < mColButtons.Length; i++) {
if (sender == mColButtons[i]) {
index = i;
break;
}
}
Debug.Assert(index != -1);
string widthStr = mSettings.GetString(AppSettings.CDLV_COL_WIDTHS, null);
Debug.Assert(!string.IsNullOrEmpty(widthStr));
ProjectView.CodeListColumnWidths widths =
ProjectView.CodeListColumnWidths.Deserialize(widthStr);
if (widths.Width[index] == 0) {
// Expand to default width. The default width changes when the font
// changes, so it's best to just reacquire the default width set as needed.
ProjectView.CodeListColumnWidths defaultWidths =
mProjectView.GetDefaultCodeListColumnWidths();
widths.Width[index] = defaultWidths.Width[index];
} else {
widths.Width[index] = 0;
}
widthStr = widths.Serialize();
mSettings.SetString(AppSettings.CDLV_COL_WIDTHS, widthStr);
SetShowHideButton(index, widths.Width[index]);
SetDirty(true);
}
private void selectFontButton_Click(object sender, EventArgs e) {
FontDialog dlg = new FontDialog();
dlg.Font = mProjectView.CodeListViewFont;
dlg.ShowEffects = false;
Debug.WriteLine("Showing font dialog...");
if (dlg.ShowDialog() != DialogResult.Cancel) {
FontConverter cvt = new FontConverter();
// Store invariant string, display localized string.
mSettings.SetString(AppSettings.CDLV_FONT, cvt.ConvertToInvariantString(dlg.Font));
currentFontDisplayLabel.Text = cvt.ConvertToString(dlg.Font);
SetDirty(true);
}
Debug.WriteLine("Font dialog done...");
dlg.Dispose();
}
private void upperHexCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_HEX_DIGITS, upperHexCheckBox.Checked);
SetDirty(true);
}
private void upperOpcodeCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_OP_MNEMONIC, upperOpcodeCheckBox.Checked);
SetDirty(true);
}
private void upperPseudoOpCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_PSEUDO_OP_MNEMONIC,
upperPseudoOpCheckBox.Checked);
SetDirty(true);
}
private void upperACheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_A, upperACheckBox.Checked);
SetDirty(true);
}
private void upperSCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_S, upperSCheckBox.Checked);
SetDirty(true);
}
private void upperXYCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.FMT_UPPER_OPERAND_XY, upperXYCheckBox.Checked);
SetDirty(true);
}
private void upperAllLowerButton_Click(object sender, EventArgs e) {
upperHexCheckBox.Checked =
upperOpcodeCheckBox.Checked =
upperPseudoOpCheckBox.Checked =
upperACheckBox.Checked =
upperSCheckBox.Checked =
upperXYCheckBox.Checked = false;
}
private void upperAllUpperButton_Click(object sender, EventArgs e) {
upperHexCheckBox.Checked =
upperOpcodeCheckBox.Checked =
upperPseudoOpCheckBox.Checked =
upperACheckBox.Checked =
upperSCheckBox.Checked =
upperXYCheckBox.Checked = true;
}
private void clipboardFormatComboBox_SelectedIndexChanged(object sender, EventArgs e) {
mSettings.SetInt(AppSettings.CLIP_LINE_FORMAT, clipboardFormatComboBox.SelectedIndex);
SetDirty(true);
}
private void enableDebugCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.DEBUG_MENU_ENABLED, enableDebugCheckBox.Checked);
SetDirty(true);
}
#endregion Code View
#region Asm Config
private void browseCc65Button_Click(object sender, EventArgs e) {
string pathName = BrowseForExecutable("cc65 CL", "cl65.exe");
if (pathName != null) {
cc65PathTextBox.Text = pathName;
mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, pathName);
}
}
private void cc65PathTextBox_TextChanged(object sender, EventArgs e) {
mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, cc65PathTextBox.Text);
SetDirty(true);
}
private void clearCc65Button_Click(object sender, EventArgs e) {
cc65PathTextBox.Text = string.Empty;
mSettings.SetString(AppSettings.ASM_CC65_EXECUTABLE, null);
SetDirty(true);
}
private void browseMerlin32Button_Click(object sender, EventArgs e) {
string pathName = BrowseForExecutable("Merlin Assembler", "Merlin32.exe");
if (pathName != null) {
merlin32PathTextBox.Text = pathName;
mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, pathName);
}
}
private void clearMerlin32Button_Click(object sender, EventArgs e) {
merlin32PathTextBox.Text = string.Empty;
mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, null);
SetDirty(true);
}
private void merlin32PathTextBox_TextChanged(object sender, EventArgs e) {
mSettings.SetString(AppSettings.ASM_MERLIN32_EXECUTABLE, merlin32PathTextBox.Text);
SetDirty(true);
}
/// <summary>
/// Creates a file dialog to search for a specific executable.
/// </summary>
/// <param name="prefix">Human-readable filter string for UI.</param>
/// <param name="name">Filename of executable.</param>
/// <returns>Path of executable, or null if dialog was canceled.</returns>
private string BrowseForExecutable(string prefix, string name) {
string pathName = null;
OpenFileDialog dlg = new OpenFileDialog();
dlg.FileName = name;
dlg.Filter = prefix + "|" + name;
dlg.RestoreDirectory = true;
if (dlg.ShowDialog() != DialogResult.Cancel) {
pathName = dlg.FileName;
SetDirty(true);
}
dlg.Dispose();
return pathName;
}
private void showAsmIdentCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.SRCGEN_ADD_IDENT_COMMENT, showAsmIdentCheckBox.Checked);
SetDirty(true);
}
private void disableLabelLocalizationCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.SRCGEN_DISABLE_LABEL_LOCALIZATION,
disableLabelLocalizationCheckBox.Checked);
SetDirty(true);
}
private void longLabelNewLineCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.SRCGEN_LONG_LABEL_NEW_LINE,
longLabelNewLineCheckBox.Checked);
SetDirty(true);
}
private void showCycleCountsCheckBox_CheckedChanged(object sender, EventArgs e) {
mSettings.SetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS,
showCycleCountsCheckBox.Checked);
SetDirty(true);
}
#endregion Asm Config
#region Display Format
/// <summary>
/// Populates the width disambiguation text boxes.
/// </summary>
private void PopulateWidthDisamSettings() {
// Operand width disambiguation. This is a little tricky -- we have to query all
// settings then set all controls, or the field-updated callback may interfere
// with us by changing AppSettings.
string opcSuffixAbs = mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS,
string.Empty);
string opcSuffixLong = mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_LONG,
string.Empty);
string opPrefixAbs = mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS,
string.Empty);
string opPrefixLong = mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG,
string.Empty);
disambSuffix16TextBox.Text = opcSuffixAbs;
disambSuffix24TextBox.Text = opcSuffixLong;
disambPrefix16TextBox.Text = opPrefixAbs;
disambPrefix24TextBox.Text = opPrefixLong;
}
/// <summary>
/// Sets all of the width disambiguation settings. Used for the quick-set buttons.
/// </summary>
private void SetWidthDisamSettings(string opcodeSuffixAbs, string opcodeSuffixLong,
string operandPrefixAbs, string operandPrefixLong) {
mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, opcodeSuffixAbs);
mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, opcodeSuffixLong);
mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, operandPrefixAbs);
mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, operandPrefixLong);
PopulateWidthDisamSettings();
}
// Called when text is typed.
private void WidthDisamControlChanged(object sender, EventArgs e) {
ExportWidthDisamSettings();
}
/// <summary>
/// Exports the current state of the width controls to the settings object.
/// </summary>
private void ExportWidthDisamSettings() {
mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, disambSuffix16TextBox.Text);
mSettings.SetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, disambSuffix24TextBox.Text);
mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_ABS, disambPrefix16TextBox.Text);
mSettings.SetString(AppSettings.FMT_OPERAND_PREFIX_LONG, disambPrefix24TextBox.Text);
SetDirty(true);
//Debug.WriteLine("disam: '" +
// mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_ABS, string.Empty) + "' '" +
// mSettings.GetString(AppSettings.FMT_OPCODE_SUFFIX_LONG, string.Empty) + "' '" +
// mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS, string.Empty) + "' '" +
// mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG, string.Empty) + "'");
}
private void shiftAfterAdjustCheckBox_CheckedChanged(object sender, EventArgs e) {
string mode = useMerlinExpressions.Checked ?
Asm65.Formatter.FormatConfig.ExpressionMode.Merlin.ToString() :
Asm65.Formatter.FormatConfig.ExpressionMode.Simple.ToString();
mSettings.SetString(AppSettings.FMT_EXPRESSION_MODE, mode);
SetDirty(true);
}
private void quickFmtDefaultButton_Click(object sender, EventArgs e) {
SetWidthDisamSettings(null, "l", "a:", "f:");
useMerlinExpressions.Checked = false;
// dirty flag set by change callbacks
}
private void quickFmtCc65Button_Click(object sender, EventArgs e) {
SetWidthDisamSettings(null, null, "a:", "f:");
useMerlinExpressions.Checked = false;
// dirty flag set by change callbacks
}
private void quickFmtMerlin32Button_Click(object sender, EventArgs e) {
SetWidthDisamSettings(":", "l", null, null);
useMerlinExpressions.Checked = true;
// dirty flag set by change callbacks
}
#endregion Display Format
#region Pseudo-Op
/// <summary>
/// Imports values from PseudoOpNames struct into text fields.
/// </summary>
private void ImportPseudoOpNames(PseudoOp.PseudoOpNames opNames) {
for (int i = 0; i < mPseudoNameMap.Length; i++) {
string str = (string)mPseudoNameMap[i].PropInfo.GetValue(opNames);
mPseudoNameMap[i].TextBox.Text = (str == null) ? string.Empty : str;
}
}
/// <summary>
/// Exports values from text fields to a PseudoOpNames object.
/// </summary>
private PseudoOp.PseudoOpNames ExportPseudoOpNames() {
PseudoOp.PseudoOpNames opNames = new PseudoOp.PseudoOpNames();
for (int i = 0; i < mPseudoNameMap.Length; i++) {
// NOTE: PseudoOpNames must be a class (not a struct) or this will fail.
// SetValue() would be invoked on a boxed copy that is discarded afterward.
mPseudoNameMap[i].PropInfo.SetValue(opNames, mPseudoNameMap[i].TextBox.Text);
}
return opNames;
}
// Invoked when text is changed in any pseudo-op text box.
private void PseudoOpTextChanged(object sender, EventArgs e) {
// Just set the dirty flag. The (somewhat expensive) export will happen
// on Apply/OK.
SetDirty(true);
}
private void quickPseudoDefaultButton_Click(object sender, EventArgs e) {
ImportPseudoOpNames(new PseudoOp.PseudoOpNames());
}
private void quickPseudoCc65Button_Click(object sender, EventArgs e) {
ImportPseudoOpNames(new PseudoOp.PseudoOpNames() {
EquDirective = "=",
OrgDirective = ".org",
DefineData1 = ".byte",
DefineData2 = ".word",
DefineData3 = ".faraddr",
DefineData4 = ".dword",
DefineBigData2 = ".dbyt",
Fill = ".res",
StrGeneric = ".byte",
StrNullTerm = ".asciiz",
});
}
private void quickPseudoMerlin32_Click(object sender, EventArgs e) {
// Note this doesn't quite match up with the Merlin generator, which uses
// the same pseudo-op for low/high ASCII but different string delimiters. We
// don't change the delimiters for the display list, so we want to tweak the
// opcode slightly.
//char hiAscii = '\u21e1';
char hiAscii = '\u2191';
ImportPseudoOpNames(new PseudoOp.PseudoOpNames() {
EquDirective = "equ",
OrgDirective = "org",
DefineData1 = "dfb",
DefineData2 = "dw",
DefineData3 = "adr",
DefineData4 = "adrl",
DefineBigData2 = "ddb",
Fill = "ds",
Dense = "hex",
StrGeneric = "asc",
StrGenericHi = "asc" + hiAscii,
StrReverse = "rev",
StrReverseHi = "rev" + hiAscii,
StrLen8 = "str",
StrLen8Hi = "str" + hiAscii,
StrLen16 = "strl",
StrLen16Hi = "strl" + hiAscii,
StrDci = "dci",
StrDciHi = "dci" + hiAscii,
});
}
#endregion Pseudo-Op
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

153
SourceGen/AppForms/EditComment.Designer.cs generated Normal file
View File

@ -0,0 +1,153 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class EditComment {
/// <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.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.instructionLabel = new System.Windows.Forms.Label();
this.textBox1 = new System.Windows.Forms.TextBox();
this.asciiOnlyLabel = new System.Windows.Forms.Label();
this.maxLengthLabel = new System.Windows.Forms.Label();
this.numCharsLabel = new System.Windows.Forms.Label();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(426, 105);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 6;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(345, 105);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 5;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// instructionLabel
//
this.instructionLabel.AutoSize = true;
this.instructionLabel.Location = new System.Drawing.Point(13, 13);
this.instructionLabel.Name = "instructionLabel";
this.instructionLabel.Size = new System.Drawing.Size(81, 13);
this.instructionLabel.TabIndex = 0;
this.instructionLabel.Text = "Enter comment:";
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(13, 30);
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(488, 20);
this.textBox1.TabIndex = 1;
this.textBox1.TextChanged += new System.EventHandler(this.textBox1_TextChanged);
//
// asciiOnlyLabel
//
this.asciiOnlyLabel.AutoSize = true;
this.asciiOnlyLabel.Location = new System.Drawing.Point(13, 57);
this.asciiOnlyLabel.Name = "asciiOnlyLabel";
this.asciiOnlyLabel.Size = new System.Drawing.Size(145, 13);
this.asciiOnlyLabel.TabIndex = 2;
this.asciiOnlyLabel.Text = "• ASCII-only is recommended";
//
// maxLengthLabel
//
this.maxLengthLabel.AutoSize = true;
this.maxLengthLabel.Location = new System.Drawing.Point(13, 74);
this.maxLengthLabel.Name = "maxLengthLabel";
this.maxLengthLabel.Size = new System.Drawing.Size(281, 13);
this.maxLengthLabel.TabIndex = 3;
this.maxLengthLabel.Text = "• Limit to 52 or fewer characters for nice 80-column output";
//
// numCharsLabel
//
this.numCharsLabel.Location = new System.Drawing.Point(386, 53);
this.numCharsLabel.Name = "numCharsLabel";
this.numCharsLabel.Size = new System.Drawing.Size(115, 23);
this.numCharsLabel.TabIndex = 4;
this.numCharsLabel.Text = "{0} characters";
this.numCharsLabel.TextAlign = System.Drawing.ContentAlignment.TopRight;
//
// EditComment
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(513, 140);
this.Controls.Add(this.numCharsLabel);
this.Controls.Add(this.maxLengthLabel);
this.Controls.Add(this.asciiOnlyLabel);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.instructionLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditComment";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Comment";
this.Load += new System.EventHandler(this.EditComment_Load);
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Label instructionLabel;
private System.Windows.Forms.TextBox textBox1;
private System.Windows.Forms.Label asciiOnlyLabel;
private System.Windows.Forms.Label maxLengthLabel;
private System.Windows.Forms.Label numCharsLabel;
}
}

View File

@ -0,0 +1,72 @@
/*
* Copyright 2018 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 System;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
namespace SourceGen.AppForms {
public partial class EditComment : Form {
/// <summary>
/// Comment string being edited.
/// </summary>
public string Comment { get; set; }
private string mNumCharsFormat;
private Color mDefaultLabelColor;
private const int RECOMMENDED_MAX_LENGTH = 52;
public EditComment() {
InitializeComponent();
}
private void EditComment_Load(object sender, EventArgs e) {
mDefaultLabelColor = asciiOnlyLabel.ForeColor;
// Extract the format string from the label.
mNumCharsFormat = numCharsLabel.Text;
textBox1.Text = Comment;
UpdateLengthLabel();
}
private void UpdateLengthLabel() {
numCharsLabel.Text = string.Format(mNumCharsFormat, textBox1.Text.Length);
}
private void textBox1_TextChanged(object sender, EventArgs e) {
UpdateLengthLabel();
if (!CommonUtil.TextUtil.IsPrintableAscii(textBox1.Text)) {
asciiOnlyLabel.ForeColor = Color.Red;
} else {
asciiOnlyLabel.ForeColor = mDefaultLabelColor;
}
if (textBox1.Text.Length > RECOMMENDED_MAX_LENGTH) {
maxLengthLabel.ForeColor = Color.Red;
} else {
maxLengthLabel.ForeColor = mDefaultLabelColor;
}
}
private void okButton_Click(object sender, EventArgs e) {
Comment = textBox1.Text;
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

559
SourceGen/AppForms/EditData.Designer.cs generated Normal file
View File

@ -0,0 +1,559 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class EditData {
/// <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.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.selectFormatLabel = new System.Windows.Forms.Label();
this.rawDataSectionLabel = new System.Windows.Forms.Label();
this.radioSingleBytes = new System.Windows.Forms.RadioButton();
this.radio16BitBig = new System.Windows.Forms.RadioButton();
this.radio16BitLittle = new System.Windows.Forms.RadioButton();
this.radio24BitLittle = new System.Windows.Forms.RadioButton();
this.radio32BitLittle = new System.Windows.Forms.RadioButton();
this.radioSimpleDataBinary = new System.Windows.Forms.RadioButton();
this.radioSimpleDataDecimal = new System.Windows.Forms.RadioButton();
this.radioSimpleDataHex = new System.Windows.Forms.RadioButton();
this.radioDenseHex = new System.Windows.Forms.RadioButton();
this.radioFill = new System.Windows.Forms.RadioButton();
this.horizontalLine1 = new System.Windows.Forms.Label();
this.horizontalLine2 = new System.Windows.Forms.Label();
this.stringSectionLabel = new System.Windows.Forms.Label();
this.horizontalLine3 = new System.Windows.Forms.Label();
this.radioStringMixed = new System.Windows.Forms.RadioButton();
this.radioStringMixedReverse = new System.Windows.Forms.RadioButton();
this.radioStringNullTerm = new System.Windows.Forms.RadioButton();
this.radioStringLen8 = new System.Windows.Forms.RadioButton();
this.radioStringLen16 = new System.Windows.Forms.RadioButton();
this.radioStringDci = new System.Windows.Forms.RadioButton();
this.symbolEntryTextBox = new System.Windows.Forms.TextBox();
this.symbolPartPanel = new System.Windows.Forms.Panel();
this.radioSymbolPartBank = new System.Windows.Forms.RadioButton();
this.radioSymbolPartHigh = new System.Windows.Forms.RadioButton();
this.radioSymbolPartLow = new System.Windows.Forms.RadioButton();
this.radioSimpleDataAscii = new System.Windows.Forms.RadioButton();
this.radioDefaultFormat = new System.Windows.Forms.RadioButton();
this.simpleDisplayAsGroupBox = new System.Windows.Forms.GroupBox();
this.radioSimpleDataAddress = new System.Windows.Forms.RadioButton();
this.radioSimpleDataSymbolic = new System.Windows.Forms.RadioButton();
this.label1 = new System.Windows.Forms.Label();
this.symbolPartPanel.SuspendLayout();
this.simpleDisplayAsGroupBox.SuspendLayout();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(438, 461);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 23;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(357, 461);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 22;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// selectFormatLabel
//
this.selectFormatLabel.AutoSize = true;
this.selectFormatLabel.Location = new System.Drawing.Point(12, 9);
this.selectFormatLabel.Name = "selectFormatLabel";
this.selectFormatLabel.Size = new System.Drawing.Size(253, 13);
this.selectFormatLabel.TabIndex = 0;
this.selectFormatLabel.Text = "Select data format ({0} bytes selected in {1} groups):";
//
// rawDataSectionLabel
//
this.rawDataSectionLabel.AutoSize = true;
this.rawDataSectionLabel.Location = new System.Drawing.Point(12, 63);
this.rawDataSectionLabel.Name = "rawDataSectionLabel";
this.rawDataSectionLabel.Size = new System.Drawing.Size(64, 13);
this.rawDataSectionLabel.TabIndex = 2;
this.rawDataSectionLabel.Text = "Simple Data";
//
// radioSingleBytes
//
this.radioSingleBytes.AutoSize = true;
this.radioSingleBytes.Location = new System.Drawing.Point(14, 89);
this.radioSingleBytes.Name = "radioSingleBytes";
this.radioSingleBytes.Size = new System.Drawing.Size(82, 17);
this.radioSingleBytes.TabIndex = 4;
this.radioSingleBytes.TabStop = true;
this.radioSingleBytes.Text = "Single &bytes";
this.radioSingleBytes.UseVisualStyleBackColor = true;
this.radioSingleBytes.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radio16BitBig
//
this.radio16BitBig.AutoSize = true;
this.radio16BitBig.Location = new System.Drawing.Point(14, 135);
this.radio16BitBig.Name = "radio16BitBig";
this.radio16BitBig.Size = new System.Drawing.Size(137, 17);
this.radio16BitBig.TabIndex = 6;
this.radio16BitBig.TabStop = true;
this.radio16BitBig.Text = "16-bit words, big-endian";
this.radio16BitBig.UseVisualStyleBackColor = true;
this.radio16BitBig.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radio16BitLittle
//
this.radio16BitLittle.AutoSize = true;
this.radio16BitLittle.Location = new System.Drawing.Point(14, 112);
this.radio16BitLittle.Name = "radio16BitLittle";
this.radio16BitLittle.Size = new System.Drawing.Size(141, 17);
this.radio16BitLittle.TabIndex = 5;
this.radio16BitLittle.TabStop = true;
this.radio16BitLittle.Text = "16-bit words, little-endian";
this.radio16BitLittle.UseVisualStyleBackColor = true;
this.radio16BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radio24BitLittle
//
this.radio24BitLittle.AutoSize = true;
this.radio24BitLittle.Location = new System.Drawing.Point(14, 158);
this.radio24BitLittle.Name = "radio24BitLittle";
this.radio24BitLittle.Size = new System.Drawing.Size(141, 17);
this.radio24BitLittle.TabIndex = 7;
this.radio24BitLittle.TabStop = true;
this.radio24BitLittle.Text = "24-bit words, little-endian";
this.radio24BitLittle.UseVisualStyleBackColor = true;
this.radio24BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radio32BitLittle
//
this.radio32BitLittle.AutoSize = true;
this.radio32BitLittle.Location = new System.Drawing.Point(14, 181);
this.radio32BitLittle.Name = "radio32BitLittle";
this.radio32BitLittle.Size = new System.Drawing.Size(141, 17);
this.radio32BitLittle.TabIndex = 8;
this.radio32BitLittle.TabStop = true;
this.radio32BitLittle.Text = "32-bit words, little-endian";
this.radio32BitLittle.UseVisualStyleBackColor = true;
this.radio32BitLittle.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioSimpleDataBinary
//
this.radioSimpleDataBinary.AutoSize = true;
this.radioSimpleDataBinary.Location = new System.Drawing.Point(6, 64);
this.radioSimpleDataBinary.Name = "radioSimpleDataBinary";
this.radioSimpleDataBinary.Size = new System.Drawing.Size(54, 17);
this.radioSimpleDataBinary.TabIndex = 4;
this.radioSimpleDataBinary.TabStop = true;
this.radioSimpleDataBinary.Text = "Binary";
this.radioSimpleDataBinary.UseVisualStyleBackColor = true;
this.radioSimpleDataBinary.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged);
//
// radioSimpleDataDecimal
//
this.radioSimpleDataDecimal.AutoSize = true;
this.radioSimpleDataDecimal.Location = new System.Drawing.Point(6, 41);
this.radioSimpleDataDecimal.Name = "radioSimpleDataDecimal";
this.radioSimpleDataDecimal.Size = new System.Drawing.Size(63, 17);
this.radioSimpleDataDecimal.TabIndex = 3;
this.radioSimpleDataDecimal.TabStop = true;
this.radioSimpleDataDecimal.Text = "Decimal";
this.radioSimpleDataDecimal.UseVisualStyleBackColor = true;
this.radioSimpleDataDecimal.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged);
//
// radioSimpleDataHex
//
this.radioSimpleDataHex.AutoSize = true;
this.radioSimpleDataHex.Location = new System.Drawing.Point(6, 18);
this.radioSimpleDataHex.Name = "radioSimpleDataHex";
this.radioSimpleDataHex.Size = new System.Drawing.Size(44, 17);
this.radioSimpleDataHex.TabIndex = 2;
this.radioSimpleDataHex.TabStop = true;
this.radioSimpleDataHex.Text = "Hex";
this.radioSimpleDataHex.UseVisualStyleBackColor = true;
this.radioSimpleDataHex.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged);
//
// radioDenseHex
//
this.radioDenseHex.AutoSize = true;
this.radioDenseHex.Location = new System.Drawing.Point(14, 247);
this.radioDenseHex.Name = "radioDenseHex";
this.radioDenseHex.Size = new System.Drawing.Size(130, 17);
this.radioDenseHex.TabIndex = 11;
this.radioDenseHex.TabStop = true;
this.radioDenseHex.Text = "Densely-&packed bytes";
this.radioDenseHex.UseVisualStyleBackColor = true;
this.radioDenseHex.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioFill
//
this.radioFill.AutoSize = true;
this.radioFill.Location = new System.Drawing.Point(14, 270);
this.radioFill.Name = "radioFill";
this.radioFill.Size = new System.Drawing.Size(88, 17);
this.radioFill.TabIndex = 12;
this.radioFill.TabStop = true;
this.radioFill.Text = "&Fill with value";
this.radioFill.UseVisualStyleBackColor = true;
this.radioFill.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// horizontalLine1
//
this.horizontalLine1.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.horizontalLine1.Location = new System.Drawing.Point(12, 81);
this.horizontalLine1.Name = "horizontalLine1";
this.horizontalLine1.Size = new System.Drawing.Size(500, 2);
this.horizontalLine1.TabIndex = 3;
//
// horizontalLine2
//
this.horizontalLine2.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.horizontalLine2.Location = new System.Drawing.Point(12, 239);
this.horizontalLine2.Name = "horizontalLine2";
this.horizontalLine2.Size = new System.Drawing.Size(320, 2);
this.horizontalLine2.TabIndex = 10;
//
// stringSectionLabel
//
this.stringSectionLabel.AutoSize = true;
this.stringSectionLabel.Location = new System.Drawing.Point(12, 308);
this.stringSectionLabel.Name = "stringSectionLabel";
this.stringSectionLabel.Size = new System.Drawing.Size(34, 13);
this.stringSectionLabel.TabIndex = 13;
this.stringSectionLabel.Text = "String";
//
// horizontalLine3
//
this.horizontalLine3.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;
this.horizontalLine3.Location = new System.Drawing.Point(12, 326);
this.horizontalLine3.Name = "horizontalLine3";
this.horizontalLine3.Size = new System.Drawing.Size(320, 2);
this.horizontalLine3.TabIndex = 14;
//
// radioStringMixed
//
this.radioStringMixed.AutoSize = true;
this.radioStringMixed.Location = new System.Drawing.Point(14, 334);
this.radioStringMixed.Name = "radioStringMixed";
this.radioStringMixed.Size = new System.Drawing.Size(257, 17);
this.radioStringMixed.TabIndex = 15;
this.radioStringMixed.TabStop = true;
this.radioStringMixed.Text = "Mixed ASCII ({0} bytes) and non-ASCII ({1} bytes)";
this.radioStringMixed.UseVisualStyleBackColor = true;
this.radioStringMixed.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioStringMixedReverse
//
this.radioStringMixedReverse.AutoSize = true;
this.radioStringMixedReverse.Location = new System.Drawing.Point(14, 357);
this.radioStringMixedReverse.Name = "radioStringMixedReverse";
this.radioStringMixedReverse.Size = new System.Drawing.Size(275, 17);
this.radioStringMixedReverse.TabIndex = 16;
this.radioStringMixedReverse.TabStop = true;
this.radioStringMixedReverse.Text = "Reversed ASCII ({0} bytes) and non-ASCII ({1} bytes)";
this.radioStringMixedReverse.UseVisualStyleBackColor = true;
this.radioStringMixedReverse.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioStringNullTerm
//
this.radioStringNullTerm.AutoSize = true;
this.radioStringNullTerm.Location = new System.Drawing.Point(14, 381);
this.radioStringNullTerm.Name = "radioStringNullTerm";
this.radioStringNullTerm.Size = new System.Drawing.Size(151, 17);
this.radioStringNullTerm.TabIndex = 17;
this.radioStringNullTerm.TabStop = true;
this.radioStringNullTerm.Text = "Null-terminated strings ({0})";
this.radioStringNullTerm.UseVisualStyleBackColor = true;
this.radioStringNullTerm.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioStringLen8
//
this.radioStringLen8.AutoSize = true;
this.radioStringLen8.Location = new System.Drawing.Point(14, 405);
this.radioStringLen8.Name = "radioStringLen8";
this.radioStringLen8.Size = new System.Drawing.Size(197, 17);
this.radioStringLen8.TabIndex = 18;
this.radioStringLen8.TabStop = true;
this.radioStringLen8.Text = "Strings prefixed with 8-bit length ({0})";
this.radioStringLen8.UseVisualStyleBackColor = true;
this.radioStringLen8.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioStringLen16
//
this.radioStringLen16.AutoSize = true;
this.radioStringLen16.Location = new System.Drawing.Point(14, 429);
this.radioStringLen16.Name = "radioStringLen16";
this.radioStringLen16.Size = new System.Drawing.Size(203, 17);
this.radioStringLen16.TabIndex = 19;
this.radioStringLen16.TabStop = true;
this.radioStringLen16.Text = "Strings prefixed with 16-bit length ({0})";
this.radioStringLen16.UseVisualStyleBackColor = true;
this.radioStringLen16.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// radioStringDci
//
this.radioStringDci.AutoSize = true;
this.radioStringDci.Location = new System.Drawing.Point(14, 453);
this.radioStringDci.Name = "radioStringDci";
this.radioStringDci.Size = new System.Drawing.Size(170, 17);
this.radioStringDci.TabIndex = 20;
this.radioStringDci.TabStop = true;
this.radioStringDci.Text = "Dextral character inverted ({0})";
this.radioStringDci.UseVisualStyleBackColor = true;
this.radioStringDci.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// symbolEntryTextBox
//
this.symbolEntryTextBox.Location = new System.Drawing.Point(108, 62);
this.symbolEntryTextBox.Name = "symbolEntryTextBox";
this.symbolEntryTextBox.Size = new System.Drawing.Size(200, 20);
this.symbolEntryTextBox.TabIndex = 1;
this.symbolEntryTextBox.TextChanged += new System.EventHandler(this.symbolEntryTextBox_TextChanged);
//
// symbolPartPanel
//
this.symbolPartPanel.Controls.Add(this.radioSymbolPartBank);
this.symbolPartPanel.Controls.Add(this.radioSymbolPartHigh);
this.symbolPartPanel.Controls.Add(this.radioSymbolPartLow);
this.symbolPartPanel.Location = new System.Drawing.Point(108, 86);
this.symbolPartPanel.Name = "symbolPartPanel";
this.symbolPartPanel.Size = new System.Drawing.Size(200, 20);
this.symbolPartPanel.TabIndex = 27;
//
// radioSymbolPartBank
//
this.radioSymbolPartBank.AutoSize = true;
this.radioSymbolPartBank.Location = new System.Drawing.Point(103, 1);
this.radioSymbolPartBank.Name = "radioSymbolPartBank";
this.radioSymbolPartBank.Size = new System.Drawing.Size(50, 17);
this.radioSymbolPartBank.TabIndex = 2;
this.radioSymbolPartBank.TabStop = true;
this.radioSymbolPartBank.Text = "Bank";
this.radioSymbolPartBank.UseVisualStyleBackColor = true;
this.radioSymbolPartBank.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged);
//
// radioSymbolPartHigh
//
this.radioSymbolPartHigh.AutoSize = true;
this.radioSymbolPartHigh.Location = new System.Drawing.Point(52, 1);
this.radioSymbolPartHigh.Name = "radioSymbolPartHigh";
this.radioSymbolPartHigh.Size = new System.Drawing.Size(47, 17);
this.radioSymbolPartHigh.TabIndex = 1;
this.radioSymbolPartHigh.TabStop = true;
this.radioSymbolPartHigh.Text = "High";
this.radioSymbolPartHigh.UseVisualStyleBackColor = true;
this.radioSymbolPartHigh.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged);
//
// radioSymbolPartLow
//
this.radioSymbolPartLow.AutoSize = true;
this.radioSymbolPartLow.Location = new System.Drawing.Point(4, 1);
this.radioSymbolPartLow.Name = "radioSymbolPartLow";
this.radioSymbolPartLow.Size = new System.Drawing.Size(45, 17);
this.radioSymbolPartLow.TabIndex = 0;
this.radioSymbolPartLow.TabStop = true;
this.radioSymbolPartLow.Text = "Low";
this.radioSymbolPartLow.UseVisualStyleBackColor = true;
this.radioSymbolPartLow.CheckedChanged += new System.EventHandler(this.PartGroup_CheckedChanged);
//
// radioSimpleDataAscii
//
this.radioSimpleDataAscii.AutoSize = true;
this.radioSimpleDataAscii.Location = new System.Drawing.Point(6, 87);
this.radioSimpleDataAscii.Name = "radioSimpleDataAscii";
this.radioSimpleDataAscii.Size = new System.Drawing.Size(52, 17);
this.radioSimpleDataAscii.TabIndex = 5;
this.radioSimpleDataAscii.TabStop = true;
this.radioSimpleDataAscii.Text = "ASCII";
this.radioSimpleDataAscii.UseVisualStyleBackColor = true;
this.radioSimpleDataAscii.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged);
//
// radioDefaultFormat
//
this.radioDefaultFormat.AutoSize = true;
this.radioDefaultFormat.Location = new System.Drawing.Point(14, 30);
this.radioDefaultFormat.Name = "radioDefaultFormat";
this.radioDefaultFormat.Size = new System.Drawing.Size(59, 17);
this.radioDefaultFormat.TabIndex = 1;
this.radioDefaultFormat.TabStop = true;
this.radioDefaultFormat.Text = "&Default";
this.radioDefaultFormat.UseVisualStyleBackColor = true;
this.radioDefaultFormat.CheckedChanged += new System.EventHandler(this.MainGroup_CheckedChanged);
//
// simpleDisplayAsGroupBox
//
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataAddress);
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataSymbolic);
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataAscii);
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataHex);
this.simpleDisplayAsGroupBox.Controls.Add(this.symbolPartPanel);
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataBinary);
this.simpleDisplayAsGroupBox.Controls.Add(this.symbolEntryTextBox);
this.simpleDisplayAsGroupBox.Controls.Add(this.radioSimpleDataDecimal);
this.simpleDisplayAsGroupBox.Location = new System.Drawing.Point(190, 84);
this.simpleDisplayAsGroupBox.Name = "simpleDisplayAsGroupBox";
this.simpleDisplayAsGroupBox.Size = new System.Drawing.Size(320, 114);
this.simpleDisplayAsGroupBox.TabIndex = 24;
this.simpleDisplayAsGroupBox.TabStop = false;
this.simpleDisplayAsGroupBox.Text = "Display As...";
//
// radioSimpleDataAddress
//
this.radioSimpleDataAddress.AutoSize = true;
this.radioSimpleDataAddress.Location = new System.Drawing.Point(89, 18);
this.radioSimpleDataAddress.Name = "radioSimpleDataAddress";
this.radioSimpleDataAddress.Size = new System.Drawing.Size(63, 17);
this.radioSimpleDataAddress.TabIndex = 6;
this.radioSimpleDataAddress.TabStop = true;
this.radioSimpleDataAddress.Text = "&Address";
this.radioSimpleDataAddress.UseVisualStyleBackColor = true;
//
// radioSimpleDataSymbolic
//
this.radioSimpleDataSymbolic.AutoSize = true;
this.radioSimpleDataSymbolic.Location = new System.Drawing.Point(89, 41);
this.radioSimpleDataSymbolic.Name = "radioSimpleDataSymbolic";
this.radioSimpleDataSymbolic.Size = new System.Drawing.Size(115, 17);
this.radioSimpleDataSymbolic.TabIndex = 7;
this.radioSimpleDataSymbolic.TabStop = true;
this.radioSimpleDataSymbolic.Text = "&Symbolic reference";
this.radioSimpleDataSymbolic.UseVisualStyleBackColor = true;
this.radioSimpleDataSymbolic.CheckedChanged += new System.EventHandler(this.SimpleDisplay_CheckedChanged);
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(12, 221);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(54, 13);
this.label1.TabIndex = 9;
this.label1.Text = "Bulk Data";
//
// EditData
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(525, 496);
this.Controls.Add(this.label1);
this.Controls.Add(this.simpleDisplayAsGroupBox);
this.Controls.Add(this.radioDefaultFormat);
this.Controls.Add(this.radioStringDci);
this.Controls.Add(this.radioStringLen16);
this.Controls.Add(this.radioStringLen8);
this.Controls.Add(this.radioStringNullTerm);
this.Controls.Add(this.radioStringMixedReverse);
this.Controls.Add(this.radioStringMixed);
this.Controls.Add(this.stringSectionLabel);
this.Controls.Add(this.horizontalLine1);
this.Controls.Add(this.horizontalLine2);
this.Controls.Add(this.horizontalLine3);
this.Controls.Add(this.radioFill);
this.Controls.Add(this.radioDenseHex);
this.Controls.Add(this.radio32BitLittle);
this.Controls.Add(this.radio24BitLittle);
this.Controls.Add(this.radio16BitLittle);
this.Controls.Add(this.radio16BitBig);
this.Controls.Add(this.radioSingleBytes);
this.Controls.Add(this.rawDataSectionLabel);
this.Controls.Add(this.selectFormatLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditData";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Data";
this.Load += new System.EventHandler(this.EditData_Load);
this.Shown += new System.EventHandler(this.EditData_Shown);
this.symbolPartPanel.ResumeLayout(false);
this.symbolPartPanel.PerformLayout();
this.simpleDisplayAsGroupBox.ResumeLayout(false);
this.simpleDisplayAsGroupBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Label selectFormatLabel;
private System.Windows.Forms.Label rawDataSectionLabel;
private System.Windows.Forms.RadioButton radioSingleBytes;
private System.Windows.Forms.RadioButton radio16BitBig;
private System.Windows.Forms.RadioButton radio16BitLittle;
private System.Windows.Forms.RadioButton radio24BitLittle;
private System.Windows.Forms.RadioButton radio32BitLittle;
private System.Windows.Forms.RadioButton radioSimpleDataBinary;
private System.Windows.Forms.RadioButton radioSimpleDataDecimal;
private System.Windows.Forms.RadioButton radioSimpleDataHex;
private System.Windows.Forms.RadioButton radioDenseHex;
private System.Windows.Forms.RadioButton radioFill;
private System.Windows.Forms.Label horizontalLine1;
private System.Windows.Forms.Label horizontalLine2;
private System.Windows.Forms.Label stringSectionLabel;
private System.Windows.Forms.Label horizontalLine3;
private System.Windows.Forms.RadioButton radioStringMixed;
private System.Windows.Forms.RadioButton radioStringMixedReverse;
private System.Windows.Forms.RadioButton radioStringNullTerm;
private System.Windows.Forms.RadioButton radioStringLen8;
private System.Windows.Forms.RadioButton radioStringLen16;
private System.Windows.Forms.RadioButton radioStringDci;
private System.Windows.Forms.TextBox symbolEntryTextBox;
private System.Windows.Forms.Panel symbolPartPanel;
private System.Windows.Forms.RadioButton radioSymbolPartBank;
private System.Windows.Forms.RadioButton radioSymbolPartHigh;
private System.Windows.Forms.RadioButton radioSymbolPartLow;
private System.Windows.Forms.RadioButton radioSimpleDataAscii;
private System.Windows.Forms.RadioButton radioDefaultFormat;
private System.Windows.Forms.GroupBox simpleDisplayAsGroupBox;
private System.Windows.Forms.RadioButton radioSimpleDataSymbolic;
private System.Windows.Forms.Label label1;
private System.Windows.Forms.RadioButton radioSimpleDataAddress;
}
}

View File

@ -0,0 +1,953 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows.Forms;
using CommonUtil;
namespace SourceGen.AppForms {
public partial class EditData : Form {
/// <summary>
/// Result set that describes the formatting to perform. Not all regions will have
/// the same format, e.g. the "mixed ASCII" mode will alternate strings and bytes
/// (rather than a dedicated "mixed ASCII" format type).
/// </summary>
public SortedList<int, FormatDescriptor> Results { get; private set; }
/// <summary>
/// Selected offsets. An otherwise contiguous range of offsets can be broken up
/// by user-specified labels and address discontinuities, so this needs to be
/// processed by range.
/// </summary>
public TypedRangeSet Selection { private get; set; }
/// <summary>
/// FormatDescriptor from the first offset. May be null if the offset doesn't
/// have a format descriptor specified. This will be used to configure the
/// dialog controls if the format is suited to the selection. The goal is to
/// make single-item editing work as expected.
/// </summary>
public FormatDescriptor FirstFormatDescriptor { private get; set; }
/// <summary>
/// Raw file data.
/// </summary>
private byte[] mFileData;
/// <summary>
/// Symbol table to use when resolving symbolic values.
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Formatter to use when displaying addresses and hex values.
/// </summary>
private Asm65.Formatter mFormatter;
/// <summary>
/// Set this during initial control configuration, so we know to ignore the CheckedChanged
/// events.
/// </summary>
private bool mIsInitialSetup;
/// <summary>
/// Set to true if, during the initial setup, the format defined by FirstFormatDescriptor
/// was unavailable.
/// </summary>
private bool mPreferredFormatUnavailable;
public EditData(byte[] fileData, SymbolTable symbolTable, Asm65.Formatter formatter) {
InitializeComponent();
mFileData = fileData;
mSymbolTable = symbolTable;
mFormatter = formatter;
//Results = new List<Result>();
}
private void EditData_Load(object sender, EventArgs e) {
DateTime startWhen = DateTime.Now;
mIsInitialSetup = true;
// Determine which of the various options is suitable for the selected offsets.
// Disable any radio buttons that won't work.
AnalyzeRanges();
// Configure the dialog from the FormatDescriptor, if one is available.
Debug.WriteLine("First FD: " + FirstFormatDescriptor);
SetControlsFromDescriptor(FirstFormatDescriptor);
if (mPreferredFormatUnavailable) {
// This can happen when e.g. a bunch of stuff is formatted as null-terminated
// strings. We don't recognize a lone zero as a string, but we allow it if
// it's next to a bunch of others. If you come back later and try to format
// just that one byte, you end up here.
// TODO(maybe): make it more obvious what's going on?
Debug.WriteLine("NOTE: preferred format unavailable");
}
mIsInitialSetup = false;
UpdateControls();
Debug.WriteLine("EditData dialog load time: " +
(DateTime.Now - startWhen).TotalMilliseconds + " ms");
}
private void EditData_Shown(object sender, EventArgs e) {
// Start with the focus in the text box if the initial format allows for a
// symbolic reference. This way they can start typing immediately.
if (simpleDisplayAsGroupBox.Enabled) {
symbolEntryTextBox.Focus();
}
}
/// <summary>
/// Handles CheckedChanged event for all radio buttons in main group. This will
/// fire twice when a radio button is clicked (once to un-check the old, once
/// to check the new).
/// </summary>
private void MainGroup_CheckedChanged(object sender, EventArgs e) {
// Enable/disable the style group and the low/high/bank radio group.
// Update preview window.
UpdateControls();
}
/// <summary>
/// Handles CheckedChanged event for radio buttons in the simple-data "display as"
/// group box.
/// </summary>
private void SimpleDisplay_CheckedChanged(object sender, EventArgs e) {
// Enable/disable the low/high/bank radio group.
UpdateControls();
}
/// <summary>
/// Handles CheckedChanged event for all radio buttons in symbol-part group.
/// </summary>
private void PartGroup_CheckedChanged(object sender, EventArgs e) {
// not currently using a preview window; could add one for single items?
}
private void symbolEntryTextBox_TextChanged(object sender, EventArgs e) {
// Make sure Symbol is checked if they're typing text in.
Debug.Assert(radioSimpleDataSymbolic.Enabled);
radioSimpleDataSymbolic.Checked = true;
// Update OK button based on symbol validity.
UpdateControls();
}
private void okButton_Click(object sender, EventArgs e) {
CreateDescriptorListFromControls();
FormatDescriptor.DebugDumpSortedList(Results);
}
/// <summary>
/// Updates all of the controls to reflect the current internal state.
/// </summary>
private void UpdateControls() {
if (mIsInitialSetup) {
return;
}
// Configure the simple data "display as" style box.
bool wantStyle = false;
int simpleWidth = -1;
bool isBigEndian = false;
if (radioSingleBytes.Checked) {
wantStyle = true;
simpleWidth = 1;
} else if (radio16BitLittle.Checked) {
wantStyle = true;
simpleWidth = 2;
} else if (radio16BitBig.Checked) {
wantStyle = true;
simpleWidth = 2;
isBigEndian = true;
} else if (radio24BitLittle.Checked) {
wantStyle = true;
simpleWidth = 3;
} else if (radio32BitLittle.Checked) {
wantStyle = true;
simpleWidth = 4;
}
bool focusOnSymbol = !simpleDisplayAsGroupBox.Enabled && wantStyle;
simpleDisplayAsGroupBox.Enabled = wantStyle;
if (wantStyle) {
// TODO(soon): compute on first need and save results; this is getting called
// 2x as radio buttons are hit, and might be slow on large data sets
radioSimpleDataAscii.Enabled = IsRawAsciiCompatible(simpleWidth, isBigEndian);
}
// Enable the symbolic reference entry box if the "display as" group is enabled.
// That way instead of "click 16-bit", "click symbol", "enter symbol", the user
// can skip the second step.
symbolEntryTextBox.Enabled = simpleDisplayAsGroupBox.Enabled;
symbolPartPanel.Enabled = radioSimpleDataSymbolic.Checked;
// If we just enabled the group box, set the focus on the symbol entry box. This
// removes another click from the steps, though it's a bit aggressive if you're
// trying to arrow your way through the items.
if (focusOnSymbol) {
symbolEntryTextBox.Focus();
}
bool isOk = true;
if (radioSimpleDataSymbolic.Checked) {
// Just check for correct format. References to non-existent labels are allowed.
isOk = Asm65.Label.ValidateLabel(symbolEntryTextBox.Text);
// Actually, let's discourage references to auto-labels.
if (isOk && mSymbolTable.TryGetValue(symbolEntryTextBox.Text, out Symbol sym)) {
isOk = sym.SymbolSource != Symbol.Source.Auto;
}
}
okButton.Enabled = isOk;
}
/// <summary>
/// Analyzes the selection to see which data formatting options are suitable.
/// Disables radio buttons and updates labels.
///
/// Call this once, when the dialog is first loaded.
/// </summary>
private void AnalyzeRanges() {
Debug.Assert(Selection.Count != 0);
string fmt = (Selection.RangeCount == 1) ?
Properties.Resources.FMT_FORMAT_SINGLE_GROUP :
Properties.Resources.FMT_FORMAT_MULTIPLE_GROUPS;
selectFormatLabel.Text = string.Format(fmt, Selection.Count, Selection.RangeCount);
IEnumerator<TypedRangeSet.TypedRange> iter = Selection.RangeListIterator;
int mixedAsciiOkCount = 0;
int mixedAsciiNotCount = 0;
int nullTermStringCount = 0;
int len8StringCount = 0;
int len16StringCount = 0;
int dciStringCount = 0;
//int revDciStringCount = 0;
// For each range, check to see if the data within qualifies for the various
// options. If any of them fail to meet the criteria, the option is disabled
// for all ranges.
while (iter.MoveNext()) {
TypedRangeSet.TypedRange rng = iter.Current;
Debug.WriteLine("Testing [" + rng.Low + ", " + rng.High + "]");
// Start with the easy ones. Single-byte and dense are always enabled.
int count = rng.High - rng.Low + 1;
Debug.Assert(count > 0);
if ((count & 0x01) != 0) {
// not divisible by 2, disallow 16-bit entries
radio16BitLittle.Enabled = false;
radio16BitBig.Enabled = false;
}
if ((count & 0x03) != 0) {
// not divisible by 4, disallow 32-bit entries
radio32BitLittle.Enabled = false;
}
if ((count / 3) * 3 != count) {
// not divisible by 3, disallow 24-bit entries
radio24BitLittle.Enabled = false;
}
// Check for run of bytes (2 or more of the same thing). Remember that
// we check this one region at a time, and each region could have different
// bytes, but so long as the bytes are all the same within a region we're good.
if (radioFill.Enabled && count > 1 &&
DataAnalysis.RecognizeRun(mFileData, rng.Low, rng.High) == count) {
// LGTM
} else {
radioFill.Enabled = false;
}
// See if there's enough string data to make it worthwhile. We use an
// arbitrary threshold of 2+ ASCII characters, and require twice as many
// ASCII as non-ASCII. We arbitrarily require the strings to be either
// high or low ASCII, and treat the other as non-ASCII. (We could relax
// this -- we generate separate items for each string and non-ASCII chunk --
// but I'm trying to hide the option when the buffer doesn't really seem
// to be holding strings. Could replace with some sort of minimum string
// length requirement?)
if (radioStringMixed.Enabled) {
int asciiCount;
DataAnalysis.CountAsciiBytes(mFileData, rng.Low, rng.High,
out int lowAscii, out int highAscii, out int nonAscii);
if (highAscii > lowAscii) {
asciiCount = highAscii;
nonAscii += lowAscii;
} else {
asciiCount = lowAscii;
nonAscii += highAscii;
}
if (asciiCount >= 2 && asciiCount >= nonAscii * 2) {
// Looks good
mixedAsciiOkCount += asciiCount;
mixedAsciiNotCount += nonAscii;
} else {
// Fail
radioStringMixed.Enabled = false;
radioStringMixedReverse.Enabled = false;
mixedAsciiOkCount = mixedAsciiNotCount = -1;
}
}
// Check for null-terminated strings. Zero-length strings are allowed, but
// not counted -- we want to have some actual character data. Individual
// strings need to be entirely high-ASCII or low-ASCII, but not all strings
// in a region have to be the same.
if (radioStringNullTerm.Enabled) {
int strCount = DataAnalysis.RecognizeNullTerminatedStrings(mFileData,
rng.Low, rng.High);
if (strCount > 0) {
nullTermStringCount += strCount;
} else {
radioStringNullTerm.Enabled = false;
nullTermStringCount = -1;
}
}
// Check for strings prefixed with an 8-bit length.
if (radioStringLen8.Enabled) {
int strCount = DataAnalysis.RecognizeLen8Strings(mFileData, rng.Low, rng.High);
if (strCount > 0) {
len8StringCount += strCount;
} else {
radioStringLen8.Enabled = false;
len8StringCount = -1;
}
}
// Check for strings prefixed with a 16-bit length.
if (radioStringLen16.Enabled) {
int strCount = DataAnalysis.RecognizeLen16Strings(mFileData, rng.Low, rng.High);
if (strCount > 0) {
len16StringCount += strCount;
} else {
radioStringLen16.Enabled = false;
len16StringCount = -1;
}
}
// Check for DCI strings. All strings within a single range must have the
// same "polarity", e.g. low ASCII terminated by high ASCII.
if (radioStringDci.Enabled) {
int strCount = DataAnalysis.RecognizeDciStrings(mFileData, rng.Low, rng.High);
if (strCount > 0) {
dciStringCount += strCount;
} else {
radioStringDci.Enabled = false;
dciStringCount = -1;
}
}
//// Check for reverse DCI strings. All strings within a single range must have the
//// same "polarity", e.g. low ASCII terminated by high ASCII.
//if (radioStringDciReverse.Enabled) {
// int strCount = DataAnalysis.RecognizeReverseDciStrings(mFileData,
// rng.Low, rng.High);
// if (strCount > 0) {
// revDciStringCount += strCount;
// } else {
// radioStringDciReverse.Enabled = false;
// revDciStringCount = -1;
// }
//}
}
// Update the dialog with string and character counts, summed across all regions.
if (mixedAsciiOkCount > 0) {
Debug.Assert(radioStringMixed.Enabled);
radioStringMixed.Text = string.Format(radioStringMixed.Text,
mixedAsciiOkCount, mixedAsciiNotCount);
radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text,
mixedAsciiOkCount, mixedAsciiNotCount);
} else {
Debug.Assert(!radioStringMixed.Enabled);
radioStringMixed.Text = string.Format(radioStringMixed.Text, "xx", "xx");
radioStringMixedReverse.Text = string.Format(radioStringMixedReverse.Text,
"xx", "xx");
}
if (nullTermStringCount > 0) {
Debug.Assert(radioStringNullTerm.Enabled);
radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, nullTermStringCount);
} else {
Debug.Assert(!radioStringNullTerm.Enabled);
radioStringNullTerm.Text = string.Format(radioStringNullTerm.Text, "xx");
}
if (len8StringCount > 0) {
Debug.Assert(radioStringLen8.Enabled);
radioStringLen8.Text = string.Format(radioStringLen8.Text, len8StringCount);
} else {
Debug.Assert(!radioStringLen8.Enabled);
radioStringLen8.Text = string.Format(radioStringLen8.Text, "xx");
}
if (len16StringCount > 0) {
Debug.Assert(radioStringLen16.Enabled);
radioStringLen16.Text = string.Format(radioStringLen16.Text, len16StringCount);
} else {
Debug.Assert(!radioStringLen16.Enabled);
radioStringLen16.Text = string.Format(radioStringLen16.Text, "xx");
}
if (dciStringCount > 0) {
Debug.Assert(radioStringDci.Enabled);
radioStringDci.Text = string.Format(radioStringDci.Text, dciStringCount);
} else {
Debug.Assert(!radioStringDci.Enabled);
radioStringDci.Text = string.Format(radioStringDci.Text, "xx");
}
//if (revDciStringCount > 0) {
// Debug.Assert(radioStringDciReverse.Enabled);
// radioStringDciReverse.Text =
// string.Format(radioStringDciReverse.Text, revDciStringCount);
//} else {
// Debug.Assert(!radioStringDciReverse.Enabled);
// radioStringDciReverse.Text = string.Format(radioStringDciReverse.Text, "xx");
//}
}
/// <summary>
/// Determines whether the data in the buffer can be represented as ASCII values.
/// Using ".DD1 'A'" for 0x41 is obvious, but we also allow ".DD2 'A'" for
/// 0x41 0x00. 16-bit character constants are more likely as intermediate
/// operands, but could be found in data areas.
///
/// High and low ASCII are allowed, and may be freely mixed.
///
/// Testing explicitly is probably excessive, and possibly counter-productive if
/// the user is trying to flag an area that is a mix of ASCII and non-ASCII and
/// just wants hex for the rest, but we'll give it a try.
/// </summary>
/// <param name="wordWidth">Number of bytes per character.</param>
/// <param name="isBigEndian">Word endian-ness.</param>
/// <returns>True if data in all regions can be represented as high or low ASCII.</returns>
private bool IsRawAsciiCompatible(int wordWidth, bool isBigEndian) {
IEnumerator<TypedRangeSet.TypedRange> iter = Selection.RangeListIterator;
while (iter.MoveNext()) {
TypedRangeSet.TypedRange rng = iter.Current;
Debug.Assert(((rng.High - rng.Low + 1) / wordWidth) * wordWidth ==
rng.High - rng.Low + 1);
for (int i = rng.Low; i <= rng.High; i += wordWidth) {
int val = RawData.GetWord(mFileData, rng.Low, wordWidth, isBigEndian);
if (val < 0x20 || (val >= 0x7f && val < 0xa0) || val >= 0xff) {
// bad value, fail
return false;
}
}
}
return true;
}
/// <summary>
/// Configures the dialog controls based on the provided format descriptor. If
/// the desired options are unavailable, a suitable default is selected instead.
/// </summary>
/// <param name="dfd">FormatDescriptor to use.</param>
private void SetControlsFromDescriptor(FormatDescriptor dfd) {
Debug.Assert(mIsInitialSetup);
radioSimpleDataHex.Checked = true;
radioSymbolPartLow.Checked = true;
if (dfd == null) {
radioDefaultFormat.Checked = true;
return;
}
RadioButton preferredFormat;
switch (dfd.FormatType) {
case FormatDescriptor.Type.NumericLE:
case FormatDescriptor.Type.NumericBE:
switch (dfd.Length) {
case 1:
preferredFormat = radioSingleBytes;
break;
case 2:
preferredFormat =
(dfd.FormatType == FormatDescriptor.Type.NumericLE ?
radio16BitLittle : radio16BitBig);
break;
case 3:
preferredFormat = radio24BitLittle;
break;
case 4:
preferredFormat = radio32BitLittle;
break;
default:
Debug.Assert(false);
preferredFormat = radioDefaultFormat;
break;
}
if (preferredFormat.Enabled) {
switch (dfd.FormatSubType) {
case FormatDescriptor.SubType.None:
case FormatDescriptor.SubType.Hex:
radioSimpleDataHex.Checked = true;
break;
case FormatDescriptor.SubType.Decimal:
radioSimpleDataDecimal.Checked = true;
break;
case FormatDescriptor.SubType.Binary:
radioSimpleDataBinary.Checked = true;
break;
case FormatDescriptor.SubType.Ascii:
radioSimpleDataAscii.Checked = true;
break;
case FormatDescriptor.SubType.Address:
radioSimpleDataAddress.Checked = true;
break;
case FormatDescriptor.SubType.Symbol:
radioSimpleDataSymbolic.Checked = true;
switch (dfd.SymbolRef.ValuePart) {
case WeakSymbolRef.Part.Low:
radioSymbolPartLow.Checked = true;
break;
case WeakSymbolRef.Part.High:
radioSymbolPartHigh.Checked = true;
break;
case WeakSymbolRef.Part.Bank:
radioSymbolPartBank.Checked = true;
break;
default:
Debug.Assert(false);
break;
}
Debug.Assert(dfd.HasSymbol);
symbolEntryTextBox.Text = dfd.SymbolRef.Label;
break;
default:
Debug.Assert(false);
break;
}
} else {
// preferred format not enabled; leave Hex/Low checked
}
break;
case FormatDescriptor.Type.String:
switch (dfd.FormatSubType) {
case FormatDescriptor.SubType.None:
preferredFormat = radioStringMixed;
break;
case FormatDescriptor.SubType.Reverse:
preferredFormat = radioStringMixedReverse;
break;
case FormatDescriptor.SubType.CString:
preferredFormat = radioStringNullTerm;
break;
case FormatDescriptor.SubType.L8String:
preferredFormat = radioStringLen8;
break;
case FormatDescriptor.SubType.L16String:
preferredFormat = radioStringLen16;
break;
case FormatDescriptor.SubType.Dci:
preferredFormat = radioStringDci;
break;
case FormatDescriptor.SubType.DciReverse:
preferredFormat = radioDefaultFormat;
break;
default:
Debug.Assert(false);
preferredFormat = radioDefaultFormat;
break;
}
break;
case FormatDescriptor.Type.Dense:
preferredFormat = radioDenseHex;
break;
case FormatDescriptor.Type.Fill:
preferredFormat = radioFill;
break;
default:
// Should not be here.
Debug.Assert(false);
preferredFormat = radioDefaultFormat;
break;
}
if (preferredFormat.Enabled) {
preferredFormat.Checked = true;
} else {
mPreferredFormatUnavailable = true;
radioDefaultFormat.Checked = true;
}
}
/// <summary>
/// Creates a list of FormatDescriptors, based on the current control configuration.
///
/// The entries in the list are guaranteed to be sorted by start address and not
/// overlap.
///
/// We assume that whatever the control gives us is correct, e.g. it's not going
/// to tell us to put a buffer full of zeroes into a DCI string.
/// </summary>
/// <returns>Result list.</returns>
private void CreateDescriptorListFromControls() {
FormatDescriptor.Type type = FormatDescriptor.Type.Default;
FormatDescriptor.SubType subType = FormatDescriptor.SubType.None;
WeakSymbolRef symbolRef = null;
int chunkLength = -1;
// Decode the "display as" panel, if it's relevant.
if (radioSimpleDataHex.Enabled) {
if (radioSimpleDataHex.Checked) {
subType = FormatDescriptor.SubType.Hex;
} else if (radioSimpleDataDecimal.Checked) {
subType = FormatDescriptor.SubType.Decimal;
} else if (radioSimpleDataBinary.Checked) {
subType = FormatDescriptor.SubType.Binary;
} else if (radioSimpleDataAscii.Checked) {
subType = FormatDescriptor.SubType.Ascii;
} else if (radioSimpleDataAddress.Checked) {
subType = FormatDescriptor.SubType.Address;
} else if (radioSimpleDataSymbolic.Checked) {
WeakSymbolRef.Part part;
if (radioSymbolPartLow.Checked) {
part = WeakSymbolRef.Part.Low;
} else if (radioSymbolPartHigh.Checked) {
part = WeakSymbolRef.Part.High;
} else if (radioSymbolPartBank.Checked) {
part = WeakSymbolRef.Part.Bank;
} else {
Debug.Assert(false);
part = WeakSymbolRef.Part.Low;
}
subType = FormatDescriptor.SubType.Symbol;
symbolRef = new WeakSymbolRef(symbolEntryTextBox.Text, part);
} else {
Debug.Assert(false);
}
} else {
subType = 0; // set later, or doesn't matter
}
// Decode the main format.
if (radioDefaultFormat.Checked) {
// Default/None; note this would create a multi-byte Default format, which isn't
// really allowed. What we actually want to do is remove the explicit formatting
// from all spanned offsets, so we use a dedicated type for that.
type = FormatDescriptor.Type.REMOVE;
} else if (radioSingleBytes.Checked) {
type = FormatDescriptor.Type.NumericLE;
chunkLength = 1;
} else if (radio16BitLittle.Checked) {
type = FormatDescriptor.Type.NumericLE;
chunkLength = 2;
} else if (radio16BitBig.Checked) {
type = FormatDescriptor.Type.NumericBE;
chunkLength = 2;
} else if (radio24BitLittle.Checked) {
type = FormatDescriptor.Type.NumericLE;
chunkLength = 3;
} else if (radio32BitLittle.Checked) {
type = FormatDescriptor.Type.NumericLE;
chunkLength = 4;
} else if (radioDenseHex.Checked) {
type = FormatDescriptor.Type.Dense;
} else if (radioFill.Checked) {
type = FormatDescriptor.Type.Fill;
} else if (radioStringMixed.Checked) {
type = FormatDescriptor.Type.String;
} else if (radioStringMixedReverse.Checked) {
type = FormatDescriptor.Type.String;
subType = FormatDescriptor.SubType.Reverse;
} else if (radioStringNullTerm.Checked) {
type = FormatDescriptor.Type.String;
subType = FormatDescriptor.SubType.CString;
} else if (radioStringLen8.Checked) {
type = FormatDescriptor.Type.String;
subType = FormatDescriptor.SubType.L8String;
} else if (radioStringLen16.Checked) {
type = FormatDescriptor.Type.String;
subType = FormatDescriptor.SubType.L16String;
} else if (radioStringDci.Checked) {
type = FormatDescriptor.Type.String;
subType = FormatDescriptor.SubType.Dci;
//} else if (radioStringDciReverse.Checked) {
// type = FormatDescriptor.Type.String;
// subType = FormatDescriptor.SubType.DciReverse;
} else {
Debug.Assert(false);
// default/none
}
Results = new SortedList<int, FormatDescriptor>();
IEnumerator<TypedRangeSet.TypedRange> iter = Selection.RangeListIterator;
while (iter.MoveNext()) {
TypedRangeSet.TypedRange rng = iter.Current;
if (type == FormatDescriptor.Type.String) {
// We want to create one FormatDescriptor object per string. That way
// each string gets its own line.
if ((subType == FormatDescriptor.SubType.None ||
subType == FormatDescriptor.SubType.Reverse)) {
CreateMixedStringEntries(rng.Low, rng.High, subType);
} else if (subType == FormatDescriptor.SubType.CString) {
CreateCStringEntries(rng.Low, rng.High, subType);
} else if (subType == FormatDescriptor.SubType.L8String ||
subType == FormatDescriptor.SubType.L16String) {
CreateLengthStringEntries(rng.Low, rng.High, subType);
} else if (subType == FormatDescriptor.SubType.Dci ||
subType == FormatDescriptor.SubType.DciReverse) {
CreateDciStringEntries(rng.Low, rng.High, subType);
} else {
Debug.Assert(false);
CreateMixedStringEntries(rng.Low, rng.High, subType); // shrug
}
} else {
CreateSimpleEntries(type, subType, chunkLength, symbolRef, rng.Low, rng.High);
}
}
}
/// <summary>
/// Creates one or more FormatDescriptor entries for the specified range, adding them
/// to the Results list.
///
/// This will either create one entry that spans the entire range (for e.g. strings
/// and bulk data), or create equal-sized chunks.
/// </summary>
/// <param name="type">Region data type.</param>
/// <param name="subType">Region data sub-type.</param>
/// <param name="chunkLength">Length of a chunk, or -1 for full buffer.</param>
/// <param name="symbolRef">Symbol reference, or null if not applicable.</param>
/// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param>
private void CreateSimpleEntries(FormatDescriptor.Type type,
FormatDescriptor.SubType subType, int chunkLength,
WeakSymbolRef symbolRef, int low, int high) {
if (chunkLength == -1) {
chunkLength = (high - low) + 1;
}
Debug.Assert(((high - low + 1) / chunkLength) * chunkLength == high - low + 1);
// Either we have one chunk, or we have multiple chunks with the same type and
// length. Either way, we only need to create the descriptor once. (This is
// safe because FormatDescriptor instances are immutable.)
//
// Because certain details, like the fill byte and high-vs-low ASCII, are pulled
// out of the data stream at format time, we don't have to dig for them now.
FormatDescriptor dfd;
if (subType == FormatDescriptor.SubType.Symbol) {
dfd = FormatDescriptor.Create(chunkLength, symbolRef,
type == FormatDescriptor.Type.NumericBE);
} else {
dfd = FormatDescriptor.Create(chunkLength, type, subType);
}
while (low <= high) {
Results.Add(low, dfd);
low += chunkLength;
}
}
/// <summary>
/// Creates one or more FormatDescriptor entries for the specified range, adding them
/// to the Results list.
/// </summary>
/// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param>
private void CreateMixedStringEntries(int low, int high,
FormatDescriptor.SubType subType) {
int stringStart = -1;
int highBit = 0;
int cur;
for (cur = low; cur <= high; cur++) {
byte val = mFileData[cur];
if (CommonUtil.TextUtil.IsHiLoAscii(val)) {
// is ASCII
if (stringStart >= 0) {
// was in a string
if (highBit != (val & 0x80)) {
// end of string due to high bit flip, output
CreateStringOrByte(stringStart, cur - stringStart, subType);
// start a new string
stringStart = cur;
} else {
// still in string, keep going
}
} else {
// wasn't in a string, start one
stringStart = cur;
}
highBit = val & 0x80;
} else {
// not ASCII
if (stringStart >= 0) {
// was in a string, output it
CreateStringOrByte(stringStart, cur - stringStart, subType);
stringStart = -1;
}
// output as single byte
CreateByteFD(cur, FormatDescriptor.SubType.Hex);
}
}
if (stringStart >= 0) {
// close out the string
CreateStringOrByte(stringStart, cur - stringStart, subType);
}
}
/// <summary>
/// Creates a format descriptor for ASCII data. If the data is only one byte long,
/// a single-byte ASCII char item is emitted instead.
/// </summary>
/// <param name="offset">Offset of first byte.</param>
/// <param name="length">Length of string.</param>
/// <param name="subType">String sub-type.</param>
private void CreateStringOrByte(int offset, int length,
FormatDescriptor.SubType subType) {
Debug.Assert(length > 0);
if (length == 1) {
// single byte, output as single ASCII char rather than 1-byte string
CreateByteFD(offset, FormatDescriptor.SubType.Ascii);
} else {
FormatDescriptor dfd;
dfd = FormatDescriptor.Create(length,
FormatDescriptor.Type.String, subType);
Results.Add(offset, dfd);
}
}
/// <summary>
/// Creates a format descriptor for a single-byte numeric value.
/// </summary>
/// <param name="offset">File offset.</param>
/// <param name="subType">How to format the item.</param>
private void CreateByteFD(int offset, FormatDescriptor.SubType subType) {
FormatDescriptor dfd = FormatDescriptor.Create(1,
FormatDescriptor.Type.NumericLE, subType);
Results.Add(offset, dfd);
}
/// <summary>
/// Creates one or more FormatDescriptor entries for the specified range, adding them
/// to the Results list.
/// </summary>
/// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param>
private void CreateCStringEntries(int low, int high,
FormatDescriptor.SubType subType) {
int startOffset = low;
for (int i = low; i <= high; i++) {
if (mFileData[i] == 0x00) {
// End of string. Zero-length strings are allowed.
FormatDescriptor dfd = FormatDescriptor.Create(
i - startOffset + 1, FormatDescriptor.Type.String, subType);
Results.Add(startOffset, dfd);
startOffset = i + 1;
} else {
// keep going
}
}
// Earlier analysis guaranteed that the last byte in the buffer is 0x00.
Debug.Assert(startOffset == high + 1);
}
/// <summary>
/// Creates one or more FormatDescriptor entries for the specified range, adding them
/// to the Results list.
/// </summary>
/// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param>
private void CreateLengthStringEntries(int low, int high,
FormatDescriptor.SubType subType) {
int i;
for (i = low; i <= high;) {
int length = mFileData[i];
if (subType == FormatDescriptor.SubType.L16String) {
length |= mFileData[i + 1] << 8;
length += 2;
} else {
length++;
}
// Zero-length strings are allowed.
FormatDescriptor dfd = FormatDescriptor.Create(length,
FormatDescriptor.Type.String, subType);
Results.Add(i, dfd);
i += length;
}
Debug.Assert(i == high + 1);
}
/// <summary>
/// Creates one or more FormatDescriptor entries for the specified range, adding them
/// to the Results list.
/// </summary>
/// <param name="low">Offset of first byte in range.</param>
/// <param name="high">Offset of last byte in range.</param>
/// <param name="subType">String sub-type.</param>
private void CreateDciStringEntries(int low, int high,
FormatDescriptor.SubType subType) {
int start, end, adj, endMask;
if (subType == FormatDescriptor.SubType.Dci) {
start = low;
end = high + 1;
adj = 1;
} else if (subType == FormatDescriptor.SubType.DciReverse) {
start = high;
end = low - 1;
adj = -1;
} else {
Debug.Assert(false);
return;
}
// Zero-length strings aren't a thing for DCI. The analyzer requires that all
// strings in a region have the same polarity, so just grab the last byte.
endMask = mFileData[end - 1] & 0x80;
int stringStart = start;
for (int i = start; i != end; i += adj) {
byte val = mFileData[i];
if ((val & 0x80) == endMask) {
// found the end of a string
int length = (i - stringStart) * adj + 1;
FormatDescriptor dfd = FormatDescriptor.Create(length,
FormatDescriptor.Type.String, subType);
Results.Add(stringStart < i ? stringStart : i, dfd);
stringStart = i + adj;
}
}
Debug.Assert(stringStart == end);
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,265 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class EditDefSymbol {
/// <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.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.labelLabel = new System.Windows.Forms.Label();
this.labelTextBox = new System.Windows.Forms.TextBox();
this.valueTextBox = new System.Windows.Forms.TextBox();
this.labelNotesLabel = new System.Windows.Forms.Label();
this.valueLabel = new System.Windows.Forms.Label();
this.valueNotesLabel = new System.Windows.Forms.Label();
this.commentLabel = new System.Windows.Forms.Label();
this.commentTextBox = new System.Windows.Forms.TextBox();
this.commentNotesLabel = new System.Windows.Forms.Label();
this.symbolTypeGroupBox = new System.Windows.Forms.GroupBox();
this.constantRadioButton = new System.Windows.Forms.RadioButton();
this.addressRadioButton = new System.Windows.Forms.RadioButton();
this.labelUniqueLabel = new System.Windows.Forms.Label();
this.symbolTypeGroupBox.SuspendLayout();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(203, 253);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 5;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(122, 253);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 4;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// labelLabel
//
this.labelLabel.AutoSize = true;
this.labelLabel.Location = new System.Drawing.Point(14, 16);
this.labelLabel.Name = "labelLabel";
this.labelLabel.Size = new System.Drawing.Size(36, 13);
this.labelLabel.TabIndex = 6;
this.labelLabel.Text = "Label:";
//
// labelTextBox
//
this.labelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.labelTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.labelTextBox.Location = new System.Drawing.Point(77, 14);
this.labelTextBox.Name = "labelTextBox";
this.labelTextBox.Size = new System.Drawing.Size(200, 20);
this.labelTextBox.TabIndex = 0;
this.labelTextBox.TextChanged += new System.EventHandler(this.labelTextBox_TextChanged);
//
// valueTextBox
//
this.valueTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.valueTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.valueTextBox.Location = new System.Drawing.Point(77, 81);
this.valueTextBox.Name = "valueTextBox";
this.valueTextBox.Size = new System.Drawing.Size(200, 20);
this.valueTextBox.TabIndex = 1;
this.valueTextBox.TextChanged += new System.EventHandler(this.valueTextBox_TextChanged);
//
// labelNotesLabel
//
this.labelNotesLabel.AutoSize = true;
this.labelNotesLabel.Location = new System.Drawing.Point(74, 37);
this.labelNotesLabel.Name = "labelNotesLabel";
this.labelNotesLabel.Size = new System.Drawing.Size(187, 13);
this.labelNotesLabel.TabIndex = 9;
this.labelNotesLabel.Text = "• 2+ alphanumerics, starting with letter";
//
// valueLabel
//
this.valueLabel.AutoSize = true;
this.valueLabel.Location = new System.Drawing.Point(14, 83);
this.valueLabel.Name = "valueLabel";
this.valueLabel.Size = new System.Drawing.Size(37, 13);
this.valueLabel.TabIndex = 7;
this.valueLabel.Text = "Value:";
//
// valueNotesLabel
//
this.valueNotesLabel.AutoSize = true;
this.valueNotesLabel.Location = new System.Drawing.Point(74, 104);
this.valueNotesLabel.Name = "valueNotesLabel";
this.valueNotesLabel.Size = new System.Drawing.Size(155, 13);
this.valueNotesLabel.TabIndex = 11;
this.valueNotesLabel.Text = "• Decimal, hex ($), or binary (%)";
//
// commentLabel
//
this.commentLabel.AutoSize = true;
this.commentLabel.Location = new System.Drawing.Point(17, 137);
this.commentLabel.Name = "commentLabel";
this.commentLabel.Size = new System.Drawing.Size(54, 13);
this.commentLabel.TabIndex = 8;
this.commentLabel.Text = "Comment:";
//
// commentTextBox
//
this.commentTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.commentTextBox.Font = new System.Drawing.Font("Consolas", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
this.commentTextBox.Location = new System.Drawing.Point(77, 134);
this.commentTextBox.Name = "commentTextBox";
this.commentTextBox.Size = new System.Drawing.Size(200, 20);
this.commentTextBox.TabIndex = 2;
//
// commentNotesLabel
//
this.commentNotesLabel.AutoSize = true;
this.commentNotesLabel.Location = new System.Drawing.Point(74, 157);
this.commentNotesLabel.Name = "commentNotesLabel";
this.commentNotesLabel.Size = new System.Drawing.Size(55, 13);
this.commentNotesLabel.TabIndex = 12;
this.commentNotesLabel.Text = "• Optional";
//
// symbolTypeGroupBox
//
this.symbolTypeGroupBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.symbolTypeGroupBox.Controls.Add(this.constantRadioButton);
this.symbolTypeGroupBox.Controls.Add(this.addressRadioButton);
this.symbolTypeGroupBox.Location = new System.Drawing.Point(13, 190);
this.symbolTypeGroupBox.Name = "symbolTypeGroupBox";
this.symbolTypeGroupBox.Size = new System.Drawing.Size(264, 48);
this.symbolTypeGroupBox.TabIndex = 3;
this.symbolTypeGroupBox.TabStop = false;
this.symbolTypeGroupBox.Text = "Symbol Type";
//
// constantRadioButton
//
this.constantRadioButton.AutoSize = true;
this.constantRadioButton.Location = new System.Drawing.Point(99, 20);
this.constantRadioButton.Name = "constantRadioButton";
this.constantRadioButton.Size = new System.Drawing.Size(67, 17);
this.constantRadioButton.TabIndex = 1;
this.constantRadioButton.TabStop = true;
this.constantRadioButton.Text = "Constant";
this.constantRadioButton.UseVisualStyleBackColor = true;
//
// addressRadioButton
//
this.addressRadioButton.AutoSize = true;
this.addressRadioButton.Location = new System.Drawing.Point(6, 20);
this.addressRadioButton.Name = "addressRadioButton";
this.addressRadioButton.Size = new System.Drawing.Size(63, 17);
this.addressRadioButton.TabIndex = 0;
this.addressRadioButton.TabStop = true;
this.addressRadioButton.Text = "Address";
this.addressRadioButton.UseVisualStyleBackColor = true;
//
// labelUniqueLabel
//
this.labelUniqueLabel.AutoSize = true;
this.labelUniqueLabel.Location = new System.Drawing.Point(74, 53);
this.labelUniqueLabel.Name = "labelUniqueLabel";
this.labelUniqueLabel.Size = new System.Drawing.Size(160, 13);
this.labelUniqueLabel.TabIndex = 10;
this.labelUniqueLabel.Text = "• Unique among project symbols";
//
// EditDefSymbol
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(290, 288);
this.Controls.Add(this.labelUniqueLabel);
this.Controls.Add(this.symbolTypeGroupBox);
this.Controls.Add(this.commentNotesLabel);
this.Controls.Add(this.commentTextBox);
this.Controls.Add(this.commentLabel);
this.Controls.Add(this.valueNotesLabel);
this.Controls.Add(this.valueLabel);
this.Controls.Add(this.labelNotesLabel);
this.Controls.Add(this.valueTextBox);
this.Controls.Add(this.labelTextBox);
this.Controls.Add(this.labelLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditDefSymbol";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Symbol";
this.Load += new System.EventHandler(this.EditDefSymbol_Load);
this.symbolTypeGroupBox.ResumeLayout(false);
this.symbolTypeGroupBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Label labelLabel;
private System.Windows.Forms.TextBox labelTextBox;
private System.Windows.Forms.TextBox valueTextBox;
private System.Windows.Forms.Label labelNotesLabel;
private System.Windows.Forms.Label valueLabel;
private System.Windows.Forms.Label valueNotesLabel;
private System.Windows.Forms.Label commentLabel;
private System.Windows.Forms.TextBox commentTextBox;
private System.Windows.Forms.Label commentNotesLabel;
private System.Windows.Forms.GroupBox symbolTypeGroupBox;
private System.Windows.Forms.RadioButton constantRadioButton;
private System.Windows.Forms.RadioButton addressRadioButton;
private System.Windows.Forms.Label labelUniqueLabel;
}
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2018 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;
using Asm65;
namespace SourceGen.AppForms {
public partial class EditDefSymbol : Form {
/// <summary>
/// Set to previous value before calling; may be null if creating a new symbol.
/// Will be set to new value on OK result.
/// </summary>
public DefSymbol DefSym { get; set; }
/// <summary>
/// Format object to use when formatting addresses and constants.
/// </summary>
private Formatter NumFormatter { get; set; }
/// <summary>
/// List of existing symbols, for uniqueness check. The list will not be modified.
/// </summary>
private SortedList<String, DefSymbol> DefSymbolList { get; set; }
// Saved off at dialog load time.
private Color mDefaultLabelColor;
public EditDefSymbol(Formatter formatter, SortedList<String, DefSymbol> defList) {
InitializeComponent();
NumFormatter = formatter;
DefSymbolList = defList;
}
private void EditDefSymbol_Load(object sender, EventArgs e) {
mDefaultLabelColor = labelNotesLabel.ForeColor;
if (DefSym != null) {
labelTextBox.Text = DefSym.Label;
valueTextBox.Text = NumFormatter.FormatValueInBase(DefSym.Value,
DefSym.DataDescriptor.NumBase);
commentTextBox.Text = DefSym.Comment;
if (DefSym.SymbolType == Symbol.Type.Constant) {
constantRadioButton.Checked = true;
} else {
addressRadioButton.Checked = true;
}
} else {
addressRadioButton.Checked = true;
}
UpdateControls();
}
private void UpdateControls() {
bool labelValid, labelUnique, valueValid;
// Label must be valid and not already exist in project symbol list. (It's okay
// if it exists elsewhere.)
labelValid = Asm65.Label.ValidateLabel(labelTextBox.Text);
if (DefSymbolList.TryGetValue(labelTextBox.Text, out DefSymbol existing)) {
// It's okay if it's the same object.
labelUnique = (existing == DefSym);
} else {
labelUnique = true;
}
// Value must be blank, meaning "erase any earlier definition", or valid value.
// (Hmm... don't currently have a way to specify "no symbol" in DefSymbol.)
//if (!string.IsNullOrEmpty(valueTextBox.Text)) {
valueValid = ParseValue(out int unused1, out int unused2);
//} else {
// valueValid = true;
//}
labelNotesLabel.ForeColor = labelValid ? mDefaultLabelColor : Color.Red;
labelUniqueLabel.ForeColor = labelUnique ? mDefaultLabelColor : Color.Red;
valueNotesLabel.ForeColor = valueValid ? mDefaultLabelColor : Color.Red;
okButton.Enabled = labelValid && labelUnique && valueValid;
}
private bool ParseValue(out int value, out int numBase) {
string str = valueTextBox.Text;
if (str.IndexOf('/') >= 0) {
// treat as address
numBase = 16;
return Asm65.Address.ParseAddress(str, (1 << 24) - 1, out value);
} else {
return Asm65.Number.TryParseInt(str, out value, out numBase);
}
}
private void okButton_Click(object sender, EventArgs e) {
bool isConstant = constantRadioButton.Checked;
ParseValue(out int value, out int numBase);
FormatDescriptor.SubType subType = FormatDescriptor.GetSubTypeForBase(numBase);
DefSym = new DefSymbol(labelTextBox.Text, value, Symbol.Source.Project,
isConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr,
subType, commentTextBox.Text, string.Empty);
}
private void labelTextBox_TextChanged(object sender, EventArgs e) {
UpdateControls();
}
private void valueTextBox_TextChanged(object sender, EventArgs e) {
UpdateControls();
}
}
}

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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

225
SourceGen/AppForms/EditLabel.Designer.cs generated Normal file
View File

@ -0,0 +1,225 @@
/*
* Copyright 2018 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.
*/
namespace SourceGen.AppForms {
partial class EditLabel {
/// <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.cancelButton = new System.Windows.Forms.Button();
this.okButton = new System.Windows.Forms.Button();
this.instructionLabel = new System.Windows.Forms.Label();
this.labelTextBox = new System.Windows.Forms.TextBox();
this.maxLengthLabel = new System.Windows.Forms.Label();
this.firstLetterLabel = new System.Windows.Forms.Label();
this.validCharsLabel = new System.Windows.Forms.Label();
this.notDuplicateLabel = new System.Windows.Forms.Label();
this.labelTypeGroupBox = new System.Windows.Forms.GroupBox();
this.radioButtonExport = new System.Windows.Forms.RadioButton();
this.radioButtonGlobal = new System.Windows.Forms.RadioButton();
this.radioButtonLocal = new System.Windows.Forms.RadioButton();
this.labelTypeGroupBox.SuspendLayout();
this.SuspendLayout();
//
// cancelButton
//
this.cancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.cancelButton.Location = new System.Drawing.Point(267, 202);
this.cancelButton.Name = "cancelButton";
this.cancelButton.Size = new System.Drawing.Size(75, 23);
this.cancelButton.TabIndex = 8;
this.cancelButton.Text = "Cancel";
this.cancelButton.UseVisualStyleBackColor = true;
//
// okButton
//
this.okButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK;
this.okButton.Location = new System.Drawing.Point(186, 202);
this.okButton.Name = "okButton";
this.okButton.Size = new System.Drawing.Size(75, 23);
this.okButton.TabIndex = 7;
this.okButton.Text = "OK";
this.okButton.UseVisualStyleBackColor = true;
this.okButton.Click += new System.EventHandler(this.okButton_Click);
//
// instructionLabel
//
this.instructionLabel.AutoSize = true;
this.instructionLabel.Location = new System.Drawing.Point(13, 13);
this.instructionLabel.Name = "instructionLabel";
this.instructionLabel.Size = new System.Drawing.Size(60, 13);
this.instructionLabel.TabIndex = 0;
this.instructionLabel.Text = "Enter label:";
//
// labelTextBox
//
this.labelTextBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
this.labelTextBox.Location = new System.Drawing.Point(13, 30);
this.labelTextBox.Name = "labelTextBox";
this.labelTextBox.Size = new System.Drawing.Size(325, 20);
this.labelTextBox.TabIndex = 1;
this.labelTextBox.TextChanged += new System.EventHandler(this.labelTextBox_TextChanged);
//
// maxLengthLabel
//
this.maxLengthLabel.AutoSize = true;
this.maxLengthLabel.Location = new System.Drawing.Point(13, 57);
this.maxLengthLabel.Name = "maxLengthLabel";
this.maxLengthLabel.Size = new System.Drawing.Size(251, 13);
this.maxLengthLabel.TabIndex = 2;
this.maxLengthLabel.Text = "• Must be 2-32 characters long (or blank to remove)";
//
// firstLetterLabel
//
this.firstLetterLabel.AutoSize = true;
this.firstLetterLabel.Location = new System.Drawing.Point(12, 73);
this.firstLetterLabel.Name = "firstLetterLabel";
this.firstLetterLabel.Size = new System.Drawing.Size(187, 13);
this.firstLetterLabel.TabIndex = 3;
this.firstLetterLabel.Text = "• Must start with a letter or underscore";
//
// validCharsLabel
//
this.validCharsLabel.AutoSize = true;
this.validCharsLabel.Location = new System.Drawing.Point(12, 89);
this.validCharsLabel.Name = "validCharsLabel";
this.validCharsLabel.Size = new System.Drawing.Size(297, 13);
this.validCharsLabel.TabIndex = 4;
this.validCharsLabel.Text = "• Valid characters are ASCII letters, numbers, and underscore";
//
// notDuplicateLabel
//
this.notDuplicateLabel.AutoSize = true;
this.notDuplicateLabel.Location = new System.Drawing.Point(13, 105);
this.notDuplicateLabel.Name = "notDuplicateLabel";
this.notDuplicateLabel.Size = new System.Drawing.Size(217, 13);
this.notDuplicateLabel.TabIndex = 5;
this.notDuplicateLabel.Text = "• Must not be a duplicate of an existing label";
//
// labelTypeGroupBox
//
this.labelTypeGroupBox.Controls.Add(this.radioButtonExport);
this.labelTypeGroupBox.Controls.Add(this.radioButtonGlobal);
this.labelTypeGroupBox.Controls.Add(this.radioButtonLocal);
this.labelTypeGroupBox.Location = new System.Drawing.Point(13, 132);
this.labelTypeGroupBox.Name = "labelTypeGroupBox";
this.labelTypeGroupBox.Size = new System.Drawing.Size(137, 93);
this.labelTypeGroupBox.TabIndex = 6;
this.labelTypeGroupBox.TabStop = false;
this.labelTypeGroupBox.Text = "Label Type";
//
// radioButtonExport
//
this.radioButtonExport.AutoSize = true;
this.radioButtonExport.Location = new System.Drawing.Point(7, 67);
this.radioButtonExport.Name = "radioButtonExport";
this.radioButtonExport.Size = new System.Drawing.Size(120, 17);
this.radioButtonExport.TabIndex = 2;
this.radioButtonExport.TabStop = true;
this.radioButtonExport.Text = "Global and &exported";
this.radioButtonExport.UseVisualStyleBackColor = true;
//
// radioButtonGlobal
//
this.radioButtonGlobal.AutoSize = true;
this.radioButtonGlobal.Location = new System.Drawing.Point(7, 43);
this.radioButtonGlobal.Name = "radioButtonGlobal";
this.radioButtonGlobal.Size = new System.Drawing.Size(55, 17);
this.radioButtonGlobal.TabIndex = 1;
this.radioButtonGlobal.TabStop = true;
this.radioButtonGlobal.Text = "&Global";
this.radioButtonGlobal.UseVisualStyleBackColor = true;
//
// radioButtonLocal
//
this.radioButtonLocal.AutoSize = true;
this.radioButtonLocal.Location = new System.Drawing.Point(7, 20);
this.radioButtonLocal.Name = "radioButtonLocal";
this.radioButtonLocal.Size = new System.Drawing.Size(106, 17);
this.radioButtonLocal.TabIndex = 0;
this.radioButtonLocal.TabStop = true;
this.radioButtonLocal.Text = "&Local (if possible)";
this.radioButtonLocal.UseVisualStyleBackColor = true;
//
// EditLabel
//
this.AcceptButton = this.okButton;
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.cancelButton;
this.ClientSize = new System.Drawing.Size(354, 237);
this.Controls.Add(this.labelTypeGroupBox);
this.Controls.Add(this.notDuplicateLabel);
this.Controls.Add(this.validCharsLabel);
this.Controls.Add(this.firstLetterLabel);
this.Controls.Add(this.maxLengthLabel);
this.Controls.Add(this.labelTextBox);
this.Controls.Add(this.instructionLabel);
this.Controls.Add(this.okButton);
this.Controls.Add(this.cancelButton);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "EditLabel";
this.ShowInTaskbar = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Edit Label";
this.Load += new System.EventHandler(this.EditLabel_Load);
this.labelTypeGroupBox.ResumeLayout(false);
this.labelTypeGroupBox.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private System.Windows.Forms.Button cancelButton;
private System.Windows.Forms.Button okButton;
private System.Windows.Forms.Label instructionLabel;
private System.Windows.Forms.TextBox labelTextBox;
private System.Windows.Forms.Label maxLengthLabel;
private System.Windows.Forms.Label firstLetterLabel;
private System.Windows.Forms.Label validCharsLabel;
private System.Windows.Forms.Label notDuplicateLabel;
private System.Windows.Forms.GroupBox labelTypeGroupBox;
private System.Windows.Forms.RadioButton radioButtonLocal;
private System.Windows.Forms.RadioButton radioButtonGlobal;
private System.Windows.Forms.RadioButton radioButtonExport;
}
}

Some files were not shown because too many files have changed in this diff Show More