From e020969df090f197599a5c3a6c10a579e3613e51 Mon Sep 17 00:00:00 2001 From: feyris-tan <4116042+feyris-tan@users.noreply.github.com> Date: Sat, 26 Jul 2025 23:53:25 +0200 Subject: [PATCH] Added a viewer for the IQ chart files. --- .gitignore | 2 + .../Handlers/IQHandler.cs | 68 ++++++++++ .../IFileTypeHandler.cs | 23 ++++ .../ImageViewer.Designer.cs | 62 +++++++++ GUIs/skyscraper8.AnagramViewer/ImageViewer.cs | 23 ++++ .../ImageViewer.resx | 120 ++++++++++++++++++ GUIs/skyscraper8.AnagramViewer/Program.cs | 77 +++++++++++ .../Properties/launchSettings.json | 8 ++ .../skyscraper8.AnagramViewer.csproj | 15 +++ .../skyscraper8.AnagramViewer.csproj.user | 8 ++ skyscraper8.sln | 7 + skyscraper8/Skyscraper/Drawing/IqChartData.cs | 25 +++- skyscraper8/Skyscraper/IO/StreamExtensions.cs | 21 +++ 13 files changed, 456 insertions(+), 3 deletions(-) create mode 100644 GUIs/skyscraper8.AnagramViewer/Handlers/IQHandler.cs create mode 100644 GUIs/skyscraper8.AnagramViewer/IFileTypeHandler.cs create mode 100644 GUIs/skyscraper8.AnagramViewer/ImageViewer.Designer.cs create mode 100644 GUIs/skyscraper8.AnagramViewer/ImageViewer.cs create mode 100644 GUIs/skyscraper8.AnagramViewer/ImageViewer.resx create mode 100644 GUIs/skyscraper8.AnagramViewer/Program.cs create mode 100644 GUIs/skyscraper8.AnagramViewer/Properties/launchSettings.json create mode 100644 GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj create mode 100644 GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj.user diff --git a/.gitignore b/.gitignore index 2662269..26e55e0 100644 --- a/.gitignore +++ b/.gitignore @@ -114,3 +114,5 @@ imgui.ini /PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/obj/Debug/net8.0 /PrivateDataSpecifiers/skyscraper8.EPGCollectorPort/obj /.vs/skyscraper8/CopilotIndices/17.14.827.52834 +/GUIs/skyscraper8.AnagramViewer/obj +/GUIs/skyscraper8.AnagramViewer/bin/Debug/net8.0-windows diff --git a/GUIs/skyscraper8.AnagramViewer/Handlers/IQHandler.cs b/GUIs/skyscraper8.AnagramViewer/Handlers/IQHandler.cs new file mode 100644 index 0000000..e2fd58e --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/Handlers/IQHandler.cs @@ -0,0 +1,68 @@ +using System; +using System.Collections.Generic; +using System.Drawing.Imaging; +using System.Linq; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using skyscraper5.Skyscraper.IO; +using skyscraper8.Skyscraper.Drawing; + +namespace skyscraper8.AnagramViewer.Handlers +{ + internal class IQHandler : IFileTypeHandler + { + public DataType CheckDataType(Stream input) + { + byte readUInt8 = input.ReadUInt8(); + if (readUInt8 == 0x41) + return DataType.Image; + return DataType.Unknown; + } + + public Image AsImage(Stream input) + { + IqChartData iqChartData = IqChartData.LoadFrom(input); + + Bitmap bitmap = new Bitmap(256, 256,PixelFormat.Format24bppRgb); + BitmapData bitmapData = bitmap.LockBits(new Rectangle(0, 0, 256, 256), ImageLockMode.WriteOnly, PixelFormat.Format24bppRgb); + + byte[] buffer = new byte[(256 * 256) * 3]; + int oPos = 0; + for (int y = 0; y < 256; y++) + { + for (int x = 0; x < 256; x++) + { + byte b = iqChartData.IQ[y][x]; + if (b != 0) + { + buffer[oPos] = b; //R + buffer[oPos + 1] = 0; //G + buffer[oPos + 2] = (byte)(255 - b); //B + + buffer[oPos] = (byte)(~buffer[oPos]); + buffer[oPos + 1] = 0; + buffer[oPos + 2] = (byte)(~buffer[oPos + 2]); + } + else + { + buffer[oPos] = 255; + buffer[oPos + 1] = 255; + buffer[oPos + 2] = 255; + } + + oPos += 3; + } + } + + Marshal.Copy(buffer, 0, bitmapData.Scan0, buffer.Length); + bitmap.UnlockBits(bitmapData); + return bitmap; + } + + public string AsText(Stream input) + { + throw new NotImplementedException(); + } + } +} diff --git a/GUIs/skyscraper8.AnagramViewer/IFileTypeHandler.cs b/GUIs/skyscraper8.AnagramViewer/IFileTypeHandler.cs new file mode 100644 index 0000000..39a4a6e --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/IFileTypeHandler.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace skyscraper8.AnagramViewer +{ + internal interface IFileTypeHandler + { + DataType CheckDataType(Stream input); + Image AsImage(Stream input); + string AsText(Stream input); + + } + + internal enum DataType + { + Unknown, + Image, + Text + } +} diff --git a/GUIs/skyscraper8.AnagramViewer/ImageViewer.Designer.cs b/GUIs/skyscraper8.AnagramViewer/ImageViewer.Designer.cs new file mode 100644 index 0000000..f97bf62 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/ImageViewer.Designer.cs @@ -0,0 +1,62 @@ +namespace skyscraper8.AnagramViewer +{ + partial class ImageViewer + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + pictureBox1 = new PictureBox(); + ((System.ComponentModel.ISupportInitialize)pictureBox1).BeginInit(); + SuspendLayout(); + // + // pictureBox1 + // + pictureBox1.BorderStyle = BorderStyle.Fixed3D; + pictureBox1.Dock = DockStyle.Fill; + pictureBox1.Location = new Point(0, 0); + pictureBox1.Name = "pictureBox1"; + pictureBox1.Size = new Size(800, 450); + pictureBox1.TabIndex = 0; + pictureBox1.TabStop = false; + // + // ImageViewer + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(800, 450); + Controls.Add(pictureBox1); + FormBorderStyle = FormBorderStyle.FixedToolWindow; + Name = "ImageViewer"; + Text = "ImageViewer"; + ((System.ComponentModel.ISupportInitialize)pictureBox1).EndInit(); + ResumeLayout(false); + } + + #endregion + + internal PictureBox pictureBox1; + } +} \ No newline at end of file diff --git a/GUIs/skyscraper8.AnagramViewer/ImageViewer.cs b/GUIs/skyscraper8.AnagramViewer/ImageViewer.cs new file mode 100644 index 0000000..60e8980 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/ImageViewer.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace skyscraper8.AnagramViewer +{ + public partial class ImageViewer : Form + { + public ImageViewer(Image asImage) + { + InitializeComponent(); + pictureBox1.Image = asImage; + this.Width = asImage.Width; + this.Height = asImage.Height; + } + } +} diff --git a/GUIs/skyscraper8.AnagramViewer/ImageViewer.resx b/GUIs/skyscraper8.AnagramViewer/ImageViewer.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/ImageViewer.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/GUIs/skyscraper8.AnagramViewer/Program.cs b/GUIs/skyscraper8.AnagramViewer/Program.cs new file mode 100644 index 0000000..3eb2b32 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/Program.cs @@ -0,0 +1,77 @@ +using System.Reflection; + +namespace skyscraper8.AnagramViewer +{ + internal static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main(string[] args) + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + + if (args.Length == 0) + { + MessageBox.Show("Invalid function call!"); + return; + } + + FileInfo fi = new FileInfo(args[0]); + if (!fi.Exists) + { + MessageBox.Show("Couldn't find {0}!", args[0]); + return; + } + + FileStream fileStream = fi.OpenRead(); + BufferedStream bufferedStream = new BufferedStream(fileStream, 2048); + + IFileTypeHandler usableHandler = null; + DataType determiendDataType = DataType.Unknown; + Type fileHandlerType = typeof(IFileTypeHandler); + Assembly assembly = fileHandlerType.Assembly; + Type[] types = assembly.GetTypes(); + foreach (Type typeCandidate in types) + { + if (!typeCandidate.IsAssignableTo(fileHandlerType)) + continue; + + if (typeCandidate.IsInterface) + continue; + + bufferedStream.Position = 0; + IFileTypeHandler instance = (IFileTypeHandler)Activator.CreateInstance(typeCandidate); + DataType checkDataType = instance.CheckDataType(bufferedStream); + if (checkDataType != DataType.Unknown) + { + usableHandler = instance; + determiendDataType = checkDataType; + bufferedStream.Position = 0; + break; + } + } + + if (usableHandler == null) + { + MessageBox.Show(String.Format("Don't know what type of file {0} is.", fi.FullName)); + return; + } + + switch (determiendDataType) + { + case DataType.Image: + Image asImage = usableHandler.AsImage(bufferedStream); + ImageViewer imageViewer = new ImageViewer(asImage); + Application.Run(imageViewer); + return; + default: + MessageBox.Show(String.Format("Don't know how to handle files of type {0}", determiendDataType.ToString())); + return; + } + } + } +} \ No newline at end of file diff --git a/GUIs/skyscraper8.AnagramViewer/Properties/launchSettings.json b/GUIs/skyscraper8.AnagramViewer/Properties/launchSettings.json new file mode 100644 index 0000000..d9e6455 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "skyscraper8.AnagramViewer": { + "commandName": "Project", + "commandLineArgs": "\"C:\\Users\\Sascha Schiemann\\source\\repos\\skyscraper8\\skyscraper8\\bin\\Debug\\net8.0\\iq.bin\"" + } + } +} \ No newline at end of file diff --git a/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj b/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj new file mode 100644 index 0000000..90879e7 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj @@ -0,0 +1,15 @@ + + + + WinExe + net8.0-windows + enable + true + enable + + + + + + + \ No newline at end of file diff --git a/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj.user b/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj.user new file mode 100644 index 0000000..176f0c4 --- /dev/null +++ b/GUIs/skyscraper8.AnagramViewer/skyscraper8.AnagramViewer.csproj.user @@ -0,0 +1,8 @@ + + + + + Form + + + \ No newline at end of file diff --git a/skyscraper8.sln b/skyscraper8.sln index a3c1471..5980047 100644 --- a/skyscraper8.sln +++ b/skyscraper8.sln @@ -61,6 +61,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.UI.ImGui", "GUI EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.EPGCollectorPort", "PrivateDataSpecifiers\skyscraper8.EPGCollectorPort\skyscraper8.EPGCollectorPort.csproj", "{CF21D250-9804-4191-89F5-95821E3AF39D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "skyscraper8.AnagramViewer", "GUIs\skyscraper8.AnagramViewer\skyscraper8.AnagramViewer.csproj", "{EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -151,6 +153,10 @@ Global {CF21D250-9804-4191-89F5-95821E3AF39D}.Debug|Any CPU.Build.0 = Debug|Any CPU {CF21D250-9804-4191-89F5-95821E3AF39D}.Release|Any CPU.ActiveCfg = Release|Any CPU {CF21D250-9804-4191-89F5-95821E3AF39D}.Release|Any CPU.Build.0 = Release|Any CPU + {EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EBB6B1CF-2597-4962-AA31-2B42B4C28C7D}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -175,6 +181,7 @@ Global {46CACA1C-F9B2-2FE0-2068-716F381325E9} = {E23457C5-3A34-48EE-8107-C91E2C174B2D} {BDBDB7A9-D0A4-9B89-0801-2935B2066551} = {E23457C5-3A34-48EE-8107-C91E2C174B2D} {CF21D250-9804-4191-89F5-95821E3AF39D} = {56729C39-B90E-4DF3-A557-DB93436FB5FF} + {EBB6B1CF-2597-4962-AA31-2B42B4C28C7D} = {E23457C5-3A34-48EE-8107-C91E2C174B2D} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5147EFA3-3D4E-4FDE-8A36-5840E8F1B80E} diff --git a/skyscraper8/Skyscraper/Drawing/IqChartData.cs b/skyscraper8/Skyscraper/Drawing/IqChartData.cs index 3d6995d..0a619e7 100644 --- a/skyscraper8/Skyscraper/Drawing/IqChartData.cs +++ b/skyscraper8/Skyscraper/Drawing/IqChartData.cs @@ -8,7 +8,7 @@ using skyscraper5.Skyscraper.IO; namespace skyscraper8.Skyscraper.Drawing { - internal class IqChartData + public class IqChartData { private IqChartData() { @@ -31,6 +31,25 @@ namespace skyscraper8.Skyscraper.Drawing return child; } + public static IqChartData LoadFrom(Stream indata) + { + if (indata.ReadUInt8() != 0x41) + throw new ArgumentException("That stream isn't IQ Data!", nameof(indata)); + + IqChartData result = null; + while (indata.GetAvailableBytes() >= 4) + { + if (result == null) + result = new IqChartData(); + + int packSize = indata.ReadInt32LE(); + sbyte[] sbytes = indata.ReadSBytes(packSize); + result.PushPacket(sbytes); + } + + return result; + } + public bool IsComplete { get; private set; } @@ -38,8 +57,8 @@ namespace skyscraper8.Skyscraper.Drawing { for (long l = 0; l < buffer.Length; l += 2) { - byte i = (byte)buffer[l]; - byte q = (byte)buffer[l + 1]; + byte i = (byte)(buffer[l] + sbyte.MaxValue); + byte q = (byte)(buffer[l + 1] + sbyte.MaxValue); if (IQ[i][q] != 255) { diff --git a/skyscraper8/Skyscraper/IO/StreamExtensions.cs b/skyscraper8/Skyscraper/IO/StreamExtensions.cs index b091f0a..435c033 100644 --- a/skyscraper8/Skyscraper/IO/StreamExtensions.cs +++ b/skyscraper8/Skyscraper/IO/StreamExtensions.cs @@ -123,6 +123,27 @@ namespace skyscraper5.Skyscraper.IO return buffer; } + [DebuggerStepThrough] + public static sbyte[] ReadSBytes(this Stream stream, long length) + { + if (length > Int32.MaxValue) + throw new ArgumentOutOfRangeException(); + if (length == 0) + return new sbyte[0]; + if (length < 0) + throw new ArgumentOutOfRangeException(); + + int length32 = (int)length; + + sbyte[] buffer = new sbyte[length32]; + for (int i = 0; i < length32; i++) + { + buffer[i] = (sbyte)stream.ReadUInt8(); + } + + return buffer; + } + [DebuggerStepThrough] public static byte ReadUInt8(this Stream stream) {