diff --git a/CommonUtil/AddressMap.cs b/CommonUtil/AddressMap.cs index 0e1b4e8..14c2274 100644 --- a/CommonUtil/AddressMap.cs +++ b/CommonUtil/AddressMap.cs @@ -1716,8 +1716,8 @@ namespace CommonUtil { // Fixed region follows. Test_Expect(AddResult.Okay, ref result, map.AddEntry(0x000200, 0x0100, 0x3000)); - string mapStr = map.FormatAddressMap(); // DEBUG - format the map and - Debug.WriteLine(mapStr); // DEBUG - print it to the console + //string mapStr = map.FormatAddressMap(); // DEBUG - format the map and + //Debug.WriteLine(mapStr); // DEBUG - print it to the console // Add a region that starts in the middle of the floating region (becoming // a sibling), and ends after the fixed region (becoming its parent). diff --git a/SourceGen/DailyTips.cs b/SourceGen/DailyTips.cs new file mode 100644 index 0000000..bd45b5c --- /dev/null +++ b/SourceGen/DailyTips.cs @@ -0,0 +1,179 @@ +/* + * Copyright 2021 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.IO; +using System.Web.Script.Serialization; +using System.Windows.Media.Imaging; + +namespace SourceGen { + /// + /// Holds the collection of daily tips and associated images. + /// + public class DailyTips { + private static string TIPS_FILE = "daily-tips.json"; + private static string TIPS_DIR = "Tips"; + + /// + /// A tip is a text string with an optional bitmap. + /// + public class Tip { + public string Text { get; private set; } + public string ImageFileName { get; private set; } + public BitmapSource Bitmap { get; } + + public Tip(string text, string imageFileName) { + Text = text; + ImageFileName = imageFileName; + + if (!string.IsNullOrEmpty(imageFileName)) { + Bitmap = LoadImage(imageFileName); + } + } + + private static BitmapSource LoadImage(string fileName) { + string pathName = RuntimeDataAccess.GetPathName(TIPS_DIR); + pathName = Path.Combine(pathName, fileName); + try { + BitmapImage bitmap = new BitmapImage(); + bitmap.BeginInit(); + bitmap.UriSource = new Uri(pathName); + bitmap.CacheOption = BitmapCacheOption.OnLoad; // don't hold file open + bitmap.EndInit(); + return bitmap; + } catch (Exception ex) { + Debug.WriteLine("Unable to load bitmap '" + fileName + "': " + ex); + return null; + } + } + + private static BitmapSource LoadImage1(string fileName) { + string pathName = RuntimeDataAccess.GetPathName(TIPS_DIR); + pathName = Path.Combine(pathName, fileName); + try { + // From "How to: Encode and Decode a PNG Image". + // Note: this holds the file open (try deleting the file). + BitmapSource bitmapSource; + Stream imageStreamSource = new FileStream(pathName, FileMode.Open, + FileAccess.Read, FileShare.Read); + PngBitmapDecoder decoder = new PngBitmapDecoder(imageStreamSource, + BitmapCreateOptions.PreservePixelFormat, BitmapCacheOption.Default); + bitmapSource = decoder.Frames[0]; + //imageStreamSource.Dispose(); // image becomes blank + return bitmapSource; + } catch (IOException ex) { + Debug.WriteLine("Unable to load image '" + fileName + "': " + ex); + return null; + } + } + } + + /// + /// List of tips. + /// + private List mTips = new List(); + + public DailyTips() { } + + /// + /// Loads the list of tips. The list will always have at least one element. + /// + public bool Load() { + mTips.Clear(); + if (!DoLoadTips()) { + mTips.Add(new Tip(Res.Strings.TIPS_NOT_AVAILABLE, null)); + return false; + } else { + Debug.WriteLine("Loaded " + mTips.Count + " tips"); + return true; + } + } + + public int DailyNumber { + get { + // We show a different tip every day by taking the day-of-year value and + // modding it by the number of tips we have. Doesn't do the right thing + // at the end of year transition, but everybody is off partying anyway. + if (mTips.Count > 0) { + int doy = DateTime.Now.DayOfYear; + return doy % mTips.Count; + } else { + return 0; + } + } + } + + public int Count { get { return mTips.Count; } } + + /// + /// Returns the Nth tip. + /// + public Tip Get(int index) { + if (mTips.Count == 0) { + // Load tips, or at least generate a "not available" entry. + Load(); + } + if (index < 0 || index >= mTips.Count) { + Debug.WriteLine("Invalid request for tip " + index); + return mTips[0]; + } + + return mTips[index]; + } + + + [Serializable] + internal class SerTip { + public string Text { get; set; } + public string Image { get; set; } + } + internal class SerTipFile { + public List Tips { get; set; } + } + + private bool DoLoadTips() { + string pathName = RuntimeDataAccess.GetPathName(TIPS_DIR); + pathName = Path.Combine(pathName, TIPS_FILE); + string cereal; + try { + cereal = File.ReadAllText(pathName); + } catch (IOException ex) { + Debug.WriteLine("Failed reading tip file '" + pathName + "': " + ex.Message); + return false; + } + + JavaScriptSerializer ser = new JavaScriptSerializer(); + SerTipFile tipFile; + try { + tipFile = ser.Deserialize(cereal); + } catch (Exception ex) { + Debug.WriteLine("Failed deserializing tip JSON: " + ex.Message); + return false; + } + if (tipFile == null) { + Debug.WriteLine("Failed to find tip list"); + return false; + } + + foreach (SerTip serTip in tipFile.Tips) { + mTips.Add(new Tip(serTip.Text, serTip.Image)); + } + + return true; + } + } +} diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml index 1b92d8a..bdec3c5 100644 --- a/SourceGen/Res/Strings.xaml +++ b/SourceGen/Res/Strings.xaml @@ -193,6 +193,8 @@ limitations under the License. Symbol Import Imported {0} global symbols. No global+export symbols were found. + Loading tips... + Daily tips are not available. 6502bench SourceGen (save needed) [new project] diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs index 878f2c2..83495d1 100644 --- a/SourceGen/Res/Strings.xaml.cs +++ b/SourceGen/Res/Strings.xaml.cs @@ -367,6 +367,10 @@ namespace SourceGen.Res { (string)Application.Current.FindResource("str_SymbolImportGoodFmt"); public static string SYMBOL_IMPORT_NONE = (string)Application.Current.FindResource("str_SymbolImportNone"); + public static string TIPS_LOADING = + (string)Application.Current.FindResource("str_TipsLoading"); + public static string TIPS_NOT_AVAILABLE = + (string)Application.Current.FindResource("str_TipsNotAvailable"); public static string TITLE_BASE = (string)Application.Current.FindResource("str_TitleBase"); public static string TITLE_MODIFIED = diff --git a/SourceGen/RuntimeData/Tips/daily-tips.json b/SourceGen/RuntimeData/Tips/daily-tips.json new file mode 100644 index 0000000..34314bc --- /dev/null +++ b/SourceGen/RuntimeData/Tips/daily-tips.json @@ -0,0 +1,52 @@ +{ + "_copyright" : "Copyright 2021 faddenSoft. All rights reserved.", + "_license" : "Apache 2.0; see LICENSE.txt for details", + "Tips" : [ + { + "Text" : "Many disassemblers assume everything is code, and ask you to separate out the data. SourceGen automatically finds all reachable code, so you just need to identify the places where the code starts." + }, + { + "Text" : "Data that follows a JSR or JSL should be marked as \"inline data\". This allows the code analyzer to skip over it. Common situations, such as null-terminated strings and addresses, can be handled automatically.", + "Image" : "print-inline-sample.png" + }, + { + "Text" : "Most screen elements will respond to a double-click, either by jumping to a symbol or opening an appropriate editor. For example, you can double-click in the Bytes column to see a hex dump at that address." + }, + { + "Text" : "You can jump to the address referred to by an instruction operand by selecting the line and hitting Ctrl+J, or simply by double-clicking on the opcode.", + "Image" : "dbl-click-opcode.png" + }, + { + "Text" : "You can configure SourceGen to look more like your favorite assembler. The Application Settings editor lets you configure pseudo-op directives, upper/lower case, and much more.", + "Image" : "pseudo-op-names.png" + }, + { + "Text" : "The References window shows all locations that reference the selected line. Double-click on an entry to jump directly there." + }, + { + "Text" : "Use the Goto feature (Ctrl+G) to jump to an address, file offset, or label." + }, + { + "Text" : "All actions that affect the project are added to the undo/redo buffer. Feel free to experiment." + }, + { + "Text" : "Notes are like full-line comments, but they don't appear in generated source code, so you can use them to make notes while you work. They also serve as color-coded bookmarks.", + "Image" : "note-sample.png" + }, + { + "Text" : "You're not limited to global labels. You can create non-unique local labels, like \"@LOOP\", and define multiple labels for zero-page addresses in local variable tables." + }, + { + "Text" : "2D bitmap images and 3D wireframe meshes can be converted to images that are displayed inline. This can make it much easier to figure out what a piece of code is drawing." + }, + { + "Text" : "Large address tables can be formatted with a single operation. Various arrangements of address bytes are supported." + }, + { + "Text" : "Source code can be generated for several cross-assemblers, or exported to HTML with embedded graphics. Animations can be exported as animated GIFs." + }, + { + "Text" : "The online tutorial at 6502bench.com has many more tips." + } + ] +} diff --git a/SourceGen/RuntimeData/Tips/dbl-click-opcode.png b/SourceGen/RuntimeData/Tips/dbl-click-opcode.png new file mode 100644 index 0000000..9da203b Binary files /dev/null and b/SourceGen/RuntimeData/Tips/dbl-click-opcode.png differ diff --git a/SourceGen/RuntimeData/Tips/note-sample.png b/SourceGen/RuntimeData/Tips/note-sample.png new file mode 100644 index 0000000..17dedb3 Binary files /dev/null and b/SourceGen/RuntimeData/Tips/note-sample.png differ diff --git a/SourceGen/RuntimeData/Tips/print-inline-sample.png b/SourceGen/RuntimeData/Tips/print-inline-sample.png new file mode 100644 index 0000000..3ae6229 Binary files /dev/null and b/SourceGen/RuntimeData/Tips/print-inline-sample.png differ diff --git a/SourceGen/RuntimeData/Tips/pseudo-op-names.png b/SourceGen/RuntimeData/Tips/pseudo-op-names.png new file mode 100644 index 0000000..39d8663 Binary files /dev/null and b/SourceGen/RuntimeData/Tips/pseudo-op-names.png differ diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj index 5ef41c2..16567cc 100644 --- a/SourceGen/SourceGen.csproj +++ b/SourceGen/SourceGen.csproj @@ -40,6 +40,7 @@ + @@ -75,6 +76,7 @@ + diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml index 6791931..f23d666 100644 --- a/SourceGen/WpfGui/MainWindow.xaml +++ b/SourceGen/WpfGui/MainWindow.xaml @@ -640,12 +640,16 @@ limitations under the License. + + + + - + @@ -662,13 +666,14 @@ limitations under the License. - + + + + + + + + + + + + + + + + + + + + + +