Merge pull request #19 from haven1433/dev

Usability Improvements
This commit is contained in:
haven1433
2019-02-05 21:01:08 -06:00
committed by GitHub
80 changed files with 796 additions and 366 deletions

View File

@@ -3,11 +3,11 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.28010.2016
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gen3Hex.WPF", "src\Gen3Hex.WPF\Gen3Hex.WPF.csproj", "{386DC8E2-BABC-475C-94B6-37B3986F5F4C}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexManiac.WPF", "src\HexManiac.WPF\HexManiac.WPF.csproj", "{386DC8E2-BABC-475C-94B6-37B3986F5F4C}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gen3Hex.Core", "src\Gen3Hex.Core\Gen3Hex.Core.csproj", "{B7B9D932-4271-493D-AADD-6CF147C91373}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexManiac.Core", "src\HexManiac.Core\HexManiac.Core.csproj", "{B7B9D932-4271-493D-AADD-6CF147C91373}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Gen3Hex.Tests", "src\Gen3Hex.Tests\Gen3Hex.Tests.csproj", "{B6A52732-E0F8-466D-8E29-664C797A21FA}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "HexManiac.Tests", "src\HexManiac.Tests\HexManiac.Tests.csproj", "{B6A52732-E0F8-466D-8E29-664C797A21FA}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution

View File

@@ -1,3 +0,0 @@
using System.Reflection;
[assembly: AssemblyTitle("Gen3Hex.Core")]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

View File

@@ -0,0 +1,3 @@
using System.Reflection;
[assembly: AssemblyTitle("HexManiac.Core")]

View File

@@ -6,8 +6,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B7B9D932-4271-493D-AADD-6CF147C91373}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>HavenSoft.Gen3Hex.Core</RootNamespace>
<AssemblyName>Gen3Hex.Core</AssemblyName>
<RootNamespace>HavenSoft.HexManiac.Core</RootNamespace>
<AssemblyName>HexManiac.Core</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<Deterministic>true</Deterministic>
@@ -47,6 +47,7 @@
<Compile Include="Models\AutoSearchModel.cs" />
<Compile Include="Models\ErrorInfo.cs" />
<Compile Include="Models\IDataModel.cs" />
<Compile Include="Models\Runs\AsciiRun.cs" />
<Compile Include="Models\Runs\IFormattedRun.cs" />
<Compile Include="Models\ModelDelta.cs" />
<Compile Include="Models\Runs\PCSRun.cs" />

View File

@@ -1,6 +1,6 @@
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core {
namespace HavenSoft.HexManiac.Core {
public static class ICommandExtensions {
/// <summary>
/// Runs execute on the command with a null parameter.

View File

@@ -1,8 +1,8 @@
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.HexManiac.Core.Models.Runs;
using System.Linq;
using static HavenSoft.Gen3Hex.Core.Models.Runs.ArrayRun;
using static HavenSoft.HexManiac.Core.Models.Runs.ArrayRun;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class AutoSearchModel : PokemonModel {
public const string
Ruby = "AXVE",
@@ -11,19 +11,44 @@ namespace HavenSoft.Gen3Hex.Core.Models {
FireRed = "BPRE",
LeafGreen = "BPGE";
private readonly string gameCode;
private readonly ModelDelta noChangeDelta = new NoDataChangeDeltaModel();
/// <summary>
/// The first 0x100 bytes of the GBA rom is always the header.
/// The next 0x100 bytes contains some tables and some startup code, but nothing interesting to point to.
/// Choosing 0x200 might prevent us from seeing an actual anchor, but it will also remove a bunch
/// of false positives and keep us from getting conflicts with the RomName (see DecodeHeader).
/// </summary>
public override int EarliestAllowedAnchor => 0x200;
public AutoSearchModel(byte[] data, StoredMetadata metadata = null) : base(data, metadata) {
if (metadata != null) return;
var noChangeDelta = new NoDataChangeDeltaModel();
ClearFormat(noChangeDelta, -1, 0x101); // starting before the beginning to clear the anchor at the very start
var gameCode = string.Concat(Enumerable.Range(0xAC, 4).Select(i => ((char)data[i]).ToString()));
gameCode = string.Concat(Enumerable.Range(0xAC, 4).Select(i => ((char)data[i]).ToString()));
// in vanilla emerald, this pointer isn't four-byte aligned
// it's at the very front of the ROM, so if there's no metadata we can be pretty sure that the pointer is still there
if (gameCode == Emerald && data[0x1C3] == 0x08) ObserveRunWritten(noChangeDelta, new PointerRun(0x1C0));
var gamesToDecode = new[] { Ruby, Sapphire, Emerald, FireRed, LeafGreen };
if (gamesToDecode.Contains(gameCode)) {
DecodeHeader();
DecodeNameArrays();
}
}
private void DecodeHeader() {
ObserveAnchorWritten(noChangeDelta, "GameTitle", new AsciiRun(0xA0, 12));
ObserveAnchorWritten(noChangeDelta, "GameCode", new AsciiRun(0xAC, 4));
ObserveAnchorWritten(noChangeDelta, "MakerCode", new AsciiRun(0xB0, 2));
if (gameCode != Ruby && gameCode != Sapphire) {
ObserveAnchorWritten(noChangeDelta, "RomName", new AsciiRun(0x108, 0x20));
}
}
private void DecodeNameArrays() {
// pokenames
if (TrySearch(this, "[name\"\"11]", out var pokenames)) {
ObserveAnchorWritten(noChangeDelta, "pokenames", pokenames);

View File

@@ -1,3 +1,3 @@
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public enum Direction { Left, Right, Up, Down }
}

View File

@@ -1,6 +1,6 @@
using System;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class ErrorInfo : IEquatable<ErrorInfo> {
public static ErrorInfo NoError { get; } = new ErrorInfo(null);
public bool HasError { get; }

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public interface IDataModel : IReadOnlyList<byte> {
byte[] RawData { get; }
new byte this[int index] { get; set; }

View File

@@ -1,6 +1,6 @@
using System;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public interface IFileSystem {
string CopyText { get; set; }

View File

@@ -1,4 +1,4 @@
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class LoadedFile {
public string Name { get; }

View File

@@ -1,8 +1,8 @@
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.HexManiac.Core.Models.Runs;
using System.Collections.Generic;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
/// <summary>
/// Represents a single conceptual change in the loaded data, including
/// editing data, adding / removing formats / format names,

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class PCSString {
public static IReadOnlyList<string> PCS;
public static IReadOnlyList<byte> Newlines;

View File

@@ -1,6 +1,6 @@
using System;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public struct Point : IEquatable<Point> {
public int X { get; }
public int Y { get; }

View File

@@ -1,15 +1,16 @@
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using static HavenSoft.Gen3Hex.Core.Models.Runs.ArrayRun;
using static HavenSoft.Gen3Hex.Core.Models.Runs.BaseRun;
using static HavenSoft.Gen3Hex.Core.Models.Runs.PCSRun;
using static HavenSoft.HexManiac.Core.Models.Runs.ArrayRun;
using static HavenSoft.HexManiac.Core.Models.Runs.AsciiRun;
using static HavenSoft.HexManiac.Core.Models.Runs.BaseRun;
using static HavenSoft.HexManiac.Core.Models.Runs.PCSRun;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class PokemonModel : BaseModel {
// list of runs, in sorted address order. Includes no names
private readonly List<IFormattedRun> runs = new List<IFormattedRun>();
@@ -24,6 +25,8 @@ namespace HavenSoft.Gen3Hex.Core.Models {
private readonly Dictionary<string, List<int>> unmappedNameToSources = new Dictionary<string, List<int>>();
private readonly Dictionary<int, string> sourceToUnmappedName = new Dictionary<int, string>();
public virtual int EarliestAllowedAnchor => 0;
#region Constructor
public PokemonModel(byte[] data, StoredMetadata metadata = null) : base(data) {
@@ -50,13 +53,34 @@ namespace HavenSoft.Gen3Hex.Core.Models {
}
}
/// <summary>
/// Finds pointers based on Heuristics.
/// This is definitely wrong, but it's pretty good.
/// </summary>
private void SearchForPointers(Dictionary<int, List<int>> pointersForDestination, SortedList<int, int> destinationForSource) {
// pointers must be 4-byte aligned
for (int i = 0; i < RawData.Length - 3; i += 4) {
// pointers must end in 08 or 09
if (RawData[i + 3] != 0x08 && RawData[i + 3] != 0x09) continue;
// pointers must point to locations that are 4-byte aligned
if (RawData[i] % 4 != 0) continue;
var source = i;
var destination = ReadPointer(i);
// pointers must point into the data
if (destination >= RawData.Length) continue;
// pointers must not point at the header
if (destination < EarliestAllowedAnchor) continue;
// pointers must point at something useful, not just a bunch of FF
bool pointsToManyFF = true;
for (int j = 0; j < 4 && pointsToManyFF && destination + j < RawData.Length; j++) pointsToManyFF = RawData[destination + j] == 0xFF;
if (pointsToManyFF) continue;
// we found a pointer!
if (!pointersForDestination.ContainsKey(destination)) pointersForDestination[destination] = new List<int>();
pointersForDestination[destination].Add(source);
destinationForSource.Add(source, destination);
@@ -279,6 +303,8 @@ namespace HavenSoft.Gen3Hex.Core.Models {
ClearFormat(changeToken, run.Start, run.Length);
}
var existingRun = (index >= 0 && index < runs.Count) ? runs[index] : null;
if (anchorForAddress.TryGetValue(location, out string oldAnchorName)) {
anchorForAddress.Remove(location);
addressForAnchor.Remove(oldAnchorName);
@@ -296,7 +322,8 @@ namespace HavenSoft.Gen3Hex.Core.Models {
changeToken.AddName(location, anchorName);
}
var sources = GetSourcesPointingToNewAnchor(changeToken, anchorName);
var seakPointers = existingRun?.PointerSources == null || existingRun?.Start != location;
var sources = GetSourcesPointingToNewAnchor(changeToken, anchorName, seakPointers);
var newRun = run.MergeAnchor(sources);
ObserveRunWritten(changeToken, newRun);
}
@@ -585,13 +612,17 @@ namespace HavenSoft.Gen3Hex.Core.Models {
private static (string, string) SplitNameAndFormat(string text) {
var name = text.Substring(1).Trim();
string format = string.Empty;
int split = -1;
if (name.Contains(ArrayStart)) {
var split = name.IndexOf(ArrayStart);
format = name.Substring(split);
name = name.Substring(0, split);
split = name.IndexOf(ArrayStart);
} else if (name.Contains(StringDelimeter)) {
var split = name.IndexOf(StringDelimeter);
split = name.IndexOf(StringDelimeter);
} else if (name.Contains(StreamDelimeter)) {
split = name.IndexOf(StreamDelimeter);
}
if (split != -1) {
format = name.Substring(split);
name = name.Substring(0, split);
}
@@ -610,6 +641,12 @@ namespace HavenSoft.Gen3Hex.Core.Models {
return new ErrorInfo($"Format was specified as a string, but a string would overlap the next anchor.");
}
run = new PCSRun(dataIndex, length);
} else if (format.StartsWith(StreamDelimeter + "asc" + StreamDelimeter)) {
if (int.TryParse(format.Substring(5), out var length)) {
run = new AsciiRun(dataIndex, length);
} else {
return new ErrorInfo($"Ascii runs must include a length.");
}
} else if (TryParse(model, format, dataIndex, null, out var arrayRun)) {
run = arrayRun;
} else if (format != string.Empty) {
@@ -621,7 +658,7 @@ namespace HavenSoft.Gen3Hex.Core.Models {
private static ErrorInfo ValidateAnchorNameAndFormat(IDataModel model, IFormattedRun runToWrite, string name, string format, int dataIndex) {
var existingRun = model.GetNextRun(dataIndex);
var nextRun = existingRun.Start > dataIndex ? existingRun : model.GetNextRun(existingRun.Start + existingRun.Length);
var nextRun = existingRun.Start > dataIndex ? existingRun : model.GetNextRun(existingRun.Start + Math.Max(existingRun.Length, 1));
if (name.ToLower() == "null") {
return new ErrorInfo("'null' is a reserved word and cannot be used as an anchor name.");
@@ -664,9 +701,15 @@ namespace HavenSoft.Gen3Hex.Core.Models {
/// <returns>
/// The list of sources that point at the new anchor
/// </returns>
private IReadOnlyList<int> GetSourcesPointingToNewAnchor(ModelDelta changeToken, string anchorName) {
private IReadOnlyList<int> GetSourcesPointingToNewAnchor(ModelDelta changeToken, string anchorName, bool seakPointers) {
if (!addressForAnchor.TryGetValue(anchorName, out int location)) return new List<int>(); // new anchor is unnamed, so nothing points to it yet
if (!unmappedNameToSources.TryGetValue(anchorName, out var sources)) return new List<int>(); // no pointer was waiting for this anchor to be created
if (!unmappedNameToSources.TryGetValue(anchorName, out var sources)) {
// no pointer was waiting for this anchor to be created
// but the user thinks there's something pointing here
if (seakPointers) return SearchForPointersToAnchor(changeToken, location);
return new List<int>();
}
foreach (var source in sources) {
var index = BinarySearch(source);
@@ -681,6 +724,27 @@ namespace HavenSoft.Gen3Hex.Core.Models {
return sources;
}
private IReadOnlyList<int> SearchForPointersToAnchor(ModelDelta changeToken, int address) {
var results = new List<int>();
for (int i = 3; i < RawData.Length; i++) {
if (RawData[i] != 0x08 && RawData[i] != 0x09) continue;
int destination = ReadPointer(i - 3);
if (destination != address) continue;
var index = BinarySearch(i-3);
if (index >= 0) continue;
index = ~index;
if (index < runs.Count && runs[index].Start <= i) continue;
if (index > 0 && runs[index - 1].Start + runs[index - 1].Length > i - 3) continue;
var newRun = new PointerRun(i - 3);
runs.Insert(index, newRun);
changeToken.AddRun(newRun);
results.Add(i - 3);
}
return results;
}
private IFormattedRun MoveRun(ModelDelta changeToken, IFormattedRun run, int newStart) {
// repoint
foreach (var source in run.PointerSources) {
@@ -740,6 +804,9 @@ namespace HavenSoft.Gen3Hex.Core.Models {
return true;
}
// if an existing run starts exactly at start, return that index
// otherwise, return a number such that ~index would be inserted into the list at the correct index
// so ~index - 1 is the previous run, and ~index is the next run
private int BinarySearch(int start) {
var index = runs.BinarySearch(new CompareFormattedRun(start), FormattedRunComparer.Instance);
return index;

View File

@@ -1,10 +1,10 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace HavenSoft.Gen3Hex.Core.Models.Runs {
namespace HavenSoft.HexManiac.Core.Models.Runs {
public enum ElementContentType {
Unknown,
PCS,

View File

@@ -0,0 +1,22 @@
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System.Collections.Generic;
namespace HavenSoft.HexManiac.Core.Models.Runs {
public class AsciiRun : BaseRun {
public const char StreamDelimeter = '`';
public override int Length { get; }
public override string FormatString => "`asc`" + Length;
public AsciiRun(int start, int length, IReadOnlyList<int> pointerSources = null) : base(start, pointerSources) => Length = length;
public override IDataFormat CreateDataFormat(IDataModel data, int index) {
return new Ascii(Start, index - Start, (char)data[index]);
}
protected override IFormattedRun Clone(IReadOnlyList<int> newPointerSources) {
return new AsciiRun(Start, Length, newPointerSources);
}
}
}

View File

@@ -1,9 +1,9 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.Models.Runs {
namespace HavenSoft.HexManiac.Core.Models.Runs {
public interface IFormattedRun {
int Start { get; }
int Length { get; }

View File

@@ -1,7 +1,7 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System.Collections.Generic;
namespace HavenSoft.Gen3Hex.Core.Models.Runs {
namespace HavenSoft.HexManiac.Core.Models.Runs {
public class PCSRun : BaseRun {
public const char StringDelimeter = '"';

View File

@@ -1,7 +1,7 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System.Collections.Generic;
namespace HavenSoft.Gen3Hex.Core.Models.Runs {
namespace HavenSoft.HexManiac.Core.Models.Runs {
public class PointerRun : BaseRun {
public const char PointerStart = '<';
public const char PointerEnd = '>';

View File

@@ -2,7 +2,7 @@
using System.Diagnostics;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public interface ISearchByte {
bool Match(byte value);
}

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Globalization;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.Models {
namespace HavenSoft.HexManiac.Core.Models {
public class StoredMetadata {
public IReadOnlyList<StoredAnchor> NamedAnchors { get; }
public IReadOnlyList<StoredUnmappedPointers> UnmappedPointers { get; }

View File

@@ -1,6 +1,6 @@
using System;
namespace HavenSoft.Gen3Hex.Core {
namespace HavenSoft.HexManiac.Core {
public static class NumericExtensions {
public static T LimitToRange<T>(this T value, T lower, T upper) where T : IComparable<T> {
if (upper.CompareTo(lower) < 0) throw new ArgumentException($"upper value {upper} is less than lower value {lower}");

View File

@@ -3,7 +3,7 @@ using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
/// <summary>
/// Represents a history of changes that can undo / redo.
/// The change can be reperesented by any class with an empty constructor.

View File

@@ -1,6 +1,6 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.HexManiac.Core.Models;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
/// <summary>
/// exactly replaces a ViewPort, except that it also has a known parent view
/// </summary>

View File

@@ -5,7 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
namespace HavenSoft.Gen3Hex.Core.ViewModels.DataFormats {
namespace HavenSoft.HexManiac.Core.ViewModels.DataFormats {
public interface IDataFormat : IEquatable<IDataFormat> {
void Visit(IDataFormatVisitor visitor, byte data);
}
@@ -19,6 +19,7 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels.DataFormats {
void Visit(PCS pcs, byte data);
void Visit(EscapedPCS pcs, byte data);
void Visit(ErrorPCS pcs, byte data);
void Visit(Ascii ascii, byte data);
}
/// <summary>
@@ -154,4 +155,19 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels.DataFormats {
public void Visit(IDataFormatVisitor visitor, byte data) => visitor.Visit(this, data);
}
public class Ascii : IDataFormat {
public int Source { get; }
public int Position { get; }
public char ThisCharacter { get; }
public Ascii(int source, int position, char value) => (Source, Position, ThisCharacter) = (source, position, value);
public bool Equals(IDataFormat other) {
if (!(other is Ascii ascii)) return false;
return ascii.Source == Source && ascii.Position == Position && ascii.ThisCharacter == ThisCharacter;
}
public void Visit(IDataFormatVisitor visitor, byte data) => visitor.Visit(this, data);
}
}

View File

@@ -1,4 +1,4 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.HexManiac.Core.Models;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -6,10 +6,11 @@ using System.Collections.Specialized;
using System.ComponentModel;
using System.Linq;
using System.Windows.Input;
using static HavenSoft.Gen3Hex.Core.ICommandExtensions;
using static HavenSoft.HexManiac.Core.ICommandExtensions;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class EditorViewModel : ViewModelCore, IEnumerable<ITabContent>, INotifyCollectionChanged {
private const int MaxReasonableResults = 200; // limit for performance reasons
private readonly IFileSystem fileSystem;
private readonly List<ITabContent> tabs;
@@ -43,7 +44,6 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
toggleMatrix = new StubCommand();
private readonly Dictionary<Func<ITabContent, ICommand>, EventHandler> forwardExecuteChangeNotifications;
private (IViewPort tab, int)[] recentFindResults = new (IViewPort, int)[0];
private int currentFindResultIndex;
@@ -374,6 +374,11 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
return;
}
if (results.Count > MaxReasonableResults) {
ErrorMessage = $"Found {results.Count} results: please refine your search.";
return;
}
var newTab = new SearchResultsViewPort(search);
foreach (var (tab, offset) in results) {
newTab.Add(tab.CreateChildView(offset));

View File

@@ -2,9 +2,9 @@
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using static HavenSoft.Gen3Hex.Core.ICommandExtensions;
using static HavenSoft.HexManiac.Core.ICommandExtensions;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class GotoControlViewModel : ViewModelCore {
private readonly IViewPort viewPort;

View File

@@ -1,6 +1,6 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class HexElement {
public static readonly HexElement Undefined = new HexElement(0, DataFormats.Undefined.Instance);
public byte Value { get; }

View File

@@ -1,4 +1,4 @@
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public interface IChildViewPort : IViewPort {
IViewPort Parent { get; }
}

View File

@@ -2,7 +2,7 @@
using System.ComponentModel;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
/// <summary>
/// Each command expects an IFileSystem as its Command Parameter.
/// </summary>

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels.Tools;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public interface IViewPort : ITabContent, INotifyCollectionChanged {
string FileName { get; } // Name is dispayed in a tab. FileName lets us know when to call 'ConsiderReload'

View File

@@ -1,10 +1,10 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.HexManiac.Core.Models;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class ScrollRegion : ViewModelCore {
public static readonly IReadOnlyDictionary<Direction, Point> DirectionToDif = new Dictionary<Direction, Point> {
{ Direction.Up, new Point( 0,-1) },

View File

@@ -1,5 +1,5 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels.Tools;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -7,7 +7,7 @@ using System.Collections.Specialized;
using System.Linq;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class SearchResultsViewPort : ViewModelCore, IViewPort {
private readonly StubCommand scroll, close;
private readonly List<IChildViewPort> children = new List<IChildViewPort>();

View File

@@ -1,13 +1,13 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class Selection : ViewModelCore {
private readonly IDataModel model;
@@ -97,7 +97,11 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
if (anchor != Pointer.NULL) {
GotoAddress(anchor);
} else if (int.TryParse(address, NumberStyles.HexNumber, CultureInfo.CurrentCulture, out int result)) {
GotoAddress(result);
if (result > Scroll.DataLength || result < 0) {
OnError?.Invoke(this, $"Address {result:X2} is not within the size of the data.");
} else {
GotoAddress(result);
}
} else {
OnError?.Invoke(this, $"Unable to goto address '{address}'");
}

View File

@@ -1,4 +1,4 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.HexManiac.Core.Models;
using System;
using System.Collections;
using System.Collections.Generic;
@@ -6,7 +6,7 @@ using System.ComponentModel;
using System.Diagnostics;
using System.Windows.Input;
namespace HavenSoft.Gen3Hex.Core.ViewModels.Tools {
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
public interface IToolTrayViewModel : IReadOnlyList<IToolViewModel>, INotifyPropertyChanged {
int SelectedIndex { get; set; }

View File

@@ -1,6 +1,6 @@
using System.ComponentModel;
namespace HavenSoft.Gen3Hex.Core.ViewModels.Tools {
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
public interface IToolViewModel : INotifyPropertyChanged {
string Name { get; }
}

View File

@@ -1,10 +1,10 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Text;
namespace HavenSoft.Gen3Hex.Core.ViewModels.Tools {
namespace HavenSoft.HexManiac.Core.ViewModels.Tools {
public class PCSTool : ViewModelCore, IToolViewModel {
private readonly IDataModel model;
private readonly Selection selection;

View File

@@ -2,7 +2,7 @@
using System.ComponentModel;
using System.Runtime.CompilerServices;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
public class ViewModelCore : INotifyPropertyChanged {
public event PropertyChangedEventHandler PropertyChanged;

View File

@@ -1,7 +1,7 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.Gen3Hex.Core.ViewModels.Tools;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -12,13 +12,14 @@ using System.Globalization;
using System.IO;
using System.Linq;
using System.Windows.Input;
using static HavenSoft.Gen3Hex.Core.ICommandExtensions;
using static HavenSoft.Gen3Hex.Core.Models.Runs.ArrayRun;
using static HavenSoft.Gen3Hex.Core.Models.Runs.BaseRun;
using static HavenSoft.Gen3Hex.Core.Models.Runs.PCSRun;
using static HavenSoft.Gen3Hex.Core.Models.Runs.PointerRun;
using static HavenSoft.HexManiac.Core.ICommandExtensions;
using static HavenSoft.HexManiac.Core.Models.Runs.ArrayRun;
using static HavenSoft.HexManiac.Core.Models.Runs.AsciiRun;
using static HavenSoft.HexManiac.Core.Models.Runs.BaseRun;
using static HavenSoft.HexManiac.Core.Models.Runs.PCSRun;
using static HavenSoft.HexManiac.Core.Models.Runs.PointerRun;
namespace HavenSoft.Gen3Hex.Core.ViewModels {
namespace HavenSoft.HexManiac.Core.ViewModels {
/// <summary>
/// A range of visible data that should be displayed.
/// </summary>
@@ -327,6 +328,12 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
public bool IsSelected(Point point) => selection.IsSelected(point);
public void ClearFormat(Point point) {
var dataIndex = scroll.ViewPointToDataIndex(point);
Model.ClearFormat(history.CurrentChange, dataIndex, 1);
RefreshBackingData();
}
public void Edit(string input) {
exitEditEarly = false;
using (Tools.DeferUpdates) {
@@ -674,6 +681,10 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
return AllHexCharacters.Contains(input);
}
if (innerFormat is Ascii) {
return true;
}
if (input == PointerStart) {
// pointer edits are 4 bytes long
if(!TryCoerceSelectionToStartOfPointer(ref point, ref element)) PrepareForMultiSpaceEdit(point, 4);
@@ -682,7 +693,7 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
} else if (underEdit.CurrentText.StartsWith(PointerStart.ToString())) {
return char.IsLetterOrDigit(input) || input == PointerEnd;
} else if (underEdit.CurrentText.StartsWith(AnchorStart.ToString())) {
return char.IsLetterOrDigit(input) || char.IsWhiteSpace(input) || input == ArrayStart || input == ArrayEnd || input == StringDelimeter;
return char.IsLetterOrDigit(input) || char.IsWhiteSpace(input) || input == ArrayStart || input == ArrayEnd || input == StringDelimeter || input == StreamDelimeter;
} else if (underEdit.OriginalFormat is Anchor anchorFormat && anchorFormat.OriginalFormat is PCS) {
if (input == StringDelimeter) return true;
// if this is the start of a string (as noted by the anchor), crop off the leading " before trying to convert to a byte
@@ -764,10 +775,13 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
return true;
}
PCS stringFormat = null;
if (underEdit.OriginalFormat is Anchor anchorFormat) stringFormat = anchorFormat.OriginalFormat as PCS;
stringFormat = stringFormat ?? underEdit.OriginalFormat as PCS;
if (stringFormat != null) {
var originalFormat = underEdit.OriginalFormat;
if (originalFormat is Anchor) originalFormat = ((Anchor)originalFormat).OriginalFormat;
if (originalFormat is Ascii) {
CompleteAsciiEdit(point, underEdit.CurrentText);
return true;
}
if (originalFormat is PCS stringFormat) {
var currentText = underEdit.CurrentText;
if (currentText.StartsWith(StringDelimeter.ToString())) currentText = currentText.Substring(1);
if (stringFormat.Position != 0 && underEdit.CurrentText == StringDelimeter.ToString()) {
@@ -790,6 +804,17 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
return true;
}
private void CompleteAsciiEdit(Point point, string currentText) {
var memoryLocation = scroll.ViewPointToDataIndex(point);
var editFormat = (UnderEdit)currentView[point.X, point.Y].Format;
var asciiFormat = (Ascii)editFormat.OriginalFormat;
var content = (byte)currentText[0];
history.CurrentChange.ChangeData(Model, memoryLocation, content);
currentView[point.X, point.Y] = new HexElement(content, new Ascii(asciiFormat.Source, asciiFormat.Position, currentText[0]));
SilentScroll(memoryLocation + 1);
}
private void CompleteArrayExtension(ArrayRun arrayRun) {
var newRun = (ArrayRun)Model.RelocateForExpansion(history.CurrentChange, arrayRun, arrayRun.Length + arrayRun.ElementLength);
if (newRun != arrayRun) {
@@ -1079,6 +1104,8 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
public void Visit(EscapedPCS pcs, byte data) => Visit((None)null, data);
public void Visit(ErrorPCS pcs, byte data) => Visit((None)null, data);
public void Visit(Ascii ascii, byte data) => Result = ((char)data).ToString();
}
/// <summary>
@@ -1115,6 +1142,8 @@ namespace HavenSoft.Gen3Hex.Core.ViewModels {
public void Visit(EscapedPCS pcs, byte data) => currentChange.ChangeData(buffer, index, 0xFF);
public void Visit(ErrorPCS pcs, byte data) => currentChange.ChangeData(buffer, index, 0xFF);
public void Visit(Ascii ascii, byte data) => currentChange.ChangeData(buffer, index, 0xFF);
}
}
}

View File

@@ -1,15 +1,15 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ArrayRunTests {
[Fact]
public void CanParseStringArrayRun() {

View File

@@ -0,0 +1,46 @@
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using Xunit;
namespace HavenSoft.HexManiac.Tests {
public class AsciiRunTests {
[Fact]
public void CanCreateAsciiRun() {
var buffer = new byte[0x200];
var model = new PokemonModel(buffer);
model[0x10] = (byte)'a';
model[0x11] = (byte)'b';
model[0x12] = (byte)'c';
model[0x13] = (byte)'d';
var viewPort = new ViewPort(new LoadedFile("file.txt", buffer), model) { Width = 0x10, Height = 0x10 };
viewPort.SelectionStart = new Point(0, 1);
viewPort.Edit("^data`asc`4 ");
var format = (Ascii)viewPort[1, 1].Format;
Assert.Equal('b', format.ThisCharacter);
}
[Fact]
public void CanEditAsciiRun() {
var buffer = new byte[0x200];
var model = new PokemonModel(buffer);
model[0x10] = (byte)'a';
model[0x11] = (byte)'b';
model[0x12] = (byte)'c';
model[0x13] = (byte)'d';
model.ObserveRunWritten(new ModelDelta(), new AsciiRun(0x10, 4));
var viewPort = new ViewPort(new LoadedFile("file.txt", buffer), model) { Width = 0x10, Height = 0x10 };
viewPort.SelectionStart = new Point(1, 1);
viewPort.Edit("3");
var format = (Ascii)viewPort[1, 1].Format;
Assert.Equal('3', format.ThisCharacter);
Assert.Equal(new Point(2, 1), viewPort.SelectionStart);
}
}
}

View File

@@ -1,13 +1,13 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ChangeHistoryTests {
private readonly ChangeHistory<List<int>> history;

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using System.Collections.ObjectModel;
using System.Linq;
using System.Windows.Input;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class FindTests {
[Fact]
public void FindCanFindSingle() {

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using System;
using System.Windows.Input;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class GeneralAppTests {
private readonly EditorViewModel editor;
private readonly StubFileSystem fileSystem;

View File

@@ -8,8 +8,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B6A52732-E0F8-466D-8E29-664C797A21FA}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>HavenSoft.Gen3Hex.Tests</RootNamespace>
<AssemblyName>Gen3Hex.Tests</AssemblyName>
<RootNamespace>HavenSoft.HexManiac.Tests</RootNamespace>
<AssemblyName>HexManiac.Tests</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@@ -55,11 +55,12 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="..\..\artifacts\Gen3Hex.Tests\codegen\StubDataModel.cs">
<Compile Include="..\..\artifacts\$(AssemblyName)\codegen\StubDataModel.cs">
<Link>StubDataModel.cs</Link>
</Compile>
<Compile Include="ArrayRunTests.cs" />
<Compile Include="ChangeHistoryTests.cs" />
<Compile Include="AsciiRunTests.cs" />
<Compile Include="FindTests.cs" />
<Compile Include="GeneralAppTests.cs" />
<Compile Include="..\..\artifacts\$(AssemblyName)\codegen\StubFileSystem.cs" />
@@ -82,9 +83,9 @@
<Analyzer Include="..\..\packages\xunit.analyzers.0.10.0\analyzers\dotnet\cs\xunit.analyzers.dll" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gen3Hex.Core\Gen3Hex.Core.csproj">
<ProjectReference Include="..\HexManiac.Core\HexManiac.Core.csproj">
<Project>{b7b9d932-4271-493d-aadd-6cf147c91373}</Project>
<Name>Gen3Hex.Core</Name>
<Name>HexManiac.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
@@ -102,7 +103,7 @@
<PreBuildEvent>
if not exist $(SolutionDir)artifacts\$(AssemblyName)\codegen mkdir $(SolutionDir)artifacts\$(AssemblyName)\codegen
$(SolutionDir)packages\HavenSoft.AutoImplement.1.1.1\AutoImplement $(SolutionDir)artifacts\Gen3Hex.Core\bin\$(ConfigurationName)\Gen3Hex.Core.dll ITabContent IFileSystem IViewPort IDataModel
$(SolutionDir)packages\HavenSoft.AutoImplement.1.1.1\AutoImplement $(SolutionDir)artifacts\HexManiac.Core\bin\$(ConfigurationName)\HexManiac.Core.dll ITabContent IFileSystem IViewPort IDataModel
move /Y StubTabContent.cs $(SolutionDir)artifacts\$(AssemblyName)\codegen
move /Y StubFileSystem.cs $(SolutionDir)artifacts\$(AssemblyName)\codegen
move /Y StubViewPort.cs $(SolutionDir)artifacts\$(AssemblyName)\codegen

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using System.Collections.Generic;
using System.Windows.Input;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class NavigationTests {
[Fact]
public void AddressesAreCorrect() {

View File

@@ -1,14 +1,14 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class PointerModelTests {
[Fact]
public void PointerModelFindsNoPointersInRandomData() {
@@ -695,5 +695,34 @@ namespace HavenSoft.Gen3Hex.Tests {
}
}
}
[Fact]
public void AddingAnchorShouldSearchForPointersToThatLocation() {
var data = new byte[0x200];
var model = new PokemonModel(data);
var change = new ModelDelta();
model.WritePointer(change, 0x23, 0x050); // a pointer that isn't 4-byte aligned, pointing to data that is
model.WritePointer(change, 0x10, 0x087); // a pointer that is 4-byte aligned, but pointing to something that isn't
model.WritePointer(change, 2, 0xA2); // a pointer that isn't 4-byte aligned, pointing to something not 4-byte aligned
var viewPort = new ViewPort(new LoadedFile("test.txt", data), model) { Width = 0x10, Height = 0x10 };
// got to 50 and write an anchor
viewPort.SelectionStart = new Point(0x0, 0x5);
viewPort.Edit("^test1 ");
Assert.IsType<Pointer>(viewPort[0x3, 0x2].Format);
Assert.Single(((Anchor)viewPort[0x0, 0x5].Format).Sources);
// go to 87 and write an anchor
viewPort.SelectionStart = new Point(0x7, 0x8);
viewPort.Edit("^test2 ");
Assert.IsType<Pointer>(viewPort[0x0, 0x1].Format);
Assert.Single(((Anchor)viewPort[0x7, 0x8].Format).Sources);
// go to A2 and write an anchor
viewPort.SelectionStart = new Point(0x2, 0xA);
viewPort.Edit("^test3 ");
Assert.IsType<Pointer>(viewPort[0x2, 0x0].Format);
Assert.Single(((Anchor)viewPort[0x2, 0xA].Format).Sources);
}
}
}

View File

@@ -1,14 +1,14 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Collections.Generic;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class StringModelTests {
[Fact]
public void CanRecognizeString() {

View File

@@ -1,15 +1,15 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.Models.Runs;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.Gen3Hex.Core.ViewModels.Tools;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.Models.Runs;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ToolTests {
[Fact]
public void ViewPortHasTools() {

View File

@@ -1,8 +1,8 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ViewPortCursorTests {
[Fact]
public void CursorCanMoveAllFourDirections() {

View File

@@ -1,12 +1,12 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ViewPortEditTests {
[Fact]
public void CanEditData() {

View File

@@ -1,13 +1,13 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Xunit;
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ViewPortSaveTests {
private readonly StubFileSystem fileSystem;

View File

@@ -1,11 +1,11 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using System.Collections.Generic;
using Xunit;
[assembly: System.Reflection.AssemblyTitle("HexTests")]
namespace HavenSoft.Gen3Hex.Tests {
namespace HavenSoft.HexManiac.Tests {
public class ViewPortTests {
[Fact]
public void ViewPortNotifiesOnSizeChange() {

View File

@@ -1,8 +1,8 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.Gen3Hex.WPF.Implementations;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.WPF.Implementations;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
@@ -14,10 +14,10 @@ using System.Windows.Controls.Primitives;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;
using ModelPoint = HavenSoft.Gen3Hex.Core.Models.Point;
using ModelPoint = HavenSoft.HexManiac.Core.Models.Point;
using ScreenPoint = System.Windows.Point;
namespace HavenSoft.Gen3Hex.WPF.Controls {
namespace HavenSoft.HexManiac.WPF.Controls {
public class HexContent : FrameworkElement {
public const double CellWidth = 30, CellHeight = 20;
@@ -202,6 +202,7 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
}
if (format is PCS pcs) children.AddRange(GetStringChildren(p));
if (format is Pointer pointer) children.AddRange(GetPointerChildren(p));
if (!(format is None || format is Undefined)) children.AddRange(GetClearFormattingChildren(p));
} else {
children.AddRange(GetSearchChildren(p));
}
@@ -354,6 +355,15 @@ namespace HavenSoft.Gen3Hex.WPF.Controls {
yield return CreateFollowLinkButton("Open in main tab", p);
}
private IEnumerable<FrameworkElement> GetClearFormattingChildren(ModelPoint p) {
yield return new Button {
Content = new TextBlock { Text = "Clear Format" },
}.SetEvent(ButtonBase.ClickEvent, (sender, e) => {
((ViewPort)ViewPort).ClearFormat(p);
recentMenu.IsOpen = false;
});
}
private Button CreateFollowLinkButton(string message, ModelPoint p) {
return new Button {
Content = new StackPanel {

View File

@@ -2,7 +2,7 @@
using System.Globalization;
using System.Windows.Data;
namespace HavenSoft.Gen3Hex.WPF.Controls {
namespace HavenSoft.HexManiac.WPF.Controls {
public class IntegerToBooleanViaMatchConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
var a = value as int?;

View File

@@ -2,7 +2,7 @@
using System.Globalization;
using System.Windows.Data;
namespace HavenSoft.Gen3Hex.WPF.Controls {
namespace HavenSoft.HexManiac.WPF.Controls {
public class IntegerToHexConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
if (value is int number) return number.ToString("X6");

View File

@@ -0,0 +1,51 @@
<Grid x:Class="HavenSoft.HexManiac.WPF.Controls.StartScreen"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:solarized="clr-namespace:Solarized"
xmlns:hsg3hwr="clr-namespace:HavenSoft.HexManiac.WPF.Resources"
mc:Ignorable="d" Background="{DynamicResource Background}"
d:DesignHeight="450" d:DesignWidth="800">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="400"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Foreground="{DynamicResource Primary}" HorizontalAlignment="Center" VerticalAlignment="Center">
Drag-n-Drop to open a file.
</TextBlock>
<StackPanel Grid.Column="1" VerticalAlignment="Center" TextBlock.Foreground="{DynamicResource Primary}" >
<Border Margin="5" Padding="5" Background="{DynamicResource Background}" BorderBrush="{DynamicResource Secondary}" CornerRadius="5,5,5,5" BorderThickness="1" Cursor="Hand" MouseLeftButtonDown="PointerHelp">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
</Grid.RowDefinitions>
<TextBlock Grid.Row="1" FontFamily="Consolas" Foreground="{solarized:Theme Blue}" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="35,0">&lt;pointer&gt;</TextBlock>
<TextBlock Grid.Row="1" VerticalAlignment="Center" HorizontalAlignment="Right" Margin="35,0"><Run FontFamily="Consolas" Foreground="{solarized:Theme Blue}">^</Run>Anchor</TextBlock>
<Path Data="M0,0 A230,150 0 0 1 230,0 L228,-10 M230,0 L224,7.5" HorizontalAlignment="Center" VerticalAlignment="Bottom" StrokeThickness="1" Stroke="{solarized:Theme Blue}">
<Path.RenderTransform>
<TranslateTransform X="0" Y="10"/>
</Path.RenderTransform>
</Path>
<Border HorizontalAlignment="Center" Background="{DynamicResource Background}" Padding="5,0">
<StackPanel Orientation="Horizontal">
<Path Data="{hsg3hwr:Icon CtrlKey}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
<Path Data="{hsg3hwr:Icon ThinPlus}" Fill="{DynamicResource Secondary}" Stretch="Uniform" Margin="2" Width="10"/>
<Path Height="30" Data="{hsg3hwr:Icon LeftMouseButton}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
</StackPanel>
</Border>
<Path Grid.Row="2" Data="M230,0 A230,150 0 0 1 0,0 L2,10 M0,0 L7.5,-6" HorizontalAlignment="Center" VerticalAlignment="Top" StrokeThickness="1" Stroke="{solarized:Theme Blue}">
<Path.RenderTransform>
<TranslateTransform X="0" Y="-1"/>
</Path.RenderTransform>
</Path>
<Border Grid.Row="2" HorizontalAlignment="Center" Background="{DynamicResource Background}" Padding="5,0">
<Path Height="30" Data="{hsg3hwr:Icon RightMouseButton}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
</Border>
<TextBlock Foreground="{DynamicResource Secondary}" FontStyle="Italic" TextDecorations="Underline" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Bottom" Text="click for more info" Margin="5"/>
</Grid>
</Border>
</StackPanel>
</Grid>

View File

@@ -0,0 +1,13 @@
using System.Diagnostics;
using System.Windows.Input;
namespace HavenSoft.HexManiac.WPF.Controls {
public partial class StartScreen {
public StartScreen() => InitializeComponent();
private void PointerHelp(object sender, MouseButtonEventArgs e) {
Process.Start(new ProcessStartInfo("https://github.com/haven1433/HexManiacAdvance/wiki/Pointers-and-Anchors"));
e.Handled = true;
}
}
}

View File

@@ -6,8 +6,8 @@
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{386DC8E2-BABC-475C-94B6-37B3986F5F4C}</ProjectGuid>
<OutputType>WinExe</OutputType>
<RootNamespace>HavenSoft.Gen3Hex.WPF</RootNamespace>
<AssemblyName>Gen3Hex</AssemblyName>
<RootNamespace>HavenSoft.HexManiac.WPF</RootNamespace>
<AssemblyName>HexManiacAdvance</AssemblyName>
<TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
@@ -32,7 +32,7 @@
<DefineConstants>TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Resources\Snorlax.ico</ApplicationIcon>
<ApplicationIcon>Resources\AppIcon.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
@@ -54,6 +54,10 @@
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Page Include="Controls\StartScreen.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Windows\AboutWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
@@ -68,6 +72,9 @@
</Page>
<Compile Include="Controls\IntegerToBooleanViaMatchConverter.cs" />
<Compile Include="Controls\IntegerToHexConverter.cs" />
<Compile Include="Controls\StartScreen.xaml.cs">
<DependentUpon>StartScreen.xaml</DependentUpon>
</Compile>
<Compile Include="Windows\AboutWindow.xaml.cs">
<DependentUpon>AboutWindow.xaml</DependentUpon>
</Compile>
@@ -95,14 +102,16 @@
<None Include="Windows\App.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Gen3Hex.Core\Gen3Hex.Core.csproj">
<ProjectReference Include="..\HexManiac.Core\HexManiac.Core.csproj">
<Project>{b7b9d932-4271-493d-aadd-6cf147c91373}</Project>
<Name>Gen3Hex.Core</Name>
<Name>HexManiac.Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\Snorlax.ico" />
<Resource Include="Resources\snorlax.png" />
<Resource Include="Resources\AppIcon.ico" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\AppImage.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View File

@@ -1,12 +1,12 @@
using HavenSoft.Gen3Hex.Core.ViewModels.DataFormats;
using HavenSoft.Gen3Hex.WPF.Controls;
using HavenSoft.HexManiac.Core.ViewModels.DataFormats;
using HavenSoft.HexManiac.WPF.Controls;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Media;
namespace HavenSoft.Gen3Hex.WPF.Implementations {
namespace HavenSoft.HexManiac.WPF.Implementations {
public class FormatDrawer : IDataFormatVisitor {
public static int FontSize = 16;
@@ -21,7 +21,7 @@ namespace HavenSoft.Gen3Hex.WPF.Implementations {
public bool MouseIsOverCurrentFormat { get; set; }
public HavenSoft.Gen3Hex.Core.Models.Point Position { get; set; }
public HavenSoft.HexManiac.Core.Models.Point Position { get; set; }
public FormatDrawer(DrawingContext drawingContext, int width, int height) => (context, modelWidth, modelHeight) = (drawingContext, width, height);
@@ -77,7 +77,7 @@ namespace HavenSoft.Gen3Hex.WPF.Implementations {
brush,
1.0);
if (dataFormat.Position < Position.X || Position.X - dataFormat.Position > modelWidth - 4) {
if (dataFormat.Position > Position.X || Position.X - dataFormat.Position > modelWidth - 4) {
context.PushClip(rectangleGeometry);
context.DrawText(text, new Point(CellTextOffset.X + xOffset, CellTextOffset.Y));
context.Pop();
@@ -131,6 +131,20 @@ namespace HavenSoft.Gen3Hex.WPF.Implementations {
context.DrawText(text, CellTextOffset);
}
public void Visit(Ascii ascii, byte data) {
var typeface = new Typeface("Consolas");
var text = new FormattedText(
ascii.ThisCharacter.ToString(),
CultureInfo.CurrentCulture,
FlowDirection.LeftToRight,
typeface,
FontSize,
Solarized.Brushes.Violet,
1.0);
context.DrawText(text, CellTextOffset);
}
private void Underline(Brush brush, bool isStart, bool isEnd) {
int startPoint = isStart ? 5 : 0;
int endPoint = (int)HexContent.CellWidth - (isEnd ? 5 : 0);

View File

@@ -1,4 +1,4 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.HexManiac.Core.Models;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
@@ -7,7 +7,7 @@ using System.Linq;
using System.Windows;
using System.Windows.Threading;
namespace HavenSoft.Gen3Hex.WPF.Implementations {
namespace HavenSoft.HexManiac.WPF.Implementations {
public class WindowsFileSystem : IFileSystem {
private readonly Dictionary<string, List<FileSystemWatcher>> watchers = new Dictionary<string, List<FileSystemWatcher>>();
private readonly Dictionary<string, List<Action<IFileSystem>>> listeners = new Dictionary<string, List<Action<IFileSystem>>>();

Binary file not shown.

After

Width:  |  Height:  |  Size: 766 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

View File

@@ -4,14 +4,14 @@
This ItemsControl is meant to be used like a resource.
Instead, access these icons using IconExtensions.
-->
<ItemsControl x:Class="HavenSoft.Gen3Hex.WPF.Resources.Icons"
<ItemsControl x:Class="HavenSoft.HexManiac.WPF.Resources.Icons"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:hsv="clr-namespace:HavenSoft.Gen3Hex.WPF.Resources"
xmlns:hsv="clr-namespace:HavenSoft.HexManiac.WPF.Resources"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="300">
d:DesignHeight="360" d:DesignWidth="360">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<WrapPanel/>
@@ -163,5 +163,68 @@
</GeometryGroup>
<PathGeometry x:Name="SizeArrow" Figures="M -6,-6 L 6,-6 6,6 -6,6 Z M 4,4 L -4,4 4,-4 Z M -4,-4 l 5,0 -5,5 Z"/>
<PathGeometry x:Name="SizeArrow2" Figures="M 4,4 L -4,4 4,-4 Z M -4,-4 l 5,0 -5,5 Z"/>
<CombinedGeometry x:Name="LeftMouseButton" GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<GeometryGroup FillRule="Nonzero">
<EllipseGeometry RadiusX="10" RadiusY="10"/>
<EllipseGeometry Center="0,10" RadiusX="10" RadiusY="10"/>
<RectangleGeometry Rect="-10,0,20,10"/>
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="Nonzero">
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="Nonzero">
<RectangleGeometry Rect="0,-10,10,20"/>
<RectangleGeometry Rect="-10,0,10,10"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
<RectangleGeometry Rect="-8,0,8,4"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
<CombinedGeometry x:Name="RightMouseButton" GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<GeometryGroup FillRule="Nonzero">
<EllipseGeometry RadiusX="10" RadiusY="10"/>
<EllipseGeometry Center="0,10" RadiusX="10" RadiusY="10"/>
<RectangleGeometry Rect="-10,0,20,10"/>
</GeometryGroup>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="Nonzero">
<CombinedGeometry GeometryCombineMode="Exclude">
<CombinedGeometry.Geometry1>
<EllipseGeometry RadiusX="8" RadiusY="8"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<GeometryGroup FillRule="Nonzero">
<RectangleGeometry Rect="-10,-10,10,20"/>
<RectangleGeometry Rect="0,0,10,10"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
<RectangleGeometry Rect="0,0,8,4"/>
</GeometryGroup>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
<CombinedGeometry x:Name="CtrlKey" GeometryCombineMode="Exclude" Geometry2="{hsv:TextGeometry Ctrl,0,0,12}">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="-1,2,20,11" RadiusX="2" RadiusY="2"/>
</CombinedGeometry.Geometry1>
</CombinedGeometry>
<CombinedGeometry x:Name="ThinPlus" GeometryCombineMode="Union">
<CombinedGeometry.Geometry1>
<RectangleGeometry Rect="-5,0,12,2"/>
</CombinedGeometry.Geometry1>
<CombinedGeometry.Geometry2>
<RectangleGeometry Rect="0,-5,2,12"/>
</CombinedGeometry.Geometry2>
</CombinedGeometry>
</ItemsControl.Items>
</ItemsControl>

View File

@@ -1,4 +1,4 @@
namespace HavenSoft.Gen3Hex.WPF.Resources {
namespace HavenSoft.HexManiac.WPF.Resources {
partial class Icons {
public Icons() => InitializeComponent();
}

View File

@@ -1,16 +1,17 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Media;
namespace HavenSoft.Gen3Hex.WPF.Resources {
namespace HavenSoft.HexManiac.WPF.Resources {
/// <example>
/// {hs:RotateTransform 60, 0, 0}
/// </example>
public class RotateTransformExtension : MarkupExtension {
readonly double Angle;
readonly double CenterX;
readonly double CenterY;
public double Angle { get; }
public double CenterX { get; }
public double CenterY { get; }
public RotateTransformExtension(double angle, double centerX, double centerY) {
Angle = angle;
@@ -27,7 +28,7 @@ namespace HavenSoft.Gen3Hex.WPF.Resources {
/// {hs:Geometry 'M0,0 L0,1 1,1 1,0 Z'}
/// </example>
public class GeometryExtension : MarkupExtension {
readonly string Figures;
public string Figures { get; }
public GeometryExtension(string figures) { Figures = figures; }
@@ -36,6 +37,18 @@ namespace HavenSoft.Gen3Hex.WPF.Resources {
}
}
public class TextGeometryExtension : MarkupExtension {
public string Text { get; set; }
public Point Location { get; set; }
public int Size { get; set; }
public TextGeometryExtension() { }
public TextGeometryExtension(string text, int x, int y, int size) => (Text, Location, Size) = (text, new Point(x, y), size);
public override object ProvideValue(IServiceProvider serviceProvider) {
var formattedText = new FormattedText(Text, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Calibri"), Size, Brushes.Black, 1);
return formattedText.BuildGeometry(Location);
}
}
/// <summary>
/// Icons are taken from Icons.xaml
/// Requires that there is an Icons instance stored at "Icons" in the app resources.
@@ -45,7 +58,7 @@ namespace HavenSoft.Gen3Hex.WPF.Resources {
/// {hs:Icons Save}
/// </example>
public class IconExtension : MarkupExtension {
readonly string Name;
public string Name { get; }
public IconExtension(string name) { Name = name; }

View File

@@ -1,24 +1,24 @@
<Window x:Class="HavenSoft.Gen3Hex.WPF.Windows.AboutWindow"
<Window x:Class="HavenSoft.HexManiac.WPF.Windows.AboutWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:solarized="clr-namespace:Solarized"
Background="{DynamicResource Background}"
WindowStyle="ToolWindow"
Title="About Gen3Hex" Height="300" Width="400">
Title="About Hex Maniac Advance" Height="300" Width="400">
<DockPanel Margin="15">
<TextBlock DockPanel.Dock="Bottom" FontStyle="Italic" TextWrapping="Wrap">
Gen3Hex is a non-profit open-source fan-made tool.
Hex Maniac Advance is a non-profit open-source fan-made tool.
Only use it with legally purchased software.
Pokemon is the property of Nintendo and GameFreak.
Please support the official release.
</TextBlock>
<TextBlock DockPanel.Dock="Top" VerticalAlignment="Center" FontWeight="Bold" Text="Gen3Hex" FontSize="20"/>
<Image Source="..\Resources\Snorlax.png" DockPanel.Dock="Left" RenderOptions.BitmapScalingMode="Fant"/>
<TextBlock DockPanel.Dock="Top" VerticalAlignment="Center" FontWeight="Bold" Text="Hex Maniac Advance" FontSize="20"/>
<Image Source="..\Resources\AppImage.png" DockPanel.Dock="Left" RenderOptions.BitmapScalingMode="Fant" Width="200"/>
<StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock Name="Version"/>
<TextBlock TextWrapping="Wrap">This is a prerelease debug version: please do not distribute!</TextBlock>
<TextBlock>
<Hyperlink Foreground="{solarized:Theme Blue}" NavigateUri="https://github.com/haven1433/gen3hex/blob/master/LICENSE" RequestNavigate="Navigate">MIT License</Hyperlink>
<Hyperlink Foreground="{solarized:Theme Blue}" NavigateUri="https://github.com/haven1433/HexManiacAdvance/blob/master/LICENSE" RequestNavigate="Navigate">MIT License</Hyperlink>
</TextBlock>
<TextBlock>
<Hyperlink Foreground="{solarized:Theme Blue}" NavigateUri="https://ethanschoonover.com/solarized" RequestNavigate="Navigate">Solarized</Hyperlink>

View File

@@ -2,7 +2,7 @@
using System.Reflection;
using System.Windows.Navigation;
namespace HavenSoft.Gen3Hex.WPF.Windows {
namespace HavenSoft.HexManiac.WPF.Windows {
partial class AboutWindow {
public AboutWindow() {
InitializeComponent();

View File

@@ -1,7 +1,7 @@
<Application x:Class="HavenSoft.Gen3Hex.WPF.Windows.App"
<Application x:Class="HavenSoft.HexManiac.WPF.Windows.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hsv="clr-namespace:HavenSoft.Gen3Hex.WPF.Resources"
xmlns:hsv="clr-namespace:HavenSoft.HexManiac.WPF.Resources"
xmlns:solarized="clr-namespace:Solarized">
<Application.Resources>
<!-- Icons accessible using IconExtension -->

View File

@@ -1,13 +1,13 @@
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.WPF.Implementations;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.WPF.Implementations;
using System.IO;
using System.Reflection;
using System.Windows;
[assembly: AssemblyTitle("Gen3Hex")]
[assembly: AssemblyTitle("HexManiacAdvance")]
namespace HavenSoft.Gen3Hex.WPF.Windows {
namespace HavenSoft.HexManiac.WPF.Windows {
partial class App {
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);

View File

@@ -1,14 +1,14 @@
<Window x:Class="HavenSoft.Gen3Hex.WPF.Windows.MainWindow"
<Window x:Class="HavenSoft.HexManiac.WPF.Windows.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:hsg3ww="clr-namespace:HavenSoft.Gen3Hex.WPF.Windows"
xmlns:hsg3hv="clr-namespace:HavenSoft.Gen3Hex.WPF.Controls"
xmlns:hsv="clr-namespace:HavenSoft.Gen3Hex.WPF.Resources"
xmlns:hsg3ww="clr-namespace:HavenSoft.HexManiac.WPF.Windows"
xmlns:hsg3hv="clr-namespace:HavenSoft.HexManiac.WPF.Controls"
xmlns:hsv="clr-namespace:HavenSoft.HexManiac.WPF.Resources"
xmlns:sys="clr-namespace:System;assembly=mscorlib"
xmlns:solarized="clr-namespace:Solarized"
Icon="..\Resources\Snorlax.ico"
Icon="..\Resources\AppIcon.ico"
PreviewMouseDown="RunDeferredActions"
Title="Gen3Hex" Height="450" Width="870" AllowDrop="True" Background="{DynamicResource Background}">
Title="Hex Maniac Advance" Height="450" Width="870" AllowDrop="True" Background="{DynamicResource Background}">
<Window.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisibility" />
<hsg3hv:IntegerToBooleanViaMatchConverter x:Key="IndexMatch"/>
@@ -219,147 +219,159 @@
</MenuItem>
</Menu>
</DockPanel>
<TabControl Name="Tabs" ItemsSource="{Binding}" SelectedIndex="{Binding SelectedIndex}">
<TabControl.InputBindings>
<KeyBinding Key="Esc" Command="{Binding HideSearchControls}"/>
</TabControl.InputBindings>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="TabTextBlock" Background="Transparent" Text="{Binding Name}" Foreground="{DynamicResource Primary}" MouseDown="TabMouseDown" MouseMove="TabMouseMove" MouseUp="TabMouseUp">
<TextBlock.InputBindings>
<MouseBinding MouseAction="MiddleClick" Command="{Binding Close}" CommandParameter="{DynamicResource FileSystem}"/>
</TextBlock.InputBindings>
</TextBlock>
<Button Command="{Binding Close}" Width="15" Margin="5,0,0,0" VerticalAlignment="Stretch" Background="{DynamicResource Background}" BorderBrush="{DynamicResource Background}">
<Path Data="{hsv:Icon Exit}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
</Button>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DockPanel>
<Border DockPanel.Dock="Top" Height="19" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Backlight}">
<TextBox Text="{Binding AnchorText, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding AnchorTextVisible, Converter={StaticResource BoolToVisibility}}" Margin="60,0,25,0"/>
</Border>
<!-- Headers -->
<ItemsControl DockPanel.Dock="Left" Width="60" ItemsSource="{Binding Headers}" Background="{DynamicResource Backlight}" MouseDown="HeaderMouseDown">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{DynamicResource Secondary}" Text="{Binding}" FontFamily="Consolas" HorizontalAlignment="Right" Height="{x:Static hsg3hv:HexContent.CellHeight}" Padding="4"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Line Width="1" DockPanel.Dock="Left" Stroke="{DynamicResource Background}"/>
<StackPanel DockPanel.Dock="Right" Visibility="{Binding HasTools, Converter={StaticResource BoolToVisibility}}">
<Button Content="Text" Command="{Binding Tools.StringToolCommand}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="0">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="Tool2" Command="{Binding Tools.Tool2Command}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="1">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="Tool3" Command="{Binding Tools.Tool3Command}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="2">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Grid>
<hsg3hv:StartScreen/>
<TabControl Name="Tabs" ItemsSource="{Binding}" SelectedIndex="{Binding SelectedIndex}">
<TabControl.Style>
<Style TargetType="TabControl" BasedOn="{StaticResource {x:Type TabControl}}">
<Style.Triggers>
<Trigger Property="SelectedIndex" Value="-1">
<Setter Property="Visibility" Value="Collapsed"/>
</Trigger>
</Style.Triggers>
</Style>
</TabControl.Style>
<TabControl.InputBindings>
<KeyBinding Key="Esc" Command="{Binding HideSearchControls}"/>
</TabControl.InputBindings>
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel Orientation="Horizontal">
<TextBlock Name="TabTextBlock" Background="Transparent" Text="{Binding Name}" Foreground="{DynamicResource Primary}" MouseDown="TabMouseDown" MouseMove="TabMouseMove" MouseUp="TabMouseUp">
<TextBlock.InputBindings>
<MouseBinding MouseAction="MiddleClick" Command="{Binding Close}" CommandParameter="{DynamicResource FileSystem}"/>
</TextBlock.InputBindings>
</TextBlock>
<Button Command="{Binding Close}" Width="15" Margin="5,0,0,0" VerticalAlignment="Stretch" Background="{DynamicResource Background}" BorderBrush="{DynamicResource Background}">
<Path Data="{hsv:Icon Exit}" Fill="{DynamicResource Secondary}" Stretch="Uniform"/>
</Button>
</StackPanel>
<!-- Tools -->
<Grid DockPanel.Dock="Right" DataContext="{Binding Tools}">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="-1">
</DataTemplate>
</TabControl.ItemTemplate>
<TabControl.ContentTemplate>
<DataTemplate>
<DockPanel>
<Border DockPanel.Dock="Top" Height="19" BorderThickness="0,0,0,1" BorderBrush="{DynamicResource Backlight}">
<TextBox Text="{Binding AnchorText, UpdateSourceTrigger=PropertyChanged}" Visibility="{Binding AnchorTextVisible, Converter={StaticResource BoolToVisibility}}" Margin="60,0,25,0"/>
</Border>
<!-- Headers -->
<ItemsControl DockPanel.Dock="Left" Width="60" ItemsSource="{Binding Headers}" Background="{DynamicResource Backlight}" MouseDown="HeaderMouseDown">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Foreground="{DynamicResource Secondary}" Text="{Binding}" FontFamily="Consolas" HorizontalAlignment="Right" Height="{x:Static hsg3hv:HexContent.CellHeight}" Padding="4"/>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<Line Width="1" DockPanel.Dock="Left" Stroke="{DynamicResource Background}"/>
<StackPanel DockPanel.Dock="Right" Visibility="{Binding HasTools, Converter={StaticResource BoolToVisibility}}">
<Button Content="Text" Command="{Binding Tools.StringToolCommand}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="0">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="Tool2" Command="{Binding Tools.Tool2Command}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="1">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
<Button Content="Tool3" Command="{Binding Tools.Tool3Command}" Margin="0,2">
<Button.LayoutTransform>
<RotateTransform Angle="90"/>
</Button.LayoutTransform>
<Button.Style>
<Style TargetType="Button" BasedOn="{StaticResource {x:Type Button}}">
<Style.Triggers>
<DataTrigger Binding="{Binding Tools.SelectedIndex}" Value="2">
<Setter Property="BorderBrush" Value="{solarized:Theme Blue}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
</Button>
</StackPanel>
<!-- Tools -->
<Grid DockPanel.Dock="Right" DataContext="{Binding Tools}">
<Grid.Style>
<Style TargetType="Grid">
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="-1">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<DockPanel Name="StringToolPanel">
<DockPanel.Style>
<Style TargetType="DockPanel">
<Setter Property="Visibility" Value="Collapsed"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Grid.Style>
<DockPanel Name="StringToolPanel">
<DockPanel.Style>
<Style TargetType="DockPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="0">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock Text="Address:" Margin="5,15,0,0" DockPanel.Dock="Top"/>
<TextBox Text="{Binding StringTool.Address, Converter={StaticResource Hex}}" Margin="5" DockPanel.Dock="Top"/>
<TextBlock Text="Content:" Margin="5,30,0,0" DockPanel.Dock="Top"/>
<TextBox Margin="5,5" AcceptsReturn="True" MinHeight="50" Width="255" TextWrapping="Wrap" FontFamily="Consolas" VerticalScrollBarVisibility="Auto"
SelectionChanged="StringToolContentSelectionChanged"
IsEnabled="{Binding StringTool.Enabled}"
Text="{Binding StringTool.Content, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<StackPanel Name="Tool2Panel">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="Coming Soon!" Margin="5,15,0,0"/>
</StackPanel>
<StackPanel Name="Tool3Panel">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="2">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="Coming Soon!" Margin="5,15,0,0"/>
</StackPanel>
</Grid>
<ScrollBar DockPanel.Dock="Right"
Minimum="{Binding MinimumScroll}" Maximum="{Binding MaximumScroll}" Value="{Binding ScrollValue}"
SmallChange="1" LargeChange="{Binding Height}" />
<hsg3hv:HexContent x:Name="HexContent" ViewPort="{Binding}" ShowGrid="{Binding DataContext.ShowMatrix, RelativeSource={RelativeSource AncestorType=hsg3ww:MainWindow}}" Margin=".5,.5,0,0"/>
</DockPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="0">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DockPanel.Style>
<TextBlock Text="Address:" Margin="5,15,0,0" DockPanel.Dock="Top"/>
<TextBox Text="{Binding StringTool.Address, Converter={StaticResource Hex}}" Margin="5" DockPanel.Dock="Top"/>
<TextBlock Text="Content:" Margin="5,30,0,0" DockPanel.Dock="Top"/>
<TextBox Margin="5,5" AcceptsReturn="True" MinHeight="50" Width="255" TextWrapping="Wrap" FontFamily="Consolas" VerticalScrollBarVisibility="Auto"
SelectionChanged="StringToolContentSelectionChanged"
IsEnabled="{Binding StringTool.Enabled}"
Text="{Binding StringTool.Content, UpdateSourceTrigger=PropertyChanged}" />
</DockPanel>
<StackPanel Name="Tool2Panel">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="1">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="Coming Soon!" Margin="5,15,0,0"/>
</StackPanel>
<StackPanel Name="Tool3Panel">
<StackPanel.Style>
<Style TargetType="StackPanel">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding SelectedIndex}" Value="2">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</StackPanel.Style>
<TextBlock Text="Coming Soon!" Margin="5,15,0,0"/>
</StackPanel>
</Grid>
<ScrollBar DockPanel.Dock="Right"
Minimum="{Binding MinimumScroll}" Maximum="{Binding MaximumScroll}" Value="{Binding ScrollValue}"
SmallChange="1" LargeChange="{Binding Height}" />
<hsg3hv:HexContent x:Name="HexContent" ViewPort="{Binding}" ShowGrid="{Binding DataContext.ShowMatrix, RelativeSource={RelativeSource AncestorType=hsg3ww:MainWindow}}" Margin=".5,.5,0,0"/>
</DockPanel>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
</Grid>
</DockPanel>
</Window>

View File

@@ -1,8 +1,8 @@
using HavenSoft.Gen3Hex.Core;
using HavenSoft.Gen3Hex.Core.Models;
using HavenSoft.Gen3Hex.Core.ViewModels;
using HavenSoft.Gen3Hex.Core.ViewModels.Tools;
using HavenSoft.Gen3Hex.WPF.Controls;
using HavenSoft.HexManiac.Core;
using HavenSoft.HexManiac.Core.Models;
using HavenSoft.HexManiac.Core.ViewModels;
using HavenSoft.HexManiac.Core.ViewModels.Tools;
using HavenSoft.HexManiac.WPF.Controls;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@@ -13,7 +13,7 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
namespace HavenSoft.Gen3Hex.WPF.Windows {
namespace HavenSoft.HexManiac.WPF.Windows {
partial class MainWindow {
private readonly List<Action> deferredActions = new List<Action>();
@@ -119,8 +119,8 @@ namespace HavenSoft.Gen3Hex.WPF.Windows {
if (ViewModel.Count == 0) Close();
}
private void WikiClick(object sender, EventArgs e) => System.Diagnostics.Process.Start("https://github.com/haven1433/gen3hex/wiki");
private void ReportIssueClick(object sender, EventArgs e) => System.Diagnostics.Process.Start("https://github.com/haven1433/gen3hex/issues");
private void WikiClick(object sender, EventArgs e) => System.Diagnostics.Process.Start("https://github.com/haven1433/HexManiacAdvance/wiki");
private void ReportIssueClick(object sender, EventArgs e) => System.Diagnostics.Process.Start("https://github.com/haven1433/HexManiacAdvance/issues");
private void AboutClick(object sender, EventArgs e) => new AboutWindow().ShowDialog();
private void EditBoxVisibilityChanged(object sender, DependencyPropertyChangedEventArgs e) {