Skip to content

Instantly share code, notes, and snippets.

@stephenbradshaw
Created April 29, 2022 09:43
Show Gist options
  • Save stephenbradshaw/25fce028729d22b0f997cd3007a39748 to your computer and use it in GitHub Desktop.
Save stephenbradshaw/25fce028729d22b0f997cd3007a39748 to your computer and use it in GitHub Desktop.
C# code for extracting resources from .Net assemblies to auto named files in pwd on disk - handles costura compressed files
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Reflection;
using System.Text;
using System;
// Compile with: csc extractor.cs
// Has extremely minimal error handling and testing and takes most of its code from StackOverflow ;)
namespace Extractor
{
class Extractor {
private static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream())
{
input.CopyTo(ms);
return ms.ToArray();
}
}
private static byte[] Decompress(byte[] data)
{
byte[] decompressedArray = null;
try
{
using (MemoryStream decompressedStream = new MemoryStream())
{
using (MemoryStream compressStream = new MemoryStream(data))
{
using (DeflateStream deflateStream = new DeflateStream(compressStream, CompressionMode.Decompress))
{
deflateStream.CopyTo(decompressedStream);
}
}
decompressedArray = decompressedStream.ToArray();
}
}
catch (Exception exception)
{
Console.WriteLine(exception.ToString());
}
return decompressedArray;
}
private static void SaveDecompressedFileStream(String path, Stream stream)
{
byte[] decompressed = Decompress(ReadFully(stream));
File.WriteAllBytes(path, decompressed);
}
private static void SaveFileStream(String path, Stream stream)
{
byte[] decompressed = ReadFully(stream);
File.WriteAllBytes(path, decompressed);
}
static void Main(string[] args)
{
if (args.Length != 1) {
Console.WriteLine("Provide filename as parameter 1");
}
else {
if (File.Exists(args[0]))
{
Console.WriteLine("Extracting resources from {0}", args[0]);
Assembly assembly = Assembly.LoadFrom(args[0]);
// Create a lookup table from the Assembly References for more friendly output filenames for some costura resources
IDictionary<string, string> lowercaseLookup = new Dictionary<string, string>();
foreach (var refassembly in assembly.GetReferencedAssemblies()) {
lowercaseLookup.Add(refassembly.Name.ToLower(), refassembly.Name);
}
foreach (var resourcename in assembly.GetManifestResourceNames())
{
bool compressed = false;
StringBuilder fn = new();
string[] bits = resourcename.Split(".");
if (bits.Length > 3) {
if ( (bits[0].Equals("costura")) && (bits[bits.Length-1].Equals("compressed")) )
{
if (lowercaseLookup.ContainsKey(bits[1]))
{
bits[1] = lowercaseLookup[bits[1]];
}
string[] nb = bits.Skip(1).Take(bits.Length - 2).ToArray();
fn.Append(String.Join(".", nb));
compressed = true;
}
else{
fn.Append(resourcename);
}
}
else
{
fn.Append(resourcename);
}
Stream stream = assembly.GetManifestResourceStream(resourcename);
if (compressed) {
Console.WriteLine("Extracting compressed resource {0} into file {1}", resourcename, fn);
SaveDecompressedFileStream(fn.ToString(), stream);
}
else
{
Console.WriteLine("Extracting resource {0} into file {1}", resourcename, fn);
SaveFileStream(fn.ToString(), stream);
}
}
}
else{
Console.WriteLine("File {0} does not exist!", args[0]);
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment