Add full support for Gem color icons.

This commit is contained in:
2018-03-01 16:33:04 +00:00
parent e4a20f73af
commit d7b2df59b1
14 changed files with 875 additions and 230 deletions

View File

@@ -154,11 +154,12 @@ namespace exeinfo
{
recognized = true;
Console.Write(mzExe.Information);
if(((MZ)mzExe).resourceStream != null || ((MZ)mzExe).ResourceHeader.rsh_vrsn != 0 &&
if(((MZ)mzExe).ResourceStream != null || ((MZ)mzExe).ResourceHeader.rsh_vrsn != 0 &&
((MZ)mzExe).ResourceHeader.rsh_vrsn != 1 &&
((MZ)mzExe).ResourceHeader.rsh_vrsn != 4 &&
((MZ)mzExe).ResourceHeader.rsh_vrsn != 5)
PrintGemResources(((MZ)mzExe).ResourceHeader, ((MZ)mzExe).ResourceObjectRoots);
PrintGemResources(((MZ)mzExe).ResourceHeader, ((MZ)mzExe).ResourceObjectRoots,
((MZ)mzExe).ResourceExtension, ((MZ)mzExe).GemColorIcons);
if(mzExe.Strings != null && mzExe.Strings.Any())
{
@@ -171,11 +172,12 @@ namespace exeinfo
{
recognized = true;
Console.Write(stExe.Information);
if(((AtariST)stExe).resourceStream != null || ((AtariST)stExe).ResourceHeader.rsh_vrsn != 0 &&
if(((AtariST)stExe).ResourceStream != null || ((AtariST)stExe).ResourceHeader.rsh_vrsn != 0 &&
((AtariST)stExe).ResourceHeader.rsh_vrsn != 1 &&
((AtariST)stExe).ResourceHeader.rsh_vrsn != 4 &&
((AtariST)stExe).ResourceHeader.rsh_vrsn != 5)
PrintGemResources(((AtariST)stExe).ResourceHeader, ((AtariST)stExe).ResourceObjectRoots);
PrintGemResources(((AtariST)stExe).ResourceHeader, ((AtariST)stExe).ResourceObjectRoots,
((AtariST)stExe).ResourceExtension, ((AtariST)stExe).GemColorIcons);
if(stExe.Strings != null && stExe.Strings.Any())
{
@@ -199,7 +201,9 @@ namespace exeinfo
if(!recognized) Console.WriteLine("Executable format not recognized");
}
static void PrintGemResources(GEM.GemResourceHeader resourceHeader, IReadOnlyList<GEM.TreeObjectNode> roots)
static void PrintGemResources(GEM.GemResourceHeader resourceHeader, IReadOnlyList<GEM.TreeObjectNode> roots,
GEM.GemResourceExtension resourceExtension,
GEM.ColorIcon[] colorIcons)
{
Console.WriteLine("\t\tGEM Resources:");
Console.WriteLine("\t\t\t{0} OBJECTs start at {1}", resourceHeader.rsh_nobs, resourceHeader.rsh_object);
@@ -215,17 +219,24 @@ namespace exeinfo
Console.WriteLine("\t\t\tString data starts at {0}", resourceHeader.rsh_string);
Console.WriteLine("\t\t\tImage data starts at {0}", resourceHeader.rsh_imdata);
Console.WriteLine("\t\t\tStandard resource data is {0} bytes", resourceHeader.rsh_rssize);
if(resourceHeader.rsh_vrsn >= 4)
{
Console.WriteLine("\t\t\tColor icon table starts at {0}", resourceExtension.color_ic);
Console.WriteLine("\t\t\tThere are {0}more extensions",
resourceExtension.end_extensions == 0 ? "no " : "");
Console.WriteLine("\t\t\tExtended resource data is {0} bytes", resourceExtension.filesize);
}
if(roots == null || roots.Count <= 0) return;
for(int i = 0; i < roots.Count; i++)
{
Console.WriteLine("\t\t\tObject tree {0}:", i);
PrintGemResourceTree(roots[i], 4);
PrintGemResourceTree(roots[i], 4, colorIcons);
}
}
static void PrintGemResourceTree(GEM.TreeObjectNode node, int level)
static void PrintGemResourceTree(GEM.TreeObjectNode node, int level, GEM.ColorIcon[] colorIcons)
{
for(int i = 0; i < level; i++) Console.Write("\t");
@@ -235,7 +246,7 @@ namespace exeinfo
{
case GEM.ObjectTypes.G_BOX:
case GEM.ObjectTypes.G_IBOX:
Console.WriteLine("{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode, coordinates ({8},{9}) size {10}x{11}",
Console.WriteLine("{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode," + " coordinates ({8},{9}) size {10}x{11}",
node.type, node.flags, node.state,
(GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12),
(GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8),
@@ -262,9 +273,9 @@ namespace exeinfo
})[0];
Console.WriteLine(
"{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode, {8}, '{9}' character, coordinates ({10},{11}) size {12}x{13}",
node.type, node.flags, node.state,
(GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12),
"{0} ({1} {2}) {3} border, {4} text, {5} interior, {6} fill, {7} mode, {8}," +
" '{9}' character, coordinates ({10},{11}) size {12}x{13}", node.type, node.flags,
node.state, (GEM.ObjectColors)((node.data & 0xFFFF & GEM.BorderColorMask) >> 12),
(GEM.ObjectColors)((node.data & 0xFFFF & GEM.TextColorMask) >> 8),
(GEM.ObjectColors)((node.data & 0xFFFF & GEM.InsideColorMask) >> 8),
(GEM.ObjectFillPattern)((node.data & 0xFFFF & GEM.FillPatternMask) >> 4),
@@ -292,7 +303,7 @@ namespace exeinfo
else
thickStr = "no thickness";
Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, font {7}, {8}-justified, {9}," + " {10} border, {11} text, {12} interior, {13} fill, {14} mode, text: \"{15}\"," + " validation: \"{16}\", template: \"{17}\"",
Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, font {7}, {8}-justified," + " {9}, {10} border, {11} text, {12} interior, {13} fill, {14} mode," + " text: \"{15}\", validation: \"{16}\", template: \"{17}\"",
node.type, node.flags, node.state, node.x, node.y, node.width, node.height,
node.TedInfo.Font, node.TedInfo.Justification, thickStr, node.TedInfo.BorderColor,
node.TedInfo.TextColor, node.TedInfo.InsideColor, node.TedInfo.Fill,
@@ -323,15 +334,42 @@ namespace exeinfo
node.IconBlock.TextY, node.IconBlock.TextWidth, node.IconBlock.TextHeight);
break;
case GEM.ObjectTypes.G_CICON:
if(colorIcons == null || colorIcons.Length < node.data ||
colorIcons[node.data] == null ||
colorIcons[node.data].Monochrome == null)
{
Console.WriteLine("{0} ({1} {2}) with index {3} NOT FOUND", node.type, node.flags, node.state,
node.data);
break;
}
Console.WriteLine(
"{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, {7} foreground," +
" {8} background, char '{9}' at ({10},{11}), {12} bytes data, text \"{13}\" at" +
" ({14},{15}) within a box {16}x{17} pixels, with {18} different planes",
node.type, node.flags, node.state, colorIcons[node.data].Monochrome.X,
colorIcons[node.data].Monochrome.Y, colorIcons[node.data].Monochrome.Width,
colorIcons[node.data].Monochrome.Height,
colorIcons[node.data].Monochrome.ForegroundColor,
colorIcons[node.data].Monochrome.BackgroundColor,
colorIcons[node.data].Monochrome.Character,
colorIcons[node.data].Monochrome.CharX, colorIcons[node.data].Monochrome.CharY,
colorIcons[node.data].Monochrome.Data?.Length,
colorIcons[node.data].Monochrome.Text, colorIcons[node.data].Monochrome.TextX,
colorIcons[node.data].Monochrome.TextY,
colorIcons[node.data].Monochrome.TextWidth,
colorIcons[node.data].Monochrome.TextHeight, colorIcons[node.data].Color.Length);
break;
default:
Console.WriteLine("{0} ({1} {2}) data = {3}, coordinates ({4},{5}) size {6}x{7}", node.type,
node.flags, node.state, node.data, node.x, node.y, node.width, node.height);
break;
}
if(node.child != null) PrintGemResourceTree(node.child, level + 1);
if(node.child != null) PrintGemResourceTree(node.child, level + 1, colorIcons);
if(node.sibling != null) PrintGemResourceTree(node.sibling, level);
if(node.sibling != null) PrintGemResourceTree(node.sibling, level, colorIcons);
}
}
}

View File

@@ -0,0 +1,19 @@
using Eto.Drawing;
namespace exeinfogui.GEM
{
public static class GemColorIcon
{
public static Bitmap GemColorIconToEto(libexeinfo.GEM.ColorIconPlane icon, int width, int height, bool selected)
{
if(selected && icon.SelectedData == null) return null;
byte[] data = selected ? icon.SelectedData : icon.Data;
byte[] mask = selected ? icon.SelectedMask : icon.Mask;
int[] pixels = libexeinfo.GEM.PlaneToRaster(data, mask, width, height, icon.Planes);
return new Bitmap(width, height, PixelFormat.Format32bppRgba, pixels);
}
}
}

28
exeinfogui/GEM/GemIcon.cs Normal file
View File

@@ -0,0 +1,28 @@
using System.Collections.Generic;
using Eto.Drawing;
namespace exeinfogui.GEM
{
public static class GemIcon
{
public static Bitmap GemIconToEto(libexeinfo.GEM.Icon icon)
{
const uint COLOR = 0x00000000;
const uint BACKGROUND = 0x00FFFFFF;
const uint ALPHAMASK = 0xFF000000;
List<int> pixels = new List<int>();
byte[] data = libexeinfo.GEM.FlipPlane(icon.Data, icon.Width);
byte[] mask = libexeinfo.GEM.FlipPlane(icon.Mask, icon.Width);
for(int pos = 0; pos < data.Length; pos++)
{
for(int i = 0; i < 8; i++)
pixels.Add((int)(((data[pos] & (1 << i)) != 0 ? COLOR : BACKGROUND) +
((mask[pos] & (1 << i)) != 0 ? ALPHAMASK : 0)));
}
return new Bitmap(icon.Width, icon.Height, PixelFormat.Format32bppRgba, pixels);
}
}
}

View File

@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="UTF-8"?>
<Panel xmlns="http://schema.picoe.ca/eto.forms" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Width="500" Height="500">
<StackLayout Orientation="Vertical" HorizontalContentAlignment="Stretch" VerticalContentAlignment="Top">
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblFlags">Flags</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtFlags" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblState">State</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtState" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblCoordinates">Coordinates</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtCoordinates" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblSize">Size</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtSize" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblCharacter">Character</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtCharater" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblCharCoordinates">Character coordinates</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtCharCoordinates" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblFgColor">Character foreground color</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtFgColor" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblBgColor">Character background color</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtBgColor" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblTextCoordinates">Text coordinates</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtTextCoordinates" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblTextBoxSize">Text box size</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtTextBoxSize" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayout Orientation="Horizontal">
<StackLayoutItem>
<Label ID="lblText">Text</Label>
</StackLayoutItem>
<StackLayoutItem Expand="True" HorizontalAlignment="Stretch">
<TextBox ID="txtText" ReadOnly="True"/>
</StackLayoutItem>
</StackLayout>
<StackLayoutItem HorizontalAlignment="Center">
<Label ID="lblMonochrome">Monochrome icon</Label>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center">
<ImageView ID="imgIcon"/>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center">
<Label ID="lblColor">Color icons</Label>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Expand="True">
<StackLayout Orientation="Horizontal">
<StackLayoutItem HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Expand="True">
<GridView ID="treePlanes"/>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center" VerticalAlignment="Top" Expand="True">
<StackLayout Orientation="Vertical">
<StackLayoutItem HorizontalAlignment="Center">
<Label ID="lblColorIcon">Normal</Label>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center">
<ImageView ID="imgColorIcon"/>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center">
<Label ID="lblSelectedIcon">Selected</Label>
</StackLayoutItem>
<StackLayoutItem HorizontalAlignment="Center">
<ImageView ID="imgSelectedIcon"/>
</StackLayoutItem>
</StackLayout>
</StackLayoutItem>
</StackLayout>
</StackLayoutItem>
</StackLayout>
</Panel>

View File

@@ -0,0 +1,88 @@
using System;
using Eto.Forms;
using Eto.Serialization.Xaml;
namespace exeinfogui.GEM
{
public class PanelGemColorIcon : Panel
{
int iconHeight;
int iconWidth;
ImageView imgColorIcon;
ImageView imgIcon;
ImageView imgSelectedIcon;
GridView treePlanes;
TextBox txtBgColor;
TextBox txtCharater;
TextBox txtCharCoordinates;
TextBox txtCoordinates;
TextBox txtFgColor;
TextBox txtFlags;
TextBox txtSize;
TextBox txtState;
TextBox txtText;
TextBox txtTextBoxSize;
TextBox txtTextCoordinates;
public PanelGemColorIcon()
{
XamlReader.Load(this);
treePlanes.Columns.Add(new GridColumn
{
DataCell = new TextBoxCell
{
Binding = Binding.Property<libexeinfo.GEM.ColorIconPlane, string>(i => $"{i.Planes}")
},
HeaderText = "Planes"
});
treePlanes.AllowMultipleSelection = false;
treePlanes.SelectedItemsChanged += TreePlanesOnSelectedItemsChanged;
}
void TreePlanesOnSelectedItemsChanged(object sender, EventArgs eventArgs)
{
if(!(treePlanes.SelectedItem is libexeinfo.GEM.ColorIconPlane cicon))
{
imgColorIcon.Image = null;
imgSelectedIcon.Image = null;
return;
}
imgColorIcon.Image = GemColorIcon.GemColorIconToEto(cicon, iconWidth, iconHeight, false);
imgSelectedIcon.Image = GemColorIcon.GemColorIconToEto(cicon, iconWidth, iconHeight, true);
}
public void Update(libexeinfo.GEM.TreeObjectNode node, libexeinfo.GEM.ColorIcon colorIcon)
{
txtFlags.Text = node.flags == 0 ? "None" : node.flags.ToString();
txtState.Text = node.state == 0 ? "Normal" : node.state.ToString();
txtCoordinates.Text = $"{colorIcon.Monochrome.X},{colorIcon.Monochrome.Y}";
txtSize.Text = $"{colorIcon.Monochrome.Width}x{colorIcon.Monochrome.Height} pixels";
txtCharater.Text = $"{colorIcon.Monochrome.Character}";
txtCharCoordinates.Text = $"{colorIcon.Monochrome.CharX},{colorIcon.Monochrome.CharY}";
txtFgColor.Text = $"{colorIcon.Monochrome.ForegroundColor}";
txtBgColor.Text = $"{colorIcon.Monochrome.BackgroundColor}";
txtTextCoordinates.Text = $"{colorIcon.Monochrome.TextX},{colorIcon.Monochrome.TextY}";
txtTextBoxSize.Text = $"{colorIcon.Monochrome.TextWidth}x{colorIcon.Monochrome.TextHeight} pixels";
txtText.Text = colorIcon.Monochrome.Text;
imgIcon.Image = GemIcon.GemIconToEto(colorIcon.Monochrome);
treePlanes.DataStore = colorIcon.Color;
iconWidth = colorIcon.Monochrome.Width;
iconHeight = colorIcon.Monochrome.Height;
treePlanes.SelectRow(0);
if(colorIcon.Color != null && colorIcon.Color.Length >= 1 && colorIcon.Color[0] != null)
{
imgColorIcon.Image =
GemColorIcon.GemColorIconToEto(colorIcon.Color[0], iconWidth, iconHeight, false);
imgSelectedIcon.Image = GemColorIcon.GemColorIconToEto(colorIcon.Color[0], iconWidth, iconHeight, true);
}
else
{
imgColorIcon.Image = null;
imgSelectedIcon.Image = null;
}
}
}
}

View File

@@ -1,6 +1,4 @@
using System.Collections.Generic;
using Eto.Drawing;
using Eto.Forms;
using Eto.Forms;
using Eto.Serialization.Xaml;
namespace exeinfogui.GEM
@@ -38,67 +36,7 @@ namespace exeinfogui.GEM
txtTextCoordinates.Text = $"{node.IconBlock.TextX},{node.IconBlock.TextY}";
txtTextBoxSize.Text = $"{node.IconBlock.TextWidth}x{node.IconBlock.TextHeight} pixels";
txtText.Text = node.IconBlock.Text;
imgIcon.Image = GemIconToEto(node);
}
static Bitmap GemIconToEto(libexeinfo.GEM.TreeObjectNode node)
{
const uint COLOR = 0x00000000;
const uint BACKGROUND = 0x00FFFFFF;
const uint ALPHAMASK = 0xFF000000;
List<int> pixels = new List<int>();
byte[] data = new byte[node.IconBlock.Data.Length];
int pos = 0;
int w = node.IconBlock.Width / 8;
// This flips the image.
while(pos < data.Length)
{
for(int i = 0; i < w; i++)
{
byte b = node.IconBlock.Data[pos + i];
data[pos + i] = (byte)(b >> 7);
data[pos + i] += (byte)((b >> 5) & 0x02);
data[pos + i] += (byte)((b >> 3) & 0x04);
data[pos + i] += (byte)((b >> 1) & 0x08);
data[pos + i] += (byte)((b << 1) & 0x10);
data[pos + i] += (byte)((b << 3) & 0x20);
data[pos + i] += (byte)((b << 5) & 0x40);
data[pos + i] += (byte)(b << 7);
}
pos += w;
}
byte[] mask = new byte[node.IconBlock.Mask.Length];
pos = 0;
// This flips the mask.
while(pos < data.Length)
{
for(int i = 0; i < w; i++)
{
byte b = node.IconBlock.Mask[pos + i];
mask[pos + i] = (byte)(b >> 7);
mask[pos + i] += (byte)((b >> 5) & 0x02);
mask[pos + i] += (byte)((b >> 3) & 0x04);
mask[pos + i] += (byte)((b >> 1) & 0x08);
mask[pos + i] += (byte)((b << 1) & 0x10);
mask[pos + i] += (byte)((b << 3) & 0x20);
mask[pos + i] += (byte)((b << 5) & 0x40);
mask[pos + i] += (byte)(b << 7);
}
pos += w;
}
for(pos = 0; pos < data.Length; pos++)
{
for(int i = 0; i < 8; i++)
pixels.Add((int)(((data[pos] & (1 << i)) != 0 ? COLOR : BACKGROUND) +
((mask[pos] & (1 << i)) != 0 ? ALPHAMASK : 0)));
}
return new Bitmap(node.IconBlock.Width, node.IconBlock.Height, PixelFormat.Format32bppRgba, pixels);
imgIcon.Image = GemIcon.GemIconToEto(node.IconBlock);
}
}
}

View File

@@ -6,7 +6,9 @@ namespace exeinfogui.GEM
{
public class TabGemResources : TabPage
{
libexeinfo.GEM.ColorIcon[] colorIcons;
PanelGemBox panelBox;
PanelGemColorIcon panelColorIcon;
PanelGemGeneric panelGeneric;
PanelGemIcon panelIcon;
PanelGemImage panelImage;
@@ -31,9 +33,10 @@ namespace exeinfogui.GEM
panelBox = new PanelGemBox();
panelImage = new PanelGemImage();
panelIcon = new PanelGemIcon();
panelColorIcon = new PanelGemColorIcon();
}
public void Update(libexeinfo.GEM.TreeObjectNode[] roots)
public void Update(libexeinfo.GEM.TreeObjectNode[] roots, libexeinfo.GEM.ColorIcon[] cicons)
{
treeData = new TreeGridItemCollection();
@@ -47,6 +50,7 @@ namespace exeinfogui.GEM
}
treeResources.DataStore = treeData;
colorIcons = cicons;
}
void TreeResourcesOnSelectionChanged(object sender, EventArgs eventArgs)
@@ -86,8 +90,14 @@ namespace exeinfogui.GEM
panelIcon.Update(node);
pnlResource.Content = panelIcon;
break;
/* case libexeinfo.GEM.ObjectTypes.G_USERDEF: break;
case libexeinfo.GEM.ObjectTypes.G_CICON: break;*/
/* case libexeinfo.GEM.ObjectTypes.G_USERDEF: break;*/
case libexeinfo.GEM.ObjectTypes.G_CICON:
if(colorIcons == null || node.data >= colorIcons.Length || colorIcons[node.data] == null)
goto default;
panelColorIcon.Update(node, colorIcons[node.data]);
pnlResource.Content = panelColorIcon;
break;
default:
panelGeneric.Update(node);
pnlResource.Content = panelGeneric;

View File

@@ -86,7 +86,7 @@ namespace exeinfogui
recognizedExe = mzExe;
if(((MZ)mzExe).ResourceObjectRoots != null && ((MZ)mzExe).ResourceObjectRoots.Any())
{
tabGemResources.Update(((MZ)mzExe).ResourceObjectRoots);
tabGemResources.Update(((MZ)mzExe).ResourceObjectRoots, ((MZ)mzExe).GemColorIcons);
tabGemResources.Visible = true;
}
}
@@ -101,7 +101,7 @@ namespace exeinfogui
recognizedExe = stExe;
if(((AtariST)stExe).ResourceObjectRoots != null && ((AtariST)stExe).ResourceObjectRoots.Any())
{
tabGemResources.Update(((AtariST)stExe).ResourceObjectRoots);
tabGemResources.Update(((AtariST)stExe).ResourceObjectRoots, ((AtariST)stExe).GemColorIcons);
tabGemResources.Visible = true;
}
}

View File

@@ -38,9 +38,11 @@ namespace libexeinfo
/// </summary>
public partial class AtariST : IExecutable
{
public GEM.ColorIcon[] GemColorIcons;
public GEM.GemResourceExtension ResourceExtension;
public GEM.GemResourceHeader ResourceHeader;
public GEM.TreeObjectNode[] ResourceObjectRoots;
public Stream resourceStream;
public Stream ResourceStream;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.AtariST" /> class.
@@ -72,7 +74,7 @@ namespace libexeinfo
resourceFilePath = testPath + ".RSC";
if(resourceFilePath != null)
resourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
ResourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Initialize();
}
@@ -126,27 +128,38 @@ namespace libexeinfo
Type = "Atari ST executable";
if(resourceStream == null) return;
if(ResourceStream == null) return;
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceHeader))];
resourceStream.Position = 0;
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Position = 0;
ResourceStream.Read(buffer, 0, buffer.Length);
ResourceHeader = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.GemResourceHeader>(buffer);
if(ResourceHeader.rsh_vrsn != 0 && ResourceHeader.rsh_vrsn != 1 && ResourceHeader.rsh_vrsn != 4 &&
ResourceHeader.rsh_vrsn != 5) return;
if((ResourceHeader.rsh_vrsn & 4) == 4)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceExtension))];
ResourceStream.Position = ResourceHeader.rsh_rssize;
ResourceStream.Read(buffer, 0, buffer.Length);
ResourceExtension = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.GemResourceExtension>(buffer);
GemColorIcons = GEM.GetColorIcons(ResourceStream, ResourceExtension.color_ic, true,
Encoding.AtariSTEncoding);
}
List<string> strings = new List<string>();
if(ResourceHeader.rsh_ntree > 0)
{
resourceStream.Position = ResourceHeader.rsh_trindex;
ResourceStream.Position = ResourceHeader.rsh_trindex;
int[] treeOffsets = new int[ResourceHeader.rsh_ntree];
byte[] tmp = new byte[4];
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
resourceStream.Read(tmp, 0, 4);
ResourceStream.Read(tmp, 0, 4);
treeOffsets[i] = BitConverter.ToInt32(tmp.Reverse().ToArray(), 0);
}
@@ -154,15 +167,15 @@ namespace libexeinfo
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
if(treeOffsets[i] <= 0 || treeOffsets[i] >= resourceStream.Length) continue;
if(treeOffsets[i] <= 0 || treeOffsets[i] >= ResourceStream.Length) continue;
resourceStream.Position = treeOffsets[i];
ResourceStream.Position = treeOffsets[i];
List<GEM.ObjectNode> nodes = new List<GEM.ObjectNode>();
while(true)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Read(buffer, 0, buffer.Length);
GEM.ObjectNode node = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.ObjectNode>(buffer);
nodes.Add(node);
if(((GEM.ObjectFlags)node.ob_flags).HasFlag(GEM.ObjectFlags.Lastob)) break;
@@ -170,7 +183,7 @@ namespace libexeinfo
List<short> knownNodes = new List<short>();
ResourceObjectRoots[i] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, true,
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, ResourceStream, strings, true,
Encoding.AtariSTEncoding);
}
}
@@ -178,18 +191,18 @@ namespace libexeinfo
{
GEM.ObjectNode[] nodes = new GEM.ObjectNode[ResourceHeader.rsh_nobs];
resourceStream.Position = ResourceHeader.rsh_object;
ResourceStream.Position = ResourceHeader.rsh_object;
for(short i = 0; i < ResourceHeader.rsh_nobs; i++)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Read(buffer, 0, buffer.Length);
nodes[i] = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.ObjectNode>(buffer);
}
List<short> knownNodes = new List<short>();
ResourceObjectRoots = new GEM.TreeObjectNode[1];
ResourceObjectRoots[0] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, true,
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, ResourceStream, strings, true,
Encoding.AtariSTEncoding);
}

View File

@@ -24,8 +24,10 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -40,7 +42,7 @@ namespace libexeinfo
{
TreeObjectNode node = new TreeObjectNode
{
type = (ObjectTypes)nodes[nodeNumber].ob_type,
type = (ObjectTypes)(nodes[nodeNumber].ob_type & 0xff),
flags = (ObjectFlags)nodes[nodeNumber].ob_flags,
state = (ObjectStates)nodes[nodeNumber].ob_state,
data = nodes[nodeNumber].ob_spec,
@@ -51,7 +53,6 @@ namespace libexeinfo
};
byte[] buffer;
List<byte> chars;
switch(node.type)
{
case ObjectTypes.G_TEXT:
@@ -110,7 +111,6 @@ namespace libexeinfo
}
break;
// TODO: This is indeed a CUT from a bigger image, need to cut it out
case ObjectTypes.G_IMAGE:
if(node.data <= 0 || node.data >= resourceStream.Length) break;
@@ -166,87 +166,14 @@ namespace libexeinfo
resourceStream.Position = node.data;
buffer = new byte[Marshal.SizeOf(typeof(IconBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
IconBlock iconBlock = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<IconBlock>(buffer);
node.IconBlock = new Icon
{
Width = iconBlock.ib_wicon,
Height = iconBlock.ib_hicon,
X = iconBlock.ib_xicon,
Y = iconBlock.ib_yicon,
ForegroundColor = (ObjectColors)((iconBlock.ib_char >> 12) & 0x000F),
BackgroundColor = (ObjectColors)((iconBlock.ib_char >> 8) & 0x000F),
Character = encoding.GetString(new[] {(byte)(iconBlock.ib_char & 0xFF)})[0],
CharX = iconBlock.ib_xchar,
CharY = iconBlock.ib_ychar,
TextX = iconBlock.ib_xtext,
TextY = iconBlock.ib_ytext,
TextWidth = iconBlock.ib_wtext,
TextHeight = iconBlock.ib_htext
};
node.IconBlock = GetIconBlock(resourceStream, buffer, bigEndian, encoding);
if(iconBlock.ib_ptext > 0 && iconBlock.ib_ptext < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_ptext;
chars = new List<byte>();
while(true)
{
int character = resourceStream.ReadByte();
if(character <= 0) break;
chars.Add((byte)character);
}
node.IconBlock.Text = StringHandlers.CToString(chars.ToArray(), encoding);
if(!string.IsNullOrWhiteSpace(node.IconBlock.Text)) strings.Add(node.IconBlock.Text.Trim());
}
if(iconBlock.ib_pdata > 0 && iconBlock.ib_pdata < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_pdata;
node.IconBlock.Data = new byte[node.IconBlock.Width * node.IconBlock.Height / 8];
resourceStream.Read(node.IconBlock.Data, 0, node.IconBlock.Data.Length);
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] data = new byte[node.IconBlock.Data.Length];
for(int i = 0; i < data.Length; i += 2)
{
data[i] = node.IconBlock.Data[i + 1];
data[i + 1] = node.IconBlock.Data[i];
}
node.IconBlock.Data = data;
}
}
if(iconBlock.ib_pmask > 0 && iconBlock.ib_pmask < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_pmask;
node.IconBlock.Mask = new byte[node.IconBlock.Width * node.IconBlock.Height / 8];
resourceStream.Read(node.IconBlock.Mask, 0, node.IconBlock.Mask.Length);
// Because the mask is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] mask = new byte[node.IconBlock.Mask.Length];
for(int i = 0; i < mask.Length; i += 2)
{
mask[i] = node.IconBlock.Mask[i + 1];
mask[i + 1] = node.IconBlock.Mask[i];
}
node.IconBlock.Mask = mask;
}
}
break;
case ObjectTypes.G_CICON:
//Console.WriteLine("ColorIconBlock pointer {0}", node.data);
// Do nothing, it is done separately...
break;
case ObjectTypes.G_BUTTON:
case ObjectTypes.G_STRING:
@@ -254,7 +181,7 @@ namespace libexeinfo
if(node.data <= 0 || node.data >= resourceStream.Length) break;
resourceStream.Position = node.data;
chars = new List<byte>();
List<byte> chars = new List<byte>();
while(true)
{
int character = resourceStream.ReadByte();
@@ -281,5 +208,289 @@ namespace libexeinfo
return node;
}
static Icon GetIconBlock(Stream resourceStream, byte[] buffer, bool bigEndian, Encoding encoding)
{
long oldPosition = resourceStream.Position;
IconBlock iconBlock = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<IconBlock>(buffer);
Icon icon = new Icon
{
Width = iconBlock.ib_wicon,
Height = iconBlock.ib_hicon,
X = iconBlock.ib_xicon,
Y = iconBlock.ib_yicon,
ForegroundColor = (ObjectColors)((iconBlock.ib_char >> 12) & 0x000F),
BackgroundColor = (ObjectColors)((iconBlock.ib_char >> 8) & 0x000F),
Character = encoding.GetString(new[] {(byte)(iconBlock.ib_char & 0xFF)})[0],
CharX = iconBlock.ib_xchar,
CharY = iconBlock.ib_ychar,
TextX = iconBlock.ib_xtext,
TextY = iconBlock.ib_ytext,
TextWidth = iconBlock.ib_wtext,
TextHeight = iconBlock.ib_htext
};
if(iconBlock.ib_ptext > 0 && iconBlock.ib_ptext < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_ptext;
List<byte> chars = new List<byte>();
while(true)
{
int character = resourceStream.ReadByte();
if(character <= 0) break;
chars.Add((byte)character);
}
icon.Text = StringHandlers.CToString(chars.ToArray(), encoding);
}
if(iconBlock.ib_pdata > 0 && iconBlock.ib_pdata < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_pdata;
icon.Data = new byte[icon.Width * icon.Height / 8];
resourceStream.Read(icon.Data, 0, icon.Data.Length);
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] data = new byte[icon.Data.Length];
for(int i = 0; i < data.Length; i += 2)
{
data[i] = icon.Data[i + 1];
data[i + 1] = icon.Data[i];
}
icon.Data = data;
}
}
if(iconBlock.ib_pmask > 0 && iconBlock.ib_pmask < resourceStream.Length)
{
resourceStream.Position = iconBlock.ib_pmask;
icon.Mask = new byte[icon.Width * icon.Height / 8];
resourceStream.Read(icon.Mask, 0, icon.Mask.Length);
// Because the mask is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] mask = new byte[icon.Mask.Length];
for(int i = 0; i < mask.Length; i += 2)
{
mask[i] = icon.Mask[i + 1];
mask[i + 1] = icon.Mask[i];
}
icon.Mask = mask;
}
}
resourceStream.Position = oldPosition;
return icon;
}
public static ColorIcon[] GetColorIcons(Stream resourceStream, int colorIc, bool bigEndian, Encoding encoding)
{
byte[] buffer;
if(colorIc == -1 || colorIc >= resourceStream.Length) return null;
resourceStream.Position = colorIc;
int cicons = 0;
while(true)
{
buffer = new byte[4];
resourceStream.Read(buffer, 0, buffer.Length);
if(BitConverter.ToInt32(buffer, 0) == -1) break;
cicons++;
}
ColorIcon[] colorIcons = new ColorIcon[cicons];
for(int i = 0; i < cicons; i++)
{
buffer = new byte[Marshal.SizeOf(typeof(IconBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
IconBlock iconBlock = BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer);
int isize = iconBlock.ib_wicon * iconBlock.ib_hicon / 8;
buffer = new byte[4];
resourceStream.Position -= 2;
resourceStream.Read(buffer, 0, buffer.Length);
int numRez = BitConverter.ToInt32(buffer.Reverse().ToArray(), 0);
colorIcons[i] = new ColorIcon
{
Color = new ColorIconPlane[numRez],
Monochrome = new Icon
{
Width = iconBlock.ib_wicon,
Height = iconBlock.ib_hicon,
X = iconBlock.ib_xicon,
Y = iconBlock.ib_yicon,
ForegroundColor = (ObjectColors)((iconBlock.ib_char >> 12) & 0x000F),
BackgroundColor = (ObjectColors)((iconBlock.ib_char >> 8) & 0x000F),
Character = encoding.GetString(new[] {(byte)(iconBlock.ib_char & 0xFF)})[0],
CharX = iconBlock.ib_xchar,
CharY = iconBlock.ib_ychar,
TextX = iconBlock.ib_xtext,
TextY = iconBlock.ib_ytext,
TextWidth = iconBlock.ib_wtext,
TextHeight = iconBlock.ib_htext,
Data = new byte[isize],
Mask = new byte[isize]
}
};
resourceStream.Read(colorIcons[i].Monochrome.Data, 0, isize);
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] data = new byte[colorIcons[i].Monochrome.Data.Length];
for(int d = 0; d < data.Length; d += 2)
{
data[d] = colorIcons[d].Monochrome.Data[d + 1];
data[d + 1] = colorIcons[d].Monochrome.Data[d];
}
colorIcons[i].Monochrome.Data = data;
}
resourceStream.Read(colorIcons[i].Monochrome.Mask, 0, isize);
// Because the mask is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
byte[] mask = new byte[colorIcons[i].Monochrome.Mask.Length];
for(int m = 0; m < mask.Length; m += 2)
{
mask[m] = colorIcons[m].Monochrome.Mask[m + 1];
mask[m + 1] = colorIcons[m].Monochrome.Mask[m];
}
colorIcons[i].Monochrome.Mask = mask;
}
if(iconBlock.ib_ptext > 0 && iconBlock.ib_ptext < resourceStream.Length)
{
long oldPosition = resourceStream.Position;
resourceStream.Position = iconBlock.ib_ptext;
List<byte> chars = new List<byte>();
while(true)
{
int character = resourceStream.ReadByte();
if(character <= 0) break;
chars.Add((byte)character);
}
colorIcons[i].Monochrome.Text = StringHandlers.CToString(chars.ToArray(), encoding);
resourceStream.Position = oldPosition + 12;
}
else
{
byte[] ptext = new byte[12];
resourceStream.Read(ptext, 0, 12);
colorIcons[i].Monochrome.Text = StringHandlers.CToString(ptext, encoding);
}
colorIcons[i].Color = new ColorIconPlane[numRez];
for(int r = 0; r < numRez; r++)
{
byte[] data;
byte[] mask;
buffer = new byte[Marshal.SizeOf(typeof(ColorIconBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
ColorIconBlock cib = BigEndianMarshal.ByteArrayToStructureBigEndian<ColorIconBlock>(buffer);
colorIcons[i].Color[r] = new ColorIconPlane
{
Planes = cib.num_planes,
Data = new byte[isize * cib.num_planes],
Mask = new byte[isize]
};
resourceStream.Read(colorIcons[i].Color[r].Data, 0, isize * cib.num_planes);
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
data = new byte[colorIcons[i].Color[r].Data.Length];
for(int d = 0; d < data.Length; d += 2)
{
data[d] = colorIcons[d].Color[r].Data[d + 1];
data[d + 1] = colorIcons[d].Color[r].Data[d];
}
colorIcons[i].Color[r].Data = data;
}
resourceStream.Read(colorIcons[i].Color[r].Mask, 0, isize);
// Because the mask is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
mask = new byte[colorIcons[i].Color[r].Mask.Length];
for(int m = 0; m < mask.Length; m += 2)
{
mask[m] = colorIcons[m].Color[r].Mask[m + 1];
mask[m + 1] = colorIcons[m].Color[r].Mask[m];
}
colorIcons[i].Color[r].Mask = mask;
}
if(cib.sel_data == 0) continue;
colorIcons[i].Color[r].SelectedData = new byte[isize * cib.num_planes];
colorIcons[i].Color[r].SelectedMask = new byte[isize];
resourceStream.Read(colorIcons[i].Color[r].SelectedData, 0, isize * cib.num_planes);
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
if(!bigEndian)
{
data = new byte[colorIcons[i].Color[r].SelectedData.Length];
for(int d = 0; d < data.Length; d += 2)
{
data[d] = colorIcons[d].Color[r].SelectedData[d + 1];
data[d + 1] =
colorIcons[d].Color[r].SelectedData[d];
}
colorIcons[i].Color[r].SelectedData = data;
}
resourceStream.Read(colorIcons[i].Color[r].SelectedMask, 0, isize);
// Because the mask is stored as words, they get reversed on PC GEM (Little-endian)
if(bigEndian) continue;
mask = new byte[colorIcons[i].Color[r].SelectedMask.Length];
for(int m = 0; m < mask.Length; m += 2)
{
mask[m] = colorIcons[m].Color[r].SelectedMask[m + 1];
mask[m + 1] = colorIcons[m].Color[r].SelectedMask[m];
}
colorIcons[i].Color[r].SelectedMask = mask;
}
}
return colorIcons;
}
}
}

View File

@@ -167,6 +167,7 @@ namespace libexeinfo
{
public BitmapBlock BitBlock;
public TreeObjectNode child;
public ColorIcon ColorIcon;
public int data;
public ObjectFlags flags;
public short height;
@@ -228,6 +229,21 @@ namespace libexeinfo
public short Y;
}
public class ColorIconPlane
{
public byte[] Data;
public byte[] Mask;
public short Planes;
public byte[] SelectedData;
public byte[] SelectedMask;
}
public class ColorIcon
{
public ColorIconPlane[] Color;
public Icon Monochrome;
}
/// <summary>
/// The TEDINFO structure lets a user edit formatted text. The object types G_TEXT, G_BOXTEXT, G_FTEXT and G_FBOXTEXT
/// use their <see cref="ObjectNode.ob_spec" /> to point to TEDINFO structures.
@@ -476,7 +492,7 @@ namespace libexeinfo
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ColorIcon
public struct ColorIconBlock
{
/// <summary>
/// Number of planes in the following data
@@ -503,18 +519,5 @@ namespace libexeinfo
/// </summary>
public int next_res;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ColorIconBlock
{
/// <summary>
/// Default monochrome icon
/// </summary>
IconBlock monoblk;
/// <summary>
/// List of color icons for diferent resolutions
/// </summary>
ColorIcon[] mainlist;
}
}
}

159
libexeinfo/GEM/Vdi.cs Normal file
View File

@@ -0,0 +1,159 @@
using System;
namespace libexeinfo
{
public static partial class GEM
{
/// <summary>Palette for monochrome</summary>
public static int[] Palette1 = {0x00FFFFFF, 0x00000000};
/// <summary>Palette for 2 planes (4 colors)</summary>
public static int[] Palette2 = {0x00FFFFFF, 0x00FF0000, 0x0000FF00, 0x00000000};
/// <summary>Palette for 3 planes (8 colors)</summary>
public static int[] Palette3 =
{0x00FFFFFF, 0x00FF0000, 0x0000FF00, 0x00FFFF00, 0x000000FF, 0x00FF00FF, 0x0000FFFF, 0x00000000};
/// <summary>Palette for 4 planes (16 colors)</summary>
public static int[] Palette4 =
{
// Atari TT palette
0x00FFFFFF, 0x00FF0000, 0x0000FF00, 0x00FFFF00, 0x000000FF, 0x00FF00FF, 0x0000FFFF, 0x00AAAAAA, 0x00666666,
0x00FF9999, 0x0099FF99, 0x00FFFF99, 0x009999FF, 0x00FF99FF, 0x0099FFFF, 0x00000000
// Atari ST palette
// 0x00FFFFFF, 0x00FF0000, 0x0000FF00, 0x00FFFF00, 0x000000FF, 0x00FF00FF, 0x0000FFFF, 0x00555555,
// 0x00333333, 0x00FF3333, 0x0033FF33, 0x00FFFF33, 0x003333FF, 0x00FF33FF, 0x0033FFFF, 0x00000000
};
/// <summary>Palette for 8 planes (256 colors), from Atari TT</summary>
public static int[] Palette8 =
{
0x00FFFFFF, 0x00FF0000, 0x0000FF00, 0x00FFFF00, 0x000000FF, 0x00FF00FF, 0x0000FFFF, 0x00AAAAAA, 0x00666666,
0x00FF9999, 0x0099FF99, 0x00FFFF99, 0x009999FF, 0x00FF99FF, 0x0099FFFF, 0x00000000, 0x00FFFFFF, 0x00EEEEEE,
0x00DDDDDD, 0x00CCCCCC, 0x00BBBBBB, 0x00AAAAAA, 0x00999999, 0x00888888, 0x00777777, 0x00666666, 0x00555555,
0x00444444, 0x00333333, 0x00222222, 0x00111111, 0x00000000, 0x00FF0000, 0x00FF0011, 0x00FF0022, 0x00FF0033,
0x00FF0044, 0x00FF0055, 0x00FF0066, 0x00FF0077, 0x00FF0088, 0x00FF0099, 0x00FF00AA, 0x00FF00BB, 0x00FF00CC,
0x00FF00DD, 0x00FF00EE, 0x00FF00FF, 0x00EE00FF, 0x00DD00FF, 0x00CC00FF, 0x00BB00FF, 0x00AA00FF, 0x009900FF,
0x008800FF, 0x007700FF, 0x006600FF, 0x005500FF, 0x004400FF, 0x003300FF, 0x002200FF, 0x001100FF, 0x000000FF,
0x000011FF, 0x000022FF, 0x000033FF, 0x000044FF, 0x000055FF, 0x000066FF, 0x000077FF, 0x000088FF, 0x000099FF,
0x0000AAFF, 0x0000BBFF, 0x0000CCFF, 0x0000DDFF, 0x0000EEFF, 0x0000FFFF, 0x0000FFEE, 0x0000FFDD, 0x0000FFCC,
0x0000FFBB, 0x0000FFAA, 0x0000FF99, 0x0000FF88, 0x0000FF77, 0x0000FF66, 0x0000FF55, 0x0000FF44, 0x0000FF33,
0x0000FF22, 0x0000FF11, 0x0000FF00, 0x0011FF00, 0x0022FF00, 0x0033FF00, 0x0044FF00, 0x0055FF00, 0x0066FF00,
0x0077FF00, 0x0088FF00, 0x0099FF00, 0x00AAFF00, 0x00BBFF00, 0x00CCFF00, 0x00DDFF00, 0x00EEFF00, 0x00FFFF00,
0x00FFEE00, 0x00FFDD00, 0x00FFCC00, 0x00FFBB00, 0x00FFAA00, 0x00FF9900, 0x00FF8800, 0x00FF7700, 0x00FF6600,
0x00FF5500, 0x00FF4400, 0x00FF3300, 0x00FF2200, 0x00FF1100, 0x00BB0000, 0x00BB0011, 0x00BB0022, 0x00BB0033,
0x00BB0044, 0x00BB0055, 0x00BB0066, 0x00BB0077, 0x00BB0088, 0x00BB0099, 0x00BB00AA, 0x00BB00BB, 0x00AA00BB,
0x009900BB, 0x008800BB, 0x007700BB, 0x006600BB, 0x005500BB, 0x004400BB, 0x003300BB, 0x002200BB, 0x001100BB,
0x000000BB, 0x000011BB, 0x000022BB, 0x000033BB, 0x000044BB, 0x000055BB, 0x000066BB, 0x000077BB, 0x000088BB,
0x000099BB, 0x0000AABB, 0x0000BBBB, 0x0000BBAA, 0x0000BB99, 0x0000BB88, 0x0000BB77, 0x0000BB66, 0x0000BB55,
0x0000BB44, 0x0000BB33, 0x0000BB22, 0x0000BB11, 0x0000BB00, 0x0011BB00, 0x0022BB00, 0x0033BB00, 0x0044BB00,
0x0055BB00, 0x0066BB00, 0x0077BB00, 0x0088BB00, 0x0099BB00, 0x00AABB00, 0x00BBBB00, 0x00BBAA00, 0x00BB9900,
0x00BB8800, 0x00BB7700, 0x00BB6600, 0x00BB5500, 0x00BB4400, 0x00BB3300, 0x00BB2200, 0x00BB1100, 0x00770000,
0x00770011, 0x00770022, 0x00770033, 0x00770044, 0x00770055, 0x00770066, 0x00770077, 0x00660077, 0x00550077,
0x00440077, 0x00330077, 0x00220077, 0x00110077, 0x00000077, 0x00001177, 0x00002277, 0x00003377, 0x00004477,
0x00005577, 0x00006677, 0x00007777, 0x00007766, 0x00007755, 0x00007744, 0x00007733, 0x00007722, 0x00007711,
0x00007700, 0x00117700, 0x00227700, 0x00337700, 0x00447700, 0x00557700, 0x00667700, 0x00777700, 0x00776600,
0x00775500, 0x00774400, 0x00773300, 0x00772200, 0x00771100, 0x00440000, 0x00440011, 0x00440022, 0x00440033,
0x00440044, 0x00330044, 0x00220044, 0x00110044, 0x00000044, 0x00001144, 0x00002244, 0x00003344, 0x00004444,
0x00004433, 0x00004422, 0x00004411, 0x00004400, 0x00114400, 0x00224400, 0x00334400, 0x00444400, 0x00443300,
0x00442200, 0x00441100, 0x00FFFFFF, 0x00000000
};
/// <summary>Palette for alpha mask</summary>
public static int[] PaletteMask = {0x00000000, -16777216};
public static byte[] FlipPlane(byte[] data, int width)
{
byte[] flipped = new byte[data.Length];
int pos = 0;
int w = width / 8;
// This flips the image.
while(pos < flipped.Length)
{
for(int i = 0; i < w; i++)
{
byte b = data[pos + i];
flipped[pos + i] = (byte)(b >> 7);
flipped[pos + i] += (byte)((b >> 5) & 0x02);
flipped[pos + i] += (byte)((b >> 3) & 0x04);
flipped[pos + i] += (byte)((b >> 1) & 0x08);
flipped[pos + i] += (byte)((b << 1) & 0x10);
flipped[pos + i] += (byte)((b << 3) & 0x20);
flipped[pos + i] += (byte)((b << 5) & 0x40);
flipped[pos + i] += (byte)(b << 7);
}
pos += w;
}
return flipped;
}
public static int[] PlaneToRaster(byte[] data, byte[] mask, int width, int height, int planes)
{
int[] pixels = PlaneToRaster(data, width, height, planes);
int[] masked = PlaneToRasterIndexed(mask, width, height, 1);
for(int i = 0; i < pixels.Length; i++) pixels[i] += PaletteMask[masked[i]];
return pixels;
}
public static int[] PlaneToRaster(byte[] data, int width, int height, int planes)
{
int[] pixels = PlaneToRasterIndexed(data, width, height, planes);
int[] palette = null;
switch(planes)
{
case 1:
palette = Palette1;
break;
case 2:
palette = Palette2;
break;
case 3:
palette = Palette3;
break;
case 4:
palette = Palette4;
break;
case 8:
palette = Palette8;
break;
// What to do with other pixel formats?
default: return null;
}
for(int i = 0; i < pixels.Length; i++) pixels[i] = palette[pixels[i]];
return pixels;
}
public static int[] PlaneToRasterIndexed(byte[] data, int width, int height, int planes)
{
// No more than 24-bit RGB
if(planes > 24) return null;
int planeSize = width * height / 8;
int[] pixels = new int[width * height];
for(int p = 0; p < planes; p++)
{
int pixNum = 0;
byte[] plane = new byte[planeSize];
Array.Copy(data, p * planeSize, plane, 0, planeSize);
plane = FlipPlane(plane, width);
for(int b = 0; b < planeSize; b++)
{
for(int i = 0; i < 8; i++) pixels[pixNum++] |= ((plane[b] & (1 << i)) >> i) << p;
}
}
return pixels;
}
}
}

View File

@@ -38,13 +38,15 @@ namespace libexeinfo
/// </summary>
public partial class MZ : IExecutable
{
public GEM.ColorIcon[] GemColorIcons;
/// <summary>
/// Header for this executable
/// </summary>
internal MZHeader Header;
public GEM.GemResourceExtension ResourceExtension;
public GEM.GemResourceHeader ResourceHeader;
public GEM.TreeObjectNode[] ResourceObjectRoots;
public Stream resourceStream;
public Stream ResourceStream;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.MZ" /> class.
@@ -76,7 +78,7 @@ namespace libexeinfo
resourceFilePath = testPath + ".RSC";
if(resourceFilePath != null)
resourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
ResourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Initialize();
}
@@ -142,27 +144,37 @@ namespace libexeinfo
Type = "DOS Executable (MZ)";
if(resourceStream == null) return;
if(ResourceStream == null) return;
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceHeader))];
resourceStream.Position = 0;
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Position = 0;
ResourceStream.Read(buffer, 0, buffer.Length);
ResourceHeader = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.GemResourceHeader>(buffer);
if(ResourceHeader.rsh_vrsn != 0 && ResourceHeader.rsh_vrsn != 1 && ResourceHeader.rsh_vrsn != 4 &&
ResourceHeader.rsh_vrsn != 5) return;
if((ResourceHeader.rsh_vrsn & 4) == 4)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceExtension))];
ResourceStream.Position = ResourceHeader.rsh_rssize;
ResourceStream.Read(buffer, 0, buffer.Length);
ResourceExtension = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.GemResourceExtension>(buffer);
GemColorIcons = GEM.GetColorIcons(ResourceStream, ResourceExtension.color_ic, false, encoding);
}
List<string> strings = new List<string>();
if(ResourceHeader.rsh_ntree > 0)
{
resourceStream.Position = ResourceHeader.rsh_trindex;
ResourceStream.Position = ResourceHeader.rsh_trindex;
int[] treeOffsets = new int[ResourceHeader.rsh_ntree];
byte[] tmp = new byte[4];
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
resourceStream.Read(tmp, 0, 4);
ResourceStream.Read(tmp, 0, 4);
treeOffsets[i] = BitConverter.ToInt32(tmp, 0);
}
@@ -170,15 +182,15 @@ namespace libexeinfo
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
if(treeOffsets[i] <= 0 || treeOffsets[i] >= resourceStream.Length) continue;
if(treeOffsets[i] <= 0 || treeOffsets[i] >= ResourceStream.Length) continue;
resourceStream.Position = treeOffsets[i];
ResourceStream.Position = treeOffsets[i];
List<GEM.ObjectNode> nodes = new List<GEM.ObjectNode>();
while(true)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Read(buffer, 0, buffer.Length);
GEM.ObjectNode node = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.ObjectNode>(buffer);
nodes.Add(node);
if(((GEM.ObjectFlags)node.ob_flags).HasFlag(GEM.ObjectFlags.Lastob)) break;
@@ -186,18 +198,18 @@ namespace libexeinfo
List<short> knownNodes = new List<short>();
ResourceObjectRoots[i] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, false, encoding);
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, ResourceStream, strings, false, encoding);
}
}
else if(ResourceHeader.rsh_nobs > 0)
{
GEM.ObjectNode[] nodes = new GEM.ObjectNode[ResourceHeader.rsh_nobs];
resourceStream.Position = ResourceHeader.rsh_object;
ResourceStream.Position = ResourceHeader.rsh_object;
for(short i = 0; i < ResourceHeader.rsh_nobs; i++)
{
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
ResourceStream.Read(buffer, 0, buffer.Length);
nodes[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.ObjectNode>(buffer);
}
@@ -205,7 +217,7 @@ namespace libexeinfo
ResourceObjectRoots = new GEM.TreeObjectNode[1];
// TODO: Correct encoding?
ResourceObjectRoots[0] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, false, encoding);
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, ResourceStream, strings, false, encoding);
}
if(strings.Count > 0)

View File

@@ -53,6 +53,7 @@
<Compile Include="GEM\Enums.cs" />
<Compile Include="GEM\Resources.cs" />
<Compile Include="GEM\Structs.cs" />
<Compile Include="GEM\Vdi.cs" />
<Compile Include="IExecutable.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MZ\Consts.cs" />