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)
{