Skip to content

Commit

Permalink
More work on porting various resources.
Browse files Browse the repository at this point in the history
  • Loading branch information
Lloyd Kinsella committed Sep 5, 2018
1 parent ab94e95 commit 857fd55
Show file tree
Hide file tree
Showing 10 changed files with 452 additions and 5 deletions.
2 changes: 1 addition & 1 deletion src/Workshell.PE.Resources/Menus/MenuResource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ public async Task<Menu> GetMenuAsync(ResourceLanguage language)
}
catch (Exception ex)
{
throw new PortableExecutableImageException(Image, "Could not read dialog from stream.", ex);
throw new PortableExecutableImageException(Image, "Could not read menu from stream.", ex);
}
}
}
Expand Down
46 changes: 46 additions & 0 deletions src/Workshell.PE.Resources/Messages/MessageTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Resources.Messages
{
public sealed class MessageTable : IEnumerable<MessageTableBlock>
{
private readonly MessageTableBlock[] _blocks;

internal MessageTable(MessageTableResource resource, uint languageId, MessageTableBlock[] blocks)
{
_blocks = blocks;

Resource = resource;
Language = languageId;
Count = _blocks.Length;
}

#region Methods

public IEnumerator<MessageTableBlock> GetEnumerator()
{
foreach (var block in _blocks)
yield return block;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion

#region Properties

public MessageTableResource Resource { get; }
public ResourceLanguage Language { get; }

public int Count { get; }
public MessageTableBlock this[int index] => _blocks[index];

#endregion
}
}
48 changes: 48 additions & 0 deletions src/Workshell.PE.Resources/Messages/MessageTableBlock.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Resources.Messages
{
public sealed class MessageTableBlock : IEnumerable<MessageTableEntry>
{
private readonly MessageTableEntry[] _entries;

internal MessageTableBlock(uint lowId, uint highId, uint offsetToEntries, MessageTableEntry[] entries)
{
_entries = entries;

LowId = lowId;
HighId = highId;
OffsetToEntries = offsetToEntries;
Count = _entries.Length;
}

#region Methods

public IEnumerator<MessageTableEntry> GetEnumerator()
{
foreach (var entry in _entries)
yield return entry;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion

#region Properties

public uint LowId { get; }
public uint HighId { get; }
public uint OffsetToEntries { get; }

public int Count { get; }
public MessageTableEntry this[int index] => _entries[index];

#endregion
}
}
33 changes: 33 additions & 0 deletions src/Workshell.PE.Resources/Messages/MessageTableEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Resources.Messages
{
public sealed class MessageTableEntry
{
internal MessageTableEntry(uint id, string message, bool isUnicode)
{
Id = id;
Message = message;
IsUnicode = isUnicode;
}

#region Methods

public override string ToString()
{
return $"0x{Id:X8}: {Message}";
}

#endregion

#region Properties

public uint Id { get; }
public string Message { get; }
public bool IsUnicode { get; }

#endregion
}
}
98 changes: 98 additions & 0 deletions src/Workshell.PE.Resources/Messages/MessageTableResource.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks;
using Workshell.PE.Extensions;
using Workshell.PE.Resources.Native;

namespace Workshell.PE.Resources.Messages
{
public sealed class MessageTableResource : Resource
{
public MessageTableResource(PortableExecutableImage image, ResourceType type, ResourceDirectoryEntry entry, ResourceId id) : base(image, type, entry, id)
{
}

#region Static Methods

public static bool Register()
{
var type = new ResourceId(ResourceType.MessageTable);

return ResourceType.Register<MessageTableResource>(type);
}

#endregion

#region Methods

public MessageTable GetTable()
{
return GetTable(ResourceLanguage.English.UnitedStates);
}

public async Task<MessageTable> GetTableAsync()
{
return await GetTableAsync(ResourceLanguage.English.UnitedStates).ConfigureAwait(false);
}

public MessageTable GetTable(ResourceLanguage language)
{
return GetTableAsync(language).GetAwaiter().GetResult();
}

public async Task<MessageTable> GetTableAsync(ResourceLanguage language)
{
var buffer = await GetBytesAsync(language).ConfigureAwait(false);

using (var mem = new MemoryStream(buffer))
{
var tuples = new List<Tuple<uint, uint, uint>>();
var resourceData = await mem.ReadStructAsync<MESSAGE_RESOURCE_DATA>().ConfigureAwait(false);

for(var i = 0; i < resourceData.NumberOfBlocks; i++)
{
var block = await mem.ReadStructAsync<MESSAGE_RESOURCE_BLOCK>().ConfigureAwait(false);
var tuple = new Tuple<uint, uint, uint>(block.OffsetToEntries, block.LowId, block.HighId);

tuples.Add(tuple);
}

var blocks = new MessageTableBlock[resourceData.NumberOfBlocks];

for(var i = 0; i < tuples.Count; i++)
{
var tuple = tuples[i];

mem.Seek(tuple.Item1, SeekOrigin.Begin);

var count = (tuple.Item3 - tuple.Item2).ToInt32() + 1;
var entries = new MessageTableEntry[count];
var id = tuple.Item2;

for (var j = 0; j < count; j++)
{
var resourceEntry = await mem.ReadStructAsync<MESSAGE_RESOURCE_ENTRY>().ConfigureAwait(false);
var stringBuffer = await mem.ReadBytesAsync(resourceEntry.Length - sizeof(uint)).ConfigureAwait(false);
var message = (resourceEntry.Flags == 0 ? Encoding.ASCII.GetString(stringBuffer) : Encoding.Unicode.GetString(stringBuffer));
var entry = new MessageTableEntry(id, message, resourceEntry.Flags != 0);

entries[j] = entry;
id++;
}

var block = new MessageTableBlock(tuple.Item2, tuple.Item3, tuple.Item1, entries);

blocks[i] = block;
}

var table = new MessageTable(this, language, blocks);

return table;
}
}

#endregion
}
}
28 changes: 28 additions & 0 deletions src/Workshell.PE.Resources/Native/MESSAGETABLE.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Text;

namespace Workshell.PE.Resources.Native
{
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MESSAGE_RESOURCE_DATA
{
public uint NumberOfBlocks;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MESSAGE_RESOURCE_BLOCK
{
public uint LowId;
public uint HighId;
public uint OffsetToEntries;
}

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MESSAGE_RESOURCE_ENTRY
{
public ushort Length;
public ushort Flags;
}
}
46 changes: 46 additions & 0 deletions src/Workshell.PE.Resources/Strings/StringTable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Resources.Strings
{
public sealed class StringTable : IEnumerable<StringTableEntry>
{
private readonly StringTableEntry[] _entries;

internal StringTable(StringTableResource resource, uint languageId, StringTableEntry[] entries)
{
_entries = entries;

Count = _entries.Length;
Resource = resource;
Language = languageId;
}

#region Methods

public IEnumerator<StringTableEntry> GetEnumerator()
{
foreach (var entry in _entries)
yield return entry;
}

IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}

#endregion

#region Properties

public StringTableResource Resource { get; }
public ResourceLanguage Language { get; }

public int Count { get; }
public StringTableEntry this[int index] => _entries[index];

#endregion
}
}
35 changes: 35 additions & 0 deletions src/Workshell.PE.Resources/Strings/StringTableEntry.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Workshell.PE.Resources.Strings
{
public sealed class StringTableEntry
{
internal StringTableEntry(ushort id, string value)
{
Id = id;
Value = value;
}

#region Methods

public override string ToString()
{
if (Id == 0)
return "(Empty)";

return $"{Id} = {Value}";
}

#endregion

#region Properties

public ushort Id { get; }
public string Value { get; }
public bool IsEmpty => (Id == 0);

#endregion
}
}
Loading

0 comments on commit 857fd55

Please sign in to comment.