Port more PE extraction

This commit is contained in:
Matt Nadareski
2025-08-25 20:51:35 -04:00
parent bb93792f57
commit ef6ff85e96

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.IO;
using System.Text;
using SabreTools.IO.Compression.zlib;
using SabreTools.IO.Extensions;
using SabreTools.Matching;
using SabreTools.Serialization.Interfaces;
@@ -1084,12 +1085,140 @@ namespace SabreTools.Serialization.Wrappers
#region Extraction
/// <inheritdoc/>
/// <remarks>This only extracts overlay and resource data</remarks>
/// <remarks>
/// This extracts the following data:
/// - Archives and executables in the overlay
/// - Archives and executables in resource data
/// - CExe-compressed resource data
/// </remarks>
public bool Extract(string outputDirectory, bool includeDebug)
{
bool cexe = ExtractCExe(outputDirectory, includeDebug);
bool overlay = ExtractFromOverlay(outputDirectory, includeDebug);
bool resources = ExtractFromResources(outputDirectory, includeDebug);
return overlay || resources;
return cexe || overlay || resources;
}
/// <summary>
/// Extract a CExe-compressed executable
/// </summary>
/// <param name="outputDirectory">Output directory to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
/// <returns>True if extraction succeeded, false otherwise</returns>
public bool ExtractCExe(string outputDirectory, bool includeDebug)
{
try
{
// Get all resources of type 99 with index 2
var resources = FindResourceByNamedType("99, 2");
if (resources == null || resources.Count == 0)
return false;
// Get the first resource of type 99 with index 2
var payload = resources[0];
if (payload == null || payload.Length == 0)
return false;
// Create the output data buffer
byte[]? data = [];
// If we had the decompression DLL included, it's zlib
if (FindResourceByNamedType("99, 1").Count > 0)
data = ExtractCExeZlib(payload);
else
data = ExtractCExeLZ(payload);
// If we have no data
if (data == null)
return false;
// Create the temp filename
string tempFile = string.IsNullOrEmpty(Filename) ? "temp.sxe" : $"{Path.GetFileNameWithoutExtension(Filename)}.sxe";
tempFile = Path.Combine(outputDirectory, tempFile);
var directoryName = Path.GetDirectoryName(tempFile);
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
// Write the file data to a temp file
var tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite);
tempStream.Write(data, 0, data.Length);
return true;
}
catch (Exception ex)
{
if (includeDebug) Console.Error.WriteLine(ex);
return false;
}
}
/// <summary>
/// Extract CExe data compressed with LZ
/// </summary>
/// <param name="resource">Resource data to inflate</param>
/// <returns>Inflated data on success, null otherwise</returns>
private byte[]? ExtractCExeLZ(byte[] resource)
{
try
{
var decompressor = IO.Compression.SZDD.Decompressor.CreateSZDD(resource);
var dataStream = new MemoryStream();
decompressor.CopyTo(dataStream);
return dataStream.ToArray();
}
catch
{
// Reset the data
return null;
}
}
/// <summary>
/// Extract CExe data compressed with zlib
/// </summary>
/// <param name="resource">Resource data to inflate</param>
/// <returns>Inflated data on success, null otherwise</returns>
private byte[]? ExtractCExeZlib(byte[] resource)
{
try
{
// Inflate the data into the buffer
var zstream = new ZLib.z_stream_s();
byte[] data = new byte[resource.Length * 4];
unsafe
{
fixed (byte* payloadPtr = resource)
fixed (byte* dataPtr = data)
{
zstream.next_in = payloadPtr;
zstream.avail_in = (uint)resource.Length;
zstream.total_in = (uint)resource.Length;
zstream.next_out = dataPtr;
zstream.avail_out = (uint)data.Length;
zstream.total_out = 0;
ZLib.inflateInit_(zstream, ZLib.zlibVersion(), resource.Length);
int zret = ZLib.inflate(zstream, 1);
ZLib.inflateEnd(zstream);
}
}
// Trim the buffer to the proper size
uint read = zstream.total_out;
#if NETFRAMEWORK
var temp = new byte[read];
Array.Copy(data, temp, read);
data = temp;
#else
data = new ReadOnlySpan<byte>(data, 0, (int)read).ToArray();
#endif
return data;
}
catch
{
// Reset the data
return null;
}
}
/// <summary>
@@ -1097,7 +1226,7 @@ namespace SabreTools.Serialization.Wrappers
/// </summary>
/// <param name="outputDirectory">Output directory to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
private bool ExtractFromOverlay(string outputDirectory, bool includeDebug)
public bool ExtractFromOverlay(string outputDirectory, bool includeDebug)
{
try
{
@@ -1152,7 +1281,7 @@ namespace SabreTools.Serialization.Wrappers
/// </summary>
/// <param name="outputDirectory">Output directory to write to</param>
/// <param name="includeDebug">True to include debug data, false otherwise</param>
private bool ExtractFromResources(string outputDirectory, bool includeDebug)
public bool ExtractFromResources(string outputDirectory, bool includeDebug)
{
try
{