/** * Steamless - Copyright (c) 2015 - 2023 atom0s [atom0s@live.com] * * This work is licensed under the Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License. * To view a copy of this license, visit http://creativecommons.org/licenses/by-nc-nd/4.0/ or send a letter to * Creative Commons, PO Box 1866, Mountain View, CA 94042, USA. * * By using Steamless, you agree to the above license and its terms. * * Attribution - You must give appropriate credit, provide a link to the license and indicate if changes were * made. You must do so in any reasonable manner, but not in any way that suggests the licensor * endorses you or your use. * * Non-Commercial - You may not use the material (Steamless) for commercial purposes. * * No-Derivatives - If you remix, transform, or build upon the material (Steamless), you may not distribute the * modified material. You are, however, allowed to submit the modified works back to the original * Steamless project in attempt to have it added to the original project. * * You may not apply legal terms or technological measures that legally restrict others * from doing anything the license permits. * * No warranties are given. */ namespace Steamless.CLI { using Steamless.API; using Steamless.API.Events; using Steamless.API.Model; using Steamless.API.Services; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; internal class Program { /// /// Steamless API Version /// /// Main define for this is within DataService.cs and should match that value. /// private static readonly Version SteamlessApiVersion = new Version(1, 0); /// /// Prints the Steamless header information. /// static void PrintHeader() { Console.WriteLine(" _________ __ .__ "); Console.WriteLine(" / _____// |_ ____ _____ _____ | | ____ ______ ______"); Console.WriteLine(" \\_____ \\\\ __\\/ __ \\\\__ \\ / \\| | _/ __ \\ / ___// ___/"); Console.WriteLine(" / \\| | \\ ___/ / __ \\| Y Y \\ |_\\ ___/ \\___ \\ \\___ \\ "); Console.WriteLine("/_______ /|__| \\___ >____ /__|_| /____/\\___ >____ >____ >"); Console.WriteLine(" \\/ \\/ \\/ \\/ \\/ \\/ \\/ \n"); Console.WriteLine("Steamless - SteamStub DRM Remover"); Console.WriteLine("by atom0s\n"); Console.WriteLine("GitHub : https://github.com/atom0s/Steamless"); Console.WriteLine("Homepage : https://atom0s.com"); Console.WriteLine("Donations : https://paypal.me/atom0s"); Console.WriteLine("Donations : https://github.com/sponsors/atom0s"); Console.WriteLine("Donations : https://patreon.com/atom0s\n"); } /// /// Prints the Steamless command line help information. /// static void PrintHelp() { Console.WriteLine("Usage:"); Console.WriteLine(" Steamless.CLI.exe [options] [file]\n\n"); Console.WriteLine("Options:"); Console.WriteLine(" --quiet - Disables output of debug log messages."); Console.WriteLine(" --keepbind - Keeps the .bind section in the unpacked file."); Console.WriteLine(" --keepstub - Keeps the DOS stub in the unpacked file."); Console.WriteLine(" --dumppayload - Dumps the stub payload to disk."); Console.WriteLine(" --dumpdrmp - Dumps the SteamDRMP.dll to disk."); Console.WriteLine(" --realign - Realigns the unpacked file sections."); Console.WriteLine(" --recalcchecksum - Recalculates the unpacked file checksum."); Console.WriteLine(" --exp - Use experimental features."); } /// /// Obtains a list of available Steamless plugins. /// /// static List GetSteamlessPlugins(LoggingService logService) { try { // The list of valid plugins.. var plugins = new List(); // Build a path to the plugins folder.. var path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins"); // Loop the DLL files and attempt to load them.. foreach (var dll in Directory.GetFiles(path, "*.dll")) { // Skip the Steamless.API.dll file.. if (dll.ToLower().Contains("steamless.api.dll")) continue; try { // Load the assembly.. var asm = Assembly.Load(File.ReadAllBytes(dll)); // Locate the class inheriting the plugin base.. var baseClass = asm.GetTypes().SingleOrDefault(t => t.BaseType == typeof(SteamlessPlugin)); if (baseClass == null) continue; // Locate the SteamlessApiVersion attribute on the base class.. var baseAttr = baseClass.GetCustomAttributes(typeof(SteamlessApiVersionAttribute), false); if (baseAttr.Length == 0) continue; // Validate the interface version.. var apiVersion = (SteamlessApiVersionAttribute)baseAttr[0]; if (apiVersion.Version != SteamlessApiVersion) continue; // Create an instance of the plugin.. var plugin = (SteamlessPlugin)Activator.CreateInstance(baseClass); if (!plugin.Initialize(logService)) continue; plugins.Add(plugin); } catch { } } // Order the plugins by their name.. return plugins.OrderBy(p => p.Name).ToList(); } catch { return new List(); } } /// /// Application entry point. /// /// static int Main(string[] args) { // AssemblyResolve override to load modules from the Plugins folder.. AppDomain.CurrentDomain.AssemblyResolve += (sender, e) => { // Obtain the name of the assembly being loaded.. var name = e.Name.Contains(",") ? e.Name.Substring(0, e.Name.IndexOf(",", StringComparison.InvariantCultureIgnoreCase)) : e.Name.Replace(".dll", ""); // Ignore resource assembly loading.. if (name.ToLower().EndsWith(".resources")) return null; // Build a full path to the possible embedded file.. var fullName = $"{Assembly.GetExecutingAssembly().EntryPoint.DeclaringType?.Namespace}.Embedded.{new AssemblyName(e.Name).Name}.dll"; using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(fullName)) { // If not embedded try to load from the plugin folder.. if (stream == null) { var f = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", name + ".dll"); return File.Exists(f) ? Assembly.Load(File.ReadAllBytes(f)) : null; } // Read and load the embedded resource.. var data = new byte[stream.Length]; stream.Read(data, 0, (int)stream.Length); return Assembly.Load(data); } }; return Program.Run(args); } /// /// Runs the Steamless command line operations. /// /// /// static int Run(string[] args) { var logService = new LoggingService(); var opts = new SteamlessOptions(); var file = string.Empty; // Prepare the logging service.. logService.AddLogMessage += (sender, e) => { if (!opts.VerboseOutput && e.MessageType == LogMessageType.Debug) return; try { if (sender != null) e.Message = $"[{sender.GetType().Assembly.GetName().Name}] {e.Message}"; else e.Message = $"[Steamless] {e.Message}"; } catch { } Console.WriteLine(e.Message); }; // Print the program header.. Program.PrintHeader(); // Process command line arguments for the various Steamless options.. foreach (var arg in args) { if (arg.ToLower() == "--quiet") opts.VerboseOutput = false; if (arg.ToLower() == "--keepbind") opts.KeepBindSection = true; if (arg.ToLower() == "--keepstub") opts.ZeroDosStubData = false; if (arg.ToLower() == "--dumppayload") opts.DumpPayloadToDisk = true; if (arg.ToLower() == "--dumpdrmp") opts.DumpSteamDrmpToDisk = true; if (arg.ToLower() == "--realign") opts.DontRealignSections = false; if (arg.ToLower() == "--recalcchecksum") opts.RecalculateFileChecksum = true; if (arg.ToLower() == "--exp") opts.UseExperimentalFeatures = true; if (!arg.StartsWith("--")) file = arg; } // Ensure an input file was given.. if (string.IsNullOrEmpty(file)) { Program.PrintHelp(); return 1; } // Ensure the input file exists.. if (!File.Exists(file)) { logService.OnAddLogMessage(null, new LogMessageEventArgs("Invalid input file given; cannot continue.", LogMessageType.Error)); return 1; } // Collect the list of available plugins.. var plugins = GetSteamlessPlugins(logService); plugins.ForEach(p => logService.OnAddLogMessage(null, new LogMessageEventArgs($"Loaded plugin: {p.Name} - by {p.Author} (v.{p.Version})", LogMessageType.Success))); // Ensure plugins were found and loaded.. if (plugins.Count == 0) { logService.OnAddLogMessage(null, new LogMessageEventArgs("No plugins were loaded; be sure to fully extract Steamless before running!", LogMessageType.Error)); return 1; } // Loop through the plugins and try to unpack the file.. foreach (var p in plugins) { // Check if the plugin can process the file.. if (p.CanProcessFile(file)) { var ret = p.ProcessFile(file, opts); logService.OnAddLogMessage(null, !ret ? new LogMessageEventArgs("Failed to unpack file.", LogMessageType.Error) : new LogMessageEventArgs("Successfully unpacked file!", LogMessageType.Success)); if (ret) return 0; } } logService.OnAddLogMessage(null, new LogMessageEventArgs("All unpackers failed to unpack file.", LogMessageType.Error)); return 1; } } }