2018-02-27 04:48:47 +00:00
|
|
|
|
//
|
2018-02-27 20:53:03 +00:00
|
|
|
|
// Resources.cs
|
2018-02-27 04:48:47 +00:00
|
|
|
|
//
|
|
|
|
|
|
// Author:
|
|
|
|
|
|
// Natalia Portillo <claunia@claunia.com>
|
|
|
|
|
|
//
|
|
|
|
|
|
// Copyright (c) 2017 Copyright © Claunia.com
|
|
|
|
|
|
//
|
|
|
|
|
|
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
|
|
|
// of this software and associated documentation files (the "Software"), to deal
|
|
|
|
|
|
// in the Software without restriction, including without limitation the rights
|
|
|
|
|
|
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
|
|
|
// copies of the Software, and to permit persons to whom the Software is
|
|
|
|
|
|
// furnished to do so, subject to the following conditions:
|
|
|
|
|
|
//
|
|
|
|
|
|
// The above copyright notice and this permission notice shall be included in
|
|
|
|
|
|
// all copies or substantial portions of the Software.
|
|
|
|
|
|
//
|
|
|
|
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
|
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
|
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
|
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
|
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
|
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
|
|
|
|
// THE SOFTWARE.
|
|
|
|
|
|
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Runtime.InteropServices;
|
2018-02-27 20:53:03 +00:00
|
|
|
|
using System.Text;
|
2018-02-27 04:48:47 +00:00
|
|
|
|
|
|
|
|
|
|
namespace libexeinfo
|
|
|
|
|
|
{
|
2018-02-27 20:53:03 +00:00
|
|
|
|
public static partial class GEM
|
2018-02-27 04:48:47 +00:00
|
|
|
|
{
|
2018-02-27 20:53:03 +00:00
|
|
|
|
internal static TreeObjectNode ProcessResourceObject(IList<ObjectNode> nodes, ref List<short> knownNodes,
|
|
|
|
|
|
short nodeNumber, Stream resourceStream,
|
|
|
|
|
|
List<string> strings, bool bigEndian,
|
|
|
|
|
|
Encoding encoding)
|
2018-02-27 04:48:47 +00:00
|
|
|
|
{
|
|
|
|
|
|
TreeObjectNode node = new TreeObjectNode
|
|
|
|
|
|
{
|
|
|
|
|
|
type = (ObjectTypes)nodes[nodeNumber].ob_type,
|
|
|
|
|
|
flags = (ObjectFlags)nodes[nodeNumber].ob_flags,
|
|
|
|
|
|
state = (ObjectStates)nodes[nodeNumber].ob_state,
|
|
|
|
|
|
data = nodes[nodeNumber].ob_spec,
|
|
|
|
|
|
x = nodes[nodeNumber].ob_x,
|
|
|
|
|
|
y = nodes[nodeNumber].ob_y,
|
|
|
|
|
|
width = nodes[nodeNumber].ob_width,
|
|
|
|
|
|
height = nodes[nodeNumber].ob_height
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2018-02-27 20:21:27 +00:00
|
|
|
|
byte[] buffer;
|
|
|
|
|
|
List<byte> chars;
|
|
|
|
|
|
switch(node.type)
|
|
|
|
|
|
{
|
|
|
|
|
|
case ObjectTypes.G_TEXT:
|
|
|
|
|
|
case ObjectTypes.G_BOXTEXT:
|
|
|
|
|
|
case ObjectTypes.G_FTEXT:
|
|
|
|
|
|
case ObjectTypes.G_FBOXTEXT:
|
|
|
|
|
|
if(node.data <= 0 || node.data >= resourceStream.Length) break;
|
|
|
|
|
|
|
|
|
|
|
|
resourceStream.Position = node.data;
|
|
|
|
|
|
buffer = new byte[Marshal.SizeOf(typeof(TedInfo))];
|
|
|
|
|
|
resourceStream.Read(buffer, 0, buffer.Length);
|
2018-02-27 20:53:03 +00:00
|
|
|
|
TedInfo ted = bigEndian
|
|
|
|
|
|
? BigEndianMarshal.ByteArrayToStructureBigEndian<TedInfo>(buffer)
|
|
|
|
|
|
: BigEndianMarshal.ByteArrayToStructureLittleEndian<TedInfo>(buffer);
|
2018-02-27 20:21:27 +00:00
|
|
|
|
|
|
|
|
|
|
node.TedInfo = new TextBlock
|
|
|
|
|
|
{
|
|
|
|
|
|
Font = (ObjectFont)ted.te_font,
|
|
|
|
|
|
Justification = (ObjectJustification)ted.te_just,
|
|
|
|
|
|
BorderColor = (ObjectColors)((ted.te_color & BorderColorMask) >> 12),
|
|
|
|
|
|
TextColor = (ObjectColors)((ted.te_color & TextColorMask) >> 8),
|
|
|
|
|
|
Transparency = (ted.te_color & TransparentColor) != TransparentColor,
|
|
|
|
|
|
Fill = (ObjectFillPattern)((ted.te_color & FillPatternMask) >> 4),
|
|
|
|
|
|
InsideColor = (ObjectColors)(ted.te_color & InsideColorMask),
|
|
|
|
|
|
Thickness = ted.te_thickness
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
byte[] tmpStr;
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
if(ted.te_ptext > 0 && ted.te_ptext < resourceStream.Length && ted.te_txtlen > 0)
|
2018-02-27 20:21:27 +00:00
|
|
|
|
{
|
2018-02-28 20:05:39 +00:00
|
|
|
|
tmpStr = new byte[ted.te_txtlen];
|
2018-02-27 20:21:27 +00:00
|
|
|
|
resourceStream.Position = ted.te_ptext;
|
2018-02-28 20:05:39 +00:00
|
|
|
|
resourceStream.Read(tmpStr, 0, ted.te_txtlen);
|
|
|
|
|
|
node.TedInfo.Text = StringHandlers.CToString(tmpStr, encoding);
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(node.TedInfo.Text)) strings.Add(node.TedInfo.Text.Trim());
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
if(ted.te_pvalid > 0 && ted.te_pvalid < resourceStream.Length && ted.te_txtlen > 0)
|
2018-02-27 20:21:27 +00:00
|
|
|
|
{
|
2018-02-28 20:05:39 +00:00
|
|
|
|
tmpStr = new byte[ted.te_txtlen];
|
2018-02-27 20:21:27 +00:00
|
|
|
|
resourceStream.Position = ted.te_pvalid;
|
2018-02-28 20:05:39 +00:00
|
|
|
|
resourceStream.Read(tmpStr, 0, ted.te_txtlen);
|
|
|
|
|
|
node.TedInfo.Validation = StringHandlers.CToString(tmpStr, encoding);
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(node.TedInfo.Validation))
|
|
|
|
|
|
strings.Add(node.TedInfo.Validation.Trim());
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
if(ted.te_ptmplt > 0 && ted.te_ptmplt < resourceStream.Length && ted.te_tmplen > 0)
|
2018-02-27 20:21:27 +00:00
|
|
|
|
{
|
2018-02-28 20:05:39 +00:00
|
|
|
|
tmpStr = new byte[ted.te_tmplen];
|
2018-02-27 20:21:27 +00:00
|
|
|
|
resourceStream.Position = ted.te_ptmplt;
|
2018-02-28 20:05:39 +00:00
|
|
|
|
resourceStream.Read(tmpStr, 0, ted.te_tmplen);
|
|
|
|
|
|
node.TedInfo.Template = StringHandlers.CToString(tmpStr, encoding);
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(node.TedInfo.Template)) strings.Add(node.TedInfo.Template.Trim());
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
resourceStream.Position = node.data;
|
|
|
|
|
|
buffer = new byte[Marshal.SizeOf(typeof(BitBlock))];
|
|
|
|
|
|
resourceStream.Read(buffer, 0, buffer.Length);
|
2018-02-27 20:53:03 +00:00
|
|
|
|
BitBlock bitBlock = bigEndian
|
|
|
|
|
|
? BigEndianMarshal.ByteArrayToStructureBigEndian<BitBlock>(buffer)
|
|
|
|
|
|
: BigEndianMarshal.ByteArrayToStructureLittleEndian<BitBlock>(buffer);
|
2018-02-27 20:21:27 +00:00
|
|
|
|
|
|
|
|
|
|
node.BitBlock = new BitmapBlock
|
|
|
|
|
|
{
|
|
|
|
|
|
Color = (ObjectColors)bitBlock.bi_color,
|
|
|
|
|
|
Height = bitBlock.bi_hl,
|
|
|
|
|
|
Width = bitBlock.bi_wb * 8,
|
|
|
|
|
|
X = bitBlock.bi_x,
|
|
|
|
|
|
Y = bitBlock.bi_y
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
if(bitBlock.bi_pdata == 0 || bitBlock.bi_pdata >= resourceStream.Length) break;
|
|
|
|
|
|
|
|
|
|
|
|
node.BitBlock.Data = new byte[bitBlock.bi_wb * bitBlock.bi_hl];
|
|
|
|
|
|
resourceStream.Position = bitBlock.bi_pdata;
|
|
|
|
|
|
resourceStream.Read(node.BitBlock.Data, 0, node.BitBlock.Data.Length);
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
// Because the image is stored as words, they get reversed on PC GEM (Little-endian)
|
|
|
|
|
|
if(!bigEndian)
|
|
|
|
|
|
{
|
|
|
|
|
|
byte[] data = new byte[node.BitBlock.Data.Length];
|
|
|
|
|
|
for(int i = 0; i < data.Length; i += 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
data[i] = node.BitBlock.Data[i + 1];
|
|
|
|
|
|
data[i + 1] = node.BitBlock.Data[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
node.BitBlock.Data = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-27 20:21:27 +00:00
|
|
|
|
break;
|
|
|
|
|
|
case ObjectTypes.G_USERDEF:
|
|
|
|
|
|
if(node.data <= 0 || node.data >= resourceStream.Length) break;
|
|
|
|
|
|
|
|
|
|
|
|
resourceStream.Position = node.data;
|
|
|
|
|
|
buffer = new byte[Marshal.SizeOf(typeof(UserBlock))];
|
|
|
|
|
|
resourceStream.Read(buffer, 0, buffer.Length);
|
2018-02-27 20:53:03 +00:00
|
|
|
|
node.UserBlock = bigEndian
|
|
|
|
|
|
? BigEndianMarshal.ByteArrayToStructureBigEndian<UserBlock>(buffer)
|
|
|
|
|
|
: BigEndianMarshal.ByteArrayToStructureLittleEndian<UserBlock>(buffer);
|
2018-02-27 20:21:27 +00:00
|
|
|
|
break;
|
|
|
|
|
|
case ObjectTypes.G_ICON:
|
|
|
|
|
|
if(node.data <= 0 || node.data >= resourceStream.Length) break;
|
|
|
|
|
|
|
|
|
|
|
|
resourceStream.Position = node.data;
|
|
|
|
|
|
buffer = new byte[Marshal.SizeOf(typeof(IconBlock))];
|
|
|
|
|
|
resourceStream.Read(buffer, 0, buffer.Length);
|
2018-02-27 20:53:03 +00:00
|
|
|
|
IconBlock iconBlock = bigEndian
|
|
|
|
|
|
? BigEndianMarshal.ByteArrayToStructureBigEndian<IconBlock>(buffer)
|
|
|
|
|
|
: BigEndianMarshal.ByteArrayToStructureLittleEndian<IconBlock>(buffer);
|
2018-02-27 20:21:27 +00:00
|
|
|
|
|
|
|
|
|
|
node.IconBlock = new Icon
|
|
|
|
|
|
{
|
|
|
|
|
|
Width = iconBlock.ib_wicon,
|
|
|
|
|
|
Height = iconBlock.ib_hicon,
|
|
|
|
|
|
X = iconBlock.ib_xicon,
|
|
|
|
|
|
Y = iconBlock.ib_yicon,
|
2018-02-27 20:53:03 +00:00
|
|
|
|
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
|
2018-02-27 20:21:27 +00:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
node.IconBlock.Text = StringHandlers.CToString(chars.ToArray(), encoding);
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(node.IconBlock.Text)) strings.Add(node.IconBlock.Text.Trim());
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2018-02-28 20:05:39 +00:00
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
}
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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);
|
2018-02-28 20:05:39 +00:00
|
|
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
|
}
|
2018-02-27 20:21:27 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ObjectTypes.G_CICON:
|
|
|
|
|
|
//Console.WriteLine("ColorIconBlock pointer {0}", node.data);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case ObjectTypes.G_BUTTON:
|
|
|
|
|
|
case ObjectTypes.G_STRING:
|
|
|
|
|
|
case ObjectTypes.G_TITLE:
|
|
|
|
|
|
if(node.data <= 0 || node.data >= resourceStream.Length) break;
|
|
|
|
|
|
|
|
|
|
|
|
resourceStream.Position = node.data;
|
|
|
|
|
|
chars = new List<byte>();
|
|
|
|
|
|
while(true)
|
|
|
|
|
|
{
|
|
|
|
|
|
int character = resourceStream.ReadByte();
|
|
|
|
|
|
|
|
|
|
|
|
if(character <= 0) break;
|
|
|
|
|
|
|
|
|
|
|
|
chars.Add((byte)character);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-28 20:05:39 +00:00
|
|
|
|
node.String = StringHandlers.CToString(chars.ToArray(), encoding);
|
|
|
|
|
|
if(!string.IsNullOrWhiteSpace(node.String)) strings.Add(node.String.Trim());
|
2018-02-27 20:21:27 +00:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2018-02-27 04:48:47 +00:00
|
|
|
|
knownNodes.Add(nodeNumber);
|
|
|
|
|
|
|
|
|
|
|
|
if(nodes[nodeNumber].ob_head > 0 && !knownNodes.Contains(nodes[nodeNumber].ob_head))
|
2018-02-27 20:53:03 +00:00
|
|
|
|
node.child = ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_head, resourceStream,
|
|
|
|
|
|
strings, bigEndian, encoding);
|
2018-02-27 04:48:47 +00:00
|
|
|
|
|
|
|
|
|
|
if(nodes[nodeNumber].ob_next > 0 && !knownNodes.Contains(nodes[nodeNumber].ob_next))
|
2018-02-27 20:53:03 +00:00
|
|
|
|
node.sibling = ProcessResourceObject(nodes, ref knownNodes, nodes[nodeNumber].ob_next, resourceStream,
|
|
|
|
|
|
strings, bigEndian, encoding);
|
2018-02-27 04:48:47 +00:00
|
|
|
|
|
|
|
|
|
|
return node;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|