diff --git a/CommonWPF/Helper.cs b/CommonWPF/Helper.cs
index a278b70..94a131f 100644
--- a/CommonWPF/Helper.cs
+++ b/CommonWPF/Helper.cs
@@ -78,5 +78,22 @@ namespace CommonWPF {
return fmt.Width;
}
+
+ ///
+ /// Converts a System.Windows.Media.Color value into 32-bit ARGB.
+ ///
+ public static int ColorToInt(Color color) {
+ return (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
+ }
+
+ ///
+ /// Creates a System.Windows.Media.Color value from 32-bit ARGB.
+ ///
+ ///
+ ///
+ public static Color ColorFromInt(int colorInt) {
+ return Color.FromArgb((byte)(colorInt >> 24), (byte)(colorInt >> 16),
+ (byte)(colorInt >> 8), (byte)colorInt);
+ }
}
}
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index f2be80e..ce4e88e 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -4387,7 +4387,19 @@ namespace SourceGen {
return;
}
string sgecPathName = Path.GetFullPath(fileDlg.FileName);
- if (!Sgec.ExportToFile(sgecPathName, mProject, out string detailMsg)) {
+
+ MessageBoxResult res = MessageBox.Show("Use relative offsets?", "Question",
+ MessageBoxButton.YesNoCancel, MessageBoxImage.Question);
+ bool resMode;
+ if (res == MessageBoxResult.Cancel) {
+ return;
+ } else if (res == MessageBoxResult.Yes) {
+ resMode = true;
+ } else {
+ resMode = false;
+ }
+
+ if (!Sgec.ExportToFile(sgecPathName, mProject, resMode, out string detailMsg)) {
MessageBox.Show("Failed: " + detailMsg);
} else {
MessageBox.Show("Success: " + detailMsg);
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index a3efe64..243d9de 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -273,7 +273,7 @@ namespace SourceGen {
Text = mlc.Text;
BoxMode = mlc.BoxMode;
MaxWidth = mlc.MaxWidth;
- BackgroundColor = ColorToInt(mlc.BackgroundColor);
+ BackgroundColor = CommonWPF.Helper.ColorToInt(mlc.BackgroundColor);
}
}
public class SerSymbol {
@@ -730,7 +730,7 @@ namespace SourceGen {
continue;
}
proj.Notes[intKey] = new MultiLineComment(kvp.Value.Text,
- ColorFromInt(kvp.Value.BackgroundColor));
+ CommonWPF.Helper.ColorFromInt(kvp.Value.BackgroundColor));
}
// Deserialize user-defined labels.
@@ -1261,14 +1261,5 @@ namespace SourceGen {
return false;
}
}
-
- private static int ColorToInt(Color color) {
- return (color.A << 24) | (color.R << 16) | (color.G << 8) | color.B;
- }
-
- private static Color ColorFromInt(int colorInt) {
- return Color.FromArgb((byte)(colorInt >> 24), (byte)(colorInt >> 16),
- (byte)(colorInt >> 8), (byte)colorInt);
- }
}
}
diff --git a/SourceGen/Sgec.cs b/SourceGen/Sgec.cs
index ef84c82..d02d053 100644
--- a/SourceGen/Sgec.cs
+++ b/SourceGen/Sgec.cs
@@ -18,6 +18,10 @@ using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
+using System.Text.RegularExpressions;
+using System.Web.Script.Serialization;
+
+using CommonUtil;
namespace SourceGen {
///
@@ -30,6 +34,56 @@ namespace SourceGen {
public static class Sgec {
public const string SGEC_EXT = "_sgec.txt";
+ // Commands.
+ private const string SET_COMMENT = "set-comment";
+ private const string SET_LONG_COMMENT = "set-long-comment";
+ private const string SET_NOTE = "set-note";
+
+ // Regular expression used for parsing lines.
+ private const string LINE_PATTERN = @"^([A-Za-z-]+)\s([^:]+):(.+)$";
+ private static Regex sLineRegex = new Regex(LINE_PATTERN);
+ private const int GROUP_CMD = 1;
+ private const int GROUP_POS = 2;
+ private const int GROUP_VALUE = 3;
+
+ private static bool OUTPUT_ADDR = true; // addr vs. offset
+
+ // Lifted from ProjectFile.
+ public class SerMultiLineComment {
+ // NOTE: Text must be CRLF at line breaks.
+ public string Text { get; set; }
+ public bool BoxMode { get; set; }
+ public int MaxWidth { get; set; }
+ public int BackgroundColor { get; set; }
+
+ public SerMultiLineComment() { }
+ public SerMultiLineComment(MultiLineComment mlc) {
+ Text = mlc.Text;
+ BoxMode = mlc.BoxMode;
+ MaxWidth = mlc.MaxWidth;
+ BackgroundColor = CommonWPF.Helper.ColorToInt(mlc.BackgroundColor);
+ }
+ }
+
+ ///
+ /// Generates a position string, which may be a file offset, an address, or a
+ /// position delta.
+ ///
+ private static string PositionStr(int offset, int prevOffset, AddressMap addrMap,
+ bool relMode) {
+ if (prevOffset < 0 || !relMode) {
+ // hex offset or address
+ if (OUTPUT_ADDR) {
+ return '$' + addrMap.OffsetToAddress(offset).ToString("x4");
+ } else {
+ return '+' + offset.ToString("x6");
+ }
+ } else {
+ // decimal delta
+ return '>' + (offset - prevOffset).ToString();
+ }
+ }
+
///
/// Exports comments in SGEC format.
///
@@ -37,19 +91,43 @@ namespace SourceGen {
/// Project object.
/// Details on failure or success.
/// True on success.
- public static bool ExportToFile(string pathName, DisasmProject proj, out string detailMsg) {
- int numComments = 0;
+ public static bool ExportToFile(string pathName, DisasmProject proj, bool relMode,
+ out string detailMsg) {
+ int numItems = 0;
+ JavaScriptSerializer ser = new JavaScriptSerializer();
+
+ int prevOffset = -1;
using (StreamWriter sw = new StreamWriter(pathName, false, new UTF8Encoding(false))) {
for (int offset = 0; offset < proj.FileDataLength; offset++) {
if (!string.IsNullOrEmpty(proj.Comments[offset])) {
- sw.WriteLine("set-comment +" + offset.ToString("x6") + ':' +
+ sw.WriteLine(SET_COMMENT + " " +
+ PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
proj.Comments[offset]);
- numComments++;
+ prevOffset = offset;
+ numItems++;
+ }
+ if (proj.LongComments.TryGetValue(offset, out MultiLineComment lc)) {
+ SerMultiLineComment serCom = new SerMultiLineComment(lc);
+ string cereal = ser.Serialize(serCom);
+ sw.WriteLine(SET_LONG_COMMENT + " " +
+ PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
+ cereal);
+ prevOffset = offset;
+ numItems++;
+ }
+ if (proj.Notes.TryGetValue(offset, out MultiLineComment nt)) {
+ SerMultiLineComment serCom = new SerMultiLineComment(nt);
+ string cereal = ser.Serialize(serCom);
+ sw.WriteLine(SET_NOTE + " " +
+ PositionStr(offset, prevOffset, proj.AddrMap, relMode) + ':' +
+ cereal);
+ prevOffset = offset;
+ numItems++;
}
}
}
- detailMsg = "Exported " + numComments + " comments.";
+ detailMsg = "exported " + numItems + " items.";
return true;
}
@@ -72,49 +150,139 @@ namespace SourceGen {
return false;
}
- List changed = new List(lines.Length);
+ JavaScriptSerializer ser = new JavaScriptSerializer();
- string setComment = "set-comment +";
+ int lineNum = 0;
+ int prevOffset = -1;
foreach (string line in lines) {
- if (!line.StartsWith(setComment)) {
- Debug.WriteLine("Ignoring " + line);
+ lineNum++; // first line is 1
+ if (string.IsNullOrEmpty(line) || line[0] == '#') {
+ // ignore
continue;
}
-
- int offset;
- try {
- offset = Convert.ToInt32(line.Substring(setComment.Length, 6), 16);
- } catch (Exception ex) {
- Debug.WriteLine("Failed on " + line);
- detailMsg = ex.Message;
+ MatchCollection matches = sLineRegex.Matches(line);
+ if (matches.Count != 1) {
+ detailMsg = "Line " + lineNum + ": unable to parse into tokens";
return false;
}
- if (changed.Contains(offset)) {
- Debug.WriteLine("Skipping repeated entry +" + offset.ToString("X6"));
- continue;
+ string posStr = matches[0].Groups[GROUP_POS].Value;
+ int offset;
+ if (posStr[0] == '+') {
+ // offset
+ if (!Asm65.Number.TryParseIntHex(posStr.Substring(1), out offset)) {
+ detailMsg = "Line " + lineNum + ": unable to parse offset '" +
+ posStr + "'";
+ return false;
+ }
+ } else if (posStr[0] == '$') {
+ // address
+ if (!Asm65.Address.ParseAddress(posStr, (1 << 24) - 1, out int addr)) {
+ detailMsg = "Line " + lineNum + ": unable to parse address '" +
+ posStr + "'";
+ return false;
+ }
+ offset = proj.AddrMap.AddressToOffset(0, addr);
+ } else if (posStr[0] == '>') {
+ // relative offset
+ if (prevOffset < 0) {
+ detailMsg = "Line " + lineNum + ": first address/offset cannot be relative";
+ return false;
+ }
+ if (!Asm65.Number.TryParseInt(posStr.Substring(1), out int delta, out int _)) {
+ detailMsg = "Line " + lineNum + ": unable to parse delta";
+ return false;
+ }
+ offset = prevOffset + delta;
+ } else {
+ detailMsg = "Line " + lineNum + ": unknown position type '" + posStr[0] + "'";
+ return false;
}
- string oldComment = proj.Comments[offset];
- string newComment = line.Substring(setComment.Length + 7);
- if (oldComment == newComment) {
- // no change
- continue;
- }
- if (!string.IsNullOrEmpty(oldComment)) {
- // overwriting existing entry
- Debug.WriteLine("Replacing comment +" + offset.ToString("x6") +
- " '" + oldComment + "'");
+ prevOffset = offset;
+
+ string cmdStr = matches[0].Groups[GROUP_CMD].Value;
+ string valueStr = matches[0].Groups[GROUP_VALUE].Value;
+ switch (cmdStr) {
+ case SET_COMMENT: {
+ string oldComment = proj.Comments[offset];
+ string newComment = valueStr;
+ if (oldComment == newComment) {
+ // no change
+ break;
+ }
+ if (!string.IsNullOrEmpty(oldComment)) {
+ // overwriting existing entry; make a note
+ Debug.WriteLine("Replacing comment +" + offset.ToString("x6") +
+ " '" + oldComment + "'");
+ }
+ UndoableChange uc = UndoableChange.CreateCommentChange(offset,
+ oldComment, newComment);
+ cs.Add(uc);
+ }
+ break;
+ case SET_LONG_COMMENT: {
+ if (!DeserializeMlc(ser, valueStr, false,
+ out MultiLineComment newComment)) {
+ detailMsg = "Line " + lineNum + ": failed to deserialize value";
+ return false;
+ }
+ proj.LongComments.TryGetValue(offset, out MultiLineComment oldComment);
+ if (oldComment == newComment) {
+ // no change
+ break;
+ }
+ UndoableChange uc = UndoableChange.CreateLongCommentChange(offset,
+ oldComment, newComment);
+ cs.Add(uc);
+ }
+ break;
+ case SET_NOTE: {
+ if (!DeserializeMlc(ser, valueStr, true,
+ out MultiLineComment newNote)) {
+ detailMsg = "Line " + lineNum + ": failed to deserialize value";
+ return false;
+ }
+ proj.Notes.TryGetValue(offset, out MultiLineComment oldNote);
+ if (oldNote == newNote) {
+ // no change
+ break;
+ }
+ UndoableChange uc = UndoableChange.CreateNoteChange(offset,
+ oldNote, newNote);
+ cs.Add(uc);
+
+ }
+ break;
+ default:
+ detailMsg = "Line " + lineNum + ": unknown command '" + cmdStr + "'";
+ return false;
}
- UndoableChange uc = UndoableChange.CreateCommentChange(offset,
- oldComment, newComment);
- cs.Add(uc);
- changed.Add(offset);
}
detailMsg = "applied " + cs.Count + " changes.";
return true;
}
+
+ private static bool DeserializeMlc(JavaScriptSerializer ser, string cereal, bool isNote,
+ out MultiLineComment mlc) {
+ mlc = null;
+ SerMultiLineComment smlc;
+ try {
+ smlc = ser.Deserialize(cereal);
+ } catch (Exception ex) {
+ Debug.WriteLine("Deserialization failed: " + ex.Message);
+ return false;
+ }
+
+ if (isNote) {
+ mlc = new MultiLineComment(smlc.Text,
+ CommonWPF.Helper.ColorFromInt(smlc.BackgroundColor));
+ } else {
+ mlc = new MultiLineComment(smlc.Text, smlc.BoxMode, smlc.MaxWidth);
+ }
+ return true;
+ }
}
}