├── .gitignore
├── images
├── 1.JPG
├── 10.JPG
├── 11.JPG
├── 11a.JPG
├── 11b.jpg
├── 12.JPG
├── 13.JPG
├── 14.jpg
├── 15.jpg
├── 16.jpg
├── 17.jpg
├── 18.jpg
├── 2.JPG
├── 2.psd
├── 3.JPG
├── 4.JPG
├── 5.JPG
├── 6.JPG
├── 7.JPG
├── 8.JPG
├── 9.JPG
├── Arrow2.jpg
├── Image11b.gst
├── Image18.gst
├── Image2.gst
├── screen1.jpg
└── Image11.1.gst
├── Resources
├── 12.bmp
├── 34.bmp
├── Left.bmp
├── Right.bmp
├── redo.bmp
├── setup.bmp
├── undo.bmp
├── zx0.exe
├── zx1.exe
├── zx2.exe
├── Default.fnt
├── Delete.bmp
├── apultra.exe
├── config.bmp
├── config.png
├── FontMaker.ico
├── about_1_6.png
├── about_1_6.rdw
├── altirraPAL.pal
├── arrow-down.ico
├── arrow-up.ico
├── b_accept.bmp
├── b_cancel.bmp
├── font-shift.bmp
├── basicremfont.lst
├── CopyAreaHMirror.bmp
├── CopyAreaInvert.bmp
├── CopyAreaShiftUp.bmp
├── CopyAreaVMirror.bmp
├── ShiftLeftInsert.bmp
├── CopyAreaShiftDown.bmp
├── CopyAreaShiftLeft.bmp
├── DeleteShiftRight.bmp
├── ShiftRightInsert.bmp
├── CopyAreaRotateLeft.bmp
├── CopyAreaRotateRight.bmp
├── CopyAreaShiftRight.bmp
├── FontShiftLeftRotate.bmp
└── FontShiftRightRotate.bmp
├── Properties
├── launchSettings.json
├── PublishProfiles
│ ├── FolderWithRuntime.pubxml.user
│ ├── FolderProfile.pubxml
│ ├── FolderWithRuntime.pubxml
│ └── FolderProfile.pubxml.user
├── Resources.Designer.cs
└── Resources.resx
├── Program.cs
├── Status.cs
├── Constants.cs
├── FontMaker.sln
├── FontMakerConfigurationWindow.cs
├── Keyboard.cs
├── FontMaker.csproj.user
├── AtrViewInfoJson.cs
├── AtariViewUndoBuffer.cs
├── AtariViewConfigWindow.cs
├── FontMaker.csproj
├── AtariColorSelector.cs
├── AtariFontUndoBuffer.cs
├── AtariView.cs
├── AtariColorSelector.Designer.cs
├── Helpers.cs
├── Compressors.cs
├── FontMakerConfigurationWindow.Designer.cs
├── PageEditor.Designer.cs
├── AtariViewConfigWindow.resx
├── FontAnalysisWindow.resx
├── FontMakerConfigurationWindow.resx
├── AtariColorSelector.resx
├── ExportFontWindow.resx
├── ImportViewWindow.resx
├── ExportViewWindow.resx
├── PageData.cs
├── Configuration.cs
├── ViewActionsWindow.cs
├── PageEditor.cs
├── ExportFontWindow.Designer.cs
├── ImportViewWindow.cs
├── PageEditor.resx
├── General.cs
├── AtariViewConfigWindow.Designer.cs
├── .editorconfig
└── TileSet.cs
/.gitignore:
--------------------------------------------------------------------------------
1 | .vs/
2 | bin/
3 | obj/
4 |
5 |
--------------------------------------------------------------------------------
/images/1.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/1.JPG
--------------------------------------------------------------------------------
/images/10.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/10.JPG
--------------------------------------------------------------------------------
/images/11.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/11.JPG
--------------------------------------------------------------------------------
/images/11a.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/11a.JPG
--------------------------------------------------------------------------------
/images/11b.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/11b.jpg
--------------------------------------------------------------------------------
/images/12.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/12.JPG
--------------------------------------------------------------------------------
/images/13.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/13.JPG
--------------------------------------------------------------------------------
/images/14.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/14.jpg
--------------------------------------------------------------------------------
/images/15.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/15.jpg
--------------------------------------------------------------------------------
/images/16.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/16.jpg
--------------------------------------------------------------------------------
/images/17.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/17.jpg
--------------------------------------------------------------------------------
/images/18.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/18.jpg
--------------------------------------------------------------------------------
/images/2.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/2.JPG
--------------------------------------------------------------------------------
/images/2.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/2.psd
--------------------------------------------------------------------------------
/images/3.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/3.JPG
--------------------------------------------------------------------------------
/images/4.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/4.JPG
--------------------------------------------------------------------------------
/images/5.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/5.JPG
--------------------------------------------------------------------------------
/images/6.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/6.JPG
--------------------------------------------------------------------------------
/images/7.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/7.JPG
--------------------------------------------------------------------------------
/images/8.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/8.JPG
--------------------------------------------------------------------------------
/images/9.JPG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/9.JPG
--------------------------------------------------------------------------------
/Resources/12.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/12.bmp
--------------------------------------------------------------------------------
/Resources/34.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/34.bmp
--------------------------------------------------------------------------------
/Resources/Left.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/Left.bmp
--------------------------------------------------------------------------------
/Resources/Right.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/Right.bmp
--------------------------------------------------------------------------------
/Resources/redo.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/redo.bmp
--------------------------------------------------------------------------------
/Resources/setup.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/setup.bmp
--------------------------------------------------------------------------------
/Resources/undo.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/undo.bmp
--------------------------------------------------------------------------------
/Resources/zx0.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/zx0.exe
--------------------------------------------------------------------------------
/Resources/zx1.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/zx1.exe
--------------------------------------------------------------------------------
/Resources/zx2.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/zx2.exe
--------------------------------------------------------------------------------
/images/Arrow2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/Arrow2.jpg
--------------------------------------------------------------------------------
/images/Image11b.gst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/Image11b.gst
--------------------------------------------------------------------------------
/images/Image18.gst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/Image18.gst
--------------------------------------------------------------------------------
/images/Image2.gst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/Image2.gst
--------------------------------------------------------------------------------
/images/screen1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/screen1.jpg
--------------------------------------------------------------------------------
/Resources/Default.fnt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/Default.fnt
--------------------------------------------------------------------------------
/Resources/Delete.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/Delete.bmp
--------------------------------------------------------------------------------
/Resources/apultra.exe:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/apultra.exe
--------------------------------------------------------------------------------
/Resources/config.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/config.bmp
--------------------------------------------------------------------------------
/Resources/config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/config.png
--------------------------------------------------------------------------------
/images/Image11.1.gst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/images/Image11.1.gst
--------------------------------------------------------------------------------
/Resources/FontMaker.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/FontMaker.ico
--------------------------------------------------------------------------------
/Resources/about_1_6.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/about_1_6.png
--------------------------------------------------------------------------------
/Resources/about_1_6.rdw:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/about_1_6.rdw
--------------------------------------------------------------------------------
/Resources/altirraPAL.pal:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/altirraPAL.pal
--------------------------------------------------------------------------------
/Resources/arrow-down.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/arrow-down.ico
--------------------------------------------------------------------------------
/Resources/arrow-up.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/arrow-up.ico
--------------------------------------------------------------------------------
/Resources/b_accept.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/b_accept.bmp
--------------------------------------------------------------------------------
/Resources/b_cancel.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/b_cancel.bmp
--------------------------------------------------------------------------------
/Resources/font-shift.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/font-shift.bmp
--------------------------------------------------------------------------------
/Resources/basicremfont.lst:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/basicremfont.lst
--------------------------------------------------------------------------------
/Resources/CopyAreaHMirror.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaHMirror.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaInvert.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaInvert.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaShiftUp.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaShiftUp.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaVMirror.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaVMirror.bmp
--------------------------------------------------------------------------------
/Resources/ShiftLeftInsert.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/ShiftLeftInsert.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaShiftDown.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaShiftDown.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaShiftLeft.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaShiftLeft.bmp
--------------------------------------------------------------------------------
/Resources/DeleteShiftRight.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/DeleteShiftRight.bmp
--------------------------------------------------------------------------------
/Resources/ShiftRightInsert.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/ShiftRightInsert.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaRotateLeft.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaRotateLeft.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaRotateRight.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaRotateRight.bmp
--------------------------------------------------------------------------------
/Resources/CopyAreaShiftRight.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/CopyAreaShiftRight.bmp
--------------------------------------------------------------------------------
/Resources/FontShiftLeftRotate.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/FontShiftLeftRotate.bmp
--------------------------------------------------------------------------------
/Resources/FontShiftRightRotate.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/matosimi/atari-fontmaker/HEAD/Resources/FontShiftRightRotate.bmp
--------------------------------------------------------------------------------
/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "FontMaker": {
4 | "commandName": "Project"
5 | }
6 | }
7 | }
--------------------------------------------------------------------------------
/Properties/PublishProfiles/FolderWithRuntime.pubxml.user:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | True|2023-03-05T09:46:56.3779491Z;True|2023-03-05T11:44:46.7687606+02:00;
8 |
9 |
10 |
--------------------------------------------------------------------------------
/Program.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | internal static class Program
4 | {
5 | public static FontMakerForm MainForm { get; set; }
6 |
7 | ///
8 | /// The main entry point for the application.
9 | ///
10 | [STAThread]
11 | static void Main()
12 | {
13 | Application.SetHighDpiMode(HighDpiMode.DpiUnaware);
14 |
15 | // To customize application configuration such as set high DPI settings or default font,
16 | // see https://aka.ms/applicationconfiguration.
17 | ApplicationConfiguration.Initialize();
18 | MainForm = new FontMakerForm();
19 | Application.Run(MainForm);
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/Properties/PublishProfiles/FolderProfile.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\publish\FontMaker64
10 | FileSystem
11 | <_TargetId>Folder
12 | net9.0-windows7.0
13 | win-x64
14 | false
15 | true
16 | true
17 |
18 |
--------------------------------------------------------------------------------
/Properties/PublishProfiles/FolderWithRuntime.pubxml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | Release
8 | Any CPU
9 | bin\publish\FontMaker64Full
10 | FileSystem
11 | <_TargetId>Folder
12 | net7.0-windows
13 | win-x64
14 | true
15 | true
16 | true
17 |
18 |
--------------------------------------------------------------------------------
/Status.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | internal class Status
4 | {
5 | }
6 |
7 | // All code for section E - Status display and some buttons
8 | public partial class FontMakerForm
9 | {
10 |
11 | public void CheckDuplicate()
12 | {
13 | if ((checkBoxShowDuplicates.Checked == false) || (checkBoxShowDuplicates.Enabled == false))
14 | {
15 | return;
16 | }
17 |
18 | DuplicateCharacterIndex = SelectedCharacterIndex;
19 |
20 | if (FindDuplicateChar() != SelectedCharacterIndex)
21 | {
22 | timerDuplicates.Enabled = true;
23 | }
24 | else
25 | {
26 | timerDuplicates.Enabled = false;
27 | pictureBoxDuplicateIndicator.Visible = false;
28 | }
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/Constants.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | internal static class Constants
4 | {
5 | public const int NumColors = 10; // The # of colors we can set at a maximum (1 lumo, 9 colors)
6 |
7 | public static readonly string Title = "Atari FontMaker";
8 |
9 | public static Rectangle[] WhereAreTheFontBanksComingFrom =
10 | [
11 | new(0, 0, 512, 256),
12 | new(0, 256, 512, 512),
13 | new(0, 512, 512, 768),
14 | new(0, 768, 512, 1024)
15 | ];
16 |
17 | public static int[] FontYOffset = [0, 128, 256, 384];
18 |
19 | public static int[] FontPageOffset = [0, 0, 256, 256];
20 |
21 | public static readonly byte[] ColorIndex2Bits = [0, 0, 1, 2, 3, 3];
22 | public static readonly byte[] Bits2ColorIndex = [1, 2, 3, 4];
23 |
24 | public static readonly byte[] ColorIndex2FourBits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
25 | public static readonly byte[] FourBits2ColorIndex = [0, 1, 2, 3, 4, 5, 6, 7, 8, 8, 8, 8, 4, 5, 6, 7];
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Properties/PublishProfiles/FolderProfile.pubxml.user:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 | True|2025-04-29T06:44:39.6498023Z||;True|2025-04-12T20:31:38.4741760+02:00||;True|2025-04-12T14:14:54.7812399+02:00||;False|2025-04-12T14:14:33.3943147+02:00||;True|2025-03-12T18:32:23.8776885+02:00||;True|2025-03-12T16:01:21.1344768+02:00||;True|2025-01-01T20:04:40.4267033+02:00||;True|2023-03-05T12:54:42.0857746+02:00||;True|2023-03-05T12:51:52.3865797+02:00||;True|2023-03-05T12:47:33.3242651+02:00||;True|2023-03-05T12:17:13.1889879+02:00||;True|2023-03-05T12:16:22.1133159+02:00||;True|2023-03-05T12:09:32.2235560+02:00||;True|2023-03-05T12:05:13.2002146+02:00||;True|2023-03-05T12:04:01.6005147+02:00||;True|2023-03-05T12:00:45.0555316+02:00||;True|2023-03-05T11:53:09.6768585+02:00||;True|2023-03-05T11:47:16.6970347+02:00||;True|2023-03-05T11:43:52.6460612+02:00||;True|2023-03-03T16:13:05.8276461+02:00||;
8 |
9 |
10 |
--------------------------------------------------------------------------------
/FontMaker.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.4.33213.308
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FontMaker", "FontMaker.csproj", "{9178804E-69ED-4308-848E-F5F6C3C7C078}"
7 | EndProject
8 | Global
9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
10 | Debug|Any CPU = Debug|Any CPU
11 | Release|Any CPU = Release|Any CPU
12 | EndGlobalSection
13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
14 | {9178804E-69ED-4308-848E-F5F6C3C7C078}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
15 | {9178804E-69ED-4308-848E-F5F6C3C7C078}.Debug|Any CPU.Build.0 = Debug|Any CPU
16 | {9178804E-69ED-4308-848E-F5F6C3C7C078}.Release|Any CPU.ActiveCfg = Release|Any CPU
17 | {9178804E-69ED-4308-848E-F5F6C3C7C078}.Release|Any CPU.Build.0 = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(SolutionProperties) = preSolution
20 | HideSolutionNode = FALSE
21 | EndGlobalSection
22 | GlobalSection(ExtensibilityGlobals) = postSolution
23 | SolutionGuid = {7758BBDA-A93A-46F3-96C5-357AB66A3E35}
24 | EndGlobalSection
25 | EndGlobal
26 |
--------------------------------------------------------------------------------
/FontMakerConfigurationWindow.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 |
3 | namespace FontMaker;
4 | public partial class FontMakerConfigurationWindow : Form
5 | {
6 | [DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
7 | public Compressors.CompressorType CompressorId { get; set; }
8 |
9 | public FontMakerConfigurationWindow(Compressors.CompressorType compressorId)
10 | {
11 | InitializeComponent();
12 |
13 | CompressorId = compressorId;
14 |
15 | switch (compressorId)
16 | {
17 | case Compressors.CompressorType.APULTRA: radioButtonApultra.Checked = true; break;
18 | case Compressors.CompressorType.ZX2: radioButtonZX2.Checked = true; break;
19 | case Compressors.CompressorType.ZX1: radioButtonZX1.Checked = true; break;
20 | case Compressors.CompressorType.ZX0:
21 | default:
22 | radioButtonZX0.Checked = true; break;
23 | }
24 | }
25 |
26 | private void buttonOk_Click(object sender, EventArgs e)
27 | {
28 | if (radioButtonZX0.Checked)
29 | CompressorId = Compressors.CompressorType.ZX0;
30 | else if (radioButtonZX1.Checked)
31 | CompressorId = Compressors.CompressorType.ZX1;
32 | else if (radioButtonZX2.Checked)
33 | CompressorId = Compressors.CompressorType.ZX2;
34 | else if (radioButtonApultra.Checked)
35 | CompressorId = Compressors.CompressorType.APULTRA;
36 | else
37 | {
38 | CompressorId = Compressors.CompressorType.ZX0;
39 | }
40 |
41 | DialogResult = DialogResult.OK;
42 | Close();
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/Keyboard.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | internal class Keyboard
4 | {
5 | }
6 |
7 | public partial class FontMakerForm
8 | {
9 |
10 | public void ExecuteSelectPreviousCharacter()
11 | {
12 | if (!buttonMegaCopy.Checked)
13 | {
14 | if (CharacterEdited())
15 | {
16 | AtariFontUndoBuffer.Add2Undo(true);
17 | }
18 | }
19 |
20 | SelectedCharacterIndex--;
21 | if (SelectedCharacterIndex < 0) SelectedCharacterIndex += 512;
22 | SetCharCursor();
23 | }
24 |
25 | public void ExecuteSelectNextCharacter()
26 | {
27 | if (!buttonMegaCopy.Checked)
28 | {
29 | if (CharacterEdited())
30 | {
31 | AtariFontUndoBuffer.Add2Undo(true);
32 | }
33 | }
34 |
35 | SelectedCharacterIndex++;
36 | if (SelectedCharacterIndex >= 512) SelectedCharacterIndex -= 512;
37 | SetCharCursor();
38 | }
39 |
40 | public void ExecuteEscapeKeyPressed()
41 | {
42 | if (buttonMegaCopy.Checked)
43 | {
44 | switch (megaCopyStatus)
45 | {
46 | case MegaCopyStatusFlags.None:
47 | break;
48 |
49 | case MegaCopyStatusFlags.Selecting:
50 | ResetMegaCopyStatus();
51 | break;
52 |
53 | case MegaCopyStatusFlags.Selected:
54 | break;
55 |
56 | case MegaCopyStatusFlags.Pasting:
57 | case MegaCopyStatusFlags.PastingView:
58 | case MegaCopyStatusFlags.PastingFont:
59 | ResetMegaCopyStatus();
60 | break;
61 | }
62 | }
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/FontMaker.csproj.user:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | <_LastSelectedProfileId>F:\Projects\Cerebus\Atari\AtariFontMaker\Properties\PublishProfiles\FolderProfile.pubxml
5 |
6 |
7 |
8 | Form
9 |
10 |
11 | Form
12 |
13 |
14 | Form
15 |
16 |
17 | Form
18 |
19 |
20 | Form
21 |
22 |
23 | Form
24 |
25 |
26 | Form
27 |
28 |
29 | Form
30 |
31 |
32 | Form
33 |
34 |
35 | Form
36 |
37 |
38 | Form
39 |
40 |
41 |
--------------------------------------------------------------------------------
/AtrViewInfoJson.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker;
2 | ///
3 | /// The data structure of the .atrview file
4 | ///
5 | public class AtrViewInfoJson
6 | {
7 | public string? Version { get; set; }
8 |
9 | ///
10 | /// Mode 4/5 indicator
11 | /// 0 = mode 2 B & W
12 | /// 1 = mode 4 Color
13 | /// 2 = mode 5 Color
14 | /// 3 = mode 10 9 colors
15 | ///
16 | public string ColoredGfx { get; set; } = string.Empty;
17 |
18 | ///
19 | /// Characters in the view
20 | ///
21 | public string Chars { get; set; } = string.Empty;
22 |
23 | public int Width { get; set; } = 40;
24 | public int Height { get; set; } = 26;
25 |
26 | ///
27 | /// Use which font on which of the 26 lines of the view "1234"
28 | ///
29 | public string Lines { get; set; } = string.Empty;
30 |
31 | ///
32 | /// Hex encoded array of the 6 selected colors
33 | ///
34 | public string Colors { get; set; } = string.Empty;
35 |
36 | public string Fontname1 { get; set; } = string.Empty;
37 | public string Fontname2 { get; set; } = string.Empty;
38 | public string? Fontname3 { get; set; }
39 | public string? Fontname4 { get; set; }
40 |
41 | ///
42 | /// Hex encoded 1024 bytes per font
43 | ///
44 | public string Data { get; set; } = string.Empty;
45 |
46 | public string FortyBytes { get; set; } = string.Empty;
47 |
48 | ///
49 | /// For each page we have some data:
50 | /// - name
51 | /// - characters (40x26)
52 | /// - selected font
53 | ///
54 | public List? Pages { get; set; }
55 |
56 | ///
57 | /// For each tile we have:
58 | /// - index where to store it
59 | /// - characters (5x5)
60 | /// - selected font
61 | ///
62 | public List? Tiles { get; set; }
63 | }
64 |
--------------------------------------------------------------------------------
/AtariViewUndoBuffer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker;
2 |
3 | ///
4 | /// Every page contains an undo buffer.
5 | /// The buffer is fed with AtariView data before something is changed.
6 | /// We maintain a linked list of undo data, and a stack of redo data.
7 | /// The LinkList is limited to 250 entries, after which the oldest get discarded.
8 | ///
9 | public class AtariViewUndoBuffer
10 | {
11 | public const int UndoBufferSize = 250;
12 |
13 | private readonly LinkedList _undoCommands = [];
14 | private readonly Stack _redoCommands = new();
15 |
16 | ///
17 | /// Push the current AtariView into the undo buffer
18 | ///
19 | public void Push()
20 | {
21 | while(_undoCommands.Count >= UndoBufferSize)
22 | {
23 | _undoCommands.RemoveFirst();
24 | }
25 | _undoCommands.AddLast(AtariView.BuildForUndo());
26 | if (_redoCommands.Count > 0)
27 | _redoCommands.Clear();
28 | }
29 |
30 | public void Undo()
31 | {
32 | if (_undoCommands.Count > 0)
33 | {
34 | // Save the current screen
35 | _redoCommands.Push(AtariView.BuildForUndo());
36 |
37 | // Get the last screen and restore it
38 | RestoreScreen(_undoCommands.Last());
39 | _undoCommands.RemoveLast();
40 | }
41 | }
42 |
43 | public void Redo()
44 | {
45 | if (_redoCommands.Count <= 0)
46 | {
47 | // Nothing to redo
48 | return;
49 | }
50 |
51 | // Save the current screen
52 | while (_undoCommands.Count >= UndoBufferSize)
53 | {
54 | _undoCommands.RemoveFirst();
55 | }
56 | _undoCommands.AddLast(AtariView.BuildForUndo());
57 |
58 | // Get the last screen and restore it
59 | RestoreScreen(_redoCommands.Pop());
60 | }
61 |
62 | private static void RestoreScreen(AtariViewUndoInfo data)
63 | {
64 | if (data is { ViewBytes: not null })
65 | {
66 | AtariView.RestoreFromUndo(data);
67 | }
68 | }
69 |
70 | public (bool, bool) GetRedoUndoButtonState()
71 | {
72 | return (_undoCommands.Count > 0, _redoCommands.Count > 0);
73 | }
74 | }
--------------------------------------------------------------------------------
/AtariViewConfigWindow.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker;
2 | public partial class AtariViewConfigWindow : Form
3 | {
4 | public int NewWidth => (int)numericWidth.Value;
5 | public int NewHeight => (int)numericHeight.Value;
6 | public byte NewFontNr
7 | {
8 | get
9 | {
10 | if (radio2.Checked)
11 | return 2;
12 | if (radio3.Checked)
13 | return 3;
14 | if (radio4.Checked)
15 | return 4;
16 | return 1;
17 | }
18 | }
19 |
20 | public AtariViewConfigWindow()
21 | {
22 | InitializeComponent();
23 |
24 | labelCurrentInfo.Text = $"{AtariView.Width}x{AtariView.Height} @ {AtariView.Width * AtariView.Height} bytes";
25 |
26 | numericWidth.Value = AtariView.Width;
27 | numericHeight.Value = AtariView.Height;
28 | ActionNewInfoUpdate();
29 | }
30 |
31 | private void numericWidth_ValueChanged(object sender, EventArgs e)
32 | {
33 | ActionNewInfoUpdate();
34 | }
35 |
36 | private void numericHeight_ValueChanged(object sender, EventArgs e)
37 | {
38 | ActionNewInfoUpdate();
39 | }
40 |
41 | private void ActionNewInfoUpdate(bool limit = true)
42 | {
43 | if (limit)
44 | {
45 | if (numericWidth.Value < 40)
46 | numericWidth.Value = 40;
47 |
48 | if (numericHeight.Value < 26)
49 | numericHeight.Value = 26;
50 | }
51 |
52 | labelNewInfo.Text = $"{numericWidth.Value}x{numericHeight.Value} @ {numericWidth.Value * numericHeight.Value} bytes";
53 |
54 | if (numericWidth.Value != AtariView.Width || numericHeight.Value != AtariView.Height)
55 | {
56 | var xChange = (int)numericWidth.Value - AtariView.Width;
57 | var yChange = (int)numericHeight.Value - AtariView.Height;
58 | labelNewDifference.Text = $"Change in width: {xChange} Change in height: {yChange}";
59 | //buttonResize.Enabled = true;
60 | }
61 | else
62 | {
63 | labelNewDifference.Text = "No change!";
64 | //buttonResize.Enabled = false;
65 | }
66 | }
67 |
68 | private void numericWidth_KeyPress(object sender, KeyPressEventArgs e)
69 | {
70 | //ActionNewInfoUpdate(false);
71 | }
72 |
73 | private void buttonResize_Click(object sender, EventArgs e)
74 | {
75 | if ((int)numericWidth.Value != AtariView.Width || (int)numericHeight.Value != AtariView.Height)
76 | {
77 | // Something has changed
78 | var doResize = true;
79 |
80 | if ((int)numericWidth.Value < AtariView.Width || (int)numericHeight.Value < AtariView.Height)
81 | {
82 | // Ok before a shrink
83 | var ok = MessageBox.Show(@"Do you want to change the view size? You might loose data?", Constants.Title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
84 | doResize = ok == DialogResult.Yes;
85 | }
86 |
87 | if (doResize)
88 | {
89 | DialogResult = DialogResult.OK;
90 | Close();
91 | return;
92 | }
93 | }
94 | else
95 | {
96 | Close();
97 | }
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/FontMaker.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | WinExe
5 | net9.0-windows7.0
6 | enable
7 | true
8 | enable
9 | true
10 |
11 | true
12 | false
13 | SystemAware
14 | Segoe UI, 8.25pt
15 | Resources\FontMaker.ico
16 | 1.6.17.4
17 | 1.6.17.4
18 | False
19 | 1.6.17.4
20 | True
21 |
22 |
23 |
24 | full
25 | 1701;1702;MSB3825
26 |
27 |
28 |
29 | full
30 | 1701;1702;MSB3825
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | True
62 | True
63 | Resources.resx
64 |
65 |
66 |
67 |
68 |
69 | PublicResXFileCodeGenerator
70 | Resources.Designer.cs
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/Properties/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace FontMaker.Properties {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | public class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | public static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("FontMaker.Properties.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | public static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/AtariColorSelector.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | public partial class AtariColorSelectorForm : Form
4 | {
5 | private static readonly SolidBrush WhiteBrush = new(Color.White);
6 |
7 | public AtariColorSelectorForm()
8 | {
9 | InitializeComponent();
10 |
11 | this.FormClosed += FormClose!;
12 | this.Load += FormCreate!;
13 | }
14 |
15 | public void FormCreate(object sender, EventArgs e)
16 | {
17 | DoubleBuffered = true;
18 | DrawPalette();
19 | DrawActualColor();
20 | }
21 |
22 | public void DrawPalette()
23 | {
24 | using var foreColorBrush = new SolidBrush(ForeColor);
25 |
26 | var matrix = new Bitmap(128 + 16, 256 + 16);
27 | using var gr = Graphics.FromImage(matrix);
28 | gr.Clear(this.BackColor);
29 |
30 | for (var y = 0; y < 16; y++)
31 | {
32 | // Vertical marks
33 | gr.DrawString($"{y:X}", this.Font, foreColorBrush, 16 * 8 + 2, y * 16);
34 | for (var x = 0; x < 8; x++)
35 | {
36 | using var thisColorBrush = new SolidBrush(palette[y * 16 + x * 2]);
37 | gr.FillRectangle(thisColorBrush, x * 16, y * 16, 16, 16);
38 | }
39 | }
40 | using var pen = new Pen(WhiteBrush);
41 | gr.DrawRectangle(pen, (selectedColorIndex % 16) * 8, (selectedColorIndex / 16) * 16, 15, 15);
42 |
43 | // Horizontal marks
44 | for (var x = 0; x < 8; x++)
45 | gr.DrawString($"{(x * 2):X}", this.Font, foreColorBrush, 16 * x, 16 * 16 + 2);
46 |
47 | // Clear the old image and set new bitmap
48 | ImagePalette.Image?.Dispose();
49 | ImagePalette.Image = matrix;
50 | }
51 |
52 | public void DrawActualColor()
53 | {
54 | var w = ImageSelected.Width;
55 | var h = ImageSelected.Height;
56 | LabelOldColor.Text = $@"${selectedColorIndex:X2} - {selectedColorIndex}";
57 | LabelActualColor.Text = $@"${actualColorIndex:X2} - {actualColorIndex}";
58 |
59 | var clr = new Bitmap(w, h);
60 | using var gr = Graphics.FromImage(clr);
61 |
62 | using var selColorBrush = new SolidBrush(palette[selectedColorIndex]);
63 | gr.FillRectangle(selColorBrush, 0, 0, w, h / 2);
64 |
65 | using var actualColorBrush = new SolidBrush(palette[actualColorIndex]);
66 | gr.FillRectangle(actualColorBrush, 0, h / 2, w, h / 2);
67 |
68 | ImageSelected.Image?.Dispose();
69 | ImageSelected.Image = clr;
70 | }
71 |
72 | public void ImagePaletteMouseMove(object sender, MouseEventArgs e)
73 | {
74 | if (e.X < 16 * 8 && e.Y < 16 * 16)
75 | {
76 | actualColorIndex = (byte)((e.Y / 16) * 16 + (e.X / 16) * 2);
77 | actualColor = palette[actualColorIndex];
78 | DrawActualColor();
79 | }
80 | }
81 |
82 | public void ImagePaletteMouseDown(object sender, MouseEventArgs e)
83 | {
84 | selectedColorIndex = actualColorIndex;
85 | Close();
86 | }
87 |
88 | public void SetSelectedColorIndex(byte paletteIndex)
89 | {
90 | selectedColorIndex = paletteIndex;
91 | }
92 |
93 | public void FormClose(object sender, FormClosedEventArgs action)
94 | {
95 | if (DialogResult == DialogResult.OK)
96 | {
97 | selectedColorIndex = actualColorIndex;
98 | }
99 | else
100 | {
101 | actualColorIndex = selectedColorIndex;
102 | }
103 | }
104 |
105 | public void SetPalette(Color[] pal)
106 | {
107 | pal.CopyTo(palette, 0);
108 | }
109 |
110 | internal Color[] palette = new Color[256];
111 | public byte selectedColorIndex;
112 | public byte actualColorIndex;
113 | public Color actualColor = Color.Empty;
114 |
115 | private void AtariColorSelectorForm_KeyDown(object sender, KeyEventArgs e)
116 | {
117 | if (e.KeyCode == Keys.Escape)
118 | {
119 | Close();
120 | }
121 | }
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/AtariFontUndoBuffer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | public static class AtariFontUndoBuffer
4 | {
5 | public const int UndoBufferSize = 250;
6 |
7 | public static byte[,] undoBuffer = new byte[UndoBufferSize + 1, AtariFont.NumFontBytes];
8 |
9 | public static int[] undoBufferFlags = new int[UndoBufferSize + 1];
10 | public static int undoBufferIndex;
11 |
12 | public static void Setup()
13 | {
14 | undoBufferIndex = 0;
15 | for (var i = 0; i <= UndoBufferSize; i++)
16 | {
17 | undoBufferFlags[i] = -1;
18 | }
19 | }
20 |
21 | public static void Add2UndoInitial()
22 | {
23 | var prevUndoIndex = undoBufferIndex;
24 |
25 | for (var i = 0; i < AtariFont.NumFontBytes; i++)
26 | {
27 | undoBuffer[undoBufferIndex, i] = AtariFont.FontBytes[i];
28 | }
29 |
30 | undoBufferFlags[undoBufferIndex] = undoBufferFlags[prevUndoIndex] + 1;
31 | undoBufferFlags[(undoBufferIndex + 1) % UndoBufferSize] = -1; //disallow redo when change
32 | }
33 |
34 | public static void Add2Undo(bool difference)
35 | {
36 | if (difference)
37 | {
38 | var prevUndoIndex = undoBufferIndex;
39 | undoBufferIndex = (undoBufferIndex + 1) % UndoBufferSize; // size of undo buffer
40 |
41 | for (var i = 0; i < AtariFont.NumFontBytes; i++)
42 | {
43 | undoBuffer[undoBufferIndex, i] = AtariFont.FontBytes[i];
44 | }
45 |
46 | undoBufferFlags[undoBufferIndex] = undoBufferFlags[prevUndoIndex] + 1;
47 | undoBufferFlags[(undoBufferIndex + 1) % UndoBufferSize] = -1; // disallow redo when change
48 | }
49 | }
50 |
51 | public static void Add2UndoFullDifferenceScan()
52 | {
53 | var i = 0;
54 | var difference = false;
55 |
56 | // check the difference between last undo buffer and current font
57 | while ((i < AtariFont.NumFontBytes) && (!difference))
58 | {
59 | if (AtariFont.FontBytes[i] != undoBuffer[undoBufferIndex, i])
60 | {
61 | difference = true;
62 | }
63 |
64 | i++;
65 | }
66 |
67 | if (difference)
68 | {
69 | AtariFontUndoBuffer.Add2Undo(true);
70 | }
71 |
72 | // Update the GUI state now
73 | }
74 |
75 |
76 | public static int GetPrevUndoIndex()
77 | {
78 | return undoBufferIndex - 1 < 0 ? UndoBufferSize - 1 : undoBufferIndex - 1;
79 | }
80 |
81 | public static void Undo()
82 | {
83 | var prevUndoIndex = GetPrevUndoIndex();
84 |
85 | for (var i = 0; i < AtariFont.NumFontBytes; i++)
86 | {
87 | AtariFont.FontBytes[i] = undoBuffer[prevUndoIndex, i];
88 | }
89 |
90 | undoBufferIndex = prevUndoIndex;
91 | }
92 |
93 | public static void Redo()
94 | {
95 | var nextUndoIndex = (undoBufferIndex + 1) % UndoBufferSize;
96 |
97 | if (undoBufferFlags[nextUndoIndex] > -1)
98 | {
99 | for (var i = 0; i < AtariFont.NumFontBytes; i++)
100 | {
101 | AtariFont.FontBytes[i] = undoBuffer[nextUndoIndex, i];
102 | }
103 | }
104 |
105 | undoBufferIndex = nextUndoIndex;
106 | }
107 |
108 | public static (bool, bool) GetRedoUndoButtonState(bool edited)
109 | {
110 | bool redoButtonEnabled;
111 | bool undoButtonEnabled;
112 |
113 | var nextUndoBufferIndex = (undoBufferIndex + 1) % UndoBufferSize;
114 | var prevUndoBufferIndex = GetPrevUndoIndex();
115 |
116 | // redo button handling
117 | if (undoBufferFlags[nextUndoBufferIndex] == -1)
118 | {
119 | redoButtonEnabled = false;
120 | }
121 | else if (edited)
122 | {
123 | redoButtonEnabled = false;
124 | }
125 | else
126 | {
127 | redoButtonEnabled = true;
128 | }
129 |
130 | // undo button handling
131 | if (edited)
132 | {
133 | undoButtonEnabled = true;
134 | }
135 | else if ((undoBufferFlags[undoBufferIndex] > undoBufferFlags[prevUndoBufferIndex]) && (undoBufferFlags[prevUndoBufferIndex] > -1))
136 | {
137 | undoButtonEnabled = true;
138 | }
139 | else
140 | {
141 | undoButtonEnabled = false;
142 | }
143 |
144 | return (redoButtonEnabled, undoButtonEnabled);
145 | }
146 | }
147 | }
148 |
--------------------------------------------------------------------------------
/AtariView.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 |
4 | public class AtariViewUndoInfo
5 | {
6 | public byte[,] ViewBytes;
7 | public byte[] UseFontOnLine;
8 |
9 | public int Width { get; set; }
10 | public int Height { get; set; }
11 |
12 | public int OffsetX { get; set; }
13 | public int OffsetY { get; set; }
14 | }
15 |
16 | public static class AtariView
17 | {
18 | public const int VIEW_WIDTH = 40;
19 | public const int VIEW_HEIGHT = 26;
20 | public const int VIEW_HEIGHT_TALL = 13;
21 |
22 | public const int VIEW_PIXEL_WIDTH = 48 * 8 * 2; // Each char is 8x8 and the x2 is for the double scale factor
23 | public const int VIEW_PIXEL_HEIGHT = 26 * 8 * 2;
24 |
25 | #region Data
26 |
27 | ///
28 | /// All bytes showing on the 40x26 screen
29 | ///
30 | public static byte[,] ViewBytes = new byte[VIEW_WIDTH, VIEW_HEIGHT];
31 |
32 | ///
33 | /// Which fontNr is used on which line of the view
34 | ///
35 | public static byte[] UseFontOnLine = new byte[VIEW_HEIGHT];
36 |
37 | public static int Width { get; set; } = VIEW_WIDTH;
38 | public static int Height { get; set; } = VIEW_HEIGHT;
39 |
40 | public static int OffsetX { get; set; } = 0;
41 | public static int OffsetY { get; set; } = 0;
42 |
43 | #endregion
44 |
45 | public static AtariViewUndoInfo BuildForUndo()
46 | {
47 | return new AtariViewUndoInfo()
48 | {
49 | ViewBytes = ViewBytes.Clone() as byte[,],
50 | UseFontOnLine = UseFontOnLine.Clone() as byte[],
51 | Width = Width,
52 | Height = Height,
53 | OffsetX = OffsetX,
54 | OffsetY = OffsetY,
55 | };
56 | }
57 |
58 | public static void RestoreFromUndo(AtariViewUndoInfo undoInfo)
59 | {
60 | ViewBytes = undoInfo.ViewBytes.Clone() as byte[,];
61 | UseFontOnLine = undoInfo.UseFontOnLine.Clone() as byte[];
62 | Width = undoInfo.Width;
63 | Height = undoInfo.Height;
64 | OffsetX = undoInfo.OffsetX;
65 | OffsetY = undoInfo.OffsetY;
66 | }
67 |
68 | public static void Setup()
69 | {
70 | for (var i = 0; i < UseFontOnLine.Length; i++)
71 | {
72 | UseFontOnLine[i] = 1;
73 | }
74 | }
75 |
76 | public static void ForcedResize(int width, int height)
77 | {
78 | Width = width;
79 | Height = height;
80 | ViewBytes = new byte[Width, Height];
81 | UseFontOnLine = new byte[Height];
82 |
83 | OffsetX = 0;
84 | OffsetY = 0;
85 |
86 | Setup();
87 | }
88 |
89 | public static void Resize(int width, int height, byte fontNr)
90 | {
91 | // Change the size of the view to the new size
92 | var newViewBytes = new byte[width, height];
93 | var newUseFontOnLine = new byte[height];
94 |
95 | for (var y = 0; y < height; ++y)
96 | newUseFontOnLine[y] = fontNr;
97 |
98 | // Copy as much as possible of the old view into the new view
99 | for (var y = 0; y < height && y < Height; ++y)
100 | {
101 | for (var x = 0; x < width && x < Width; ++x)
102 | {
103 | newViewBytes[x, y] = ViewBytes[x, y];
104 | }
105 | newUseFontOnLine[y] = UseFontOnLine[y];
106 | }
107 |
108 | // Set the new view
109 | ViewBytes = newViewBytes;
110 | UseFontOnLine = newUseFontOnLine;
111 | Width = width;
112 | Height = height;
113 |
114 | if (OffsetX+40 >= width)
115 | OffsetX = width-40;
116 | if (OffsetY + 26 >= height)
117 | OffsetY = height - 26;
118 | }
119 |
120 | public static void Load(string lineTypes, string characterBytes, int viewWidth)
121 | {
122 | var useFontOnLine = Convert.FromHexString(lineTypes);
123 | for (var i = 0; i < useFontOnLine.Length; i++)
124 | {
125 | UseFontOnLine[i] = useFontOnLine[i];
126 | // If OLD format issues
127 | // Font numbers 1-4
128 | if (UseFontOnLine[i] == 0)
129 | ++UseFontOnLine[i];
130 | }
131 |
132 | var bytes = Convert.FromHexString(characterBytes);
133 | var idx = 0;
134 | for (var y = 0; y < Height; ++y)
135 | {
136 | for (var x = 0; x < viewWidth; ++x)
137 | {
138 | ViewBytes[x, y] = bytes[idx];
139 | ++idx;
140 | }
141 | }
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/AtariColorSelector.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | partial class AtariColorSelectorForm
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | ImagePalette = new PictureBox();
32 | ImageSelected = new PictureBox();
33 | LabelActualColor = new Label();
34 | LabelOldColor = new Label();
35 | ((System.ComponentModel.ISupportInitialize)ImagePalette).BeginInit();
36 | ((System.ComponentModel.ISupportInitialize)ImageSelected).BeginInit();
37 | SuspendLayout();
38 | //
39 | // ImagePalette
40 | //
41 | ImagePalette.Location = new Point(8, 0);
42 | ImagePalette.Name = "ImagePalette";
43 | ImagePalette.Size = new Size(145, 272);
44 | ImagePalette.TabIndex = 0;
45 | ImagePalette.TabStop = false;
46 | ImagePalette.MouseDown += ImagePaletteMouseDown;
47 | ImagePalette.MouseMove += ImagePaletteMouseMove;
48 | //
49 | // ImageSelected
50 | //
51 | ImageSelected.Location = new Point(159, 72);
52 | ImageSelected.Name = "ImageSelected";
53 | ImageSelected.Size = new Size(105, 121);
54 | ImageSelected.TabIndex = 1;
55 | ImageSelected.TabStop = false;
56 | //
57 | // LabelActualColor
58 | //
59 | LabelActualColor.AutoSize = true;
60 | LabelActualColor.Location = new Point(192, 199);
61 | LabelActualColor.Name = "LabelActualColor";
62 | LabelActualColor.Size = new Size(40, 13);
63 | LabelActualColor.TabIndex = 2;
64 | LabelActualColor.Text = "$01 - 1";
65 | LabelActualColor.TextAlign = ContentAlignment.MiddleCenter;
66 | //
67 | // LabelOldColor
68 | //
69 | LabelOldColor.AutoSize = true;
70 | LabelOldColor.Location = new Point(192, 53);
71 | LabelOldColor.Name = "LabelOldColor";
72 | LabelOldColor.Size = new Size(40, 13);
73 | LabelOldColor.TabIndex = 3;
74 | LabelOldColor.Text = "$00 - 0";
75 | LabelOldColor.TextAlign = ContentAlignment.MiddleCenter;
76 | //
77 | // AtariColorSelectorForm
78 | //
79 | AutoScaleDimensions = new SizeF(6F, 13F);
80 | AutoScaleMode = AutoScaleMode.Font;
81 | BackgroundImageLayout = ImageLayout.None;
82 | ClientSize = new Size(284, 280);
83 | Controls.Add(ImagePalette);
84 | Controls.Add(ImageSelected);
85 | Controls.Add(LabelActualColor);
86 | Controls.Add(LabelOldColor);
87 | FormBorderStyle = FormBorderStyle.FixedDialog;
88 | Name = "AtariColorSelectorForm";
89 | Text = "AtariColorSelector";
90 | KeyDown += AtariColorSelectorForm_KeyDown;
91 | ((System.ComponentModel.ISupportInitialize)ImagePalette).EndInit();
92 | ((System.ComponentModel.ISupportInitialize)ImageSelected).EndInit();
93 | ResumeLayout(false);
94 | PerformLayout();
95 | }
96 |
97 | #endregion
98 |
99 | private System.Windows.Forms.PictureBox ImagePalette;
100 | private System.Windows.Forms.PictureBox ImageSelected;
101 | private System.Windows.Forms.Label LabelActualColor;
102 | private System.Windows.Forms.Label LabelOldColor;
103 | }
104 | }
--------------------------------------------------------------------------------
/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 |
5 | namespace FontMaker
6 | {
7 | public static class Helpers
8 | {
9 | public static byte AtariConvertChar(byte character)
10 | {
11 | if (character == 32)
12 | {
13 | return 0;
14 | }
15 |
16 | if (character is >= 48 and <= 90)
17 | {
18 | return (byte)(character - 32);
19 | }
20 |
21 | return character;
22 | }
23 |
24 | public static Image GetImage(PictureBox src)
25 | {
26 | var w = src.Bounds.Width;
27 | var h = src.Bounds.Height;
28 |
29 | if (src.Image != null)
30 | return src.Image;
31 |
32 | src.Image = new Bitmap(w, h);
33 |
34 | return src.Image;
35 | }
36 |
37 | public static Image NewImage(PictureBox src)
38 | {
39 | var w = src.Bounds.Width;
40 | var h = src.Bounds.Height;
41 |
42 | if (src.Image != null)
43 | src.Image.Dispose();
44 |
45 | src.Image = new Bitmap(w, h);
46 |
47 | return src.Image;
48 | }
49 |
50 | ///
51 | /// Open a given url in the default browser
52 | ///
53 | /// url to open
54 | public static void OpenUrl(string url)
55 | {
56 | try
57 | {
58 | Process.Start(url);
59 | }
60 | catch
61 | {
62 | // hack because of this: https://github.com/dotnet/corefx/issues/10361
63 | if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
64 | {
65 | url = url.Replace("&", "^&");
66 | Process.Start(new ProcessStartInfo(url) { UseShellExecute = true });
67 | }
68 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
69 | {
70 | Process.Start("xdg-open", url);
71 | }
72 | else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
73 | {
74 | Process.Start("open", url);
75 | }
76 | else
77 | {
78 | throw;
79 | }
80 | }
81 | }
82 |
83 | public static void GetBuildInfo(ref int v1, ref int v2, ref int v3, ref int v4)
84 | {
85 | try
86 | {
87 | var thisAssembly = typeof(Helpers).Assembly;
88 | var thisAssemblyName = thisAssembly.GetName();
89 |
90 | var ver = thisAssemblyName.Version;
91 | if (ver == null) return;
92 | v1 = ver.Major;
93 | v2 = ver.Minor;
94 | v3 = ver.Build;
95 | v4 = ver.Revision;
96 | }
97 | catch (Exception)
98 | {
99 | // ignored
100 | }
101 | }
102 |
103 | public static string GetBuildInfoAsString()
104 | {
105 | var v1 = 0;
106 | var v2 = 0;
107 | var v3 = 0;
108 | var v4 = 0;
109 | GetBuildInfo(ref v1, ref v2, ref v3, ref v4);
110 | return $"{v1}.{v2}.{v3}.{v4}";
111 | }
112 |
113 | public static T GetResource(string shortResourceName)
114 | {
115 | var assembly = Assembly.GetExecutingAssembly();
116 | var resourceName = $"FontMaker.Resources.{shortResourceName}";
117 | if (typeof(T) == typeof(string))
118 | {
119 | using var stream = assembly.GetManifestResourceStream(resourceName);
120 | using var reader = new StreamReader(stream);
121 | return (T)(object)reader.ReadToEnd();
122 | }
123 | else if (typeof(T) == typeof(byte[]))
124 | {
125 | using var stream = assembly.GetManifestResourceStream(resourceName);
126 | using var reader = new BinaryReader(stream);
127 | return (T)(object)reader.ReadBytes((int)stream.Length);
128 | }
129 | else
130 | throw new Exception("Unsupported resource object type");
131 | }
132 |
133 | ///
134 | /// Find the AtariPalette entry that best approximates the request RGB color
135 | ///
136 | /// Red color component
137 | /// Green color component
138 | /// Blue color component
139 | /// Reference AtariPalette where the best fit is searched in
140 | /// Index in the AtariPalette that best matches the input RGB
141 | public static byte FindClosest(byte rr, byte gg, byte bb, Color[] pal)
142 | {
143 | byte best = 0;
144 | var bestDistance = 9999999;
145 |
146 | for (var j = 0; j < 128; j++)
147 | {
148 | var i = j * 2; //only compare with EVEN AtariPalette indexes
149 | var distR = rr - pal[i].R;
150 | var distG = gg - pal[i].G;
151 | var distB = bb - pal[i].B;
152 | var newDistance = distR * distR + distG * distG + distB * distB;
153 |
154 | if (bestDistance > newDistance)
155 | {
156 | bestDistance = newDistance;
157 | best = (byte)i;
158 | }
159 | }
160 |
161 | return best;
162 | }
163 | }
164 | }
165 |
--------------------------------------------------------------------------------
/Compressors.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 |
3 | namespace FontMaker;
4 |
5 | public static class Compressors
6 | {
7 | public enum CompressorType
8 | {
9 | ZX0 = 0,
10 | ZX1 = 1,
11 | ZX2 = 2,
12 | APULTRA = 3,
13 | }
14 |
15 | private static bool IsCompressor0Available { get; set;}
16 | private static bool IsCompressor1Available { get; set; }
17 | private static bool IsCompressor2Available { get; set; }
18 | private static bool IsCompressor3Available { get; set; }
19 |
20 | private static string TempPath { get; set; }
21 | private static string Zx0Exe { get; set; }
22 | private static string Zx1Exe { get; set; }
23 | private static string Zx2Exe { get; set; }
24 | private static string ApultraExe { get; set; }
25 |
26 | // ==========================================================================
27 | // ZX0 compressor interface
28 | // ==========================================================================
29 | public static void Prepare()
30 | {
31 | try
32 | {
33 | TempPath = Path.Combine(Path.GetTempPath(), "afm2025");
34 | Directory.CreateDirectory(TempPath);
35 |
36 | Zx0Exe = Path.Combine(TempPath, "zx0.exe");
37 | if (!File.Exists(Zx0Exe))
38 | {
39 | // Create the zx0.exe file
40 | var buffer = Helpers.GetResource("zx0.exe");
41 | using var fs = new FileStream(Zx0Exe, FileMode.Create, FileAccess.Write);
42 | fs.Write(buffer, 0, buffer.Length);
43 | fs.Close();
44 | }
45 |
46 | Zx1Exe = Path.Combine(TempPath, "zx1.exe");
47 | if (!File.Exists(Zx1Exe))
48 | {
49 | // Create the zx1.exe file
50 | var buffer = Helpers.GetResource("zx1.exe");
51 | using var fs = new FileStream(Zx1Exe, FileMode.Create, FileAccess.Write);
52 | fs.Write(buffer, 0, buffer.Length);
53 | fs.Close();
54 | }
55 |
56 | Zx2Exe = Path.Combine(TempPath, "zx2.exe");
57 | if (!File.Exists(Zx2Exe))
58 | {
59 | // Create the zx2.exe file
60 | var buffer = Helpers.GetResource("zx2.exe");
61 | using var fs = new FileStream(Zx2Exe, FileMode.Create, FileAccess.Write);
62 | fs.Write(buffer, 0, buffer.Length);
63 | fs.Close();
64 | }
65 |
66 | ApultraExe = Path.Combine(TempPath, "apultra.exe");
67 | if (!File.Exists(ApultraExe))
68 | {
69 | // Create the apultra.exe file
70 | var buffer = Helpers.GetResource("apultra.exe");
71 | using var fs = new FileStream(ApultraExe, FileMode.Create, FileAccess.Write);
72 | fs.Write(buffer, 0, buffer.Length);
73 | fs.Close();
74 | }
75 |
76 | IsCompressor0Available = File.Exists(Zx0Exe);
77 | IsCompressor1Available = File.Exists(Zx1Exe);
78 | IsCompressor2Available = File.Exists(Zx2Exe);
79 | IsCompressor3Available = File.Exists(ApultraExe);
80 | }
81 | catch (Exception ex)
82 | {
83 | IsCompressor0Available = false;
84 | IsCompressor1Available = false;
85 | IsCompressor2Available = false;
86 | IsCompressor3Available = false;
87 | }
88 | }
89 |
90 | public static byte[] Compress(byte[] data, CompressorType which)
91 | {
92 | var (available, exe, cmdLine) = which switch
93 | {
94 | CompressorType.ZX0 => (IsCompressor0Available, Zx0Exe, "-f"),
95 | CompressorType.ZX1 => (IsCompressor1Available, Zx1Exe, "-f"),
96 | CompressorType.ZX2 => (IsCompressor2Available, Zx2Exe, "-f"),
97 | CompressorType.APULTRA => (IsCompressor2Available, ApultraExe, string.Empty),
98 | _ => (false, null, string.Empty)
99 | };
100 | if (!available) return data;
101 |
102 | try
103 | {
104 | var tempFile = Path.Combine(TempPath, "temp.zx");
105 | using (var fs = new FileStream(tempFile, FileMode.Create, FileAccess.Write))
106 | {
107 | fs.Write(data, 0, data.Length);
108 | fs.Close();
109 | }
110 |
111 | // Make sure the output file doesn't exist
112 | var outputPath = Path.Combine(TempPath, "output.zx");
113 | if (File.Exists(outputPath))
114 | File.Delete(outputPath);
115 |
116 | var process = new Process
117 | {
118 | StartInfo = new ProcessStartInfo
119 | {
120 | FileName = exe,
121 | Arguments = $"{cmdLine} {tempFile} {outputPath}",
122 | CreateNoWindow = true,
123 | UseShellExecute = false,
124 | RedirectStandardOutput = true,
125 | RedirectStandardError = true
126 | }
127 | };
128 | process.Start();
129 | process.WaitForExit();
130 | var compressedData = File.ReadAllBytes(outputPath);
131 | File.Delete(outputPath);
132 | return compressedData;
133 | }
134 | catch (Exception ex)
135 | {
136 | // Handle the exception as needed
137 | return data;
138 | }
139 | }
140 |
141 | public static string GetName(CompressorType which)
142 | {
143 | return which switch
144 | {
145 | CompressorType.ZX0 => "ZX0",
146 | CompressorType.ZX1 => "ZX1",
147 | CompressorType.ZX2 => "ZX2",
148 | CompressorType.APULTRA => "apultra",
149 | _ => "Unknown"
150 | };
151 |
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/FontMakerConfigurationWindow.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker;
2 |
3 | partial class FontMakerConfigurationWindow
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | buttonCancel = new Button();
32 | buttonOk = new Button();
33 | groupBox1 = new GroupBox();
34 | radioButtonApultra = new RadioButton();
35 | radioButtonZX1 = new RadioButton();
36 | radioButtonZX2 = new RadioButton();
37 | radioButtonZX0 = new RadioButton();
38 | groupBox1.SuspendLayout();
39 | SuspendLayout();
40 | //
41 | // buttonCancel
42 | //
43 | buttonCancel.Location = new Point(156, 84);
44 | buttonCancel.Name = "buttonCancel";
45 | buttonCancel.Size = new Size(100, 40);
46 | buttonCancel.TabIndex = 5;
47 | buttonCancel.Text = "Cancel";
48 | buttonCancel.UseVisualStyleBackColor = true;
49 | //
50 | // buttonOk
51 | //
52 | buttonOk.Location = new Point(12, 84);
53 | buttonOk.Name = "buttonOk";
54 | buttonOk.Size = new Size(100, 40);
55 | buttonOk.TabIndex = 6;
56 | buttonOk.Text = "Ok";
57 | buttonOk.UseVisualStyleBackColor = true;
58 | buttonOk.Click += buttonOk_Click;
59 | //
60 | // groupBox1
61 | //
62 | groupBox1.Controls.Add(radioButtonApultra);
63 | groupBox1.Controls.Add(radioButtonZX1);
64 | groupBox1.Controls.Add(radioButtonZX2);
65 | groupBox1.Controls.Add(radioButtonZX0);
66 | groupBox1.Location = new Point(12, 12);
67 | groupBox1.Name = "groupBox1";
68 | groupBox1.Size = new Size(244, 63);
69 | groupBox1.TabIndex = 8;
70 | groupBox1.TabStop = false;
71 | groupBox1.Text = "Which compressor should be used:";
72 | //
73 | // radioButtonApultra
74 | //
75 | radioButtonApultra.AutoSize = true;
76 | radioButtonApultra.Location = new Point(176, 32);
77 | radioButtonApultra.Name = "radioButtonApultra";
78 | radioButtonApultra.Size = new Size(62, 17);
79 | radioButtonApultra.TabIndex = 3;
80 | radioButtonApultra.TabStop = true;
81 | radioButtonApultra.Text = "apultra";
82 | radioButtonApultra.UseVisualStyleBackColor = true;
83 | //
84 | // radioButtonZX1
85 | //
86 | radioButtonZX1.AutoSize = true;
87 | radioButtonZX1.Location = new Point(64, 32);
88 | radioButtonZX1.Name = "radioButtonZX1";
89 | radioButtonZX1.Size = new Size(43, 17);
90 | radioButtonZX1.TabIndex = 2;
91 | radioButtonZX1.TabStop = true;
92 | radioButtonZX1.Text = "ZX1";
93 | radioButtonZX1.UseVisualStyleBackColor = true;
94 | //
95 | // radioButtonZX2
96 | //
97 | radioButtonZX2.AutoSize = true;
98 | radioButtonZX2.Location = new Point(120, 32);
99 | radioButtonZX2.Name = "radioButtonZX2";
100 | radioButtonZX2.Size = new Size(43, 17);
101 | radioButtonZX2.TabIndex = 1;
102 | radioButtonZX2.TabStop = true;
103 | radioButtonZX2.Text = "ZX2";
104 | radioButtonZX2.UseVisualStyleBackColor = true;
105 | //
106 | // radioButtonZX0
107 | //
108 | radioButtonZX0.AutoSize = true;
109 | radioButtonZX0.Location = new Point(8, 32);
110 | radioButtonZX0.Name = "radioButtonZX0";
111 | radioButtonZX0.Size = new Size(43, 17);
112 | radioButtonZX0.TabIndex = 0;
113 | radioButtonZX0.TabStop = true;
114 | radioButtonZX0.Text = "ZX0";
115 | radioButtonZX0.UseVisualStyleBackColor = true;
116 | //
117 | // FontMakerConfigurationWindow
118 | //
119 | AcceptButton = buttonOk;
120 | AutoScaleDimensions = new SizeF(6F, 13F);
121 | AutoScaleMode = AutoScaleMode.Font;
122 | CancelButton = buttonCancel;
123 | ClientSize = new Size(265, 130);
124 | Controls.Add(groupBox1);
125 | Controls.Add(buttonOk);
126 | Controls.Add(buttonCancel);
127 | FormBorderStyle = FormBorderStyle.FixedDialog;
128 | MaximizeBox = false;
129 | MinimizeBox = false;
130 | Name = "FontMakerConfigurationWindow";
131 | StartPosition = FormStartPosition.CenterParent;
132 | Text = "Atari Font Maker Configuration";
133 | groupBox1.ResumeLayout(false);
134 | groupBox1.PerformLayout();
135 | ResumeLayout(false);
136 | }
137 |
138 | #endregion
139 |
140 | private Button buttonCancel;
141 | private Button buttonOk;
142 | private GroupBox groupBox1;
143 | private RadioButton radioButtonZX2;
144 | private RadioButton radioButtonZX0;
145 | private RadioButton radioButtonZX1;
146 | private RadioButton radioButtonApultra;
147 | }
--------------------------------------------------------------------------------
/PageEditor.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | partial class PageEditor
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | components = new System.ComponentModel.Container();
32 | var resources = new System.ComponentModel.ComponentResourceManager(typeof(PageEditor));
33 | btnCancel = new Button();
34 | label1 = new Label();
35 | txtPageName = new TextBox();
36 | btnUpdate = new Button();
37 | lbPages = new ListBox();
38 | btnUp = new Button();
39 | btnDown = new Button();
40 | toolTips = new ToolTip(components);
41 | SuspendLayout();
42 | //
43 | // btnCancel
44 | //
45 | btnCancel.Location = new Point(152, 222);
46 | btnCancel.Name = "btnCancel";
47 | btnCancel.Size = new Size(75, 23);
48 | btnCancel.TabIndex = 4;
49 | btnCancel.Text = "Cancel";
50 | btnCancel.UseVisualStyleBackColor = true;
51 | btnCancel.Click += btnCancel_Click;
52 | //
53 | // label1
54 | //
55 | label1.AutoSize = true;
56 | label1.Location = new Point(5, 16);
57 | label1.Name = "label1";
58 | label1.Size = new Size(67, 13);
59 | label1.TabIndex = 1;
60 | label1.Text = "Page Name:";
61 | //
62 | // txtPageName
63 | //
64 | txtPageName.Location = new Point(76, 12);
65 | txtPageName.Name = "txtPageName";
66 | txtPageName.Size = new Size(180, 22);
67 | txtPageName.TabIndex = 1;
68 | toolTips.SetToolTip(txtPageName, "Edit the page name");
69 | txtPageName.TextChanged += txtPageName_TextChanged;
70 | txtPageName.Enter += txtPageName_Enter;
71 | txtPageName.KeyDown += txtPageName_KeyDown;
72 | txtPageName.Leave += txtPageName_Leave;
73 | //
74 | // btnUpdate
75 | //
76 | btnUpdate.Location = new Point(22, 222);
77 | btnUpdate.Name = "btnUpdate";
78 | btnUpdate.Size = new Size(75, 23);
79 | btnUpdate.TabIndex = 3;
80 | btnUpdate.Text = "Update";
81 | toolTips.SetToolTip(btnUpdate, "Save the current page settings");
82 | btnUpdate.UseVisualStyleBackColor = true;
83 | btnUpdate.Click += btnUpdate_Click;
84 | //
85 | // lbPages
86 | //
87 | lbPages.FormattingEnabled = true;
88 | lbPages.Location = new Point(5, 38);
89 | lbPages.Name = "lbPages";
90 | lbPages.Size = new Size(222, 173);
91 | lbPages.TabIndex = 2;
92 | lbPages.SelectedIndexChanged += lbPages_SelectedIndexChanged;
93 | lbPages.Enter += lbPages_Enter;
94 | lbPages.KeyDown += lbPages_KeyDown;
95 | lbPages.Leave += lbPages_Leave;
96 | //
97 | // btnUp
98 | //
99 | btnUp.Image = (Image)resources.GetObject("btnUp.Image");
100 | btnUp.Location = new Point(235, 74);
101 | btnUp.Name = "btnUp";
102 | btnUp.Size = new Size(21, 23);
103 | btnUp.TabIndex = 3;
104 | btnUp.TabStop = false;
105 | toolTips.SetToolTip(btnUp, "Move the page up in the sequence");
106 | btnUp.UseVisualStyleBackColor = true;
107 | btnUp.Click += btnUp_Click;
108 | //
109 | // btnDown
110 | //
111 | btnDown.Image = (Image)resources.GetObject("btnDown.Image");
112 | btnDown.Location = new Point(235, 103);
113 | btnDown.Name = "btnDown";
114 | btnDown.Size = new Size(21, 23);
115 | btnDown.TabIndex = 4;
116 | btnDown.TabStop = false;
117 | toolTips.SetToolTip(btnDown, "Move the page down in the sequence");
118 | btnDown.UseVisualStyleBackColor = true;
119 | btnDown.Click += btnDown_Click;
120 | //
121 | // PageEditor
122 | //
123 | AutoScaleDimensions = new SizeF(6F, 13F);
124 | AutoScaleMode = AutoScaleMode.Font;
125 | CancelButton = btnCancel;
126 | ClientSize = new Size(262, 252);
127 | Controls.Add(btnDown);
128 | Controls.Add(btnUp);
129 | Controls.Add(lbPages);
130 | Controls.Add(btnUpdate);
131 | Controls.Add(txtPageName);
132 | Controls.Add(label1);
133 | Controls.Add(btnCancel);
134 | FormBorderStyle = FormBorderStyle.FixedDialog;
135 | MaximizeBox = false;
136 | MinimizeBox = false;
137 | Name = "PageEditor";
138 | Text = "Edit page names and order";
139 | Load += PageEditor_Load;
140 | ResumeLayout(false);
141 | PerformLayout();
142 | }
143 |
144 | #endregion
145 |
146 | private Button btnCancel;
147 | private Label label1;
148 | private TextBox txtPageName;
149 | private Button btnUpdate;
150 | private ListBox lbPages;
151 | private Button btnUp;
152 | private Button btnDown;
153 | private ToolTip toolTips;
154 | }
155 | }
--------------------------------------------------------------------------------
/AtariViewConfigWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FontAnalysisWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/FontMakerConfigurationWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/AtariColorSelector.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/Properties/Resources.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
--------------------------------------------------------------------------------
/ExportFontWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
--------------------------------------------------------------------------------
/ImportViewWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 237, 17
125 |
126 |
--------------------------------------------------------------------------------
/ExportViewWindow.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 17, 17
125 |
126 |
--------------------------------------------------------------------------------
/PageData.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable WFO1000
2 | namespace FontMaker;
3 |
4 | ///
5 | /// Page data container
6 | /// Unique name, view and font select data
7 | ///
8 | public class PageData
9 | {
10 | public PageData(byte[,] view, byte[] selectedFont) :
11 | this(1, "Page 1", view, selectedFont, 0)
12 | {
13 | }
14 |
15 | public PageData(int nr, string name, byte[,] view, byte[] selectedFont, int index)
16 | {
17 | Nr = nr;
18 | Name = name;
19 | View = (view.Clone() as byte[,])!;
20 | SelectedFont = (selectedFont.Clone() as byte[])!;
21 | Index = index;
22 |
23 | UndoBuffer = new AtariViewUndoBuffer();
24 | }
25 |
26 | public PageData(SavedPageData? pageSrc, int index)
27 | {
28 | // Init the page from saved json
29 |
30 | // Fix for an upgrade
31 | if (pageSrc.Width == 0)
32 | pageSrc.Width = 40;
33 | if (pageSrc.Height == 0)
34 | pageSrc.Height = 26;
35 |
36 | var bytes = Convert.FromHexString(pageSrc.View);
37 | var idx = 0;
38 | var viewData = new byte[pageSrc.Width, pageSrc.Height];
39 |
40 | for (var y = 0; y < pageSrc.Height; ++y)
41 | {
42 | for (var x = 0; x < pageSrc.Width; ++x)
43 | {
44 | viewData[x, y] = bytes[idx];
45 | ++idx;
46 | }
47 | }
48 |
49 | Nr = pageSrc.Nr;
50 | Name = pageSrc.Name;
51 | Width = pageSrc.Width;
52 | Height = pageSrc.Height;
53 | View = viewData;
54 | SelectedFont = Convert.FromHexString(pageSrc.SelectedFont);
55 | Index = index;
56 |
57 | UndoBuffer = new AtariViewUndoBuffer();
58 | }
59 |
60 | public PageData(PageData from) :
61 | this(from.Nr, from.Name, from.View, from.SelectedFont, from.Index)
62 | {
63 | Width = from.Width;
64 | Height = from.Height;
65 | }
66 |
67 | ///
68 | /// Save the current AtariView data to the page container
69 | ///
70 | ///
71 | ///
72 | public void Save()
73 | {
74 | View = AtariView.ViewBytes.Clone() as byte[,];
75 | SelectedFont = AtariView.UseFontOnLine.Clone() as byte[];
76 | Width = AtariView.Width;
77 | Height = AtariView.Height;
78 | OffsetX = AtariView.OffsetX;
79 | OffsetY = AtariView.OffsetY;
80 | }
81 |
82 |
83 | ///
84 | /// Unique nr of the page in the list of pages
85 | ///
86 | public int Nr { get; set; }
87 |
88 | public string Name { get; set; }
89 |
90 | public byte[,] View { get; set; }
91 |
92 | public byte[] SelectedFont { get; set; }
93 |
94 | public int Index { get; set; }
95 |
96 | public AtariViewUndoBuffer UndoBuffer { get; set; }
97 |
98 | public int Width { get; set; } = 40;
99 | public int Height { get; set; } = 26;
100 | public int OffsetX { get; set; } = 0;
101 | public int OffsetY { get; set; } = 0;
102 | }
103 |
104 | public class SavedPageData
105 | {
106 | public int Nr { get; set; }
107 | public string Name { get; set; }
108 | public string View { get; set; }
109 | public string SelectedFont { get; set; }
110 |
111 | public int Width { get; set; } = 40;
112 | public int Height { get; set; } = 26;
113 |
114 | public SavedPageData(PageData? srcPage)
115 | {
116 | if (srcPage == null)
117 | return;
118 |
119 | Nr = srcPage.Nr;
120 | Name = srcPage.Name;
121 | SelectedFont = Convert.ToHexString(srcPage.SelectedFont);
122 | Width = srcPage.Width;
123 | Height = srcPage.Height;
124 |
125 | var pageView = string.Empty;
126 | for (var y = 0; y < srcPage.Height; y++)
127 | {
128 | for (var x = 0; x < srcPage.Width; x++)
129 | {
130 | pageView = pageView + $"{srcPage.View[x, y]:X2}";
131 | }
132 | }
133 |
134 | View = pageView;
135 | }
136 | }
137 |
138 | public partial class FontMakerForm
139 | {
140 | // Everything to do with pages goes here
141 | public List Pages { get; set; } = [];
142 |
143 | public PageData? CurrentPage { get; set; }
144 |
145 | ///
146 | /// The index in the combo box that is currently selected
147 | ///
148 | public int CurrentPageIndex { get; set; } = -1;
149 |
150 | public bool InPagesSetup { get; set; }
151 |
152 | ///
153 | /// Clear the old page data
154 | ///
155 | public void ClearPageList()
156 | {
157 | Pages = [];
158 | CurrentPageIndex = -1;
159 | CurrentPage = null;
160 | }
161 |
162 | ///
163 | /// After data has been loaded from file build the initial page data
164 | ///
165 | public void BuildPageList()
166 | {
167 | // If the page list is empty then create ONE default page
168 | if (Pages.Count == 0)
169 | {
170 | var pageOne = new PageData(AtariView.ViewBytes, AtariView.UseFontOnLine);
171 | Pages.Add(pageOne);
172 |
173 | CurrentPageIndex = 0;
174 | }
175 |
176 | InPagesSetup = true;
177 | comboBoxPages.Items.Clear();
178 | comboBoxPages.ResetText();
179 |
180 | foreach (var page in Pages)
181 | {
182 | var idx = comboBoxPages.Items.Add(page.Name);
183 | page.Index = idx;
184 | }
185 |
186 | comboBoxPages.SelectedIndex = 0;
187 | CurrentPageIndex = comboBoxPages.SelectedIndex;
188 | CurrentPage = Pages[CurrentPageIndex];
189 |
190 | InPagesSetup = false;
191 |
192 | UpdatePageDisplay();
193 | TransferPagesToViewActions();
194 | }
195 |
196 | public void SavePageSwitch(int nextPage)
197 | {
198 | if (nextPage < 0 || nextPage >= Pages.Count) return;
199 | comboBoxPages.SelectedIndex = nextPage;
200 | }
201 | private void SaveCurrentPage()
202 | {
203 | // Save the current page data
204 | var currentPage = Pages[CurrentPageIndex];
205 | currentPage.Save();
206 | }
207 |
208 | private void SwopPage(bool saveCurrent)
209 | {
210 | var nextPageIndex = comboBoxPages.SelectedIndex;
211 | if (nextPageIndex == -1) return;
212 |
213 | if (saveCurrent)
214 | {
215 | SaveCurrentPage();
216 | }
217 |
218 | SwopPageAction(nextPageIndex);
219 | }
220 |
221 | private void SwopToPage(int nextPageIndex)
222 | {
223 | SaveCurrentPage();
224 | SwopPageAction(nextPageIndex);
225 | }
226 |
227 | private void SwopPageAction(int nextPageIndex)
228 | {
229 | // Select the next page and copy the data
230 | var page = Pages[nextPageIndex];
231 |
232 | AtariView.ViewBytes = page.View.Clone() as byte[,];
233 | AtariView.UseFontOnLine = page.SelectedFont.Clone() as byte[];
234 | AtariView.Width = page.Width;
235 | AtariView.Height = page.Height;
236 | AtariView.OffsetX = page.OffsetX;
237 | AtariView.OffsetY = page.OffsetY;
238 |
239 | UpdateHVScrollBars(AtariView.OffsetX, AtariView.OffsetY);
240 |
241 | CurrentPageIndex = nextPageIndex;
242 | CurrentPage = Pages[CurrentPageIndex];
243 | }
244 |
245 | private void UpdatePageDisplay()
246 | {
247 | labelCurrentPageInfo.Text = $"{comboBoxPages.SelectedIndex+1} of {comboBoxPages.Items.Count}";
248 | RedrawView();
249 | RedrawLineTypes();
250 |
251 | buttonDeletePage.Enabled = Pages.Count > 1;
252 |
253 | UpdateViewUndoButtons();
254 | }
255 | }
--------------------------------------------------------------------------------
/Configuration.cs:
--------------------------------------------------------------------------------
1 | using System.Text;
2 | using TinyJson;
3 |
4 | namespace FontMaker
5 | {
6 | public class ConfigurationJson
7 | {
8 | public List? ColorSets { get; set; } = [];
9 | public int AnalysisColor { get; set; }
10 | public int AnalysisAlpha { get; set; }
11 |
12 | public bool AnalysisDuplicates { get; set; }
13 | public int AnalysisDupColor { get; set; }
14 | public int AnalysisDupAlpha { get; set; }
15 |
16 | public bool ExportViewRemember {get; set; }
17 | public int ExportViewExportType { get; set; }
18 | public int ExportViewDataType { get; set; }
19 | public int ExportViewRegionX { get; set; }
20 | public int ExportViewRegionY { get; set; }
21 | public int ExportViewRegionW { get; set; }
22 | public int ExportViewRegionH { get; set; }
23 | public int ExportViewOffsetX { get; set; }
24 | public int ExportViewOffsetY { get; set; }
25 |
26 | public bool ExportViewTranspose { get; set; }
27 |
28 | public bool ImportViewRemember { get; set; }
29 | public int ImportLineWidth { get; set; }
30 | public int ImportSkipX { get; set; }
31 | public int ImportSkipY { get; set; }
32 | public int ImportWidth { get; set; }
33 | public int ImportHeight { get; set; }
34 |
35 | public int CompressorId { get; set; } // 0 = zx0, 1 = zx1, 2 = zx2
36 |
37 | }
38 | public static class Configuration
39 | {
40 |
41 | private static readonly string Filename = Path.Join(AppContext.BaseDirectory, "FontMaker.json");
42 |
43 | public static ConfigurationJson Values { get; set; } = new ConfigurationJson();
44 |
45 | public static void Load()
46 | {
47 | try
48 | {
49 | var jsonText = File.ReadAllText(Filename); // Load JSON configuration file
50 | Values = jsonText.FromJson(); // Parse the JSON into an object
51 | }
52 | catch
53 | {
54 | // ignored
55 | }
56 |
57 | VerifyDefaults();
58 | }
59 |
60 | public static void Save()
61 | {
62 | try
63 | {
64 | var txt = Values.ToJson();
65 |
66 | File.WriteAllText(Filename, txt, Encoding.UTF8);
67 | }
68 | catch
69 | {
70 | // ignored
71 | }
72 | }
73 |
74 | public static void VerifyDefaults()
75 | {
76 | Values ??= new ConfigurationJson();
77 | // Make sure that there are 6 color sets
78 | Values.ColorSets ??= [];
79 |
80 | if (Values.ColorSets.Count < 6)
81 | {
82 | for (var i = Values.ColorSets.Count; i < 6; ++i)
83 | {
84 | Values.ColorSets.Add("0E0028CA9446");
85 | }
86 | }
87 |
88 | if (Values.AnalysisColor < FontAnalysisWindow.AnalysisMinColorIndex || Values.AnalysisColor > FontAnalysisWindow.AnalysisMaxColorIndex)
89 | {
90 | Values.AnalysisColor = FontAnalysisWindow.AnalysisMinColorIndex;
91 | }
92 |
93 | if (Values.AnalysisAlpha < FontAnalysisWindow.AnalysisMinAlpha || Values.AnalysisAlpha > FontAnalysisWindow.AnalysisMaxAlpha)
94 | {
95 | Values.AnalysisAlpha = FontAnalysisWindow.AnalysisMinAlpha + (FontAnalysisWindow.AnalysisMaxAlpha - FontAnalysisWindow.AnalysisMinAlpha) / 2;
96 | }
97 |
98 | if (Values.AnalysisDupColor < FontAnalysisWindow.AnalysisMinColorIndex || Values.AnalysisDupColor > FontAnalysisWindow.AnalysisMaxDuplicateColorIndex)
99 | {
100 | Values.AnalysisDupColor = FontAnalysisWindow.AnalysisMinColorIndex;
101 | }
102 | if (Values.AnalysisDupAlpha < FontAnalysisWindow.AnalysisMinAlpha || Values.AnalysisDupAlpha > FontAnalysisWindow.AnalysisMaxAlpha)
103 | {
104 | Values.AnalysisDupAlpha = FontAnalysisWindow.AnalysisMinAlpha + (FontAnalysisWindow.AnalysisMaxAlpha - FontAnalysisWindow.AnalysisMinAlpha) / 2;
105 | }
106 |
107 | if (Values.ExportViewRegionX is < 0 or >= 40) Values.ExportViewRegionX = 0;
108 | if (Values.ExportViewRegionY is < 0 or >= 26) Values.ExportViewRegionY = 0;
109 | if (Values.ExportViewRegionX + Values.ExportViewRegionW >= 40) Values.ExportViewRegionW = 1;
110 | if (Values.ExportViewRegionY + Values.ExportViewRegionH >= 26) Values.ExportViewRegionH = 1;
111 |
112 | if (Values.ImportLineWidth is < 1) Values.ImportLineWidth = 1;
113 | if (Values.ImportWidth is < 1) Values.ImportWidth = 1;
114 | if (Values.ImportHeight is < 1) Values.ImportHeight = 1;
115 | }
116 | }
117 |
118 | public partial class FontMakerForm
119 | {
120 | public void LoadConfiguration()
121 | {
122 | Configuration.Load();
123 |
124 | // Transfer the loaded values
125 | ColorSets = Configuration.Values.ColorSets;
126 |
127 | FontAnalysisWindowForm.SetDefaults(
128 | Configuration.Values.AnalysisColor,
129 | Configuration.Values.AnalysisAlpha,
130 | Configuration.Values.AnalysisDuplicates,
131 | Configuration.Values.AnalysisDupColor,
132 | Configuration.Values.AnalysisDupAlpha
133 | );
134 | ExportViewWindowForm.LoadConfiguration(
135 | Configuration.Values.ExportViewRemember,
136 | Configuration.Values.ExportViewExportType,
137 | Configuration.Values.ExportViewDataType,
138 | new Rectangle(Configuration.Values.ExportViewRegionX, Configuration.Values.ExportViewRegionY, Configuration.Values.ExportViewRegionW, Configuration.Values.ExportViewRegionH),
139 | new Point(Configuration.Values.ExportViewOffsetX, Configuration.Values.ExportViewOffsetY),
140 | Configuration.Values.ExportViewTranspose
141 | );
142 |
143 | ImportViewWindowForm.LoadConfiguration(
144 | Configuration.Values.ImportViewRemember,
145 | Configuration.Values.ImportLineWidth,
146 | Configuration.Values.ImportSkipX,
147 | Configuration.Values.ImportSkipY,
148 | Configuration.Values.ImportWidth,
149 | Configuration.Values.ImportHeight
150 | );
151 |
152 | CompressorId = (Compressors.CompressorType)Configuration.Values.CompressorId;
153 | }
154 |
155 | public void SaveConfiguration()
156 | {
157 | // Transfer the settings into the configuration
158 | Configuration.Values.ColorSets = ColorSets;
159 | Configuration.Values.AnalysisColor = FontAnalysisWindowForm.GetHighlightColor;
160 | Configuration.Values.AnalysisAlpha = FontAnalysisWindowForm.GetHighlightAlpha;
161 |
162 | Configuration.Values.AnalysisDuplicates = FontAnalysisWindowForm.GetDuplicates;
163 | Configuration.Values.AnalysisDupColor = FontAnalysisWindowForm.GetDuplicateColor;
164 | Configuration.Values.AnalysisDupAlpha = FontAnalysisWindowForm.GetDuplicateAlpha;
165 |
166 | (
167 | Configuration.Values.ExportViewRemember,
168 | Configuration.Values.ExportViewExportType,
169 | Configuration.Values.ExportViewDataType,
170 | var exportRegion,
171 | var exportOffset,
172 | Configuration.Values.ExportViewTranspose
173 | ) = ExportViewWindowForm.SaveConfiguration();
174 |
175 | Configuration.Values.ExportViewRegionX = exportRegion.X;
176 | Configuration.Values.ExportViewRegionY = exportRegion.Y;
177 | Configuration.Values.ExportViewRegionW = exportRegion.Width;
178 | Configuration.Values.ExportViewRegionH = exportRegion.Height;
179 | Configuration.Values.ExportViewOffsetX = exportOffset.X;
180 | Configuration.Values.ExportViewOffsetY = exportOffset.Y;
181 |
182 | (
183 | Configuration.Values.ImportViewRemember,
184 | Configuration.Values.ImportLineWidth,
185 | Configuration.Values.ImportSkipX,
186 | Configuration.Values.ImportSkipY,
187 | Configuration.Values.ImportWidth,
188 | Configuration.Values.ImportHeight
189 | ) = ImportViewWindowForm.SaveConfiguration();
190 |
191 | Configuration.Values.CompressorId = (int)CompressorId;
192 |
193 | Configuration.Save();
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/ViewActionsWindow.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable WFO1000
2 | namespace FontMaker
3 | {
4 | public partial class ViewActionsWindow : Form
5 | {
6 | private FontMakerForm? MainForm { get; set; }
7 |
8 | ///
9 | /// The index in the combo box that is currently selected
10 | ///
11 | public int CurrentPageIndex { get; set; } = -1;
12 |
13 | public Rectangle ActionArea { get; set; }
14 |
15 | private bool InPagesSetup { get; set; }
16 |
17 | #region Data from the outside
18 | public List? Pages { get; set; } = null;
19 | #endregion
20 |
21 | private byte ReplaceThisChar { get; set; }
22 | private byte ReplaceWithThisChar { get; set; }
23 | private bool CanReplaceInArea { get; set; }
24 |
25 | private byte FillWithThisChar { get; set; }
26 |
27 | public ViewActionsWindow(FontMakerForm? mainForm)
28 | {
29 | MainForm = mainForm;
30 | InitializeComponent();
31 | }
32 |
33 | private void btnClose_Click(object sender, EventArgs e)
34 | {
35 | this.Hide();
36 | }
37 |
38 | private void ViewActionsWindow_FormClosing(object sender, FormClosingEventArgs e)
39 | {
40 | this.Hide();
41 | e.Cancel = true; // This cancels the close event
42 | }
43 |
44 | public void RebuildPagesDropDown(List? pages, int selectIndex)
45 | {
46 | InPagesSetup = true;
47 |
48 | Pages = pages;
49 | // Clear the old list
50 | comboBoxPages.Items.Clear();
51 | comboBoxPages.ResetText();
52 |
53 | if (Pages is null || Pages.Count == 0)
54 | return;
55 |
56 | foreach (var page in Pages)
57 | {
58 | var idx = comboBoxPages.Items.Add(page.Name);
59 | page.Index = idx;
60 | }
61 |
62 | comboBoxPages.SelectedIndex = selectIndex; //comboBoxPages.Items.Count - 1;
63 | CurrentPageIndex = selectIndex;
64 |
65 | InPagesSetup = false;
66 | }
67 |
68 | ///
69 | ///
70 | ///
71 | ///
72 | public void SelectPage(int selectIndex)
73 | {
74 | InPagesSetup = true;
75 |
76 | comboBoxPages.SelectedIndex = selectIndex; //comboBoxPages.Items.Count - 1;
77 | CurrentPageIndex = selectIndex;
78 |
79 | InPagesSetup = false;
80 | }
81 |
82 | private void ViewActionsWindow_Pages_SelectedIndexChanged(object sender, EventArgs e)
83 | {
84 | if (InPagesSetup) return;
85 |
86 | MainForm?.PickPage(comboBoxPages.SelectedIndex);
87 | }
88 |
89 | private void pictureBoxX_Click(object sender, EventArgs e)
90 | {
91 | ReplaceThisChar = MainForm?.RenderCharIntoPictureBox(pictureBoxX) ?? 0;
92 | labelReplaceX.Text = $"#{ReplaceThisChar}";
93 |
94 | EnableReplaceCharButton();
95 | }
96 |
97 | private void pictureBoxY_Click(object sender, EventArgs e)
98 | {
99 | ReplaceWithThisChar = MainForm?.RenderCharIntoPictureBox(pictureBoxY) ?? 0;
100 | labelReplaceY.Text = $"#{ReplaceWithThisChar}";
101 |
102 | EnableReplaceCharButton();
103 | }
104 | private void checkFont_CheckedChanged(object sender, EventArgs e)
105 | {
106 | EnableReplaceCharButton();
107 | }
108 |
109 | private void EnableReplaceCharButton()
110 | {
111 | var isEnabled = ReplaceThisChar != ReplaceWithThisChar;
112 | var haveOneFont = checkFont1.Checked || checkFont2.Checked || checkFont3.Checked || checkFont4.Checked;
113 |
114 | buttonReplaceXwithYInView.Enabled = isEnabled && haveOneFont;
115 | buttonReplaceXwithYInArea.Enabled = CanReplaceInArea && isEnabled && haveOneFont;
116 | }
117 | private void ButtonReplaceXwithYInViewClick(object sender, EventArgs e)
118 | {
119 | MainForm?.ReplaceCharXWithY(ReplaceThisChar, ReplaceWithThisChar, checkFont1.Checked, checkFont2.Checked, checkFont3.Checked, checkFont4.Checked, new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1));
120 | }
121 |
122 | private void buttonReplaceXwithYInArea_Click(object sender, EventArgs e)
123 | {
124 | MainForm?.ReplaceCharXWithY(ReplaceThisChar, ReplaceWithThisChar, checkFont1.Checked, checkFont2.Checked, checkFont3.Checked, checkFont4.Checked, ActionArea);
125 | }
126 |
127 | #region External interface
128 |
129 | public void UpdateViewInformation(bool isMegaCopy, bool haveRegion, Rectangle region)
130 | {
131 | ActionArea = region;
132 |
133 | buttonAreaShiftLeft.Enabled = isMegaCopy && haveRegion;
134 | buttonAreaShiftRight.Enabled = isMegaCopy && haveRegion;
135 | buttonAreaShiftUp.Enabled = isMegaCopy && haveRegion;
136 | buttonAreaShiftDown.Enabled = isMegaCopy && haveRegion;
137 | buttonClearArea.Enabled = isMegaCopy && haveRegion;
138 | buttonFillArea.Enabled = isMegaCopy && haveRegion;
139 |
140 | CanReplaceInArea = isMegaCopy && haveRegion;
141 |
142 | if (isMegaCopy && haveRegion)
143 | {
144 | labelAreaInfo.Text = $"X:{region.Left} Y:{region.Top} W:{region.Width + 1} H:{region.Height + 1}";
145 | }
146 | else
147 | {
148 | labelAreaInfo.Text = string.Empty;
149 | }
150 |
151 | EnableReplaceCharButton();
152 | }
153 | #endregion
154 |
155 | private void buttonAreaShiftUp_Click(object sender, EventArgs e)
156 | {
157 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Up, ActionArea);
158 | }
159 | private void buttonAreaShiftDown_Click(object sender, EventArgs e)
160 | {
161 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Down, ActionArea);
162 | }
163 | private void buttonAreaShiftLeft_Click(object sender, EventArgs e)
164 | {
165 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Left, ActionArea);
166 | }
167 | private void buttonAreaShiftRight_Click(object sender, EventArgs e)
168 | {
169 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Right, ActionArea);
170 | }
171 |
172 | private void buttonViewShiftUp_Click(object sender, EventArgs e)
173 | {
174 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Up, new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1));
175 | }
176 | private void buttonViewShiftDown_Click(object sender, EventArgs e)
177 | {
178 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Down, new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1));
179 | }
180 | private void buttonViewShiftLeft_Click(object sender, EventArgs e)
181 | {
182 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Left, new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1));
183 | }
184 | private void buttonViewShiftRight_Click(object sender, EventArgs e)
185 | {
186 | MainForm?.ActionAreaShift(comboBoxPages.SelectedIndex, FontMakerForm.DirectionFlags.Right, new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1));
187 | }
188 |
189 | private void buttonClearView_Click(object sender, EventArgs e)
190 | {
191 | MainForm?.FillArea(new Rectangle(0, 0, AtariView.Width-1, AtariView.Height-1), 0);
192 | }
193 |
194 | private void buttonClearArea_Click(object sender, EventArgs e)
195 | {
196 | MainForm?.FillArea(ActionArea, 0);
197 | }
198 |
199 | private void pictureBoxFill_Click(object sender, EventArgs e)
200 | {
201 | FillWithThisChar = MainForm?.RenderCharIntoPictureBox(pictureBoxFill) ?? 0;
202 | labelFiller.Text = $"#{FillWithThisChar}";
203 |
204 | EnableFillerButton();
205 | }
206 | private void EnableFillerButton()
207 | {
208 | var isEnabled = FillWithThisChar != ReplaceWithThisChar;
209 | var haveOneFont = checkFont1.Checked || checkFont2.Checked || checkFont3.Checked || checkFont4.Checked;
210 |
211 | buttonReplaceXwithYInView.Enabled = isEnabled && haveOneFont;
212 | buttonReplaceXwithYInArea.Enabled = CanReplaceInArea && isEnabled && haveOneFont;
213 | }
214 |
215 | private void buttonFillView_Click(object sender, EventArgs e)
216 | {
217 | MainForm?.FillArea(new Rectangle(0, 0, AtariView.Width - 1, AtariView.Height - 1), FillWithThisChar);
218 | }
219 |
220 | private void buttonFillArea_Click(object sender, EventArgs e)
221 | {
222 | MainForm?.FillArea(ActionArea, FillWithThisChar);
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/PageEditor.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 |
3 | namespace FontMaker
4 | {
5 | public partial class PageEditor : Form
6 | {
7 | private Color _restoreTextBoxColor;
8 | private Color _restoreListBoxToColor;
9 |
10 | ///
11 | /// Build the new list of page data in the correct order
12 | ///
13 | ///
14 | public List GetPages()
15 | {
16 | var lst = new List();
17 | for (var idx = 0; idx < PageOrder.Count; ++idx)
18 | {
19 | lst.Add(MyPages[PageOrder[idx]]);
20 | }
21 |
22 | return lst;
23 | }
24 |
25 | ///
26 | /// Original page data copy
27 | ///
28 | private List MyPages { get; }
29 |
30 | ///
31 | /// What is the order the original pages will be arranged in.
32 | /// Uses the "Index" field of the page to set the order
33 | ///
34 | private ObservableCollection PageOrder { get; }
35 |
36 | ///
37 | /// Index into Pages[] to indicate which one is being edited
38 | ///
39 | private int CurrentPageIndex { get; set; }
40 |
41 | ///
42 | /// List of data to be displayed in the ListBox (Index, Name)
43 | ///
44 | private List _dynList;
45 |
46 | private bool _inSetup;
47 |
48 | ///
49 | /// Create the PageEditor dialog.
50 | /// Makes a deep copy of all the page data.
51 | ///
52 | /// List of current page data
53 | /// Selected page index
54 | public PageEditor(IEnumerable pages, int currentPageIndex)
55 | {
56 | InitializeComponent();
57 |
58 | MyPages = new List();
59 | foreach (var unit in pages)
60 | {
61 | MyPages.Add(new PageData(unit));
62 | }
63 | CurrentPageIndex = currentPageIndex; // Which page is currently displayed
64 | PageOrder = new ObservableCollection(MyPages.Select(pg => pg.Index).ToList()); // Index list (used to reorder the pages)
65 | }
66 |
67 | ///
68 | /// Populate the GUI
69 | ///
70 | ///
71 | ///
72 | private void PageEditor_Load(object sender, EventArgs e)
73 | {
74 | _inSetup = true;
75 |
76 | var page = MyPages[CurrentPageIndex];
77 | txtPageName.Text = page?.Name ?? "Unknown";
78 |
79 | // Build the list for the re-order list box
80 | RebuildListBox(CurrentPageIndex);
81 |
82 | _restoreTextBoxColor = txtPageName.BackColor;
83 | _restoreListBoxToColor = lbPages.BackColor;
84 |
85 | _inSetup = false;
86 | }
87 |
88 | ///
89 | /// The page order list box is dynamically linked to the _dynList data.
90 | ///
91 | /// Which entry is selected
92 | private void RebuildListBox(int selectThisIndex)
93 | {
94 | _inSetup = true;
95 |
96 | _dynList = new List();
97 | for (var idx = 0; idx < MyPages.Count; idx++)
98 | {
99 | var thisPage = MyPages[PageOrder[idx]];
100 | _dynList.Add(new { Index = thisPage.Index, Name = thisPage.Name, Page = thisPage });
101 | }
102 |
103 | lbPages.DataSource = null;
104 | lbPages.DataSource = _dynList;
105 | lbPages.DisplayMember = "Name";
106 | lbPages.ValueMember = "Index";
107 |
108 | lbPages.SetSelected(selectThisIndex, true);
109 | CurrentPageIndex = selectThisIndex;
110 | _inSetup = false;
111 | }
112 |
113 | ///
114 | /// Tell the caller that the editor is closed with OK
115 | ///
116 | private void btnUpdate_Click(object sender, EventArgs e)
117 | {
118 | // Fill the data areas
119 | DialogResult = DialogResult.OK;
120 | Close();
121 | }
122 |
123 | ///
124 | /// Tell the caller that the editor is closed via cancel
125 | ///
126 | ///
127 | ///
128 | private void btnCancel_Click(object sender, EventArgs e)
129 | {
130 | DialogResult = DialogResult.Cancel;
131 | Close();
132 | }
133 |
134 | ///
135 | /// The name of a page was edited
136 | /// - save it in the page
137 | /// - validate the [Update] button state
138 | /// - rebuild the re-order list
139 | ///
140 | private void txtPageName_TextChanged(object sender, EventArgs e)
141 | {
142 | if (_inSetup) return;
143 |
144 | var page = MyPages[PageOrder[lbPages.SelectedIndex]];
145 | page.Name = txtPageName.Text;
146 |
147 | CheckValidState();
148 |
149 | RebuildListBox(lbPages.SelectedIndex);
150 | }
151 |
152 | ///
153 | /// Check that all pages have a name.
154 | /// If not then the [Update] button is disabled
155 | ///
156 | private void CheckValidState()
157 | {
158 | var valid = true;
159 | for (var i = 0; valid && i < MyPages.Count; i++)
160 | {
161 | if (MyPages[i].Name.Trim().Length == 0)
162 | valid = false;
163 | }
164 |
165 | btnUpdate.Enabled = valid;
166 | }
167 |
168 | ///
169 | /// Move the selected entry up
170 | ///
171 | private void btnUp_Click(object sender, EventArgs e)
172 | {
173 | MoveSelectedItem(lbPages, -1);
174 | }
175 |
176 | ///
177 | /// Move the selected entry down
178 | ///
179 | private void btnDown_Click(object sender, EventArgs e)
180 | {
181 | MoveSelectedItem(lbPages, 1);
182 | }
183 |
184 | ///
185 | /// Do the actual movement in the PageOrder list
186 | ///
187 | /// The list box that we look into
188 | /// -1 / +1
189 | public void MoveSelectedItem(ListBox listBox, int direction)
190 | {
191 |
192 | // Checking selected item
193 | if (listBox.SelectedItem == null || listBox.SelectedIndex < 0)
194 | return; // No selected item - nothing to do
195 |
196 | // Calculate new index using move direction
197 | var newIndex = listBox.SelectedIndex + direction;
198 |
199 | // Checking bounds of the range
200 | if (newIndex < 0 || newIndex >= listBox.Items.Count)
201 | return; // Index out of range - nothing to do
202 |
203 | PageOrder.Move(listBox.SelectedIndex, newIndex);
204 |
205 | RebuildListBox(newIndex);
206 | }
207 |
208 | ///
209 | /// The reorder box selection has changed, so make the new page's name editable
210 | ///
211 | ///
212 | ///
213 | private void lbPages_SelectedIndexChanged(object sender, EventArgs e)
214 | {
215 | if (_inSetup) return;
216 |
217 | // A new page was selected
218 | // Map the new index to a page
219 | CurrentPageIndex = lbPages.SelectedIndex;
220 |
221 | var page = MyPages[PageOrder[CurrentPageIndex]];
222 |
223 | _inSetup = true;
224 |
225 | txtPageName.Text = page.Name;
226 |
227 | _inSetup = false;
228 | }
229 |
230 | private void txtPageName_KeyDown(object sender, KeyEventArgs e)
231 | {
232 | if (e.KeyCode == Keys.Enter)
233 | {
234 | btnUpdate.Focus();
235 | }
236 | }
237 |
238 | private void lbPages_KeyDown(object sender, KeyEventArgs e)
239 | {
240 | if (e.KeyCode == Keys.Enter)
241 | {
242 | txtPageName.Focus();
243 | }
244 | }
245 |
246 | private void lbPages_Enter(object sender, EventArgs e)
247 | {
248 | lbPages.BackColor = Color.Beige;
249 | }
250 |
251 | private void lbPages_Leave(object sender, EventArgs e)
252 | {
253 | lbPages.BackColor = _restoreListBoxToColor;
254 | }
255 |
256 | private void txtPageName_Enter(object sender, EventArgs e)
257 | {
258 | txtPageName.BackColor = Color.Beige;
259 | }
260 |
261 | private void txtPageName_Leave(object sender, EventArgs e)
262 | {
263 | txtPageName.BackColor = _restoreTextBoxColor;
264 | }
265 | }
266 | }
267 |
--------------------------------------------------------------------------------
/ExportFontWindow.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 | partial class ExportFontWindow
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | Label1 = new Label();
32 | Label2 = new Label();
33 | Label3 = new Label();
34 | ComboBoxExportType = new ComboBox();
35 | ComboBoxDataType = new ComboBox();
36 | Button_SaveAs = new Button();
37 | Button_Cancel = new Button();
38 | MemoExport = new RichTextBox();
39 | ComboBoxFontNumber = new ComboBox();
40 | ButtonCopyClipboard = new Button();
41 | saveDialog = new SaveFileDialog();
42 | withCompression = new CheckBox();
43 | labelSizeInfo = new Label();
44 | SuspendLayout();
45 | //
46 | // Label1
47 | //
48 | Label1.AutoSize = true;
49 | Label1.Location = new Point(8, 46);
50 | Label1.Name = "Label1";
51 | Label1.Size = new Size(85, 13);
52 | Label1.TabIndex = 0;
53 | Label1.Text = "Export font to :";
54 | //
55 | // Label2
56 | //
57 | Label2.AutoSize = true;
58 | Label2.Location = new Point(8, 78);
59 | Label2.Name = "Label2";
60 | Label2.Size = new Size(62, 13);
61 | Label2.TabIndex = 1;
62 | Label2.Text = "Data type :";
63 | //
64 | // Label3
65 | //
66 | Label3.AutoSize = true;
67 | Label3.Location = new Point(8, 13);
68 | Label3.Name = "Label3";
69 | Label3.Size = new Size(68, 13);
70 | Label3.TabIndex = 2;
71 | Label3.Text = "Select font :";
72 | //
73 | // ComboBoxExportType
74 | //
75 | ComboBoxExportType.DropDownStyle = ComboBoxStyle.DropDownList;
76 | ComboBoxExportType.FormattingEnabled = true;
77 | ComboBoxExportType.Items.AddRange(new object[] { "Image BMP Mono", "Image BMP Color", "Assembler", "Action! ", "Atari Basic", "FastBasic", "MADS dta", "C data {}", "MadPascal Array", "Binary Data", "Basic listing file" });
78 | ComboBoxExportType.Location = new Point(104, 43);
79 | ComboBoxExportType.Name = "ComboBoxExportType";
80 | ComboBoxExportType.Size = new Size(216, 21);
81 | ComboBoxExportType.TabIndex = 0;
82 | ComboBoxExportType.SelectedIndexChanged += ComboBoxExportTypeChange;
83 | //
84 | // ComboBoxDataType
85 | //
86 | ComboBoxDataType.DropDownStyle = ComboBoxStyle.DropDownList;
87 | ComboBoxDataType.FormattingEnabled = true;
88 | ComboBoxDataType.Location = new Point(104, 75);
89 | ComboBoxDataType.Name = "ComboBoxDataType";
90 | ComboBoxDataType.Size = new Size(216, 21);
91 | ComboBoxDataType.TabIndex = 1;
92 | ComboBoxDataType.SelectedIndexChanged += ComboBoxDataTypeChange;
93 | //
94 | // Button_SaveAs
95 | //
96 | Button_SaveAs.Location = new Point(326, 8);
97 | Button_SaveAs.Name = "Button_SaveAs";
98 | Button_SaveAs.Size = new Size(81, 25);
99 | Button_SaveAs.TabIndex = 2;
100 | Button_SaveAs.Text = "Save as...";
101 | Button_SaveAs.UseVisualStyleBackColor = true;
102 | Button_SaveAs.Click += ButtonSaveAsClick;
103 | //
104 | // Button_Cancel
105 | //
106 | Button_Cancel.Location = new Point(326, 73);
107 | Button_Cancel.Name = "Button_Cancel";
108 | Button_Cancel.Size = new Size(81, 25);
109 | Button_Cancel.TabIndex = 3;
110 | Button_Cancel.Text = "Cancel";
111 | Button_Cancel.UseVisualStyleBackColor = true;
112 | Button_Cancel.Click += Button_CancelClick;
113 | //
114 | // MemoExport
115 | //
116 | MemoExport.Location = new Point(8, 154);
117 | MemoExport.Name = "MemoExport";
118 | MemoExport.Size = new Size(399, 353);
119 | MemoExport.TabIndex = 4;
120 | MemoExport.Text = "";
121 | MemoExport.KeyPress += MemoExportKeyPress;
122 | //
123 | // ComboBoxFontNumber
124 | //
125 | ComboBoxFontNumber.DropDownStyle = ComboBoxStyle.DropDownList;
126 | ComboBoxFontNumber.FormattingEnabled = true;
127 | ComboBoxFontNumber.Items.AddRange(new object[] { "1", "2", "3", "4", "1+2", "3+4", "1+2+3+4" });
128 | ComboBoxFontNumber.Location = new Point(104, 8);
129 | ComboBoxFontNumber.Name = "ComboBoxFontNumber";
130 | ComboBoxFontNumber.Size = new Size(216, 21);
131 | ComboBoxFontNumber.TabIndex = 5;
132 | ComboBoxFontNumber.SelectedIndexChanged += ComboBoxFontNumber_SelectedIndexChanged;
133 | //
134 | // ButtonCopyClipboard
135 | //
136 | ButtonCopyClipboard.Location = new Point(326, 42);
137 | ButtonCopyClipboard.Name = "ButtonCopyClipboard";
138 | ButtonCopyClipboard.Size = new Size(81, 25);
139 | ButtonCopyClipboard.TabIndex = 6;
140 | ButtonCopyClipboard.Text = "To Clipboard";
141 | ButtonCopyClipboard.UseVisualStyleBackColor = true;
142 | ButtonCopyClipboard.Click += ButtonCopyClipboardClick;
143 | //
144 | // saveDialog
145 | //
146 | saveDialog.FileName = "font-export";
147 | //
148 | // checkZX0
149 | //
150 | withCompression.AutoSize = true;
151 | withCompression.Location = new Point(104, 108);
152 | withCompression.Name = "withCompression";
153 | withCompression.Size = new Size(169, 17);
154 | withCompression.TabIndex = 7;
155 | withCompression.Text = "Compress the data with ZX0";
156 | withCompression.UseVisualStyleBackColor = true;
157 | withCompression.CheckedChanged += WithCompressionCheckedChanged;
158 | //
159 | // labelSizeInfo
160 | //
161 | labelSizeInfo.Location = new Point(8, 132);
162 | labelSizeInfo.Name = "labelSizeInfo";
163 | labelSizeInfo.Size = new Size(399, 17);
164 | labelSizeInfo.TabIndex = 8;
165 | labelSizeInfo.Text = "...";
166 | labelSizeInfo.TextAlign = ContentAlignment.MiddleCenter;
167 | //
168 | // ExportFontWindow
169 | //
170 | AutoScaleDimensions = new SizeF(6F, 13F);
171 | AutoScaleMode = AutoScaleMode.Font;
172 | BackgroundImageLayout = ImageLayout.None;
173 | CancelButton = Button_Cancel;
174 | ClientSize = new Size(416, 516);
175 | Controls.Add(labelSizeInfo);
176 | Controls.Add(withCompression);
177 | Controls.Add(Label1);
178 | Controls.Add(Label2);
179 | Controls.Add(Label3);
180 | Controls.Add(ComboBoxExportType);
181 | Controls.Add(ComboBoxDataType);
182 | Controls.Add(Button_SaveAs);
183 | Controls.Add(Button_Cancel);
184 | Controls.Add(MemoExport);
185 | Controls.Add(ComboBoxFontNumber);
186 | Controls.Add(ButtonCopyClipboard);
187 | FormBorderStyle = FormBorderStyle.FixedDialog;
188 | MaximizeBox = false;
189 | MinimizeBox = false;
190 | Name = "ExportFontWindow";
191 | Padding = new Padding(505, 354, 0, 0);
192 | Text = "Export font to ...";
193 | ResumeLayout(false);
194 | PerformLayout();
195 | }
196 |
197 | #endregion
198 |
199 | private System.Windows.Forms.Label Label1;
200 | private System.Windows.Forms.Label Label2;
201 | private System.Windows.Forms.Label Label3;
202 | private System.Windows.Forms.ComboBox ComboBoxExportType;
203 | private System.Windows.Forms.ComboBox ComboBoxDataType;
204 | private System.Windows.Forms.Button Button_SaveAs;
205 | private System.Windows.Forms.Button Button_Cancel;
206 | private System.Windows.Forms.RichTextBox MemoExport;
207 | private System.Windows.Forms.ComboBox ComboBoxFontNumber;
208 | private System.Windows.Forms.Button ButtonCopyClipboard;
209 | private System.Windows.Forms.SaveFileDialog saveDialog;
210 | private CheckBox withCompression;
211 | private Label labelSizeInfo;
212 | }
213 | }
--------------------------------------------------------------------------------
/ImportViewWindow.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable WFO1000
2 |
3 | namespace FontMaker;
4 | public partial class ImportViewWindow : Form
5 | {
6 |
7 | private const int PREVIEW_WIDTH = 40;
8 | private const int PREVIEW_HEIGHT = 26;
9 | public bool InColorMode { get; set; }
10 | public int WhichColorMode { get; set; }
11 |
12 | private byte[]? FileDataToImportFrom { get; set; }
13 |
14 | // 40x26 view buffer
15 | private byte[,]? ImportedViewBytes { get; set; }
16 |
17 | private bool RememberSelection { get; set; }
18 | private int PreviousLineWidth { get; set; } = 1;
19 | private int PreviousSkipX { get; set; }
20 | private int PreviousSkipY { get; set; }
21 | private int PreviousWidth { get; set; } = 1;
22 | private int PreviousHeight { get; set; } = 1;
23 |
24 | public ImportViewWindow()
25 | {
26 | InitializeComponent();
27 | RememberSelection = true;
28 | UpdateDataFeedback();
29 | }
30 |
31 | public void LoadConfiguration(bool rememberSelection, int lineWidth, int skipX, int skipY, int width, int height)
32 | {
33 | if (rememberSelection)
34 | {
35 | if (lineWidth < 1)
36 | lineWidth = 1;
37 | if (skipX < 0) skipX = 0;
38 | if (skipY < 0) skipY = 0;
39 | if (width < 1) width = 1;
40 | if (height < 1) height = 1;
41 |
42 | PreviousLineWidth = lineWidth;
43 | PreviousSkipX = skipX;
44 | PreviousSkipY = skipY;
45 | PreviousWidth = width;
46 | PreviousHeight = height;
47 | }
48 | }
49 |
50 | public (bool, int, int, int, int, int) SaveConfiguration()
51 | {
52 | return (RememberSelection, PreviousLineWidth, PreviousSkipX, PreviousSkipY, PreviousWidth, PreviousHeight);
53 | }
54 |
55 | ///
56 | /// Form is loading, set the saved/initial load configuration
57 | ///
58 | ///
59 | ///
60 | private void ImportViewWindow_Load(object sender, EventArgs e)
61 | {
62 | checkBoxRememberState.Checked = RememberSelection;
63 | if (RememberSelection)
64 | {
65 | numericLineWidth.Value = PreviousLineWidth;
66 | numericSkipX.Value = PreviousSkipX;
67 | numericSkipY.Value = PreviousSkipY;
68 | numericWidth.Value = PreviousWidth;
69 | numericHeight.Value = PreviousHeight;
70 | }
71 | }
72 |
73 | ///
74 | /// Form is closing, save the configuration if needed
75 | ///
76 | ///
77 | ///
78 | private void ImportViewWindow_FormClosing(object sender, FormClosingEventArgs e)
79 | {
80 | RememberSelection = checkBoxRememberState.Checked;
81 | if (checkBoxRememberState.Checked)
82 | {
83 | PreviousLineWidth = (int)numericLineWidth.Value;
84 | PreviousSkipX = (int)numericSkipX.Value;
85 | PreviousSkipY = (int)numericSkipY.Value;
86 | PreviousWidth = (int)numericWidth.Value;
87 | PreviousHeight = (int)numericHeight.Value;
88 | }
89 | }
90 |
91 | private void Button_Cancel_Click(object sender, EventArgs e)
92 | {
93 | Close();
94 | }
95 |
96 | ///
97 | /// Load data to be imported.
98 | /// Loaded data will end up in 'FileDataToImportFrom'
99 | ///
100 | private void Button_LoadData_Click(object sender, EventArgs e)
101 | {
102 | dialogOpenFile.FileName = string.Empty;
103 | dialogOpenFile.Filter = $@"Any binary data|*.*";
104 | var ok = dialogOpenFile.ShowDialog();
105 |
106 | if (ok == DialogResult.OK)
107 | {
108 | FileDataToImportFrom = null;
109 | try
110 | {
111 | FileDataToImportFrom = File.ReadAllBytes(dialogOpenFile.FileName);
112 | if (FileDataToImportFrom == null || FileDataToImportFrom.Length == 0)
113 | {
114 | MessageBox.Show($@"Failed to load '{dialogOpenFile.FileName}'. File is empty.");
115 | FileDataToImportFrom = null;
116 | }
117 | UpdateDataFeedback();
118 | }
119 | catch (Exception ex)
120 | {
121 | MessageBox.Show($@"Failed to load '{dialogOpenFile.FileName}'. Reason:{ex.Message}");
122 | FileDataToImportFrom = null;
123 | UpdateDataFeedback();
124 | }
125 | }
126 | }
127 |
128 | private void UpdateDataFeedback()
129 | {
130 | labelFileSize.Text = FileDataToImportFrom == null ? "(Load some data)" : $"{FileDataToImportFrom.Length} bytes";
131 |
132 | numericSkipX.Enabled = FileDataToImportFrom != null;
133 | numericSkipY.Enabled = FileDataToImportFrom != null;
134 | numericWidth.Enabled = FileDataToImportFrom != null;
135 | numericHeight.Enabled = FileDataToImportFrom != null;
136 | numericLineWidth.Enabled = FileDataToImportFrom != null;
137 |
138 | UpdateConstraints(null, EventArgs.Empty);
139 | }
140 |
141 | private void UpdateConstraints(object? _, EventArgs __)
142 | {
143 | var widthOk = numericSkipX.Value + numericWidth.Value <= (int)numericLineWidth.Value;
144 | numericSkipX.ForeColor = widthOk ? Color.Black : Color.Red;
145 | numericWidth.ForeColor = widthOk ? Color.Black : Color.Red;
146 |
147 | var heightOk = ((int)numericLineWidth.Value * numericSkipY.Value + (int)numericLineWidth.Value * numericHeight.Value <= (FileDataToImportFrom?.Length ?? 0));
148 | numericSkipY.ForeColor = heightOk ? Color.Black : Color.Red;
149 | numericHeight.ForeColor = heightOk ? Color.Black : Color.Red;
150 |
151 | labelActionInfo.Text = $"Skip {numericSkipX.Value} byte and then take {numericWidth.Value} bytes.";
152 |
153 | labelSomeMoreInfo.Text = FileDataToImportFrom == null ? "..." : $"# of lines: {(FileDataToImportFrom.Length / (int)numericLineWidth.Value)}";
154 |
155 | Button_Import.Enabled = widthOk && heightOk;
156 |
157 | if (widthOk && heightOk)
158 | {
159 | GenerateData();
160 | RenderTestData();
161 | }
162 | }
163 |
164 | private void GenerateData()
165 | {
166 | if (FileDataToImportFrom == null)
167 | return;
168 |
169 | // Copy the data from FileData into ViewBytes
170 | ImportedViewBytes = new byte[AtariView.Width, AtariView.Height];
171 |
172 | var lineWidth = (int)numericLineWidth.Value;
173 | var skipX = (int)numericSkipX.Value;
174 | var skipY = (int)numericSkipY.Value;
175 | var width = (int)numericWidth.Value;
176 | var height = (int)numericHeight.Value;
177 |
178 | var srcIndex = skipY * lineWidth;
179 | for (var y = 0; y < height; ++y)
180 | {
181 | for (var x = 0; x < width; ++x)
182 | {
183 | ImportedViewBytes[x, y] = FileDataToImportFrom[srcIndex + skipX + x];
184 | }
185 |
186 | srcIndex += lineWidth;
187 | }
188 | }
189 |
190 | ///
191 | /// Render the imported data into the 40x26 view
192 | ///
193 | private void RenderTestData()
194 | {
195 | if (ImportedViewBytes == null)
196 | return;
197 |
198 | var colorOffset = InColorMode ? 512 : 0;
199 | var img = Helpers.GetImage(pictureBoxAtariViewSmall);
200 | using (var gr = Graphics.FromImage(img))
201 | {
202 | var destRect = new Rectangle
203 | {
204 | Width = 1,
205 | Height = 1,
206 | };
207 |
208 | var srcRect = new Rectangle
209 | {
210 | Width = 1,
211 | Height = 1,
212 | };
213 |
214 | for (var y = 0; y < PREVIEW_HEIGHT; y++)
215 | {
216 | for (var x = 0; x < PREVIEW_WIDTH; x++)
217 | {
218 | var rx = ImportedViewBytes[x, y] % 32;
219 | var ry = ImportedViewBytes[x, y] / 32;
220 |
221 | destRect.X = x * 8;
222 | destRect.Y = y * 8;
223 |
224 | srcRect.X = rx * 16;
225 | srcRect.Y = ry * 16 + Constants.FontYOffset[AtariView.UseFontOnLine[y] - 1] + colorOffset;
226 |
227 | for (var h = 0; h < 8; ++h)
228 | {
229 | for (var w = 0; w < 8; ++w)
230 | {
231 | gr.DrawImage(AtariFontRenderer.BitmapFontBanks, destRect, srcRect, GraphicsUnit.Pixel);
232 | destRect.X++;
233 | srcRect.X += 2;
234 | }
235 |
236 | destRect.X -= 8;
237 | srcRect.X -= 16;
238 | destRect.Y++;
239 | srcRect.Y += 2;
240 | }
241 |
242 | }
243 | }
244 | }
245 | pictureBoxAtariViewSmall.Refresh();
246 | }
247 |
248 | private void Button_Import_Click(object sender, EventArgs e)
249 | {
250 | if (ImportedViewBytes != null)
251 | {
252 | for (var y = 0; y < AtariView.Height; ++y)
253 | {
254 | for (var x = 0; x < AtariView.Width; ++x)
255 | {
256 | AtariView.ViewBytes[x, y] = ImportedViewBytes[x, y];
257 | }
258 | }
259 | }
260 |
261 | Close();
262 | }
263 | }
264 |
--------------------------------------------------------------------------------
/PageEditor.resx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 | text/microsoft-resx
110 |
111 |
112 | 2.0
113 |
114 |
115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
116 |
117 |
118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
119 |
120 |
121 | 17, 17
122 |
123 |
124 | 17, 17
125 |
126 |
127 |
128 |
129 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
130 | vAAADrwBlbxySQAAAZZJREFUOE+Vj8tLQkEYxYdIihZBUS3zH2gf6DaiopYRiSJR0QMsWkQRLXJVBFkU
131 | PQgxb8iNDBJ0IWmYJhk93bTRhfZcBCm1yUDzxMyVwqlrdeDH/ThzzjdzCZFRJgtF51JAbJzdd6cy2TL+
132 | vKAAFOlWgiLRbIDSMOP1J1Ppaj73o+jN/ZYTMy1qnPcMOjebDrxv77+8hN28fiwSrcCKo0fPDLZEK6Bl
133 | IeBJptJVfI+J3ty3eWEmelteOW+J3oamxcPvL6FlneVMJF1baHfcYMifgMGfwJA/yb7SnEC74xY007Ic
134 | 8jy9pmtY+SWVKddaL0XSu4NWeww9nkd07+WgM0ebPQaarZ87PH96TVcTc+humAw4wNPhukeHM4fr4ets
135 | cPdzNgWvJ0kWUOT9D5XBhWZ7/IvtOKjHxwAU856kETfUQgQqIQq1NQqVEAH1+Ji8Rr2oW72SWJOgHh+T
136 | 15gPSlMYyvkwaimmMMi47x8LJgKonD5lVOSgHh+T12QQJcYQSqdCKDEesZl6fExeYz6pwPNn8cUCCz4A
137 | IQdbtv1Vld0AAAAASUVORK5CYII=
138 |
139 |
140 |
141 |
142 | iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
143 | vAAADrwBlbxySQAAAadJREFUOE+VkE8ow2EYx99kFzWlTG5y5Gy5Okj+XSmaxkFGaRxEaw4ksfxZZAdt
144 | /rV+DivaHCazzX7DaP4cHCgKWVG0uJja7Kvntxl7s7JvfXqfvu/3ed7n92PsL+n9+JN/a9ADlf0eAwev
145 | ElRnN0Dvh9YXhlZM0CeGsxyg80Hjfka3+1k6CfL4WGYNeqB2PkK9/YT2JGzIk8WAARea7aEEjhCaHCGQ
146 | x8cyq9+JRtstGmx3SW5BHh+TFAdkvMd6t1BlvUaV9SZ5XoM8PgYgl5kPH7SsexM8SsslKiSuoLRc/dz1
147 | bKTqGf+dnr1FYvmqlTOBddpQOhVEuekiRdmv+hvKULZyWjx5eY8qpFViccjaloIC61hH8XgAJcZzlMxy
148 | GM+lO8rULxzuvLxHi9K+h4Z0rZ2amdqKgpF9FBlOoDAEE0wGUTC6D7qrnRNdH5/xvLTmbwHIaVsMCEy1
149 | irxhEfKxAORjR1JNXr3RtxOORAv5vjTRJpqlYzNrXUauzguZzguq62a8mV/mJW1i8gvUSFRPuPbCkeQP
150 | +69ok5Z5n1Bj2HVGYplf/gJNOVXeqnqIXQAAAABJRU5ErkJggg==
151 |
152 |
153 |
--------------------------------------------------------------------------------
/General.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker
2 | {
3 |
4 | internal class General
5 | {
6 | }
7 |
8 | public partial class FontMakerForm
9 | {
10 | // Section A - General: New, Load, Save, Save As, Clear, About and Quit buttons
11 | // Methods to implement the button actions
12 |
13 | #region Button and mouse event actions
14 |
15 | public void SimulateSafeLeftMouseButtonClick()
16 | {
17 | var old_megaCopyStatus = megaCopyStatus;
18 | megaCopyStatus = MegaCopyStatusFlags.None;
19 | var mouseEvent = new MouseEventArgs(MouseButtons.Left, 0, (SelectedCharacterIndex % 32) * 16, (SelectedCharacterIndex / 32) * 16, 0);
20 | ActionFontSelectorMouseDown(mouseEvent);
21 | ActionFontSelectorMouseUp(mouseEvent);
22 | megaCopyStatus = old_megaCopyStatus;
23 | }
24 | ///
25 | /// New the font and view data
26 | ///
27 | public void ActionNewFontAndView()
28 | {
29 | var re = MessageBox.Show(@"Are you sure you want to reset to the default character set and view? Everything will be lost!", Constants.Title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
30 |
31 | if (re == DialogResult.Yes)
32 | {
33 | CurrentDataFolder = AppContext.BaseDirectory;
34 | LoadViewFile(null, true);
35 | checkBoxFontBank.Checked = false;
36 | UpdateFormCaption();
37 | RedrawFonts();
38 | RedrawLineTypes();
39 | RedrawView();
40 | RedrawPal();
41 | RedrawViewChar();
42 | RedrawChar();
43 | // SaveFont1_Click(null, EventArgs.Empty);
44 | }
45 | }
46 |
47 | ///
48 | /// Load 1 or Load 3 button was clicked
49 | /// Load a font into bank 1 or 3, or in dual mode into banks 1+2 or 3+4
50 | ///
51 | public void ActionLoadFont1()
52 | {
53 | dialogOpenFile.FileName = string.Empty;
54 | dialogOpenFile.InitialDirectory = CurrentDataFolder;
55 | dialogOpenFile.Filter = $@"Atari font {(checkBoxFontBank.Checked ? 3 : 1)} or Dual font (*.fnt,*.fn2)|*.fnt;*.fn2";
56 | var ok = dialogOpenFile.ShowDialog();
57 |
58 | if (ok == DialogResult.OK)
59 | {
60 | var fontBankOffset = checkBoxFontBank.Checked ? 2 : 0;
61 | var dual = Path.GetExtension(dialogOpenFile.FileName).ToLowerInvariant() == ".fn2";
62 |
63 | AtariFont.LoadFont(dialogOpenFile.FileName, fontBankOffset, dual);
64 |
65 | CurrentDataFolder = Path.GetDirectoryName(dialogOpenFile.FileName) + Path.DirectorySeparatorChar;
66 |
67 | if (dual)
68 | {
69 | var tempString = dialogOpenFile.FileName.Substring(0, dialogOpenFile.FileName.Length - 4);
70 | if (checkBoxFontBank.Checked == false)
71 | {
72 | Font1Filename = tempString + "-fn2-1.fnt";
73 | Font2Filename = tempString + "-fn2-2.fnt";
74 |
75 | AtariFont.SaveFont(Font1Filename, 0);
76 | AtariFont.SaveFont(Font2Filename, 1);
77 | }
78 | else
79 | {
80 | Font3Filename = tempString + "-fn2-3.fnt";
81 | Font4Filename = tempString + "-fn2-4.fnt";
82 |
83 | AtariFont.SaveFont(Font3Filename, 2);
84 | AtariFont.SaveFont(Font4Filename, 3);
85 | }
86 |
87 | }
88 | else
89 | {
90 | if (checkBoxFontBank.Checked == false)
91 | Font1Filename = dialogOpenFile.FileName;
92 | else
93 | Font3Filename = dialogOpenFile.FileName;
94 | }
95 |
96 | UpdateFormCaption();
97 | RedrawFonts();
98 |
99 | SimulateSafeLeftMouseButtonClick();
100 |
101 | RedrawView();
102 | AtariFontUndoBuffer.Add2UndoFullDifferenceScan(); // Full font scan
103 | UpdateUndoButtons(false);
104 | }
105 |
106 | CheckDuplicate();
107 | }
108 |
109 | ///
110 | /// Load 2 or Load 3 button was clicked
111 | /// Load a font into bank 2 or 4
112 | ///
113 | public void ActionLoadFont2()
114 | {
115 | dialogOpenFile.FileName = string.Empty;
116 | dialogOpenFile.InitialDirectory = CurrentDataFolder;
117 | dialogOpenFile.Filter = $@"Atari font {(checkBoxFontBank.Checked ? 4 : 2)} (*.fnt)|*.fnt";
118 | var ok = dialogOpenFile.ShowDialog();
119 |
120 | if (ok == DialogResult.OK)
121 | {
122 | var fontBankOffset = checkBoxFontBank.Checked ? 2 : 0;
123 |
124 | AtariFont.LoadFont(dialogOpenFile.FileName, fontBankOffset + 1, false);
125 |
126 | CurrentDataFolder = Path.GetDirectoryName(dialogOpenFile.FileName) + Path.DirectorySeparatorChar;
127 |
128 | if (checkBoxFontBank.Checked == false)
129 | Font2Filename = dialogOpenFile.FileName;
130 | else
131 | Font4Filename = dialogOpenFile.FileName;
132 |
133 | UpdateFormCaption();
134 | RedrawFonts();
135 | SimulateSafeLeftMouseButtonClick();
136 | RedrawView();
137 | AtariFontUndoBuffer.Add2UndoFullDifferenceScan(); //full font scan
138 | UpdateUndoButtons(false);
139 | }
140 |
141 | CheckDuplicate();
142 | }
143 |
144 | ///
145 | /// Save the font in bank 1/3 away
146 | ///
147 | public void ActionSaveFont1()
148 | {
149 | var filename = checkBoxFontBank.Checked ? Font3Filename : Font1Filename;
150 | AtariFont.SaveFont(filename, checkBoxFontBank.Checked == false ? 0 : 2);
151 | }
152 |
153 | ///
154 | /// Save the font in bank 2/4 away
155 | ///
156 | public void ActionSaveFont2()
157 | {
158 | var filename = checkBoxFontBank.Checked ? Font4Filename : Font2Filename;
159 | AtariFont.SaveFont(filename, checkBoxFontBank.Checked == false ? 1 : 3);
160 | }
161 |
162 | public void ActionSaveFont1As()
163 | {
164 | dialogSaveFile.FileName = string.Empty;
165 | dialogSaveFile.InitialDirectory = CurrentDataFolder;
166 | dialogSaveFile.Filter = $@"Atari font {(checkBoxFontBank.Checked ? 3 : 1)} (*.fnt)|*.fnt";
167 | dialogSaveFile.DefaultExt = "fnt";
168 | var ok = dialogSaveFile.ShowDialog();
169 |
170 | if (ok == DialogResult.OK)
171 | {
172 | AtariFont.SaveFont(dialogSaveFile.FileName, checkBoxFontBank.Checked == false ? 0 : 2);
173 | CurrentDataFolder = Path.GetDirectoryName(dialogSaveFile.FileName) + Path.DirectorySeparatorChar;
174 |
175 | if (checkBoxFontBank.Checked == false)
176 | Font1Filename = dialogSaveFile.FileName;
177 | else
178 | Font3Filename = dialogSaveFile.FileName;
179 | }
180 |
181 | UpdateFormCaption();
182 | }
183 |
184 | public void ActionSaveFont2As()
185 | {
186 | dialogSaveFile.FileName = string.Empty;
187 | dialogSaveFile.InitialDirectory = CurrentDataFolder;
188 | dialogSaveFile.Filter = $@"Atari font {(checkBoxFontBank.Checked ? 4 : 2)} (*.fnt)|*.fnt";
189 | dialogSaveFile.DefaultExt = "fnt";
190 | var ok = dialogSaveFile.ShowDialog();
191 |
192 | if (ok == DialogResult.OK)
193 | {
194 | AtariFont.SaveFont(dialogSaveFile.FileName, checkBoxFontBank.Checked == false ? 1 : 3);
195 |
196 | CurrentDataFolder = Path.GetDirectoryName(dialogSaveFile.FileName) + Path.DirectorySeparatorChar;
197 |
198 | if (checkBoxFontBank.Checked == false)
199 | Font2Filename = dialogSaveFile.FileName;
200 | else
201 | Font4Filename = dialogSaveFile.FileName;
202 | }
203 |
204 | UpdateFormCaption();
205 | }
206 |
207 | public void ActionClearFont(int fontOffset)
208 | {
209 | var fontBankOffset = checkBoxFontBank.Checked ? 2 : 0;
210 |
211 | var re = MessageBox.Show($@"Are you sure to clear font {fontOffset + 1 + fontBankOffset}?", Constants.Title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
212 |
213 | if (re == DialogResult.Yes)
214 | {
215 | ClearFont(fontOffset + 0 + fontBankOffset);
216 | }
217 | }
218 |
219 | public void ActionShowAbout()
220 | {
221 | pictureBoxAbout.Left = pictureBoxAtariView.Left;
222 | pictureBoxAbout.Visible = !pictureBoxAbout.Visible;
223 |
224 | }
225 |
226 | public void ActionAboutUrl(bool onLeftSide)
227 | {
228 | Helpers.OpenUrl(onLeftSide ? "http://matosimi.atari.org" : "https://retro.cerebus.co.za");
229 | pictureBoxAbout.Visible = false;
230 | }
231 |
232 | public void ActionExitApplication()
233 | {
234 | var re = MessageBox.Show(@"Are you sure you want to quit?", Constants.Title, MessageBoxButtons.YesNo, MessageBoxIcon.Question);
235 |
236 | if (re == DialogResult.Yes)
237 | {
238 | SaveConfiguration();
239 | Exit();
240 | }
241 | }
242 |
243 | #endregion
244 |
245 |
246 | private void ClearFont(int fontNr)
247 | {
248 | AtariFont.ClearFont(fontNr);
249 |
250 | RedrawFonts();
251 | SimulateSafeLeftMouseButtonClick();
252 | RedrawView();
253 | CheckDuplicate();
254 | }
255 |
256 | public void Exit()
257 | {
258 | Visible = false;
259 | timerAutoCloseAboutBox.Enabled = false;
260 | pictureBoxAbout.Visible = false;
261 | Environment.Exit(Environment.ExitCode);
262 | }
263 |
264 | public void SwitchToTileDrawing()
265 | {
266 | // The font and characters have been copied to the clipboard
267 | // Enabled specific clipboard modification/action buttons
268 | if (buttonMegaCopy.Checked == false)
269 | {
270 | // Switch into MegaCopy mode
271 | buttonMegaCopy.Checked = true;
272 | MegaCopy_Click(0, EventArgs.Empty);
273 | }
274 |
275 |
276 | ConfigureClipboardActionButtons();
277 |
278 | UpdateClipboardInformation();
279 | PastingToView = true;
280 | RevalidateClipboard();
281 | ExecutePasteFromClipboard();
282 |
283 | pictureBoxAtariView.Focus();
284 | }
285 | }
286 | }
287 |
--------------------------------------------------------------------------------
/AtariViewConfigWindow.Designer.cs:
--------------------------------------------------------------------------------
1 | namespace FontMaker;
2 |
3 | partial class AtariViewConfigWindow
4 | {
5 | ///
6 | /// Required designer variable.
7 | ///
8 | private System.ComponentModel.IContainer components = null;
9 |
10 | ///
11 | /// Clean up any resources being used.
12 | ///
13 | /// true if managed resources should be disposed; otherwise, false.
14 | protected override void Dispose(bool disposing)
15 | {
16 | if (disposing && (components != null))
17 | {
18 | components.Dispose();
19 | }
20 | base.Dispose(disposing);
21 | }
22 |
23 | #region Windows Form Designer generated code
24 |
25 | ///
26 | /// Required method for Designer support - do not modify
27 | /// the contents of this method with the code editor.
28 | ///
29 | private void InitializeComponent()
30 | {
31 | numericHeight = new NumericUpDown();
32 | numericWidth = new NumericUpDown();
33 | label5 = new Label();
34 | label6 = new Label();
35 | label3 = new Label();
36 | label1 = new Label();
37 | label2 = new Label();
38 | labelCurrentInfo = new Label();
39 | buttonResize = new Button();
40 | buttonCancel = new Button();
41 | label4 = new Label();
42 | labelNewInfo = new Label();
43 | labelNewDifference = new Label();
44 | radio1 = new RadioButton();
45 | radio2 = new RadioButton();
46 | radio4 = new RadioButton();
47 | radio3 = new RadioButton();
48 | label7 = new Label();
49 | label8 = new Label();
50 | label9 = new Label();
51 | ((System.ComponentModel.ISupportInitialize)numericHeight).BeginInit();
52 | ((System.ComponentModel.ISupportInitialize)numericWidth).BeginInit();
53 | SuspendLayout();
54 | //
55 | // numericHeight
56 | //
57 | numericHeight.Location = new Point(213, 63);
58 | numericHeight.Maximum = new decimal(new int[] { 1024, 0, 0, 0 });
59 | numericHeight.Name = "numericHeight";
60 | numericHeight.Size = new Size(62, 22);
61 | numericHeight.TabIndex = 2;
62 | numericHeight.TextAlign = HorizontalAlignment.Center;
63 | numericHeight.ThousandsSeparator = true;
64 | numericHeight.Value = new decimal(new int[] { 26, 0, 0, 0 });
65 | numericHeight.ValueChanged += numericHeight_ValueChanged;
66 | numericHeight.KeyPress += numericWidth_KeyPress;
67 | //
68 | // numericWidth
69 | //
70 | numericWidth.Location = new Point(57, 63);
71 | numericWidth.Maximum = new decimal(new int[] { 1024, 0, 0, 0 });
72 | numericWidth.Name = "numericWidth";
73 | numericWidth.Size = new Size(62, 22);
74 | numericWidth.TabIndex = 1;
75 | numericWidth.TextAlign = HorizontalAlignment.Center;
76 | numericWidth.ThousandsSeparator = true;
77 | numericWidth.Value = new decimal(new int[] { 40, 0, 0, 0 });
78 | numericWidth.ValueChanged += numericWidth_ValueChanged;
79 | numericWidth.KeyPress += numericWidth_KeyPress;
80 | //
81 | // label5
82 | //
83 | label5.AutoSize = true;
84 | label5.Location = new Point(165, 65);
85 | label5.Name = "label5";
86 | label5.Size = new Size(45, 13);
87 | label5.TabIndex = 31;
88 | label5.Text = "Height:";
89 | //
90 | // label6
91 | //
92 | label6.AutoSize = true;
93 | label6.Location = new Point(8, 65);
94 | label6.Name = "label6";
95 | label6.Size = new Size(42, 13);
96 | label6.TabIndex = 30;
97 | label6.Text = "Width:";
98 | //
99 | // label3
100 | //
101 | label3.AutoSize = true;
102 | label3.Font = new Font("Segoe UI", 8.25F, FontStyle.Bold);
103 | label3.Location = new Point(2, 2);
104 | label3.Name = "label3";
105 | label3.Size = new Size(92, 13);
106 | label3.TabIndex = 34;
107 | label3.Text = "Page Dimension";
108 | //
109 | // label1
110 | //
111 | label1.AutoSize = true;
112 | label1.Location = new Point(8, 21);
113 | label1.Name = "label1";
114 | label1.Size = new Size(72, 13);
115 | label1.TabIndex = 35;
116 | label1.Text = "Current Size:";
117 | //
118 | // label2
119 | //
120 | label2.AutoSize = true;
121 | label2.Location = new Point(8, 47);
122 | label2.Name = "label2";
123 | label2.Size = new Size(56, 13);
124 | label2.TabIndex = 36;
125 | label2.Text = "New Size:";
126 | //
127 | // labelCurrentInfo
128 | //
129 | labelCurrentInfo.AutoSize = true;
130 | labelCurrentInfo.Location = new Point(83, 21);
131 | labelCurrentInfo.Name = "labelCurrentInfo";
132 | labelCurrentInfo.Size = new Size(11, 13);
133 | labelCurrentInfo.TabIndex = 42;
134 | labelCurrentInfo.Text = "-";
135 | //
136 | // buttonResize
137 | //
138 | buttonResize.Location = new Point(10, 192);
139 | buttonResize.Name = "buttonResize";
140 | buttonResize.Size = new Size(100, 40);
141 | buttonResize.TabIndex = 3;
142 | buttonResize.Text = "Resize";
143 | buttonResize.UseVisualStyleBackColor = true;
144 | buttonResize.Click += buttonResize_Click;
145 | //
146 | // buttonCancel
147 | //
148 | buttonCancel.Location = new Point(176, 195);
149 | buttonCancel.Name = "buttonCancel";
150 | buttonCancel.Size = new Size(100, 40);
151 | buttonCancel.TabIndex = 4;
152 | buttonCancel.Text = "Cancel";
153 | buttonCancel.UseVisualStyleBackColor = true;
154 | //
155 | // label4
156 | //
157 | label4.BorderStyle = BorderStyle.FixedSingle;
158 | label4.Location = new Point(4, 42);
159 | label4.Name = "label4";
160 | label4.Size = new Size(280, 2);
161 | label4.TabIndex = 45;
162 | //
163 | // labelNewInfo
164 | //
165 | labelNewInfo.Font = new Font("Segoe UI", 8.25F, FontStyle.Bold);
166 | labelNewInfo.Location = new Point(12, 90);
167 | labelNewInfo.Name = "labelNewInfo";
168 | labelNewInfo.Size = new Size(263, 13);
169 | labelNewInfo.TabIndex = 46;
170 | labelNewInfo.Text = "-";
171 | labelNewInfo.TextAlign = ContentAlignment.TopCenter;
172 | labelNewInfo.UseMnemonic = false;
173 | //
174 | // labelNewDifference
175 | //
176 | labelNewDifference.Font = new Font("Segoe UI", 8.25F, FontStyle.Bold);
177 | labelNewDifference.Location = new Point(12, 107);
178 | labelNewDifference.Name = "labelNewDifference";
179 | labelNewDifference.Size = new Size(263, 13);
180 | labelNewDifference.TabIndex = 47;
181 | labelNewDifference.Text = "-";
182 | labelNewDifference.TextAlign = ContentAlignment.TopCenter;
183 | labelNewDifference.UseMnemonic = false;
184 | //
185 | // radio1
186 | //
187 | radio1.AutoSize = true;
188 | radio1.Checked = true;
189 | radio1.Location = new Point(72, 154);
190 | radio1.Name = "radio1";
191 | radio1.Size = new Size(31, 17);
192 | radio1.TabIndex = 49;
193 | radio1.TabStop = true;
194 | radio1.Text = "1";
195 | radio1.UseVisualStyleBackColor = true;
196 | //
197 | // radio2
198 | //
199 | radio2.AutoSize = true;
200 | radio2.Location = new Point(109, 154);
201 | radio2.Name = "radio2";
202 | radio2.Size = new Size(31, 17);
203 | radio2.TabIndex = 50;
204 | radio2.Text = "2";
205 | radio2.UseVisualStyleBackColor = true;
206 | //
207 | // radio4
208 | //
209 | radio4.AutoSize = true;
210 | radio4.Location = new Point(183, 154);
211 | radio4.Name = "radio4";
212 | radio4.Size = new Size(31, 17);
213 | radio4.TabIndex = 52;
214 | radio4.Text = "4";
215 | radio4.UseVisualStyleBackColor = true;
216 | //
217 | // radio3
218 | //
219 | radio3.AutoSize = true;
220 | radio3.Location = new Point(146, 154);
221 | radio3.Name = "radio3";
222 | radio3.Size = new Size(31, 17);
223 | radio3.TabIndex = 51;
224 | radio3.Text = "3";
225 | radio3.UseVisualStyleBackColor = true;
226 | //
227 | // label7
228 | //
229 | label7.AutoSize = true;
230 | label7.Location = new Point(39, 133);
231 | label7.Name = "label7";
232 | label7.Size = new Size(208, 13);
233 | label7.TabIndex = 53;
234 | label7.Text = "What font nr to use for resized height?";
235 | //
236 | // label8
237 | //
238 | label8.BorderStyle = BorderStyle.FixedSingle;
239 | label8.Location = new Point(3, 127);
240 | label8.Name = "label8";
241 | label8.Size = new Size(280, 2);
242 | label8.TabIndex = 54;
243 | //
244 | // label9
245 | //
246 | label9.BorderStyle = BorderStyle.FixedSingle;
247 | label9.Location = new Point(3, 177);
248 | label9.Name = "label9";
249 | label9.Size = new Size(280, 2);
250 | label9.TabIndex = 55;
251 | //
252 | // AtariViewConfigWindow
253 | //
254 | AutoScaleDimensions = new SizeF(6F, 13F);
255 | AutoScaleMode = AutoScaleMode.Font;
256 | CancelButton = buttonCancel;
257 | ClientSize = new Size(287, 241);
258 | Controls.Add(label9);
259 | Controls.Add(label8);
260 | Controls.Add(label7);
261 | Controls.Add(radio4);
262 | Controls.Add(radio3);
263 | Controls.Add(radio2);
264 | Controls.Add(radio1);
265 | Controls.Add(labelNewDifference);
266 | Controls.Add(labelNewInfo);
267 | Controls.Add(label4);
268 | Controls.Add(buttonCancel);
269 | Controls.Add(buttonResize);
270 | Controls.Add(labelCurrentInfo);
271 | Controls.Add(label2);
272 | Controls.Add(label1);
273 | Controls.Add(label3);
274 | Controls.Add(numericHeight);
275 | Controls.Add(numericWidth);
276 | Controls.Add(label5);
277 | Controls.Add(label6);
278 | FormBorderStyle = FormBorderStyle.FixedDialog;
279 | KeyPreview = true;
280 | MaximizeBox = false;
281 | MinimizeBox = false;
282 | Name = "AtariViewConfigWindow";
283 | StartPosition = FormStartPosition.CenterParent;
284 | Text = "Update Page Size";
285 | ((System.ComponentModel.ISupportInitialize)numericHeight).EndInit();
286 | ((System.ComponentModel.ISupportInitialize)numericWidth).EndInit();
287 | ResumeLayout(false);
288 | PerformLayout();
289 | }
290 |
291 | #endregion
292 |
293 | private NumericUpDown numericHeight;
294 | private NumericUpDown numericWidth;
295 | private Label label5;
296 | private Label label6;
297 | private Label label3;
298 | private Label label1;
299 | private Label label2;
300 | private Label labelCurrentInfo;
301 | private Button buttonResize;
302 | private Button buttonCancel;
303 | private Label label4;
304 | private Label labelNewInfo;
305 | private Label labelNewDifference;
306 | private RadioButton radio1;
307 | private RadioButton radio2;
308 | private RadioButton radio4;
309 | private RadioButton radio3;
310 | private Label label7;
311 | private Label label8;
312 | private Label label9;
313 | }
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # Remove the line below if you want to inherit .editorconfig settings from higher directories
2 | root = true
3 |
4 | # C# files
5 | [*.cs]
6 |
7 | #### Core EditorConfig Options ####
8 |
9 | # Indentation and spacing
10 | indent_size = 4
11 | indent_style = tab
12 | tab_width = 4
13 |
14 | # New line preferences
15 | end_of_line = crlf
16 | insert_final_newline = false
17 |
18 | #### .NET Code Actions ####
19 |
20 | # Type members
21 | dotnet_hide_advanced_members = false
22 | dotnet_member_insertion_location = with_other_members_of_the_same_kind
23 | dotnet_property_generation_behavior = prefer_throwing_properties
24 |
25 | # Symbol search
26 | dotnet_search_reference_assemblies = true
27 |
28 | #### .NET Coding Conventions ####
29 |
30 | # Organize usings
31 | dotnet_separate_import_directive_groups = false
32 | dotnet_sort_system_directives_first = false
33 | file_header_template = unset
34 |
35 | # this. and Me. preferences
36 | dotnet_style_qualification_for_event = false
37 | dotnet_style_qualification_for_field = false
38 | dotnet_style_qualification_for_method = false
39 | dotnet_style_qualification_for_property = false
40 |
41 | # Language keywords vs BCL types preferences
42 | dotnet_style_predefined_type_for_locals_parameters_members = true
43 | dotnet_style_predefined_type_for_member_access = true
44 |
45 | # Parentheses preferences
46 | dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
47 | dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
48 | dotnet_style_parentheses_in_other_operators = never_if_unnecessary
49 | dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity
50 |
51 | # Modifier preferences
52 | dotnet_style_require_accessibility_modifiers = for_non_interface_members
53 |
54 | # Expression-level preferences
55 | dotnet_prefer_system_hash_code = true
56 | dotnet_style_coalesce_expression = true
57 | dotnet_style_collection_initializer = true
58 | dotnet_style_explicit_tuple_names = true
59 | dotnet_style_namespace_match_folder = true
60 | dotnet_style_null_propagation = true
61 | dotnet_style_object_initializer = true
62 | dotnet_style_operator_placement_when_wrapping = beginning_of_line
63 | dotnet_style_prefer_auto_properties = true
64 | dotnet_style_prefer_collection_expression = when_types_loosely_match
65 | dotnet_style_prefer_compound_assignment = true
66 | dotnet_style_prefer_conditional_expression_over_assignment = true
67 | dotnet_style_prefer_conditional_expression_over_return = true
68 | dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
69 | dotnet_style_prefer_inferred_anonymous_type_member_names = true
70 | dotnet_style_prefer_inferred_tuple_names = true
71 | dotnet_style_prefer_is_null_check_over_reference_equality_method = true
72 | dotnet_style_prefer_simplified_boolean_expressions = true
73 | dotnet_style_prefer_simplified_interpolation = true
74 |
75 | # Field preferences
76 | dotnet_style_readonly_field = true
77 |
78 | # Parameter preferences
79 | dotnet_code_quality_unused_parameters = all
80 |
81 | # Suppression preferences
82 | dotnet_remove_unnecessary_suppression_exclusions = none
83 |
84 | # New line preferences
85 | dotnet_style_allow_multiple_blank_lines_experimental = true
86 | dotnet_style_allow_statement_immediately_after_block_experimental = true
87 |
88 | #### C# Coding Conventions ####
89 |
90 | # var preferences
91 | csharp_style_var_elsewhere = true
92 | csharp_style_var_for_built_in_types = true
93 | csharp_style_var_when_type_is_apparent = true
94 |
95 | # Expression-bodied members
96 | csharp_style_expression_bodied_accessors = true
97 | csharp_style_expression_bodied_constructors = false
98 | csharp_style_expression_bodied_indexers = true
99 | csharp_style_expression_bodied_lambdas = true
100 | csharp_style_expression_bodied_local_functions = false
101 | csharp_style_expression_bodied_methods = false
102 | csharp_style_expression_bodied_operators = false
103 | csharp_style_expression_bodied_properties = true
104 |
105 | # Pattern matching preferences
106 | csharp_style_pattern_matching_over_as_with_null_check = true
107 | csharp_style_pattern_matching_over_is_with_cast_check = true
108 | csharp_style_prefer_extended_property_pattern = true
109 | csharp_style_prefer_not_pattern = true
110 | csharp_style_prefer_pattern_matching = true
111 | csharp_style_prefer_switch_expression = true
112 |
113 | # Null-checking preferences
114 | csharp_style_conditional_delegate_call = true
115 |
116 | # Modifier preferences
117 | csharp_prefer_static_anonymous_function = true
118 | csharp_prefer_static_local_function = true
119 | csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async
120 | csharp_style_prefer_readonly_struct = true
121 | csharp_style_prefer_readonly_struct_member = true
122 |
123 | # Code-block preferences
124 | csharp_prefer_braces = true
125 | csharp_prefer_simple_using_statement = true
126 | csharp_prefer_system_threading_lock = true
127 | csharp_style_namespace_declarations = file_scoped
128 | csharp_style_prefer_method_group_conversion = true
129 | csharp_style_prefer_primary_constructors = true
130 | csharp_style_prefer_top_level_statements = true
131 |
132 | # Expression-level preferences
133 | csharp_prefer_simple_default_expression = true
134 | csharp_style_deconstructed_variable_declaration = true
135 | csharp_style_implicit_object_creation_when_type_is_apparent = true
136 | csharp_style_inlined_variable_declaration = true
137 | csharp_style_prefer_index_operator = true
138 | csharp_style_prefer_local_over_anonymous_function = true
139 | csharp_style_prefer_null_check_over_type_check = true
140 | csharp_style_prefer_range_operator = true
141 | csharp_style_prefer_tuple_swap = true
142 | csharp_style_prefer_utf8_string_literals = true
143 | csharp_style_throw_expression = true
144 | csharp_style_unused_value_assignment_preference = discard_variable
145 | csharp_style_unused_value_expression_statement_preference = discard_variable
146 |
147 | # 'using' directive preferences
148 | csharp_using_directive_placement = outside_namespace
149 |
150 | # New line preferences
151 | csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
152 | csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
153 | csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
154 | csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
155 | csharp_style_allow_embedded_statements_on_same_line_experimental = false
156 |
157 | #### C# Formatting Rules ####
158 |
159 | # New line preferences
160 | csharp_new_line_before_catch = true
161 | csharp_new_line_before_else = true
162 | csharp_new_line_before_finally = true
163 | csharp_new_line_before_members_in_anonymous_types = true
164 | csharp_new_line_before_members_in_object_initializers = true
165 | csharp_new_line_before_open_brace = all
166 | csharp_new_line_between_query_expression_clauses = true
167 |
168 | # Indentation preferences
169 | csharp_indent_block_contents = true
170 | csharp_indent_braces = false
171 | csharp_indent_case_contents = true
172 | csharp_indent_case_contents_when_block = false
173 | csharp_indent_labels = one_less_than_current
174 | csharp_indent_switch_labels = true
175 |
176 | # Space preferences
177 | csharp_space_after_cast = false
178 | csharp_space_after_colon_in_inheritance_clause = true
179 | csharp_space_after_comma = true
180 | csharp_space_after_dot = false
181 | csharp_space_after_keywords_in_control_flow_statements = true
182 | csharp_space_after_semicolon_in_for_statement = true
183 | csharp_space_around_binary_operators = before_and_after
184 | csharp_space_around_declaration_statements = false
185 | csharp_space_before_colon_in_inheritance_clause = true
186 | csharp_space_before_comma = false
187 | csharp_space_before_dot = false
188 | csharp_space_before_open_square_brackets = false
189 | csharp_space_before_semicolon_in_for_statement = false
190 | csharp_space_between_empty_square_brackets = false
191 | csharp_space_between_method_call_empty_parameter_list_parentheses = false
192 | csharp_space_between_method_call_name_and_opening_parenthesis = false
193 | csharp_space_between_method_call_parameter_list_parentheses = false
194 | csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
195 | csharp_space_between_method_declaration_name_and_open_parenthesis = false
196 | csharp_space_between_method_declaration_parameter_list_parentheses = false
197 | csharp_space_between_parentheses = false
198 | csharp_space_between_square_brackets = false
199 |
200 | # Wrapping preferences
201 | csharp_preserve_single_line_blocks = true
202 | csharp_preserve_single_line_statements = true
203 |
204 | #### Naming styles ####
205 |
206 | # Naming rules
207 |
208 | dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
209 | dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
210 | dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i
211 |
212 | dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
213 | dotnet_naming_rule.types_should_be_pascal_case.symbols = types
214 | dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case
215 |
216 | dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
217 | dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
218 | dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case
219 |
220 | # Symbol specifications
221 |
222 | dotnet_naming_symbols.interface.applicable_kinds = interface
223 | dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
224 | dotnet_naming_symbols.interface.required_modifiers =
225 |
226 | dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
227 | dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
228 | dotnet_naming_symbols.types.required_modifiers =
229 |
230 | dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
231 | dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
232 | dotnet_naming_symbols.non_field_members.required_modifiers =
233 |
234 | # Naming styles
235 |
236 | dotnet_naming_style.pascal_case.required_prefix =
237 | dotnet_naming_style.pascal_case.required_suffix =
238 | dotnet_naming_style.pascal_case.word_separator =
239 | dotnet_naming_style.pascal_case.capitalization = pascal_case
240 |
241 | dotnet_naming_style.begins_with_i.required_prefix = I
242 | dotnet_naming_style.begins_with_i.required_suffix =
243 | dotnet_naming_style.begins_with_i.word_separator =
244 | dotnet_naming_style.begins_with_i.capitalization = pascal_case
245 |
--------------------------------------------------------------------------------
/TileSet.cs:
--------------------------------------------------------------------------------
1 | #pragma warning disable WFO1000
2 | using System.Text;
3 |
4 | namespace FontMaker;
5 |
6 | #region Load/Save tile / tile-set data
7 | public class AtrTileSetJson
8 | {
9 | public const string MY_VERSION = "1";
10 |
11 | public string? Version { get; set; }
12 |
13 | ///
14 | /// For each tile we have:
15 | /// - index where to store it
16 | /// - characters (5x5)
17 | /// - selected font
18 | ///
19 | public List? Tiles { get; set; }
20 | }
21 |
22 | public class AtrTileJson
23 | {
24 | public const string MY_VERSION = "1";
25 | public string? Version { get; set; }
26 | public SavedTileData Tile { get; set; }
27 | }
28 |
29 | public class SavedTileData
30 | {
31 | public int Nr { get; set; }
32 | public string View { get; set; }
33 | public string Font { get; set; }
34 | ///
35 | /// Indicate which spot is a null
36 | /// 1 = null
37 | /// 0 = data
38 | ///
39 | public string Nulls { get; set; }
40 |
41 | public int Width { get; set; } = 5;
42 | public int Height { get; set; } = 5;
43 | }
44 | #endregion
45 |
46 | // A tile set is a collection of tiles that can be used to paint onto the view window.
47 |
48 | public class TileData
49 | {
50 | public const int OLD_TILE_WIDTH = 5;
51 | public const int OLD_TILE_HEIGHT = 5;
52 |
53 | public const int TILE_WIDTH = 8;
54 | public const int TILE_HEIGHT = 8;
55 |
56 | public bool IsValid()
57 | {
58 | for (var y = 0; y < TILE_HEIGHT; y++)
59 | {
60 | for (var x = 0; x < TILE_WIDTH; ++x)
61 | {
62 | if (View[x, y] != null)
63 | return true;
64 | }
65 | }
66 |
67 | return false;
68 | }
69 |
70 | ///
71 | /// The 5x5 byte? array that represents the characters (or null) of the tile
72 | ///
73 | public byte?[,] View { get; set; }
74 | public byte[] SelectedFont { get; set;} = new byte[TILE_HEIGHT]; // 1,2,3,4
75 |
76 | private static readonly byte?[,] WorkBuffer = new byte?[TILE_WIDTH, TILE_HEIGHT];
77 |
78 | public TileData()
79 | {
80 | View = new byte?[TILE_WIDTH, TILE_HEIGHT];
81 | for (var a = 0; a < TILE_HEIGHT; a++)
82 | {
83 | SelectedFont[a] = 1;
84 | }
85 | for (var a = 0; a < TILE_WIDTH; a++)
86 | {
87 | for (var b = 0; b < TILE_HEIGHT; b++)
88 | {
89 | View[a, b] = null;
90 | }
91 | }
92 | }
93 |
94 | public TileData(byte?[,]? view, byte[] selectedFont)
95 | {
96 | if (view == null)
97 | View = new byte?[TILE_WIDTH, TILE_HEIGHT];
98 | else
99 | View = (view.Clone() as byte?[,])!;
100 | SelectedFont = (selectedFont.Clone() as byte[])!;
101 | }
102 |
103 | public TileData(TileData from) :
104 | this(from.View, from.SelectedFont)
105 | {
106 | }
107 |
108 | ///
109 | /// Generate a string representation of the tile data
110 | ///
111 | ///
112 | public SavedTileData? Save(int tileNr)
113 | {
114 | var numNulls = 0;
115 | // Check if the tile is empty
116 | var sbView = new StringBuilder();
117 | var sbNulls = new StringBuilder();
118 | for (var y = 0; y < TILE_HEIGHT; y++)
119 | {
120 | for (var x = 0; x < TILE_WIDTH; x++)
121 | {
122 | var theChar = View[x, y];
123 | if (theChar == null)
124 | {
125 | sbNulls.Append('1');
126 | theChar = 0;
127 | ++numNulls;
128 | }
129 | else
130 | {
131 | sbNulls.Append('0');
132 | }
133 | sbView.Append($"{theChar:X2}");
134 | }
135 | }
136 |
137 | if (numNulls == TILE_HEIGHT * TILE_WIDTH)
138 | return null;
139 |
140 | var data = new SavedTileData()
141 | {
142 | Nr = tileNr,
143 | View = sbView.ToString(),
144 | Font = Convert.ToHexString(SelectedFont),
145 | Nulls = sbNulls.ToString(),
146 | Width = TILE_WIDTH,
147 | Height = TILE_HEIGHT,
148 | };
149 | return data;
150 | }
151 |
152 | public bool Load(SavedTileData data)
153 | {
154 | var width = data.Width;
155 | var height = data.Height;
156 |
157 | if (width == 0) width = OLD_TILE_WIDTH;
158 | if (height == 0) height = OLD_TILE_HEIGHT;
159 |
160 | var viewData = Convert.FromHexString(data.View); // Convert the HEX string into bytes and store in the View
161 |
162 | var index = 0;
163 | for (var y = 0; y < height; ++y)
164 | {
165 | for (var x = 0; x < width; ++x)
166 | {
167 | View[x, y] = data.Nulls[index] == '0' ? viewData[index] : null;
168 | ++index;
169 | }
170 | }
171 |
172 | var selectedFont = Convert.FromHexString(data.Font);
173 | for (var i = 0; i < height; ++i)
174 | {
175 | SelectedFont[i] = selectedFont[i];
176 | }
177 |
178 | return true;
179 | }
180 |
181 | public void RotateRight()
182 | {
183 | Push();
184 | for (var y = 0; y < TILE_HEIGHT; ++y)
185 | {
186 | for (var x = 0; x < TILE_WIDTH; ++x)
187 | {
188 | WorkBuffer[x, y] = View[y, TILE_WIDTH - x - 1];
189 | }
190 | }
191 | for (var y = 0; y < TILE_HEIGHT; ++y)
192 | {
193 | for (var x = 0; x < TILE_WIDTH; ++x)
194 | {
195 | View[x, y] = WorkBuffer[x, y];
196 | }
197 | }
198 | }
199 |
200 | public void RotateLeft()
201 | {
202 | Push();
203 | for (var y = 0; y < TILE_HEIGHT; ++y)
204 | {
205 | for (var x = 0; x < TILE_WIDTH; ++x)
206 | {
207 | WorkBuffer[x, y] = View[TILE_HEIGHT - y - 1, x];
208 | }
209 | }
210 | for (var y = 0; y < TILE_HEIGHT; ++y)
211 | {
212 | for (var x = 0; x < TILE_WIDTH; ++x)
213 | {
214 | View[x, y] = WorkBuffer[x, y];
215 | }
216 | }
217 | }
218 |
219 | public void MirrorHorizontal()
220 | {
221 | Push();
222 | for (var y = 0; y < TILE_HEIGHT; ++y)
223 | {
224 | for (var x = 0; x < TILE_WIDTH / 2; ++x)
225 | {
226 | (View[x, y], View[TILE_WIDTH - x - 1, y]) = (View[TILE_WIDTH - x - 1, y], View[x, y]);
227 | }
228 | }
229 | }
230 |
231 | public void MirrorVertical()
232 | {
233 | Push();
234 | for (var x = 0; x < TILE_WIDTH; ++x)
235 | {
236 | for (var y = 0; y < TILE_HEIGHT / 2; ++y)
237 | {
238 | (View[x, y], View[x, TILE_HEIGHT - y - 1]) = (View[x, TILE_HEIGHT - y - 1], View[x, y]);
239 | }
240 | }
241 | }
242 |
243 | public void ShiftLeft()
244 | {
245 | Push();
246 | for (var y = 0; y < TILE_HEIGHT; ++y)
247 | {
248 | // Store the leftmost byte of the current row
249 | var leftmost = View[0, y];
250 |
251 | // Shift all elements in the row to the left
252 | for (var x = 0; x < TILE_WIDTH - 1; ++x)
253 | {
254 | View[x, y] = View[x + 1, y];
255 | }
256 |
257 | // Place the leftmost byte in the rightmost position
258 | View[TILE_WIDTH - 1, y] = leftmost;
259 | }
260 | }
261 |
262 | public void ShiftRight()
263 | {
264 | Push();
265 | for (var y = 0; y < TILE_HEIGHT; ++y)
266 | {
267 | // Store the rightmost byte of the current row
268 | var rightmost = View[TILE_WIDTH - 1, y];
269 |
270 | // Shift all elements in the row to the right
271 | for (var x = TILE_WIDTH - 1; x > 0; --x)
272 | {
273 | View[x, y] = View[x - 1, y];
274 | }
275 |
276 | // Place the rightmost byte in the leftmost position
277 | View[0, y] = rightmost;
278 | }
279 | }
280 |
281 | public void ShiftUp()
282 | {
283 | Push();
284 | for (var x = 0; x < TILE_WIDTH; ++x)
285 | {
286 | // Store the topmost byte of the current column
287 | var topmost = View[x, 0];
288 |
289 | // Shift all elements in the column up
290 | for (var y = 0; y < TILE_HEIGHT - 1; ++y)
291 | {
292 | View[x, y] = View[x, y + 1];
293 | }
294 |
295 | // Place the topmost byte in the bottommost position
296 | View[x, TILE_HEIGHT - 1] = topmost;
297 | }
298 | }
299 |
300 | public void ShiftDown()
301 | {
302 | Push();
303 | for (var x = 0; x < TILE_WIDTH; ++x)
304 | {
305 | // Store the bottommost byte of the current column
306 | var bottommost = View[x, TILE_HEIGHT - 1];
307 |
308 | // Shift all elements in the column down
309 | for (var y = TILE_HEIGHT - 1; y > 0; --y)
310 | {
311 | View[x, y] = View[x, y - 1];
312 | }
313 |
314 | // Place the bottommost byte in the topmost position
315 | View[x, 0] = bottommost;
316 | }
317 | }
318 |
319 | #region Undo/Redo
320 |
321 | public const int UndoBufferSize = 250;
322 |
323 | private readonly LinkedList _undoCommands = new();
324 | private readonly Stack _redoCommands = new();
325 |
326 | public void Push()
327 | {
328 | while (_undoCommands.Count >= UndoBufferSize)
329 | {
330 | _undoCommands.RemoveFirst();
331 | }
332 | _undoCommands.AddLast(View.Clone() as byte?[,]);
333 | if (_redoCommands.Count > 0)
334 | _redoCommands.Clear();
335 | }
336 |
337 | public void Undo()
338 | {
339 | if (_undoCommands.Count > 0)
340 | {
341 | // Save the current screen
342 | _redoCommands.Push(View.Clone() as byte?[,]);
343 |
344 | // Get the last screen and restore it
345 | var data = _undoCommands.Last();
346 | _undoCommands.RemoveLast();
347 | if (data != null)
348 | MyBlockCopy(data);
349 | }
350 | }
351 |
352 | public void Redo()
353 | {
354 | if (_redoCommands.Count > 0)
355 | {
356 | // Save the current screen
357 | while (_undoCommands.Count >= UndoBufferSize)
358 | {
359 | _undoCommands.RemoveFirst();
360 | }
361 | _undoCommands.AddLast(View.Clone() as byte?[,]);
362 |
363 | // Get the last screen and restore it
364 | var data = _redoCommands.Pop();
365 | if (data != null)
366 | MyBlockCopy(data);
367 | }
368 | }
369 |
370 | private void MyBlockCopy(byte?[,]? data)
371 | {
372 | if (data == null)
373 | return;
374 | for (var y = 0; y < TILE_HEIGHT; ++y)
375 | {
376 | for (var x = 0; x < TILE_WIDTH; ++x)
377 | {
378 | View[x, y] = data[x, y];
379 | }
380 | }
381 | }
382 |
383 | public (bool, bool) GetRedoUndoButtonState()
384 | {
385 | return (_undoCommands.Count > 0 ? true : false, _redoCommands.Count > 0 ? true : false);
386 | }
387 |
388 | #endregion
389 | }
390 |
391 |
392 | ///
393 | /// All functions that help with TileSet management
394 | ///
395 | public static class TileSet
396 | {
397 | public const int NUM_TILES_IN_SET = 256;
398 |
399 | public static TileData[] Tiles { get; set; } = new TileData[NUM_TILES_IN_SET];
400 |
401 | public static TileData? CurrentTile { get; set; }
402 |
403 | //public static TileData? CopiedTile { get; set; }
404 |
405 | public static void Setup()
406 | {
407 | for (var i = 0; i < Tiles.Length; i++)
408 | {
409 | Tiles[i] = new TileData();
410 | }
411 |
412 | CurrentTile = Tiles[0];
413 | }
414 |
415 | ///
416 | /// Save a specific tile's data to the 'SavedTileData' format
417 | ///
418 | ///
419 | ///
420 | public static SavedTileData? Save(int tileNr)
421 | {
422 | return Tiles[tileNr].Save(tileNr);
423 | }
424 |
425 | ///
426 | /// Load a tile's data via the 'SavedTileData' format
427 | ///
428 | ///
429 | /// true if the load was ok, false if there was an error
430 | public static bool Load(SavedTileData data)
431 | {
432 | try
433 | {
434 | return Tiles[data.Nr].Load(data);
435 | }
436 | catch (Exception _)
437 | {
438 | return false;
439 | }
440 | }
441 | }
--------------------------------------------------------------------------------