GEM for PC and Atari ST's GEM used same resource format, just changing endianness.

This commit is contained in:
2018-02-27 20:53:03 +00:00
parent 6419fcf8ed
commit 8c322d9928
8 changed files with 906 additions and 756 deletions

View File

@@ -154,6 +154,11 @@ namespace exeinfo
{
recognized = true;
Console.Write(mzExe.Information);
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);
if(mzExe.Strings != null && mzExe.Strings.Any())
{
@@ -170,34 +175,7 @@ namespace exeinfo
((AtariST)stExe).ResourceHeader.rsh_vrsn != 1 &&
((AtariST)stExe).ResourceHeader.rsh_vrsn != 4 &&
((AtariST)stExe).ResourceHeader.rsh_vrsn != 5)
{
Console.WriteLine("\tResources:");
Console.WriteLine("\t\t{0} OBJECTs start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nobs,
((AtariST)stExe).ResourceHeader.rsh_object);
Console.WriteLine("\t\t{0} TEDINFOs start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nted,
((AtariST)stExe).ResourceHeader.rsh_tedinfo);
Console.WriteLine("\t\t{0} ICONBLKs start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nib,
((AtariST)stExe).ResourceHeader.rsh_iconblk);
Console.WriteLine("\t\t{0} BITBLKs start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nbb,
((AtariST)stExe).ResourceHeader.rsh_bitblk);
Console.WriteLine("\t\t{0} object trees start at {1}", ((AtariST)stExe).ResourceHeader.rsh_ntree,
((AtariST)stExe).ResourceHeader.rsh_trindex);
Console.WriteLine("\t\t{0} free strings start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nstring,
((AtariST)stExe).ResourceHeader.rsh_frstr);
Console.WriteLine("\t\t{0} free images start at {1}", ((AtariST)stExe).ResourceHeader.rsh_nimages,
((AtariST)stExe).ResourceHeader.rsh_frimg);
Console.WriteLine("\t\tString data starts at {0}", ((AtariST)stExe).ResourceHeader.rsh_string);
Console.WriteLine("\t\tImage data starts at {0}", ((AtariST)stExe).ResourceHeader.rsh_imdata);
Console.WriteLine("\t\tStandard resource data is {0} bytes",
((AtariST)stExe).ResourceHeader.rsh_rssize);
if(((AtariST)stExe).ResourceObjectRoots != null && ((AtariST)stExe).ResourceObjectRoots.Length > 0)
for(int i = 0; i < ((AtariST)stExe).ResourceObjectRoots.Length; i++)
{
Console.WriteLine("\tObject tree {0}:", i);
PrintAtariResourceTree(((AtariST)stExe).ResourceObjectRoots[i], 2);
}
}
PrintGemResources(((AtariST)stExe).ResourceHeader, ((AtariST)stExe).ResourceObjectRoots);
if(stExe.Strings != null && stExe.Strings.Any())
{
@@ -221,7 +199,33 @@ namespace exeinfo
if(!recognized) Console.WriteLine("Executable format not recognized");
}
static void PrintAtariResourceTree(AtariST.TreeObjectNode node, int level)
static void PrintGemResources(GEM.GemResourceHeader resourceHeader, IReadOnlyList<GEM.TreeObjectNode> roots)
{
Console.WriteLine("\t\tGEM Resources:");
Console.WriteLine("\t\t\t{0} OBJECTs start at {1}", resourceHeader.rsh_nobs, resourceHeader.rsh_object);
Console.WriteLine("\t\t\t{0} TEDINFOs start at {1}", resourceHeader.rsh_nted, resourceHeader.rsh_tedinfo);
Console.WriteLine("\t\t\t{0} ICONBLKs start at {1}", resourceHeader.rsh_nib, resourceHeader.rsh_iconblk);
Console.WriteLine("\t\t\t{0} BITBLKs start at {1}", resourceHeader.rsh_nbb, resourceHeader.rsh_bitblk);
Console.WriteLine("\t\t\t{0} object trees start at {1}", resourceHeader.rsh_ntree,
resourceHeader.rsh_trindex);
Console.WriteLine("\t\t\t{0} free strings start at {1}", resourceHeader.rsh_nstring,
resourceHeader.rsh_frstr);
Console.WriteLine("\t\t\t{0} free images start at {1}", resourceHeader.rsh_nimages,
resourceHeader.rsh_frimg);
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(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);
}
}
static void PrintGemResourceTree(GEM.TreeObjectNode node, int level)
{
for(int i = 0; i < level; i++) Console.Write("\t");
@@ -229,20 +233,20 @@ namespace exeinfo
switch(node.type)
{
case AtariST.ObjectTypes.G_BOX:
case AtariST.ObjectTypes.G_IBOX:
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}",
node.type, node.flags, node.state,
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.BorderColorMask) >> 12),
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.TextColorMask) >> 8),
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.InsideColorMask) >> 8),
(AtariST.ObjectFillPattern)((node.data & 0xFFFF & AtariST.FillPatternMask) >> 4),
(node.data & 0xFFFF & AtariST.TransparentColor) != 0
(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),
(node.data & 0xFFFF & GEM.TransparentColor) != 0
? "transparent"
: "replace",
node.x, node.y, node.width, node.height);
break;
case AtariST.ObjectTypes.G_BOXCHAR:
case GEM.ObjectTypes.G_BOXCHAR:
sbyte thickness = (sbyte)((node.data & 0xFF0000) >> 16);
if(thickness < 0) thickStr = $"{thickness * -1} pixels outward thickness";
@@ -260,25 +264,25 @@ namespace exeinfo
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,
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.BorderColorMask) >> 12),
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.TextColorMask) >> 8),
(AtariST.ObjectColors)((node.data & 0xFFFF & AtariST.InsideColorMask) >> 8),
(AtariST.ObjectFillPattern)((node.data & 0xFFFF & AtariST.FillPatternMask) >> 4),
(node.data & 0xFFFF & AtariST.TransparentColor) != 0
(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),
(node.data & 0xFFFF & GEM.TransparentColor) != 0
? "transparent"
: "replace",
thickStr, character, node.x, node.y, node.width, node.height);
break;
case AtariST.ObjectTypes.G_BUTTON:
case AtariST.ObjectTypes.G_STRING:
case AtariST.ObjectTypes.G_TITLE:
case GEM.ObjectTypes.G_BUTTON:
case GEM.ObjectTypes.G_STRING:
case GEM.ObjectTypes.G_TITLE:
Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}: {7}", node.type, node.flags,
node.state, node.x, node.y, node.width, node.height, node.String);
break;
case AtariST.ObjectTypes.G_TEXT:
case AtariST.ObjectTypes.G_BOXTEXT:
case AtariST.ObjectTypes.G_FTEXT:
case AtariST.ObjectTypes.G_FBOXTEXT:
case GEM.ObjectTypes.G_TEXT:
case GEM.ObjectTypes.G_BOXTEXT:
case GEM.ObjectTypes.G_FTEXT:
case GEM.ObjectTypes.G_FBOXTEXT:
if(node.TedInfo == null) goto default;
if(node.TedInfo.Thickness < 0)
@@ -295,16 +299,17 @@ namespace exeinfo
node.TedInfo.Transparency ? "transparent" : "replace", node.TedInfo.Text,
node.TedInfo.Validation, node.TedInfo.Template);
break;
case AtariST.ObjectTypes.G_IMAGE:
case GEM.ObjectTypes.G_IMAGE:
if(node.BitBlock == null) goto default;
Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, colored {7}, {8} bytes", node.type,
node.flags, node.state, node.BitBlock.X, node.BitBlock.Y, node.BitBlock.Width,
node.BitBlock.Height, node.BitBlock.Color, node.BitBlock.Data?.Length);
Console.WriteLine("{0} ({1} {2}), coordinates ({3},{4}) size {5}x{6}, colored {7}, {8} bytes",
node.type, node.flags, node.state, node.BitBlock.X, node.BitBlock.Y,
node.BitBlock.Width, node.BitBlock.Height, node.BitBlock.Color,
node.BitBlock.Data?.Length);
break;
/*
case AtariST.ObjectTypes.G_USERDEF: break;*/
case AtariST.ObjectTypes.G_ICON:
case GEM.ObjectTypes.G_USERDEF: break;*/
case GEM.ObjectTypes.G_ICON:
if(node.IconBlock == null) goto default;
Console.WriteLine(
@@ -324,9 +329,9 @@ namespace exeinfo
break;
}
if(node.child != null) PrintAtariResourceTree(node.child, level + 1);
if(node.child != null) PrintGemResourceTree(node.child, level + 1);
if(node.sibling != null) PrintAtariResourceTree(node.sibling, level);
if(node.sibling != null) PrintGemResourceTree(node.sibling, level);
}
}
}

View File

@@ -29,6 +29,7 @@ using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Claunia.Encoding;
namespace libexeinfo
{
@@ -37,6 +38,10 @@ namespace libexeinfo
/// </summary>
public partial class AtariST : IExecutable
{
public GEM.GemResourceHeader ResourceHeader;
public GEM.TreeObjectNode[] ResourceObjectRoots;
public Stream resourceStream;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.AtariST" /> class.
/// </summary>
@@ -45,30 +50,30 @@ namespace libexeinfo
{
BaseStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
string pathDir = Path.GetDirectoryName(path);
string filename = Path.GetFileNameWithoutExtension(path);
string testPath = Path.Combine(pathDir, filename);
string pathDir = Path.GetDirectoryName(path);
string filename = Path.GetFileNameWithoutExtension(path);
string testPath = Path.Combine(pathDir, filename);
string resourceFilePath = null;
if(File.Exists(testPath + ".rsc"))
resourceFilePath = testPath + ".rsc";
else if(File.Exists(testPath + ".rsC"))
if(File.Exists(testPath + ".rsc")) resourceFilePath = testPath + ".rsc";
else if(File.Exists(testPath + ".rsC"))
resourceFilePath = testPath + ".rsC";
else if(File.Exists(testPath + ".rSc"))
else if(File.Exists(testPath + ".rSc"))
resourceFilePath = testPath + ".rSc";
else if(File.Exists(testPath + ".rSC"))
else if(File.Exists(testPath + ".rSC"))
resourceFilePath = testPath + ".rSC";
else if(File.Exists(testPath + ".Rsc"))
else if(File.Exists(testPath + ".Rsc"))
resourceFilePath = testPath + ".Rsc";
else if(File.Exists(testPath + ".RsC"))
else if(File.Exists(testPath + ".RsC"))
resourceFilePath = testPath + ".RsC";
else if(File.Exists(testPath + ".RSc"))
else if(File.Exists(testPath + ".RSc"))
resourceFilePath = testPath + ".RSc";
else if(File.Exists(testPath + ".RSC"))
else if(File.Exists(testPath + ".RSC"))
resourceFilePath = testPath + ".RSC";
if(resourceFilePath != null) resourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
if(resourceFilePath != null)
resourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Initialize();
}
@@ -104,9 +109,6 @@ namespace libexeinfo
public OperatingSystem RequiredOperatingSystem =>
new OperatingSystem {Name = Header.mint == MINT_SIGNATURE ? "MiNT" : "Atari TOS"};
public IEnumerable<string> Strings { get; private set; }
public Stream resourceStream;
public AtariResourceHeader ResourceHeader;
public TreeObjectNode[] ResourceObjectRoots;
void Initialize()
{
@@ -121,25 +123,26 @@ namespace libexeinfo
Recognized = Header.signature == SIGNATURE;
if(!Recognized) return;
Type = "Atari ST executable";
if(resourceStream == null) return;
buffer = new byte[Marshal.SizeOf(typeof(AtariResourceHeader))];
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceHeader))];
resourceStream.Position = 0;
resourceStream.Read(buffer, 0, buffer.Length);
ResourceHeader = BigEndianMarshal.ByteArrayToStructureBigEndian<AtariResourceHeader>(buffer);
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 != 0 && ResourceHeader.rsh_vrsn != 1 && ResourceHeader.rsh_vrsn != 4 &&
ResourceHeader.rsh_vrsn != 5) return;
List<string> strings = new List<string>();
if(ResourceHeader.rsh_ntree > 0)
{
resourceStream.Position = ResourceHeader.rsh_trindex;
int[] treeOffsets = new int[ResourceHeader.rsh_ntree];
byte[] tmp = new byte[4];
int[] treeOffsets = new int[ResourceHeader.rsh_ntree];
byte[] tmp = new byte[4];
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
@@ -147,43 +150,47 @@ namespace libexeinfo
treeOffsets[i] = BitConverter.ToInt32(tmp.Reverse().ToArray(), 0);
}
ResourceObjectRoots = new TreeObjectNode[ResourceHeader.rsh_ntree];
ResourceObjectRoots = new GEM.TreeObjectNode[ResourceHeader.rsh_ntree];
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
if(treeOffsets[i] <= 0 || treeOffsets[i] >= resourceStream.Length) continue;
resourceStream.Position = treeOffsets[i];
List<ObjectNode> nodes = new List<ObjectNode>();
List<GEM.ObjectNode> nodes = new List<GEM.ObjectNode>();
while(true)
{
buffer = new byte[Marshal.SizeOf(typeof(ObjectNode))];
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
ObjectNode node = BigEndianMarshal.ByteArrayToStructureBigEndian<ObjectNode>(buffer);
GEM.ObjectNode node = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.ObjectNode>(buffer);
nodes.Add(node);
if(((ObjectFlags)node.ob_flags).HasFlag(ObjectFlags.Lastob)) break;
if(((GEM.ObjectFlags)node.ob_flags).HasFlag(GEM.ObjectFlags.Lastob)) break;
}
List<short> knownNodes = new List<short>();
ResourceObjectRoots[i] = ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings);
ResourceObjectRoots[i] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, true,
Encoding.AtariSTEncoding);
}
}
else if(ResourceHeader.rsh_nobs > 0)
{
ObjectNode[] nodes = new ObjectNode[ResourceHeader.rsh_nobs];
GEM.ObjectNode[] nodes = new GEM.ObjectNode[ResourceHeader.rsh_nobs];
resourceStream.Position = ResourceHeader.rsh_object;
for(short i = 0; i < ResourceHeader.rsh_nobs; i++)
{
buffer = new byte[Marshal.SizeOf(typeof(ObjectNode))];
buffer = new byte[Marshal.SizeOf(typeof(GEM.ObjectNode))];
resourceStream.Read(buffer, 0, buffer.Length);
nodes[i] = BigEndianMarshal.ByteArrayToStructureBigEndian<ObjectNode>(buffer);
nodes[i] = BigEndianMarshal.ByteArrayToStructureBigEndian<GEM.ObjectNode>(buffer);
}
List<short> knownNodes = new List<short>();
ResourceObjectRoots = new TreeObjectNode[1];
ResourceObjectRoots[0] = ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings);
ResourceObjectRoots = new GEM.TreeObjectNode[1];
ResourceObjectRoots[0] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, true,
Encoding.AtariSTEncoding);
}
if(strings.Count > 0)

View File

@@ -34,512 +34,13 @@ namespace libexeinfo
public struct AtariHeader
{
public ushort signature;
public int text_len;
public int data_len;
public int bss_len;
public int symb_len;
public int text_len;
public int data_len;
public int bss_len;
public int symb_len;
public uint mint;
public uint flags;
public ushort absflags;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AtariResourceHeader
{
/// <summary>
/// Contains the version number of the resource file. This value is 0x0000 or 0x0001 in old format RSC files and has
/// the third bit set (i.e. 0x0004) in the new file format. If file is in the new format, it is appended at the end with <see cref="AtariResourceExtension"/>
/// </summary>
public short rsh_vrsn;
/// <summary>
/// Contains an offset from the beginning of the file to the OBJECT structures.
/// </summary>
public short rsh_object;
/// <summary>
/// Contains an offset from the beginning of the file to the TEDINFO structures.
/// </summary>
public short rsh_tedinfo;
/// <summary>
/// Contains an offset from the beginning of the file to the ICONBLK structures.
/// </summary>
public short rsh_iconblk;
/// <summary>
/// Contains an offset from the beginning of the file to the BITBLK structures.
/// </summary>
public short rsh_bitblk;
/// <summary>
/// Contains an offset from the beginning of the file to the string pointer table.
/// </summary>
public short rsh_frstr;
/// <summary>
/// Contains an offset from the beginning of the file to the string data.
/// </summary>
public short rsh_string;
/// <summary>
/// Contains an offset from the beginning of the file to the image data.
/// </summary>
public short rsh_imdata;
/// <summary>
/// Contains an offset from the beginning of the file to the image pointer table.
/// </summary>
public short rsh_frimg;
/// <summary>
/// Contains an offset from the beginning of the file to the tree pointer table.
/// </summary>
public short rsh_trindex;
/// <summary>
/// Number of OBJECTs in the file.
/// </summary>
public short rsh_nobs;
/// <summary>
/// Number of object trees in the file.
/// </summary>
public short rsh_ntree;
/// <summary>
/// Number of TEDINFOs in the file.
/// </summary>
public short rsh_nted;
/// <summary>
/// Number of ICONBLKs in the file.
/// </summary>
public short rsh_nib;
/// <summary>
/// Number of BITBLKs in the file.
/// </summary>
public short rsh_nbb;
/// <summary>
/// Number of free strings in the file.
/// </summary>
public short rsh_nstring;
/// <summary>
/// Number of free images in the file.
/// </summary>
public short rsh_nimages;
/// <summary>
/// Size of the resource file (in bytes). Note that this is the size of the old format resource file. If the newer
/// format file is being used then this value can be used as an offset to the extension array.
/// </summary>
public short rsh_rssize;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct AtariResourceExtension
{
/// <summary>
/// Size of the file
/// </summary>
public int filesize;
/// <summary>
/// Slot for color icons containing an offset to <see cref="ColorIconBlock"/> table. The table is an array of <see cref="int"/> offsets in file with -1 meaning table end.
/// </summary>
public int color_ic;
/// <summary>
/// If not 0, it's an unknown extension, 0 means last extension
/// </summary>
public int end_extensions;
}
/// <summary>
/// The OBJECT structure contains values that describe the object, its relationship to the other objects in the tree, and its location relative to its parent or (in the case of the root object) the screen.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ObjectNode
{
/// <summary>
/// A word containing the index of the object's next sibling in the object tree array
/// </summary>
public short ob_next;
/// <summary>
/// A word containing the index of the first child: the head of the list of the object's children in the object tree array
/// </summary>
public short ob_head;
/// <summary>
/// A word contianing the index of the last child: the tail of the list of the object's children in the object tree array
/// </summary>
public short ob_tail;
/// <summary>
/// A word containing the object type. GEM AES ignored the high byte of this word
/// </summary>
public short ob_type;
/// <summary>
/// A word containing the object flags
/// </summary>
public ushort ob_flags;
/// <summary>
/// A word containing the object state
/// </summary>
public ushort ob_state;
/// <summary>
/// A long value containing object specific data. Depending on the object's type, can be a pointer to any combination of word and/or byte values that add up to 32 bits.
/// </summary>
public int ob_spec;
/// <summary>
/// A word containing the X-coordinate of the object relative to its parent or (for the root object) the screen
/// </summary>
public short ob_x;
/// <summary>
/// A word containing the Y-coordinate of the object relative to its parent or (for the root object) the screen
/// </summary>
public short ob_y;
/// <summary>
/// A word containing the width of the object in pixels
/// </summary>
public short ob_width;
/// <summary>
/// A word containing the height of the object in pixels
/// </summary>
public short ob_height;
}
/// <summary>
/// The OBJECT structure contains values that describe the object, its relationship to the other objects in the tree, and its location relative to its parent or (in the case of the root object) the screen.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class TreeObjectNode
{
public TreeObjectNode sibling;
public TreeObjectNode child;
public ObjectTypes type;
public ObjectFlags flags;
public ObjectStates state;
public int data;
public short x;
public short y;
public short width;
public short height;
public TextBlock TedInfo;
public BitmapBlock BitBlock;
public UserBlock UserBlock;
public Icon IconBlock;
public string String;
}
public class TextBlock
{
public string Text;
public string Template;
public string Validation;
public ObjectFont Font;
public ObjectJustification Justification;
public short Thickness;
public ObjectColors BorderColor;
public ObjectColors TextColor;
public ObjectColors InsideColor;
public bool Transparency;
public ObjectFillPattern Fill;
}
public class BitmapBlock
{
public byte[] Data;
public int Width;
public int Height;
public short X;
public short Y;
public ObjectColors Color;
}
public class Icon
{
public byte[] Mask;
public byte[] Data;
public int Width;
public int Height;
public short X;
public short Y;
public ObjectColors BackgroundColor;
public ObjectColors ForegroundColor;
public char Character;
public short CharX;
public short CharY;
public string Text;
public short TextX;
public short TextY;
public short TextWidth;
public short TextHeight;
}
/// <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.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TedInfo
{
/// <summary>
/// A pointer to the actual text. If the first character is '@', the field is blank.
/// </summary>
public int te_ptext;
/// <summary>
/// A pointer to a string template for any data entry. The editable portion is represented by underscores.
/// </summary>
public int te_ptmplt;
/// <summary>
/// A pointer to a text string contianing characters tht validate any entered text
/// </summary>
public int te_pvalid;
/// <summary>
/// A word identifying the font used to draw the text. 3 for system font, 5 for small font.
/// </summary>
public short te_font;
/// <summary>
/// Reserved for future use
/// </summary>
public short te_resvd1;
/// <summary>
/// A word identifying the type of text justification desired. 0 = left, 1 = right, 2 = center
/// </summary>
public short te_just;
/// <summary>
/// A word identifying the color and pattern of box-type objects
/// </summary>
public short te_color;
/// <summary>
/// Reserved for future use
/// </summary>
public short te_resvd2;
/// <summary>
/// A word containing the thickness in pixels of the border of the text box. 0 for none, positive for inside, negative for outside
/// </summary>
public short te_thickness;
/// <summary>
/// A word containing the length of the string pointed by <see cref="te_ptext"/>.
/// </summary>
public short te_txtlen;
/// <summary>
/// A word containing the length of the string pointed by <see cref="te_ptmplt"/>.
/// </summary>
public short te_tmplen;
}
/// <summary>
/// The ICONBLK structure is used to hold the data that defines icons. The object type G_ICON points with its <see cref="ObjectNode.ob_spec"/> pointer to an ICONBLK structure.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IconBlock
{
/// <summary>
/// A pointer to an array of words representing the mask bit image of the icon
/// </summary>
public int ib_pmask;
/// <summary>
/// A pointer to an array of words representing the data bit image of the icon
/// </summary>
public int ib_pdata;
/// <summary>
/// A pointer to the icon's text
/// </summary>
public int ib_ptext;
/// <summary>
/// A word containing a character to be drawn in the icon. The high byte contains the foreground color in the high nibble and the background color in the low nibble.
/// </summary>
public short ib_char;
/// <summary>
/// A word containing the X-coordinate of <see cref="ib_char"/>
/// </summary>
public short ib_xchar;
/// <summary>
/// A word containing the Y-coordinate of <see cref="ib_char"/>
/// </summary>
public short ib_ychar;
/// <summary>
/// A word containing the X-coordinate of the icon
/// </summary>
public short ib_xicon;
/// <summary>
/// A word containing the Y-coordinate of the icon
/// </summary>
public short ib_yicon;
/// <summary>
/// A word containing the width of the icon in pixels. Must be divisible by 16.
/// </summary>
public short ib_wicon;
/// <summary>
/// A word containing the height of the icon in pixels
/// </summary>
public short ib_hicon;
/// <summary>
/// A word containing the X-coordinate of the icon's text
/// </summary>
public short ib_xtext;
/// <summary>
/// A word containing the Y-coordinate of the icon's text
/// </summary>
public short ib_ytext;
/// <summary>
/// A word containing the width of a rectangle in which the icon's text will be centered
/// </summary>
public short ib_wtext;
/// <summary>
/// A word containing the height of the icon's text in pixels
/// </summary>
public short ib_htext;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
/// <summary>
/// The object type G_IMAGE uses the BITBLK structure to draw bit images like cursor forms or icons
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BitBlock
{
/// <summary>
/// A pointer to an array of words contianing the bit image
/// </summary>
public int bi_pdata;
/// <summary>
/// A word containing the width of the <see cref="bi_pdata"/> array in bytes
/// </summary>
public short bi_wb;
/// <summary>
/// A word containing the height of the bit block in scan lines (pixels)
/// </summary>
public short bi_hl;
/// <summary>
/// A word containing the source X in bit form, relative to the <see cref="bi_pdata"/> array
/// </summary>
public short bi_x;
/// <summary>
/// A word containing the source Y in bit form, relative to the <see cref="bi_pdata"/> array
/// </summary>
public short bi_y;
/// <summary>
/// A word containing the color GEM AES uses when displaying the bit image.
/// </summary>
public short bi_color;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
/// <summary>
/// The USERBLK structure is used to locate and call an application-defined routine that will draw and/or change an object. The object type G_UERDEF points with its <see cref="ObjectNode.ob_spec"/> pointer to an USERBLK structure.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UserBlock
{
/// <summary>
/// A pointer to the routine for drawing and/or changing the object
/// </summary>
public int ub_code;
/// <summary>
/// A long value (optionally provided by the application) passed as a parameter when calling the routine
/// </summary>
public int ub_parm;
}
/// <summary>
/// The PARMBLK structure is used to store information relevant to the application's drawing or changing an object.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ParameterBlock
{
/// <summary>
/// A pointer to the object tree that contains the application defined object
/// </summary>
public int pb_tree;
/// <summary>
/// A word containing the object index of the application defined object
/// </summary>
public short pb_obj;
/// <summary>
/// A word containing the old state of an object to be changed
/// </summary>
public short pb_prevstate;
/// <summary>
/// A word containing the changed (new) state of an object
/// </summary>
public short pb_currstate;
/// <summary>
/// A word containing the X-coordinate of a rectangle defining the location of the object on the physical screen
/// </summary>
public short pb_x;
/// <summary>
/// A word containing the Y-coordinate of a rectangle defining the location of the object on the physical screen
/// </summary>
public short pb_y;
/// <summary>
/// A word containing the width in pixels of a rectanble defining the size of the object on the physical screen
/// </summary>
public short pb_w;
/// <summary>
/// A word containing the height in pixels of a rectanble defining the size of the object on the physical screen
/// </summary>
public short pb_h;
/// <summary>
/// A word containing the X-coordinate of the current clip rectangle on the physical screen
/// </summary>
public short pb_xc;
/// <summary>
/// A word containing the Y-coordinate of the current clip rectangle on the physical screen
/// </summary>
public short pb_yc;
/// <summary>
/// A word containing the width in pixels of the current clip rectnagle on the physical screen
/// </summary>
public short pb_wc;
/// <summary>
/// A word containing the heigth in pixels of the current clip rectnagle on the physical screen
/// </summary>
public short pb_hc;
/// <summary>
/// A long value, identical to <see cref="UserBlock.ub_parm"/>, that is passed to the application when it is time for the application to draw or change the object. Low word.
/// </summary>
public short pb_parm_low;
/// <summary>
/// A long value, identical to <see cref="UserBlock.ub_parm"/>, that is passed to the application when it is time for the application to draw or change the object. High word.
/// </summary>
public short pb_parm_high;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ColorIcon
{
/// <summary>
/// Number of planes in the following data
/// </summary>
public short num_planes;
/// <summary>
/// Pointer to color bitmap in standard form
/// </summary>
public int col_data;
/// <summary>
/// Pointer to single plane mask of <see cref="col_data"/>
/// </summary>
public int col_mask;
/// <summary>
/// Pointer to color bitmap of selected icon
/// </summary>
public int sel_data;
/// <summary>
/// Pointer to single plane mask of <see cref="sel_data"/>
/// </summary>
public int sel_mask;
/// <summary>
/// Pointer to next icon
/// </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;
}
}
}

View File

@@ -1,5 +1,5 @@
//
// Structs.cs
// Enums.cs
//
// Author:
// Natalia Portillo <claunia@claunia.com>
@@ -28,8 +28,126 @@ using System;
namespace libexeinfo
{
public partial class AtariST
public static partial class GEM
{
public enum ObjectColors : byte
{
White = 0,
Black = 1,
Red = 2,
Green = 3,
Blue = 4,
Cyan = 5,
Yellow = 6,
Magenta = 7,
White2 = 8,
Black2 = 9,
LightRed = 10,
LightGreen = 11,
LightBlue = 12,
LightCyan = 13,
LightYellow = 14,
LightMagenta = 15
}
public enum ObjectFillPattern : byte
{
Hollow = 0,
Dither1 = 1,
Dither2 = 2,
Dither3 = 3,
Dither4 = 4,
Dither5 = 5,
Dither6 = 6,
Solid = 7
}
[Flags]
public enum ObjectFlags : short
{
/// <summary>
/// Indicates that the user can select the object
/// </summary>
Selectable = 0x0001,
/// <summary>
/// Indicates that the Form Library will examine the object if the user enters a carriage return. No more than one
/// object in a form can be flagged so.
/// </summary>
Default = 0x0002,
/// <summary>
/// Indicates that the Form Library will return control to the caller after the exit condition is satisfied. The
/// condition is satisfied when the user clicks on the object.
/// </summary>
Exit = 0x0004,
/// <summary>
/// Indicates that an object is editable by the user in some way.
/// </summary>
Editable = 0x0008,
/// <summary>
/// An object called a radio button.
/// </summary>
Rbutton = 0x0010,
/// <summary>
/// Indicates that an object is the last in the tree
/// </summary>
Lastob = 0x0020,
/// <summary>
/// Inidicates that the Form Library will return control to the caller afeter the exit condition is satisfied. The
/// condition is satisfied when the user clicks while the pointer is over the object.
/// </summary>
Touchexit = 0x0040,
/// <summary>
/// Makes a subtree invisible.
/// </summary>
Hidetree = 0x0080,
/// <summary>
/// Indicates that the value in <see cref="ObjectNode.ob_spec" /> is a pointer to the actual value.
/// </summary>
Indirect = 0x0100
}
public enum ObjectFont : short
{
System = 3,
Small = 5
}
public enum ObjectJustification : short
{
Left = 0,
Right = 1,
Center = 2
}
[Flags]
public enum ObjectStates : short
{
/// <summary>
/// Indicates that the object is highlighted by being drawn with reversed colors
/// </summary>
Selected = 0x0001,
/// <summary>
/// Indicates that an 'X' is drawn in the object.
/// </summary>
Crossed = 0x0002,
/// <summary>
/// Indicates that the object is drawn with a check mark.
/// </summary>
Checked = 0x0004,
/// <summary>
/// Indicates that the object is drawn faintly.
/// </summary>
Disabled = 0x0008,
/// <summary>
/// Indicates that an outline appears around a box object. This state is used for dialog boxes.
/// </summary>
Outlined = 0x0010,
/// <summary>
/// Indicates that the object (usually a box) is drawn with a drop shadow
/// </summary>
Shadowed = 0x0020
}
public enum ObjectTypes : short
{
/// <summary>
@@ -99,145 +217,31 @@ namespace libexeinfo
/// </summary>
G_TITLE = 32,
/// <summary>
/// An object that describes a color icon. Its <see cref="ObjectNode.ob_spec" /> is a pointer to an <see cref="ColorIconBlock" />
/// An object that describes a color icon. Its <see cref="ObjectNode.ob_spec" /> is a pointer to an
/// <see cref="ColorIconBlock" />
/// structure.
/// </summary>
G_CICON = 33,
}
[Flags]
public enum ObjectFlags : short
{
/// <summary>
/// Indicates that the user can select the object
/// </summary>
Selectable = 0x0001,
/// <summary>
/// Indicates that the Form Library will examine the object if the user enters a carriage return. No more than one object in a form can be flagged so.
/// </summary>
Default = 0x0002,
/// <summary>
/// Indicates that the Form Library will return control to the caller after the exit condition is satisfied. The condition is satisfied when the user clicks on the object.
/// </summary>
Exit = 0x0004,
/// <summary>
/// Indicates that an object is editable by the user in some way.
/// </summary>
Editable = 0x0008,
/// <summary>
/// An object called a radio button.
/// </summary>
Rbutton = 0x0010,
/// <summary>
/// Indicates that an object is the last in the tree
/// </summary>
Lastob = 0x0020,
/// <summary>
/// Inidicates that the Form Library will return control to the caller afeter the exit condition is satisfied. The condition is satisfied when the user clicks while the pointer is over the object.
/// </summary>
Touchexit = 0x0040,
/// <summary>
/// Makes a subtree invisible.
/// </summary>
Hidetree = 0x0080,
/// <summary>
/// Indicates that the value in <see cref="ObjectNode.ob_spec"/> is a pointer to the actual value.
/// </summary>
Indirect = 0x0100
}
[Flags]
public enum ObjectStates : short
{
/// <summary>
/// Indicates that the object is highlighted by being drawn with reversed colors
/// </summary>
Selected = 0x0001,
/// <summary>
/// Indicates that an 'X' is drawn in the object.
/// </summary>
Crossed = 0x0002,
/// <summary>
/// Indicates that the object is drawn with a check mark.
/// </summary>
Checked = 0x0004,
/// <summary>
/// Indicates that the object is drawn faintly.
/// </summary>
Disabled = 0x0008,
/// <summary>
/// Indicates that an outline appears around a box object. This state is used for dialog boxes.
/// </summary>
Outlined = 0x0010,
/// <summary>
/// Indicates that the object (usually a box) is drawn with a drop shadow
/// </summary>
Shadowed = 0x0020
}
public enum ObjectColors : byte
{
White = 0,
Black = 1,
Red = 2,
Green = 3,
Blue = 4,
Cyan = 5,
Yellow = 6,
Magenta = 7,
White2 = 8,
Black2 = 9,
LightRed = 10,
LightGreen = 11,
LightBlue = 12,
LightCyan = 13,
LightYellow = 14,
LightMagenta = 15
}
public enum ObjectFillPattern : byte
{
Hollow = 0,
Dither1 = 1,
Dither2 = 2,
Dither3 = 3,
Dither4 = 4,
Dither5 = 5,
Dither6 = 6,
Solid = 7,
}
public enum ObjectJustification : short
{
Left = 0,
Right = 1,
Center = 2
}
public enum ObjectFont : short
{
System = 3,
Small = 5
G_CICON = 33
}
/// <summary>
/// Mask for border <see cref="ObjectColors"/>
/// Mask for border <see cref="ObjectColors" />
/// </summary>
public const ushort BorderColorMask = 0xF000;
/// <summary>
/// Mask for text <see cref="ObjectColors"/>
/// Mask for text <see cref="ObjectColors" />
/// </summary>
public const ushort TextColorMask = 0x0F00;
/// <summary>
/// If set text is in transparent mode. Replace mode otherwise.
/// If set text is in transparent mode. Replace mode otherwise.
/// </summary>
public const ushort TransparentColor = 0x0080;
/// <summary>
/// Mask for <see cref="ObjectFillPattern"/>
/// Mask for <see cref="ObjectFillPattern" />
/// </summary>
public const ushort FillPatternMask = 0x0070;
/// <summary>
/// Mask for inside <see cref="ObjectColors"/>
/// Mask for inside <see cref="ObjectColors" />
/// </summary>
public const ushort InsideColorMask = 0x000F;
}

View File

@@ -1,5 +1,5 @@
//
// AtariST.cs
// Resources.cs
//
// Author:
// Natalia Portillo <claunia@claunia.com>
@@ -27,15 +27,16 @@
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using Claunia.Encoding;
using System.Text;
namespace libexeinfo
{
public partial class AtariST : IExecutable
public static partial class GEM
{
static TreeObjectNode ProcessResourceObject(IList<ObjectNode> nodes, ref List<short> knownNodes,
short nodeNumber, Stream resourceStream,
List<string> strings)
internal static TreeObjectNode ProcessResourceObject(IList<ObjectNode> nodes, ref List<short> knownNodes,
short nodeNumber, Stream resourceStream,
List<string> strings, bool bigEndian,
Encoding encoding)
{
TreeObjectNode node = new TreeObjectNode
{
@@ -62,7 +63,9 @@ namespace libexeinfo
resourceStream.Position = node.data;
buffer = new byte[Marshal.SizeOf(typeof(TedInfo))];
resourceStream.Read(buffer, 0, buffer.Length);
TedInfo ted = BigEndianMarshal.ByteArrayToStructureBigEndian<TedInfo>(buffer);
TedInfo ted = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<TedInfo>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<TedInfo>(buffer);
node.TedInfo = new TextBlock
{
@@ -83,7 +86,7 @@ namespace libexeinfo
tmpStr = new byte[ted.te_txtlen - 1];
resourceStream.Position = ted.te_ptext;
resourceStream.Read(tmpStr, 0, ted.te_txtlen - 1);
node.TedInfo.Text = Encoding.AtariSTEncoding.GetString(tmpStr);
node.TedInfo.Text = encoding.GetString(tmpStr);
strings.Add(node.TedInfo.Text.Trim());
}
@@ -92,7 +95,7 @@ namespace libexeinfo
tmpStr = new byte[ted.te_txtlen - 1];
resourceStream.Position = ted.te_pvalid;
resourceStream.Read(tmpStr, 0, ted.te_txtlen - 1);
node.TedInfo.Validation = Encoding.AtariSTEncoding.GetString(tmpStr);
node.TedInfo.Validation = encoding.GetString(tmpStr);
strings.Add(node.TedInfo.Validation.Trim());
}
@@ -101,7 +104,7 @@ namespace libexeinfo
tmpStr = new byte[ted.te_tmplen - 1];
resourceStream.Position = ted.te_ptmplt;
resourceStream.Read(tmpStr, 0, ted.te_tmplen - 1);
node.TedInfo.Template = Encoding.AtariSTEncoding.GetString(tmpStr);
node.TedInfo.Template = encoding.GetString(tmpStr);
strings.Add(node.TedInfo.Template.Trim());
}
@@ -113,7 +116,9 @@ namespace libexeinfo
resourceStream.Position = node.data;
buffer = new byte[Marshal.SizeOf(typeof(BitBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
BitBlock bitBlock = BigEndianMarshal.ByteArrayToStructureBigEndian<BitBlock>(buffer);
BitBlock bitBlock = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<BitBlock>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<BitBlock>(buffer);
node.BitBlock = new BitmapBlock
{
@@ -137,7 +142,9 @@ namespace libexeinfo
resourceStream.Position = node.data;
buffer = new byte[Marshal.SizeOf(typeof(UserBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
node.UserBlock = BigEndianMarshal.ByteArrayToStructureBigEndian<UserBlock>(buffer);
node.UserBlock = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<UserBlock>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<UserBlock>(buffer);
break;
case ObjectTypes.G_ICON:
if(node.data <= 0 || node.data >= resourceStream.Length) break;
@@ -145,7 +152,9 @@ namespace libexeinfo
resourceStream.Position = node.data;
buffer = new byte[Marshal.SizeOf(typeof(IconBlock))];
resourceStream.Read(buffer, 0, buffer.Length);
IconBlock iconBlock = BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer);
IconBlock iconBlock = bigEndian
? BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer)
: BigEndianMarshal.ByteArrayToStructureLittleEndian<IconBlock>(buffer);
node.IconBlock = new Icon
{
@@ -153,16 +162,15 @@ namespace libexeinfo
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.AtariSTEncoding.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
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)
@@ -178,7 +186,7 @@ namespace libexeinfo
chars.Add((byte)character);
}
node.IconBlock.Text = Encoding.AtariSTEncoding.GetString(chars.ToArray());
node.IconBlock.Text = encoding.GetString(chars.ToArray());
strings.Add(node.IconBlock.Text.Trim());
}
@@ -216,7 +224,7 @@ namespace libexeinfo
chars.Add((byte)character);
}
node.String = Encoding.AtariSTEncoding.GetString(chars.ToArray());
node.String = encoding.GetString(chars.ToArray());
strings.Add(node.String.Trim());
break;
}
@@ -224,12 +232,12 @@ namespace libexeinfo
knownNodes.Add(nodeNumber);
if(nodes[nodeNumber].ob_head > 0 && !knownNodes.Contains(nodes[nodeNumber].ob_head))
node.child = ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_head, resourceStream,
strings);
node.child = ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_head, resourceStream,
strings, bigEndian, encoding);
if(nodes[nodeNumber].ob_next > 0 && !knownNodes.Contains(nodes[nodeNumber].ob_next))
node.sibling =
ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_next, resourceStream, strings);
node.sibling = ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_next, resourceStream,
strings, bigEndian, encoding);
return node;
}

520
libexeinfo/GEM/Structs.cs Normal file
View File

@@ -0,0 +1,520 @@
using System.Runtime.InteropServices;
namespace libexeinfo
{
public static partial class GEM
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GemResourceHeader
{
/// <summary>
/// Contains the version number of the resource file. This value is 0x0000 or 0x0001 in old format RSC files and has
/// the third bit set (i.e. 0x0004) in the new file format. If file is in the new format, it is appended at the end
/// with <see cref="GemResourceExtension" />
/// </summary>
public short rsh_vrsn;
/// <summary>
/// Contains an offset from the beginning of the file to the OBJECT structures.
/// </summary>
public short rsh_object;
/// <summary>
/// Contains an offset from the beginning of the file to the TEDINFO structures.
/// </summary>
public short rsh_tedinfo;
/// <summary>
/// Contains an offset from the beginning of the file to the ICONBLK structures.
/// </summary>
public short rsh_iconblk;
/// <summary>
/// Contains an offset from the beginning of the file to the BITBLK structures.
/// </summary>
public short rsh_bitblk;
/// <summary>
/// Contains an offset from the beginning of the file to the string pointer table.
/// </summary>
public short rsh_frstr;
/// <summary>
/// Contains an offset from the beginning of the file to the string data.
/// </summary>
public short rsh_string;
/// <summary>
/// Contains an offset from the beginning of the file to the image data.
/// </summary>
public short rsh_imdata;
/// <summary>
/// Contains an offset from the beginning of the file to the image pointer table.
/// </summary>
public short rsh_frimg;
/// <summary>
/// Contains an offset from the beginning of the file to the tree pointer table.
/// </summary>
public short rsh_trindex;
/// <summary>
/// Number of OBJECTs in the file.
/// </summary>
public short rsh_nobs;
/// <summary>
/// Number of object trees in the file.
/// </summary>
public short rsh_ntree;
/// <summary>
/// Number of TEDINFOs in the file.
/// </summary>
public short rsh_nted;
/// <summary>
/// Number of ICONBLKs in the file.
/// </summary>
public short rsh_nib;
/// <summary>
/// Number of BITBLKs in the file.
/// </summary>
public short rsh_nbb;
/// <summary>
/// Number of free strings in the file.
/// </summary>
public short rsh_nstring;
/// <summary>
/// Number of free images in the file.
/// </summary>
public short rsh_nimages;
/// <summary>
/// Size of the resource file (in bytes). Note that this is the size of the old format resource file. If the newer
/// format file is being used then this value can be used as an offset to the extension array.
/// </summary>
public short rsh_rssize;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct GemResourceExtension
{
/// <summary>
/// Size of the file
/// </summary>
public int filesize;
/// <summary>
/// Slot for color icons containing an offset to <see cref="ColorIconBlock" /> table. The table is an array of
/// <see cref="int" /> offsets in file with -1 meaning table end.
/// </summary>
public int color_ic;
/// <summary>
/// If not 0, it's an unknown extension, 0 means last extension
/// </summary>
public int end_extensions;
}
/// <summary>
/// The OBJECT structure contains values that describe the object, its relationship to the other objects in the tree,
/// and its location relative to its parent or (in the case of the root object) the screen.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ObjectNode
{
/// <summary>
/// A word containing the index of the object's next sibling in the object tree array
/// </summary>
public short ob_next;
/// <summary>
/// A word containing the index of the first child: the head of the list of the object's children in the object tree
/// array
/// </summary>
public short ob_head;
/// <summary>
/// A word contianing the index of the last child: the tail of the list of the object's children in the object tree
/// array
/// </summary>
public short ob_tail;
/// <summary>
/// A word containing the object type. GEM AES ignored the high byte of this word
/// </summary>
public short ob_type;
/// <summary>
/// A word containing the object flags
/// </summary>
public ushort ob_flags;
/// <summary>
/// A word containing the object state
/// </summary>
public ushort ob_state;
/// <summary>
/// A long value containing object specific data. Depending on the object's type, can be a pointer to any combination
/// of word and/or byte values that add up to 32 bits.
/// </summary>
public int ob_spec;
/// <summary>
/// A word containing the X-coordinate of the object relative to its parent or (for the root object) the screen
/// </summary>
public short ob_x;
/// <summary>
/// A word containing the Y-coordinate of the object relative to its parent or (for the root object) the screen
/// </summary>
public short ob_y;
/// <summary>
/// A word containing the width of the object in pixels
/// </summary>
public short ob_width;
/// <summary>
/// A word containing the height of the object in pixels
/// </summary>
public short ob_height;
}
/// <summary>
/// The OBJECT structure contains values that describe the object, its relationship to the other objects in the tree,
/// and its location relative to its parent or (in the case of the root object) the screen.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class TreeObjectNode
{
public BitmapBlock BitBlock;
public TreeObjectNode child;
public int data;
public ObjectFlags flags;
public short height;
public Icon IconBlock;
public TreeObjectNode sibling;
public ObjectStates state;
public string String;
public TextBlock TedInfo;
public ObjectTypes type;
public UserBlock UserBlock;
public short width;
public short x;
public short y;
}
public class TextBlock
{
public ObjectColors BorderColor;
public ObjectFillPattern Fill;
public ObjectFont Font;
public ObjectColors InsideColor;
public ObjectJustification Justification;
public string Template;
public string Text;
public ObjectColors TextColor;
public short Thickness;
public bool Transparency;
public string Validation;
}
public class BitmapBlock
{
public ObjectColors Color;
public byte[] Data;
public int Height;
public int Width;
public short X;
public short Y;
}
public class Icon
{
public ObjectColors BackgroundColor;
public char Character;
public short CharX;
public short CharY;
public byte[] Data;
public ObjectColors ForegroundColor;
public int Height;
public byte[] Mask;
public string Text;
public short TextHeight;
public short TextWidth;
public short TextX;
public short TextY;
public int Width;
public short X;
public short Y;
}
/// <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.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct TedInfo
{
/// <summary>
/// A pointer to the actual text. If the first character is '@', the field is blank.
/// </summary>
public int te_ptext;
/// <summary>
/// A pointer to a string template for any data entry. The editable portion is represented by underscores.
/// </summary>
public int te_ptmplt;
/// <summary>
/// A pointer to a text string contianing characters tht validate any entered text
/// </summary>
public int te_pvalid;
/// <summary>
/// A word identifying the font used to draw the text. 3 for system font, 5 for small font.
/// </summary>
public short te_font;
/// <summary>
/// Reserved for future use
/// </summary>
public short te_resvd1;
/// <summary>
/// A word identifying the type of text justification desired. 0 = left, 1 = right, 2 = center
/// </summary>
public short te_just;
/// <summary>
/// A word identifying the color and pattern of box-type objects
/// </summary>
public short te_color;
/// <summary>
/// Reserved for future use
/// </summary>
public short te_resvd2;
/// <summary>
/// A word containing the thickness in pixels of the border of the text box. 0 for none, positive for inside, negative
/// for outside
/// </summary>
public short te_thickness;
/// <summary>
/// A word containing the length of the string pointed by <see cref="te_ptext" />.
/// </summary>
public short te_txtlen;
/// <summary>
/// A word containing the length of the string pointed by <see cref="te_ptmplt" />.
/// </summary>
public short te_tmplen;
}
/// <summary>
/// The ICONBLK structure is used to hold the data that defines icons. The object type G_ICON points with its
/// <see cref="ObjectNode.ob_spec" /> pointer to an ICONBLK structure.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct IconBlock
{
/// <summary>
/// A pointer to an array of words representing the mask bit image of the icon
/// </summary>
public int ib_pmask;
/// <summary>
/// A pointer to an array of words representing the data bit image of the icon
/// </summary>
public int ib_pdata;
/// <summary>
/// A pointer to the icon's text
/// </summary>
public int ib_ptext;
/// <summary>
/// A word containing a character to be drawn in the icon. The high byte contains the foreground color in the high
/// nibble and the background color in the low nibble.
/// </summary>
public short ib_char;
/// <summary>
/// A word containing the X-coordinate of <see cref="ib_char" />
/// </summary>
public short ib_xchar;
/// <summary>
/// A word containing the Y-coordinate of <see cref="ib_char" />
/// </summary>
public short ib_ychar;
/// <summary>
/// A word containing the X-coordinate of the icon
/// </summary>
public short ib_xicon;
/// <summary>
/// A word containing the Y-coordinate of the icon
/// </summary>
public short ib_yicon;
/// <summary>
/// A word containing the width of the icon in pixels. Must be divisible by 16.
/// </summary>
public short ib_wicon;
/// <summary>
/// A word containing the height of the icon in pixels
/// </summary>
public short ib_hicon;
/// <summary>
/// A word containing the X-coordinate of the icon's text
/// </summary>
public short ib_xtext;
/// <summary>
/// A word containing the Y-coordinate of the icon's text
/// </summary>
public short ib_ytext;
/// <summary>
/// A word containing the width of a rectangle in which the icon's text will be centered
/// </summary>
public short ib_wtext;
/// <summary>
/// A word containing the height of the icon's text in pixels
/// </summary>
public short ib_htext;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
/// <summary>
/// The object type G_IMAGE uses the BITBLK structure to draw bit images like cursor forms or icons
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BitBlock
{
/// <summary>
/// A pointer to an array of words contianing the bit image
/// </summary>
public int bi_pdata;
/// <summary>
/// A word containing the width of the <see cref="bi_pdata" /> array in bytes
/// </summary>
public short bi_wb;
/// <summary>
/// A word containing the height of the bit block in scan lines (pixels)
/// </summary>
public short bi_hl;
/// <summary>
/// A word containing the source X in bit form, relative to the <see cref="bi_pdata" /> array
/// </summary>
public short bi_x;
/// <summary>
/// A word containing the source Y in bit form, relative to the <see cref="bi_pdata" /> array
/// </summary>
public short bi_y;
/// <summary>
/// A word containing the color GEM AES uses when displaying the bit image.
/// </summary>
public short bi_color;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
/// <summary>
/// The USERBLK structure is used to locate and call an application-defined routine that will draw and/or change an
/// object. The object type G_UERDEF points with its <see cref="ObjectNode.ob_spec" /> pointer to an USERBLK structure.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct UserBlock
{
/// <summary>
/// A pointer to the routine for drawing and/or changing the object
/// </summary>
public int ub_code;
/// <summary>
/// A long value (optionally provided by the application) passed as a parameter when calling the routine
/// </summary>
public int ub_parm;
}
/// <summary>
/// The PARMBLK structure is used to store information relevant to the application's drawing or changing an object.
/// </summary>
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ParameterBlock
{
/// <summary>
/// A pointer to the object tree that contains the application defined object
/// </summary>
public int pb_tree;
/// <summary>
/// A word containing the object index of the application defined object
/// </summary>
public short pb_obj;
/// <summary>
/// A word containing the old state of an object to be changed
/// </summary>
public short pb_prevstate;
/// <summary>
/// A word containing the changed (new) state of an object
/// </summary>
public short pb_currstate;
/// <summary>
/// A word containing the X-coordinate of a rectangle defining the location of the object on the physical screen
/// </summary>
public short pb_x;
/// <summary>
/// A word containing the Y-coordinate of a rectangle defining the location of the object on the physical screen
/// </summary>
public short pb_y;
/// <summary>
/// A word containing the width in pixels of a rectanble defining the size of the object on the physical screen
/// </summary>
public short pb_w;
/// <summary>
/// A word containing the height in pixels of a rectanble defining the size of the object on the physical screen
/// </summary>
public short pb_h;
/// <summary>
/// A word containing the X-coordinate of the current clip rectangle on the physical screen
/// </summary>
public short pb_xc;
/// <summary>
/// A word containing the Y-coordinate of the current clip rectangle on the physical screen
/// </summary>
public short pb_yc;
/// <summary>
/// A word containing the width in pixels of the current clip rectnagle on the physical screen
/// </summary>
public short pb_wc;
/// <summary>
/// A word containing the heigth in pixels of the current clip rectnagle on the physical screen
/// </summary>
public short pb_hc;
/// <summary>
/// A long value, identical to <see cref="UserBlock.ub_parm" />, that is passed to the application when it is time for
/// the application to draw or change the object. Low word.
/// </summary>
public short pb_parm_low;
/// <summary>
/// A long value, identical to <see cref="UserBlock.ub_parm" />, that is passed to the application when it is time for
/// the application to draw or change the object. High word.
/// </summary>
public short pb_parm_high;
/// <summary>
/// Zeros
/// </summary>
public short empty;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct ColorIcon
{
/// <summary>
/// Number of planes in the following data
/// </summary>
public short num_planes;
/// <summary>
/// Pointer to color bitmap in standard form
/// </summary>
public int col_data;
/// <summary>
/// Pointer to single plane mask of <see cref="col_data" />
/// </summary>
public int col_mask;
/// <summary>
/// Pointer to color bitmap of selected icon
/// </summary>
public int sel_data;
/// <summary>
/// Pointer to single plane mask of <see cref="sel_data" />
/// </summary>
public int sel_mask;
/// <summary>
/// Pointer to next icon
/// </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;
}
}
}

View File

@@ -27,7 +27,9 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Claunia.Encoding;
namespace libexeinfo
{
@@ -39,7 +41,10 @@ namespace libexeinfo
/// <summary>
/// Header for this executable
/// </summary>
internal MZHeader Header;
internal MZHeader Header;
public GEM.GemResourceHeader ResourceHeader;
public GEM.TreeObjectNode[] ResourceObjectRoots;
public Stream resourceStream;
/// <summary>
/// Initializes a new instance of the <see cref="T:libexeinfo.MZ" /> class.
@@ -48,6 +53,31 @@ namespace libexeinfo
public MZ(string path)
{
BaseStream = File.Open(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
string pathDir = Path.GetDirectoryName(path);
string filename = Path.GetFileNameWithoutExtension(path);
string testPath = Path.Combine(pathDir, filename);
string resourceFilePath = null;
if(File.Exists(testPath + ".rsc")) resourceFilePath = testPath + ".rsc";
else if(File.Exists(testPath + ".rsC"))
resourceFilePath = testPath + ".rsC";
else if(File.Exists(testPath + ".rSc"))
resourceFilePath = testPath + ".rSc";
else if(File.Exists(testPath + ".rSC"))
resourceFilePath = testPath + ".rSC";
else if(File.Exists(testPath + ".Rsc"))
resourceFilePath = testPath + ".Rsc";
else if(File.Exists(testPath + ".RsC"))
resourceFilePath = testPath + ".RsC";
else if(File.Exists(testPath + ".RSc"))
resourceFilePath = testPath + ".RSc";
else if(File.Exists(testPath + ".RSC"))
resourceFilePath = testPath + ".RSC";
if(resourceFilePath != null)
resourceStream = File.Open(resourceFilePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Initialize();
}
@@ -83,7 +113,7 @@ namespace libexeinfo
public string Type { get; private set; }
public IEnumerable<Architecture> Architectures => new[] {Architecture.I86};
public OperatingSystem RequiredOperatingSystem => new OperatingSystem {Name = "DOS"};
public IEnumerable<string> Strings { get; }
public IEnumerable<string> Strings { get; private set; }
void Initialize()
{
@@ -103,6 +133,80 @@ namespace libexeinfo
if(!Recognized) return;
Type = "DOS Executable (MZ)";
if(resourceStream == null) return;
buffer = new byte[Marshal.SizeOf(typeof(GEM.GemResourceHeader))];
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;
List<string> strings = new List<string>();
if(ResourceHeader.rsh_ntree > 0)
{
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);
treeOffsets[i] = BitConverter.ToInt32(tmp, 0);
}
ResourceObjectRoots = new GEM.TreeObjectNode[ResourceHeader.rsh_ntree];
for(int i = 0; i < ResourceHeader.rsh_ntree; i++)
{
if(treeOffsets[i] <= 0 || treeOffsets[i] >= resourceStream.Length) continue;
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);
GEM.ObjectNode node = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.ObjectNode>(buffer);
nodes.Add(node);
if(((GEM.ObjectFlags)node.ob_flags).HasFlag(GEM.ObjectFlags.Lastob)) break;
}
List<short> knownNodes = new List<short>();
ResourceObjectRoots[i] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, false,
Encoding.AtariSTEncoding);
}
}
else if(ResourceHeader.rsh_nobs > 0)
{
GEM.ObjectNode[] nodes = new GEM.ObjectNode[ResourceHeader.rsh_nobs];
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);
nodes[i] = BigEndianMarshal.ByteArrayToStructureLittleEndian<GEM.ObjectNode>(buffer);
}
List<short> knownNodes = new List<short>();
ResourceObjectRoots = new GEM.TreeObjectNode[1];
// TODO: Correct encoding?
ResourceObjectRoots[0] =
GEM.ProcessResourceObject(nodes, ref knownNodes, 0, resourceStream, strings, false,
Encoding.AtariSTEncoding);
}
if(strings.Count > 0)
{
strings.Sort();
Strings = strings.Distinct();
}
}
/// <summary>

View File

@@ -49,9 +49,10 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AtariST\Enums.cs" />
<Compile Include="AtariST\Resources.cs" />
<Compile Include="Enums.cs" />
<Compile Include="GEM\Enums.cs" />
<Compile Include="GEM\Resources.cs" />
<Compile Include="GEM\Structs.cs" />
<Compile Include="IExecutable.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="MZ\Consts.cs" />