From 982e30e29e5acb4c93181019df53c7b8afed9f72 Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Tue, 31 Mar 2026 21:15:22 +0200 Subject: [PATCH 1/2] SpriteEditor: Added export to Conrado Badenas MaskedSprites library --- .../SelectExportTypeControl.axaml.cs | 8 +- .../ZXGraphics/SpriteExportDialog.axaml.cs | 144 ++++++++++ .../ZXGraphics/log/ExportManager.cs | 270 +++++++++++++++++- .../ZXGraphics/log/ServiceLayer.cs | 2 +- .../ZXGraphics/neg/ExportTypes.cs | 1 + ZXBStudio/ZXBasicStudio.csproj | 2 +- ZXBStudio/version.txt | 2 +- 7 files changed, 424 insertions(+), 5 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs index 5b92408..aca2dab 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SelectExportTypeControl.axaml.cs @@ -56,6 +56,13 @@ public bool InitializeSprite() Image = "/Svg/Seal.svg", Name = "PutChars" }); + ExportTypesList.Add(new ExportTypeDescrioptionItem() + { + Description = "Conrado Badenas MaskedSprites library.", + ExportType = ExportTypes.MaskedSprites, + Image = "/Svg/Mask.svg", + Name = "MaskedSprites" + }); /* ExportTypesList.Add(new ExportTypeDescrioptionItem() { @@ -73,7 +80,6 @@ public bool InitializeSprite() }); */ lstOptions.ItemsSource = ExportTypesList; - lstOptions.SelectionChanged += LstOptions_SelectionChanged; return true; diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs index 70ce1f8..6dfce7c 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs @@ -119,6 +119,13 @@ private void Refresh() CreateExample_PutChars(); } break; + case ExportTypes.MaskedSprites: + CreateExportPath(".bas"); + if (canExport) + { + CreateExample_MaskedSprites(); + } + break; default: grdOptions.IsVisible = false; break; @@ -264,6 +271,143 @@ private void CreateExample_PutChars() txtCode.Text = sb.ToString(); } + + private void CreateExample_MaskedSprites() + { + if (sprites == null || sprites.Count() == 0) + { + txtCode.Text = ""; + } + + var sb = new StringBuilder(); + switch (exportConfig.ExportDataType) + { + case ExportDataTypes.DIM: + { + var sprite = sprites.ElementAt(0); + sb.AppendLine("'- Includes -----------------------------------------------"); + sb.AppendLine("#INCLUDE "); + sb.AppendLine("#INCLUDE \"maskedsprites.bas\""); + sb.AppendLine(""); + sb.AppendLine("'- Sprites definition -------------------------------------"); + sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"", + Path.GetFileName(exportConfig.ExportFilePath))); + sb.AppendLine(ExportManager.Export_Sprite_MaskedSprites(exportConfig, sprites)); + sb.AppendLine("' - Init vars ---------------------------------------------"); + sb.AppendLine("#define NumberofMaskedSprites 1"); + sb.AppendLine("DIM sprDir, sprBackDir AS UInteger"); + sb.AppendLine(""); + sb.AppendLine("'- Initialise the sprite engine ---------------------------"); + sb.AppendLine("InitMaskedSpritesFileSystem()"); + sb.AppendLine("' Create sprite"); + sb.AppendLine($"sprDir = RegisterSpriteImageInMSFS(@{exportConfig.LabelName}{sprite.Name.Replace(" ", "_")}(0))"); + sb.AppendLine("' Save background for first time"); + sb.AppendLine("sprBackDir = MaskedSpritesBackground(0)"); + sb.AppendLine(""); + sb.AppendLine("'- Draw sprite --------------------------------------------"); + sb.AppendLine("DIM n AS UByte"); + sb.AppendLine("DO"); + sb.AppendLine(" ' Sync with raytrace"); + sb.AppendLine(" waitretrace"); + sb.AppendLine(" ' Restore background"); + sb.AppendLine(" RestoreBackground(100,52,sprBackDir)"); + sb.AppendLine(" "); + sb.AppendLine(" ' Print a number"); + sb.AppendLine(" PRINT AT 7,12;n;"); + sb.AppendLine(" n = n + 1"); + sb.AppendLine(" ' Draw sprite"); + sb.AppendLine(" SaveBackgroundAndDrawSpriteRegisteredInMSFS(100,52,sprBackDir,sprDir) "); + sb.AppendLine("LOOP"); + sb.AppendLine(""); + } + break; + + case ExportDataTypes.ASM: + { + var sprite = sprites.ElementAt(0); + sb.AppendLine("'- Includes -----------------------------------------------"); + sb.AppendLine("#INCLUDE "); + sb.AppendLine("#INCLUDE \"maskedsprites.bas\""); + sb.AppendLine(""); + sb.AppendLine("' - Init vars ---------------------------------------------"); + sb.AppendLine("#define NumberofMaskedSprites 1"); + sb.AppendLine("DIM sprDir, sprBackDir AS UInteger"); + sb.AppendLine(""); + sb.AppendLine("'- Initialise the sprite engine ---------------------------"); + sb.AppendLine("InitMaskedSpritesFileSystem()"); + sb.AppendLine("' Create sprite"); + sb.AppendLine($"sprDir = RegisterSpriteImageInMSFS(@{exportConfig.LabelName}_{sprite.Name.Replace(" ", "_")})"); + sb.AppendLine("' Save background for first time"); + sb.AppendLine("sprBackDir = MaskedSpritesBackground(0)"); + sb.AppendLine(""); + sb.AppendLine("'- Draw sprite --------------------------------------------"); + sb.AppendLine("DIM n AS UByte"); + sb.AppendLine("DO"); + sb.AppendLine(" ' Sync with raytrace"); + sb.AppendLine(" waitretrace"); + sb.AppendLine(" ' Restore background"); + sb.AppendLine(" RestoreBackground(100,52,sprBackDir)"); + sb.AppendLine(" "); + sb.AppendLine(" ' Print a number"); + sb.AppendLine(" PRINT AT 7,12;n;"); + sb.AppendLine(" n = n + 1"); + sb.AppendLine(" ' Draw sprite"); + sb.AppendLine(" SaveBackgroundAndDrawSpriteRegisteredInMSFS(100,52,sprBackDir,sprDir) "); + sb.AppendLine("LOOP"); + sb.AppendLine(""); + sb.AppendLine("' - This section must not be executed --------------------"); + sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"", + Path.GetFileName(exportConfig.ExportFilePath))); + sb.AppendLine(ExportManager.Export_Sprite_MaskedSprites(exportConfig, sprites)); + } + break; + + case ExportDataTypes.BIN: + { + sb.AppendLine("'- Includes -----------------------------------------------"); + sb.AppendLine("#INCLUDE "); + sb.AppendLine(""); + sb.AppendLine("'- Draw sprite --------------------------------------------"); + var sprite = sprites.ElementAt(0); + sb.AppendLine(string.Format( + "putChars(10,5,{0},{1},@{2})", + sprite.Width / 8, + sprite.Height / 8, + exportConfig.LabelName)); + sb.AppendLine(""); + sb.AppendLine("' This section must not be executed"); + sb.AppendLine(string.Format( + "{0}:", + exportConfig.LabelName)); + sb.AppendLine("ASM"); + sb.AppendLine(string.Format("\tINCBIN \"{0}\"", + Path.GetFileName(exportConfig.ExportFilePath))); + sb.AppendLine("END ASM"); + } + break; + + case ExportDataTypes.TAP: + { + sb.AppendLine("'- Includes -----------------------------------------------"); + sb.AppendLine("#INCLUDE "); + sb.AppendLine(""); + sb.AppendLine("' Load .tap data ------------------------------------------"); + sb.AppendLine("LOAD \"\" CODE"); + sb.AppendLine(""); + sb.AppendLine("'- Draw sprite --------------------------------------------"); + var sprite = sprites.ElementAt(0); + sb.AppendLine(string.Format( + "putChars(10,5,{0},{1},@{2})", + sprite.Width / 8, + sprite.Height / 8, + exportConfig.LabelName)); + sb.AppendLine(""); + } + break; + } + + txtCode.Text = sb.ToString(); + } #endregion diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs index 45aa1b0..69700ec 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs @@ -9,6 +9,7 @@ using ZXBasicStudio.BuildSystem; using ZXBasicStudio.Classes; using ZXBasicStudio.Common; +using ZXBasicStudio.Common.TAPTools; using ZXBasicStudio.DocumentEditors.ZXGraphics.neg; using ZXBasicStudio.DocumentModel.Enums; using ZXBasicStudio.DocumentModel.Interfaces; @@ -439,6 +440,10 @@ public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites exportedData = Export_Sprite_PutChars(exportConfig, sprites); createTextFile = true; break; + case ExportTypes.MaskedSprites: + exportedData = Export_Sprite_MaskedSprites(exportConfig, sprites); + createTextFile = true; + break; default: return true; } @@ -458,6 +463,8 @@ public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites } + #region PutChars + /// /// Generate the export data for PutChars Sprites /// @@ -483,6 +490,7 @@ public static string Export_Sprite_PutChars(ExportConfig exportConfig, IEnumerab #region DIM + public static string Export_Sprite_PutChars_DIM(ExportConfig exportConfig, IEnumerable sprites) { int min = 0; @@ -1024,7 +1032,7 @@ public static string Export_Sprite_PutChars_TAP(ExportConfig exportConfig, IEnum private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable sprites) { - var binData=new List(); + var binData = new List(); foreach (var sprite in sprites) { @@ -1043,6 +1051,266 @@ private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable s #endregion + + #endregion + + + #region MaskedSprites + + /// + /// Generate the export data for Masked Sprites + /// + /// ExportConfig information + /// Sprites to convert + /// string with the conversion commands for the export dialog samble textbox + public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnumerable sprites) + { + switch (exportConfig.ExportDataType) + { + case ExportDataTypes.DIM: + return Export_Sprite_MaskedSprites_DIM(exportConfig, sprites); + case ExportDataTypes.ASM: + return Export_Sprite_MaskedSprites_ASM(exportConfig, sprites); + case ExportDataTypes.BIN: + return Export_Sprite_MaskedSprites_BIN(exportConfig, sprites); + case ExportDataTypes.TAP: + return Export_Sprite_MaskedSprites_TAP(exportConfig, sprites); + default: + return "ERROR: Not implemented!"; + } + } + + + private static byte[] Export_Sprite_MaskedSprites_GenerateData(ExportConfig exportConfig, Sprite sprite) + { + try + { + var lst = new List(); + for (int n = 0; n < sprite.Frames; n += 2) + { + var data = ServiceLayer.Files_CreateBinDataUpDown(sprite.Patterns[n], sprite.Width, sprite.Height); + var mask = ServiceLayer.Files_CreateBinDataUpDown(sprite.Patterns[n + 1], sprite.Width, sprite.Height); + for (int row = 0; row < 16; row++) + { + for (int col = 0; col < 2; col++) + { + int i = row; + if (col == 1) + { + i += 16; + } + lst.Add(mask[i]); + lst.Add(data[i]); + } + } + } + return lst.ToArray(); + } + catch (Exception ex) + { + return null; + } + } + + + #region DIM + + + public static string Export_Sprite_MaskedSprites_DIM(ExportConfig exportConfig, IEnumerable sprites) + { + var sb = new StringBuilder(); + sb.AppendLine("'- Sprite definitions --------------------------------------"); + + // All sprites + foreach (var sprite in sprites) + { + if (sprite == null || !sprite.Export) + { + continue; + } + + if (sprite.Frames == 0) + { + continue; + } + + // Header + if (sprite.Frames == 2) + { + sb.AppendLine( + $"DIM {exportConfig.LabelName}{sprite.Name.Replace(" ", "_")}(63) AS UByte => {{ _"); + } + else + { + sb.AppendLine( + $"DIM {exportConfig.LabelName}{sprite.Name.Replace(" ", "_")}({(sprite.Frames/2)-1},63) AS UByte => {{ _"); + } + var rawData = Export_Sprite_MaskedSprites_GenerateData(exportConfig, sprite); + int i = 0; + for (int idFrame = 0; idFrame < sprite.Frames; idFrame += 2) + { + if (sprite.Frames != 2) + { + sb.AppendLine("\t{ _"); + } + for (int n = 0; n < 63; n += 4) + { + sb.Append("\t"); + if (sprite.Frames != 2) + { + sb.Append("\t"); + } + for (int m = 0; m < 4; m++) + { + var b = Convert.ToString(rawData[i++], 2).PadLeft(8, '0'); + sb.Append($"%{b}"); + if (m < 3) + { + sb.Append(","); + } + } + if (n >= 59) + { + sb.AppendLine(" _"); + } + else + { + sb.AppendLine(", _"); + } + } + if (sprite.Frames != 2) + { + if (idFrame >= sprite.Frames - 2) + { + sb.AppendLine("\t} _"); + } + else + { + sb.AppendLine("\t}, _"); + } + } + } + sb.AppendLine("}"); + sb.AppendLine(""); + } + + return sb.ToString(); + } + + #endregion + + + #region ASM + + public static string Export_Sprite_MaskedSprites_ASM(ExportConfig exportConfig, IEnumerable sprites) + { + var sb = new StringBuilder(); + sb.AppendLine("'- Sprite definitions --------------------------------------"); + + // All sprites + foreach (var sprite in sprites) + { + if (sprite == null || !sprite.Export) + { + continue; + } + + if (sprite.Frames == 0) + { + continue; + } + + // Header + sb.AppendLine(string.Format( + "{0}{1}:", + exportConfig.LabelName, + sprite.Name.Replace(" ", "_"))); + sb.AppendLine("ASM"); + + var rawData = Export_Sprite_MaskedSprites_GenerateData(exportConfig, sprite); + int i = 0; + for (int idFrame = 0; idFrame < sprite.Frames; idFrame += 2) + { + if (idFrame > 0) + { + sb.AppendLine(""); + } + for (int n = 0; n < 63; n += 4) + { + sb.Append("\tDB "); + for (int m = 0; m < 4; m++) + { + var b = Convert.ToString(rawData[i++], 2).PadLeft(8, '0'); + sb.Append($"%{b}"); + if (m < 3) + { + sb.Append(","); + } + } + sb.AppendLine(""); + } + } + sb.AppendLine("END ASM"); + sb.AppendLine(""); + } + + return sb.ToString(); + } + + #endregion + + + #region TAP and BIN + + + public static string Export_Sprite_MaskedSprites_BIN(ExportConfig exportConfig, IEnumerable sprites) + { + var binData = new List(); + foreach (var sprite in sprites) + { + if (sprite == null || !sprite.Export) + { + continue; + } + if (sprite.Frames == 0) + { + continue; + } + var data = Export_Sprite_MaskedSprites_GenerateData(exportConfig, sprite); + binData.AddRange(data); + } + ServiceLayer.Files_SaveFileData(exportConfig.ExportFilePath, binData.ToArray()); + return ""; + } + + + public static string Export_Sprite_MaskedSprites_TAP(ExportConfig exportConfig, IEnumerable sprites) + { + var binData = new List(); + foreach (var sprite in sprites) + { + if (sprite == null || !sprite.Export) + { + continue; + } + if (sprite.Frames == 0) + { + continue; + } + var data = Export_Sprite_MaskedSprites_GenerateData(exportConfig, sprite); + binData.AddRange(data); + } + var tapData = ServiceLayer.Bin2Tap(exportConfig.ZXFileName, exportConfig.ZXAddress, binData.ToArray()); + ServiceLayer.Files_SaveFileData(exportConfig.ExportFilePath, tapData); + return ""; + } + + + #endregion + + #endregion + + #endregion } } diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs index 2694d88..da4a606 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ServiceLayer.cs @@ -241,7 +241,7 @@ public static byte[] Files_CreateBinData_GDUorFont(FileTypeConfig fileType, IEnu /// - /// Creates the binary data for a file of type sprite or tile de arriba hacia abajo + /// Creates the binary data for a file of type sprite or tile from up to down /// /// File information /// Sprite or tile data diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs b/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs index b2d9ca0..b08d00b 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/neg/ExportTypes.cs @@ -22,6 +22,7 @@ public enum ExportTypes // Sprites PutChars, + MaskedSprites, GUSprite, FourSprites } diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index e2235c5..7abf409 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -13,7 +13,7 @@ ZX Basic Studio False - 1.8.0.2 + 1.8.0.3 diff --git a/ZXBStudio/version.txt b/ZXBStudio/version.txt index f43c0d5..782251b 100644 --- a/ZXBStudio/version.txt +++ b/ZXBStudio/version.txt @@ -1 +1 @@ -1.8.0.2 \ No newline at end of file +1.8.0.3 \ No newline at end of file From 4ea32278ea821836ba55da50943faf90609f8b2b Mon Sep 17 00:00:00 2001 From: Meta-Agents Date: Sat, 4 Apr 2026 00:15:08 +0200 Subject: [PATCH 2/2] =?UTF-8?q?v1.8.0-beta4=20-=20Sprite=20Editor;=20=20?= =?UTF-8?q?=20-=20Bug=20fixed:=20Keyboard=20shortcuts=20now=20work=20in=20?= =?UTF-8?q?the=20sprite=20editor=20=20=20-=20Export=20to=20MaskedSprites:?= =?UTF-8?q?=20=20=20=20=20-=20Added=20a=20warning=20when=20sprites=20do=20?= =?UTF-8?q?not=20meet=20the=20requirements=20for=20export=20to=20MaskedSpr?= =?UTF-8?q?ites:=20=20=20=20=20=20=20-=2016x16=20=20=20=20=20=20=20-=20Spr?= =?UTF-8?q?ites=20with=20a=20mask=20=20=20=20=20=20=20-=20Even=20frames=20?= =?UTF-8?q?=20=20-=20The=20=E2=80=98Save=E2=80=99=20button=20no=20longer?= =?UTF-8?q?=20closes=20the=20export=20window?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ZXGraphics/SpriteEditor.axaml.cs | 2 + .../ZXGraphics/SpriteExportDialog.axaml.cs | 21 ++++--- .../ZXGraphics/SpritePatternEditor.axaml.cs | 8 +++ .../ZXGraphics/log/ExportManager.cs | 61 ++++++++++++++++++- .../Controls/ZXTextEditor.axaml.cs | 2 + .../Classes/ZXDocumentProvider.cs | 2 +- ZXBStudio/ZXBasicStudio.csproj | 2 +- ZXBStudio/version.txt | 2 +- 8 files changed, 89 insertions(+), 11 deletions(-) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs index 0168356..3d27d3f 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteEditor.axaml.cs @@ -342,6 +342,8 @@ private void _Initialize(string fileName) btnInk.Tapped += BtnInk_Tapped; UpdateColorPanel(); + InitializeShortcuts(); + this.AddHandler(KeyDownEvent, Keyboard_Down, handledEventsToo: true); this.Focus(); grdProcesando.IsVisible = false; diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs index 6dfce7c..0407678 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpriteExportDialog.axaml.cs @@ -1,6 +1,7 @@ using Avalonia; using Avalonia.Controls; using Avalonia.Platform.Storage; +using MsBox.Avalonia; using System; using System.Collections; using System.Collections.Generic; @@ -279,12 +280,21 @@ private void CreateExample_MaskedSprites() txtCode.Text = ""; } + var sprite = sprites.ElementAt(0); + var exportData = ExportManager.Export_Sprite_MaskedSprites(exportConfig, sprites); + if (exportData.StartsWith("ERROR:")) + { + txtError.Text = exportData; + txtError.IsVisible = true; + return; + } + txtError.IsVisible = false; + var sb = new StringBuilder(); switch (exportConfig.ExportDataType) { case ExportDataTypes.DIM: { - var sprite = sprites.ElementAt(0); sb.AppendLine("'- Includes -----------------------------------------------"); sb.AppendLine("#INCLUDE "); sb.AppendLine("#INCLUDE \"maskedsprites.bas\""); @@ -292,7 +302,7 @@ private void CreateExample_MaskedSprites() sb.AppendLine("'- Sprites definition -------------------------------------"); sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"", Path.GetFileName(exportConfig.ExportFilePath))); - sb.AppendLine(ExportManager.Export_Sprite_MaskedSprites(exportConfig, sprites)); + sb.AppendLine(exportData); sb.AppendLine("' - Init vars ---------------------------------------------"); sb.AppendLine("#define NumberofMaskedSprites 1"); sb.AppendLine("DIM sprDir, sprBackDir AS UInteger"); @@ -324,7 +334,6 @@ private void CreateExample_MaskedSprites() case ExportDataTypes.ASM: { - var sprite = sprites.ElementAt(0); sb.AppendLine("'- Includes -----------------------------------------------"); sb.AppendLine("#INCLUDE "); sb.AppendLine("#INCLUDE \"maskedsprites.bas\""); @@ -358,7 +367,7 @@ private void CreateExample_MaskedSprites() sb.AppendLine("' - This section must not be executed --------------------"); sb.AppendLine(string.Format("' Can use: #INCLUDE \"{0}\"", Path.GetFileName(exportConfig.ExportFilePath))); - sb.AppendLine(ExportManager.Export_Sprite_MaskedSprites(exportConfig, sprites)); + sb.AppendLine(exportData); } break; @@ -368,7 +377,6 @@ private void CreateExample_MaskedSprites() sb.AppendLine("#INCLUDE "); sb.AppendLine(""); sb.AppendLine("'- Draw sprite --------------------------------------------"); - var sprite = sprites.ElementAt(0); sb.AppendLine(string.Format( "putChars(10,5,{0},{1},@{2})", sprite.Width / 8, @@ -395,7 +403,6 @@ private void CreateExample_MaskedSprites() sb.AppendLine("LOAD \"\" CODE"); sb.AppendLine(""); sb.AppendLine("'- Draw sprite --------------------------------------------"); - var sprite = sprites.ElementAt(0); sb.AppendLine(string.Format( "putChars(10,5,{0},{1},@{2})", sprite.Width / 8, @@ -431,7 +438,7 @@ private void BtnSave_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) GetConfigFromUI(); ServiceLayer.Export_SetConfigFile(fileName + ".zbs", exportConfig); Export(); - this.Close(); + //this.Close(); } private async void BtnOutputFile_Tapped(object? sender, Avalonia.Input.TappedEventArgs e) diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs index 1d1d99a..1c280e8 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/SpritePatternEditor.axaml.cs @@ -475,6 +475,10 @@ private void SetAttribute(Pattern pattern, int x, int y) private AttributeColor GetAttribute(Pattern pattern, int x, int y) { + if(pattern.Attributes == null) + { + pattern.Attributes = new AttributeColor[(SpriteData.Width / 8) * (SpriteData.Height / 8)]; + } int cW = SpriteData.Width / 8; int cX = x / 8; int cY = y / 8; @@ -1163,6 +1167,10 @@ private void GrdEditor_InvertColorsCell(double mx, double my) var inkBak = PrimaryColorIndex; var paperBak = SecondaryColorIndex; var attr=GetAttribute(SpriteData.Patterns[SpriteData.CurrentFrame], x, y); + if (attr == null) + { + return; + } PrimaryColorIndex = attr.Paper; SecondaryColorIndex = attr.Ink; SetAttribute(SpriteData.Patterns[SpriteData.CurrentFrame], x, y); diff --git a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs index 69700ec..6882bd1 100644 --- a/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs +++ b/ZXBStudio/DocumentEditors/ZXGraphics/log/ExportManager.cs @@ -28,6 +28,7 @@ public class ExportManager : IZXDocumentBuilder public Guid[]? DependsOn => null; private FileTypes fileType = FileTypes.Undefined; + private static TextWriter OutputLog = null; public bool Initialize(FileTypes fileType) { @@ -38,6 +39,7 @@ public bool Initialize(FileTypes fileType) public bool Build(string BuildPath, ZXBuildStage Stage, ZXBuildType BuildType, ZXProgram? program, TextWriter OutputLog) { + ExportManager.OutputLog = OutputLog; if (!ServiceLayer.Initialized) { ServiceLayer.Initialize(); @@ -80,7 +82,10 @@ public bool Build(string BuildPath, ZXBuildStage Stage, ZXBuildType BuildType, Z case FileTypes.Sprite: { var sprites = CreateSprites(fileData); - ExportSprites(exportConfig, sprites); + if(!ExportSprites(exportConfig, sprites)) + { + return false; + } } break; @@ -442,6 +447,10 @@ public bool ExportSprites(ExportConfig exportConfig, IEnumerable sprites break; case ExportTypes.MaskedSprites: exportedData = Export_Sprite_MaskedSprites(exportConfig, sprites); + if(exportedData.StartsWith("ERROR:")) + { + return false; + } createTextFile = true; break; default: @@ -1065,6 +1074,11 @@ private static byte[] Export_Sprite_PutChars_GetBinaryData(IEnumerable s /// string with the conversion commands for the export dialog samble textbox public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnumerable sprites) { + var res= Export_Sprite_MaskedSprites_Check(exportConfig, sprites); + if(res.StartsWith("ERROR:")) + { + return res; + } switch (exportConfig.ExportDataType) { case ExportDataTypes.DIM: @@ -1081,6 +1095,51 @@ public static string Export_Sprite_MaskedSprites(ExportConfig exportConfig, IEnu } + /// + /// Validates a collection of sprites to ensure that each sprite marked for export meets the required criteria + /// for masked sprite export. + /// + /// A sprite is considered valid for masked export if it is masked, has an even number of + /// frames, and its dimensions are exactly 16x16 pixels. Only sprites with the export flag set are validated. + /// The method returns immediately upon finding the first invalid sprite. + /// The export configuration settings that may influence validation requirements for the sprites. + /// An enumerable collection of sprites to be validated for export readiness. Each sprite is checked for + /// masking, frame count, and dimensions if it is marked for export. + /// A string indicating the result of the validation. Returns "OK" if all sprites are valid for export; + /// otherwise, returns an error message describing the first encountered issue. + private static string Export_Sprite_MaskedSprites_Check(ExportConfig exportConfig, IEnumerable sprites) + { + string txt = ""; + foreach(var sprite in sprites) + { + if (sprite != null && sprite.Export) + { + if (!sprite.Masked) + { + txt=$"ERROR: Sprite {sprite.Name} is not masked."; + } + if (sprite.Frames % 2 != 0) + { + txt=$"ERROR: Sprite {sprite.Name} has an odd number of frames."; + } + if(sprite.Width!=16 || sprite.Height != 16) + { + txt=$"ERROR: Sprite {sprite.Name} has a size different than 16x16."; + } + if (!string.IsNullOrEmpty(txt)) + { + if(OutputLog != null) + { + OutputLog.WriteLine(txt); + } + return txt; + } + } + } + return "OK"; + } + + private static byte[] Export_Sprite_MaskedSprites_GenerateData(ExportConfig exportConfig, Sprite sprite) { try diff --git a/ZXBStudio/DocumentEditors/ZXTextEditor/Controls/ZXTextEditor.axaml.cs b/ZXBStudio/DocumentEditors/ZXTextEditor/Controls/ZXTextEditor.axaml.cs index bc08cbb..d2e56a6 100644 --- a/ZXBStudio/DocumentEditors/ZXTextEditor/Controls/ZXTextEditor.axaml.cs +++ b/ZXBStudio/DocumentEditors/ZXTextEditor/Controls/ZXTextEditor.axaml.cs @@ -421,7 +421,9 @@ private void TextArea_Down(object? sender, Avalonia.Input.KeyEventArgs e) var commandId = ZXKeybMapper.GetCommandId(_docTypeId, e.Key, e.KeyModifiers); if (commandId != null && _keybCommands.ContainsKey(commandId.Value)) + { _keybCommands[commandId.Value](); + } } #endregion diff --git a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs index 1cd1a53..872318b 100644 --- a/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs +++ b/ZXBStudio/DocumentModel/Classes/ZXDocumentProvider.cs @@ -1,4 +1,4 @@ -using Avalonia.Platform.Storage; + using Avalonia.Platform.Storage; using System; using System.Collections.Generic; using System.IO; diff --git a/ZXBStudio/ZXBasicStudio.csproj b/ZXBStudio/ZXBasicStudio.csproj index 7abf409..9cada25 100644 --- a/ZXBStudio/ZXBasicStudio.csproj +++ b/ZXBStudio/ZXBasicStudio.csproj @@ -13,7 +13,7 @@ ZX Basic Studio False - 1.8.0.3 + 1.8.0.4 diff --git a/ZXBStudio/version.txt b/ZXBStudio/version.txt index 782251b..72e7a6e 100644 --- a/ZXBStudio/version.txt +++ b/ZXBStudio/version.txt @@ -1 +1 @@ -1.8.0.3 \ No newline at end of file +1.8.0.4 \ No newline at end of file