Initial code commit. Steamless v3.0.0.1 code base.

This commit is contained in:
atom0s 2017-01-06 02:17:35 -08:00
parent 2020f394e3
commit b3c1fc6e24
83 changed files with 11266 additions and 0 deletions

View File

@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>ExamplePlugin</RootNamespace>
<AssemblyName>ExamplePlugin</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Debug\Plugins\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Release\Plugins\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Steamless.API\Steamless.API.csproj">
<Project>{56c95629-3b34-47fe-b988-04274409294f}</Project>
<Name>Steamless.API</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

101
ExamplePlugin/Main.cs Normal file
View File

@ -0,0 +1,101 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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 ExamplePlugin
{
using Steamless.API;
using Steamless.API.Events;
using Steamless.API.Model;
using Steamless.API.Services;
using System;
[SteamlessApiVersion(1, 0)]
public class Main : SteamlessPlugin
{
/// <summary>
/// Internal logging service instance.
/// </summary>
private LoggingService m_LoggingService;
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "Steamless Development Team";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "Example Plugin";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "A simple plugin example.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => new Version(1, 0, 0, 0);
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
this.m_LoggingService = logService;
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs("ExamplePlugin was initialized!", LogMessageType.Debug));
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs("ExamplePlugin was asked to check if it can process a file!", LogMessageType.Debug));
return false;
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs("ExamplePlugin was asked to process a file!", LogMessageType.Debug));
return false;
}
}
}

View File

@ -0,0 +1,40 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("ExamplePlugin")]
[assembly: AssemblyDescription("Example plugin used with Steamless v3.")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("ExamplePlugin")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("97ac964a-e56f-415c-baea-d503e3d4d7b8")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,172 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Crypto
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
public class AesHelper : IDisposable
{
/// <summary>
/// Internal original key set by the user of this class.
/// </summary>
private readonly byte[] m_OriginalKey;
/// <summary>
/// Internal original iv set by the user of this class.
/// </summary>
private readonly byte[] m_OriginalIv;
/// <summary>
/// Internal AES crypto provider.
/// </summary>
private AesCryptoServiceProvider m_AesCryptoProvider;
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="key"></param>
/// <param name="iv"></param>
/// <param name="mode"></param>
/// <param name="padding"></param>
public AesHelper(byte[] key, byte[] iv, CipherMode mode = CipherMode.ECB, PaddingMode padding = PaddingMode.None)
{
// Store the original key and iv..
this.m_OriginalKey = key;
this.m_OriginalIv = iv;
// Create the AES crypto provider..
this.m_AesCryptoProvider = new AesCryptoServiceProvider
{
Key = key,
IV = iv,
Mode = mode,
Padding = padding
};
}
/// <summary>
/// Default Deconstructor
/// </summary>
~AesHelper()
{
this.Dispose(false);
}
/// <summary>
/// IDispose implementation.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// IDispose implementation.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
this.m_AesCryptoProvider?.Dispose();
this.m_AesCryptoProvider = null;
}
/// <summary>
/// Rebuilds the current iv (or the one given).
/// </summary>
/// <param name="iv"></param>
/// <returns></returns>
public bool RebuildIv(byte[] iv = null)
{
// Use the current iv if none is set..
if (iv == null)
iv = this.m_OriginalIv;
try
{
using (var decryptor = this.m_AesCryptoProvider.CreateDecryptor())
{
return decryptor.TransformBlock(iv, 0, iv.Length, this.m_OriginalIv, 0) > 0;
}
}
catch
{
return false;
}
}
/// <summary>
/// Decrypts the given data using the given mode and padding.
/// </summary>
/// <param name="data"></param>
/// <param name="mode"></param>
/// <param name="padding"></param>
/// <returns></returns>
public byte[] Decrypt(byte[] data, CipherMode mode, PaddingMode padding)
{
ICryptoTransform decryptor = null;
MemoryStream mStream = null;
CryptoStream cStream = null;
try
{
// Update the mode and padding for the decryption..
this.m_AesCryptoProvider.Mode = mode;
this.m_AesCryptoProvider.Padding = padding;
// Create the decryptor..
decryptor = this.m_AesCryptoProvider.CreateDecryptor(this.m_OriginalKey, this.m_OriginalIv);
// Create a memory stream for our data..
mStream = new MemoryStream(data);
// Create the crypto stream..
cStream = new CryptoStream(mStream, decryptor, CryptoStreamMode.Read);
// Decrypt the data..
var totalBuffer = new List<byte>();
var buffer = new byte[2048];
while ((cStream.Read(buffer, 0, 2048)) > 0)
totalBuffer.AddRange(buffer);
return totalBuffer.ToArray();
}
catch
{
return null;
}
finally
{
cStream?.Dispose();
mStream?.Dispose();
decryptor?.Dispose();
}
}
}
}

View File

@ -0,0 +1,62 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Events
{
using System;
public class LogMessageEventArgs : EventArgs
{
/// <summary>
/// Default Constructor
/// </summary>
public LogMessageEventArgs()
{
this.Message = string.Empty;
this.MessageType = LogMessageType.Debug;
}
/// <summary>
/// Overloaded Constructor
/// </summary>
/// <param name="msg"></param>
/// <param name="type"></param>
public LogMessageEventArgs(string msg, LogMessageType type)
{
this.Message = msg;
this.MessageType = type;
}
/// <summary>
/// Gets or sets the message text.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Gets or sets the type of message.
/// </summary>
public LogMessageType MessageType { get; set; }
}
}

View File

@ -0,0 +1,58 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Events
{
/// <summary>
/// Log Message Type Enumeration
/// </summary>
public enum LogMessageType
{
/// <summary>
/// Used for general purpose messages.
/// </summary>
Information = 0,
/// <summary>
/// Used for successful messages.
/// </summary>
Success = 1,
/// <summary>
/// Used for warnings.
/// </summary>
Warning = 2,
/// <summary>
/// Used for errors.
/// </summary>
Error = 3,
/// <summary>
/// Used for debug messages.
/// </summary>
Debug = 4
}
}

View File

@ -0,0 +1,42 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Extensions
{
using System.IO;
public static class FileStreamExtensions
{
/// <summary>
/// Writes a byte array to the file stream.
/// </summary>
/// <param name="fStream"></param>
/// <param name="data"></param>
public static void WriteBytes(this FileStream fStream, byte[] data)
{
fStream.Write(data, 0, data.Length);
}
}
}

View File

@ -0,0 +1,42 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Model
{
using System;
public class NavigatedEventArgs : EventArgs
{
/// <summary>
/// Gets or sets the previous view being navigated from.
/// </summary>
public ViewModelBase PreviousView { get; internal set; }
/// <summary>
/// Gets or sets the current view being navigated to.
/// </summary>
public ViewModelBase CurrentView { get; internal set; }
}
}

View File

@ -0,0 +1,104 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Model
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
[Serializable]
public class NotifiableModel : INotifyPropertyChanged
{
/// <summary>
/// Internal properties container.
/// </summary>
private readonly Dictionary<string, object> m_Properties;
/// <summary>
/// Default Constructor
/// </summary>
public NotifiableModel()
{
this.m_Properties = new Dictionary<string, object>();
}
/// <summary>
/// Event triggered when a property is changed.
/// </summary>
public event PropertyChangedEventHandler PropertyChanged;
/// <summary>
/// Method used to raise the PropertyChanged event.
/// </summary>
/// <param name="prop"></param>
public void OnPropertyChanged(string prop)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(prop));
}
/// <summary>
/// Method to raise the PropertyChanged event.
/// </summary>
/// <param name="property"></param>
protected void RaisePropertyChanged(string property)
{
if (string.IsNullOrEmpty(property))
throw new ArgumentNullException(property);
this.OnPropertyChanged(property);
}
/// <summary>
/// Gets a property from the internal container.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="prop"></param>
/// <returns></returns>
protected T Get<T>(string prop)
{
if (this.m_Properties.ContainsKey(prop))
{
return (T)this.m_Properties[prop];
}
return default(T);
}
/// <summary>
/// Sets a property in the internal container.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="prop"></param>
/// <param name="val"></param>
protected void Set<T>(string prop, T val)
{
var curr = this.Get<T>(prop);
if (Equals(curr, val))
return;
this.m_Properties[prop] = val;
this.OnPropertyChanged(prop);
}
}
}

View File

@ -0,0 +1,77 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Model
{
public class SteamlessOptions : NotifiableModel
{
/// <summary>
/// Default Constructor
/// </summary>
public SteamlessOptions()
{
this.VerboseOutput = true;
this.KeepBindSection = false;
this.DumpPayloadToDisk = false;
this.DumpSteamDrmpToDisk = false;
}
/// <summary>
/// Gets or sets the verbose output option value.
/// </summary>
public bool VerboseOutput
{
get { return this.Get<bool>("VerboseOutput"); }
set { this.Set("VerboseOutput", value); }
}
/// <summary>
/// Gets or sets the keep bind section option value.
/// </summary>
public bool KeepBindSection
{
get { return this.Get<bool>("KeepBindSection"); }
set { this.Set("KeepBindSection", value); }
}
/// <summary>
/// Gets or sets the dump payload to disk option value.
/// </summary>
public bool DumpPayloadToDisk
{
get { return this.Get<bool>("DumpPayloadToDisk"); }
set { this.Set("DumpPayloadToDisk", value); }
}
/// <summary>
/// Gets or sets the dump SteamDRMP.dll to disk option value.
/// </summary>
public bool DumpSteamDrmpToDisk
{
get { return this.Get<bool>("DumpSteamDrmpToDisk"); }
set { this.Set("DumpSteamDrmpToDisk", value); }
}
}
}

View File

@ -0,0 +1,118 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Model
{
using Services;
using System;
public abstract class SteamlessPlugin : IDisposable
{
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public virtual string Author => "Steamless Development Team";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public virtual string Name => "Steamless Plugin";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public virtual string Description => "The Steamless base plugin class.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public virtual Version Version => new Version(1, 0, 0, 0);
/// <summary>
/// Deconstructor
/// </summary>
~SteamlessPlugin()
{
this.Dispose(false);
}
/// <summary>
/// IDisposable implementation.
/// </summary>
public void Dispose()
{
this.Dispose(true);
GC.SuppressFinalize(this);
}
/// <summary>
/// IDisposable implementation.
/// </summary>
/// <param name="disposing"></param>
protected virtual void Dispose(bool disposing)
{
}
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public virtual bool Initialize(LoggingService logService)
{
return false;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public virtual bool CanProcessFile(string file)
{
return false;
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public virtual bool ProcessFile(string file, SteamlessOptions options)
{
return false;
}
/// <summary>
/// Returns a string that represents the current object.
/// </summary>
/// <returns>
/// A string that represents the current object.
/// </returns>
public string DisplayName => this.Name + " - " + this.Description;
}
}

View File

@ -0,0 +1,115 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Model
{
using System;
using System.ComponentModel;
using System.Windows;
public abstract class ViewModelBase : NotifiableModel
{
/// <summary>
/// Default Constructor
/// </summary>
protected ViewModelBase()
{
this.Events = new EventHandlerList();
}
/// <summary>
/// Internal static design mode flag.
/// </summary>
private static bool? m_IsInDesignMode;
/// <summary>
/// Gets if this ViewModelBase is in design mode.
/// </summary>
public bool IsInDesignMode => IsInDesignModeStatic;
/// <summary>
/// Gets the static ViewModelBase design mode flag.
/// </summary>
public static bool IsInDesignModeStatic
{
get
{
if (m_IsInDesignMode.HasValue)
return m_IsInDesignMode.Value;
var isInDesignModeProperty = DesignerProperties.IsInDesignModeProperty;
m_IsInDesignMode = (bool)DependencyPropertyDescriptor.FromProperty(isInDesignModeProperty, typeof(FrameworkElement)).Metadata.DefaultValue;
return m_IsInDesignMode.Value;
}
}
/// <summary>
/// Gets or sets the internal event handler list.
/// </summary>
private EventHandlerList Events
{
get { return this.Get<EventHandlerList>("Events"); }
set { this.Set("Events", value); }
}
/// <summary>
/// Event to subscribe to to be notified when a view is navigated from.
/// </summary>
public event EventHandler<NavigatedEventArgs> NavigatedFrom
{
add { this.Events.AddHandler("NavigatedFromEvent", value); }
remove { this.Events.RemoveHandler("NavigatedFromEvent", value); }
}
/// <summary>
/// Event to subscribe to to be notified when a view is navigated to.
/// </summary>
public event EventHandler<NavigatedEventArgs> NavigatedTo
{
add { this.Events.AddHandler("NavigatedToEvent", value); }
remove { this.Events.RemoveHandler("NavigatedToEvent", value); }
}
/// <summary>
/// Internal navigated from event invoker.
/// </summary>
/// <param name="e"></param>
public void OnNavigatedFrom(NavigatedEventArgs e)
{
var eventHandler = (EventHandler<NavigatedEventArgs>)this.Events["NavigatedFromEvent"];
eventHandler?.Invoke(this, e);
}
/// <summary>
/// Internal navigated to event invoker.
/// </summary>
/// <param name="e"></param>
public void OnNavigatedTo(NavigatedEventArgs e)
{
var eventHandler = (EventHandler<NavigatedEventArgs>)this.Events["NavigatedToEvent"];
eventHandler?.Invoke(this, e);
}
}
}

View File

@ -0,0 +1,603 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.PE32
{
using System;
using System.Runtime.InteropServices;
public class NativeApi32
{
/// <summary>
/// IMAGE_DOS_HEADER Structure
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ImageDosHeader32
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 2)]
public char[] e_magic;
public ushort e_cblp;
public ushort e_cp;
public ushort e_crlc;
public ushort e_cparhdr;
public ushort e_minalloc;
public ushort e_maxalloc;
public ushort e_ss;
public ushort e_sp;
public ushort e_csum;
public ushort e_ip;
public ushort e_cs;
public ushort e_lfarlc;
public ushort e_ovno;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public ushort[] e_res1;
public ushort e_oemid;
public ushort e_oeminfo;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public ushort[] e_res2;
public int e_lfanew;
/// <summary>
/// Gets if this structure is valid for a PE file.
/// </summary>
public bool IsValid => new string(this.e_magic) == "MZ";
}
/// <summary>
/// IMAGE_NT_HEADERS Structure
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct ImageNtHeaders32
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 4)]
public char[] Signature;
[FieldOffset(4)]
public ImageFileHeader32 FileHeader;
[FieldOffset(24)]
public ImageOptionalHeader32 OptionalHeader;
/// <summary>
/// Gets if this structure is valid for a PE file.
/// </summary>
public bool IsValid => new string(this.Signature).Trim('\0') == "PE";
}
/// <summary>
/// IMAGE_FILE_HEADER Structure
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ImageFileHeader32
{
public ushort Machine;
public ushort NumberOfSections;
public uint TimeDateStamp;
public uint PointerToSymbolTable;
public uint NumberOfSymbols;
public ushort SizeOfOptionalHeader;
public ushort Characteristics;
}
/// <summary>
/// Machine Type Enumeration
/// </summary>
public enum MachineType : ushort
{
Native = 0,
I386 = 0x014C,
Itanium = 0x0200,
X64 = 0x8664
}
/// <summary>
/// Magic Type Enumeration
/// </summary>
public enum MagicType : ushort
{
ImageNtOptionalHdr32Magic = 0x10B,
ImageNtOptionalHdr64Magic = 0x20B
}
/// <summary>
/// Sub System Type Enumeration
/// </summary>
public enum SubSystemType : ushort
{
ImageSubsystemUnknown = 0,
ImageSubsystemNative = 1,
ImageSubsystemWindowsGui = 2,
ImageSubsystemWindowsCui = 3,
ImageSubsystemPosixCui = 7,
ImageSubsystemWindowsCeGui = 9,
ImageSubsystemEfiApplication = 10,
ImageSubsystemEfiBootServiceDriver = 11,
ImageSubsystemEfiRuntimeDriver = 12,
ImageSubsystemEfiRom = 13,
ImageSubsystemXbox = 14
}
/// <summary>
/// Dll Characteristics Type Enumeration
/// </summary>
public enum DllCharacteristicsType : ushort
{
Reserved0 = 0x0001,
Reserved1 = 0x0002,
Reserved2 = 0x0004,
Reserved3 = 0x0008,
ImageDllCharacteristicsDynamicBase = 0x0040,
ImageDllCharacteristicsForceIntegrity = 0x0080,
ImageDllCharacteristicsNxCompat = 0x0100,
ImageDllcharacteristicsNoIsolation = 0x0200,
ImageDllcharacteristicsNoSeh = 0x0400,
ImageDllcharacteristicsNoBind = 0x0800,
Reserved4 = 0x1000,
ImageDllcharacteristicsWdmDriver = 0x2000,
ImageDllcharacteristicsTerminalServerAware = 0x8000
}
/// <summary>
/// IMAGE_OPTIONAL_HEADER Structure
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct ImageOptionalHeader32
{
[FieldOffset(0)]
public MagicType Magic;
[FieldOffset(2)]
public byte MajorLinkerVersion;
[FieldOffset(3)]
public byte MinorLinkerVersion;
[FieldOffset(4)]
public uint SizeOfCode;
[FieldOffset(8)]
public uint SizeOfInitializedData;
[FieldOffset(12)]
public uint SizeOfUninitializedData;
[FieldOffset(16)]
public uint AddressOfEntryPoint;
[FieldOffset(20)]
public uint BaseOfCode;
// PE32 contains this additional field
[FieldOffset(24)]
public uint BaseOfData;
[FieldOffset(28)]
public uint ImageBase;
[FieldOffset(32)]
public uint SectionAlignment;
[FieldOffset(36)]
public uint FileAlignment;
[FieldOffset(40)]
public ushort MajorOperatingSystemVersion;
[FieldOffset(42)]
public ushort MinorOperatingSystemVersion;
[FieldOffset(44)]
public ushort MajorImageVersion;
[FieldOffset(46)]
public ushort MinorImageVersion;
[FieldOffset(48)]
public ushort MajorSubsystemVersion;
[FieldOffset(50)]
public ushort MinorSubsystemVersion;
[FieldOffset(52)]
public uint Win32VersionValue;
[FieldOffset(56)]
public uint SizeOfImage;
[FieldOffset(60)]
public uint SizeOfHeaders;
[FieldOffset(64)]
public uint CheckSum;
[FieldOffset(68)]
public SubSystemType Subsystem;
[FieldOffset(70)]
public DllCharacteristicsType DllCharacteristics;
[FieldOffset(72)]
public uint SizeOfStackReserve;
[FieldOffset(76)]
public uint SizeOfStackCommit;
[FieldOffset(80)]
public uint SizeOfHeapReserve;
[FieldOffset(84)]
public uint SizeOfHeapCommit;
[FieldOffset(88)]
public uint LoaderFlags;
[FieldOffset(92)]
public uint NumberOfRvaAndSizes;
[FieldOffset(96)]
public ImageDataDirectory32 ExportTable;
[FieldOffset(104)]
public ImageDataDirectory32 ImportTable;
[FieldOffset(112)]
public ImageDataDirectory32 ResourceTable;
[FieldOffset(120)]
public ImageDataDirectory32 ExceptionTable;
[FieldOffset(128)]
public ImageDataDirectory32 CertificateTable;
[FieldOffset(136)]
public ImageDataDirectory32 BaseRelocationTable;
[FieldOffset(144)]
public ImageDataDirectory32 Debug;
[FieldOffset(152)]
public ImageDataDirectory32 Architecture;
[FieldOffset(160)]
public ImageDataDirectory32 GlobalPtr;
[FieldOffset(168)]
public ImageDataDirectory32 TLSTable;
[FieldOffset(176)]
public ImageDataDirectory32 LoadConfigTable;
[FieldOffset(184)]
public ImageDataDirectory32 BoundImport;
[FieldOffset(192)]
public ImageDataDirectory32 IAT;
[FieldOffset(200)]
public ImageDataDirectory32 DelayImportDescriptor;
[FieldOffset(208)]
public ImageDataDirectory32 CLRRuntimeHeader;
[FieldOffset(216)]
public ImageDataDirectory32 Reserved;
}
/// <summary>
/// IMAGE_DATA_DIRECTORY Structure
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ImageDataDirectory32
{
public uint VirtualAddress;
public uint Size;
}
/// <summary>
/// IMAGE_SECTION_HEADER Structure
/// </summary>
[StructLayout(LayoutKind.Explicit)]
public struct ImageSectionHeader32
{
[FieldOffset(0)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 8)]
public char[] Name;
[FieldOffset(8)]
public uint VirtualSize;
[FieldOffset(12)]
public uint VirtualAddress;
[FieldOffset(16)]
public uint SizeOfRawData;
[FieldOffset(20)]
public uint PointerToRawData;
[FieldOffset(24)]
public uint PointerToRelocations;
[FieldOffset(28)]
public uint PointerToLinenumbers;
[FieldOffset(32)]
public ushort NumberOfRelocations;
[FieldOffset(34)]
public ushort NumberOfLinenumbers;
[FieldOffset(36)]
public DataSectionFlags Characteristics;
/// <summary>
/// Gets the section name of this current section object.
/// </summary>
public string SectionName => new string(this.Name).Trim('\0');
/// <summary>
/// Gets if this structure is valid for a PE file.
/// </summary>
public bool IsValid => this.SizeOfRawData != 0 && this.PointerToRawData != 0;
}
/// <summary>
/// Data Section Flags Enumeration
/// </summary>
[Flags]
public enum DataSectionFlags : uint
{
/// <summary>
/// Reserved for future use.
/// </summary>
TypeReg = 0x00000000,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeDsect = 0x00000001,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeNoLoad = 0x00000002,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeGroup = 0x00000004,
/// <summary>
/// The section should not be padded to the next boundary. This flag is obsolete and is replaced by IMAGE_SCN_ALIGN_1BYTES. This is valid only for object files.
/// </summary>
TypeNoPadded = 0x00000008,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeCopy = 0x00000010,
/// <summary>
/// The section contains executable code.
/// </summary>
ContentCode = 0x00000020,
/// <summary>
/// The section contains initialized data.
/// </summary>
ContentInitializedData = 0x00000040,
/// <summary>
/// The section contains uninitialized data.
/// </summary>
ContentUninitializedData = 0x00000080,
/// <summary>
/// Reserved for future use.
/// </summary>
LinkOther = 0x00000100,
/// <summary>
/// The section contains comments or other information. The .drectve section has this type. This is valid for object files only.
/// </summary>
LinkInfo = 0x00000200,
/// <summary>
/// Reserved for future use.
/// </summary>
TypeOver = 0x00000400,
/// <summary>
/// The section will not become part of the image. This is valid only for object files.
/// </summary>
LinkRemove = 0x00000800,
/// <summary>
/// The section contains COMDAT data. For more information, see section 5.5.6, COMDAT Sections (Object Only). This is valid only for object files.
/// </summary>
LinkComDat = 0x00001000,
/// <summary>
/// Reset speculative exceptions handling bits in the TLB entries for this section.
/// </summary>
NoDeferSpecExceptions = 0x00004000,
/// <summary>
/// The section contains data referenced through the global pointer (GP).
/// </summary>
RelativeGp = 0x00008000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemPurgeable = 0x00020000,
/// <summary>
/// Reserved for future use.
/// </summary>
Memory16Bit = 0x00020000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemoryLocked = 0x00040000,
/// <summary>
/// Reserved for future use.
/// </summary>
MemoryPreload = 0x00080000,
/// <summary>
/// Align data on a 1-byte boundary. Valid only for object files.
/// </summary>
Align1Bytes = 0x00100000,
/// <summary>
/// Align data on a 2-byte boundary. Valid only for object files.
/// </summary>
Align2Bytes = 0x00200000,
/// <summary>
/// Align data on a 4-byte boundary. Valid only for object files.
/// </summary>
Align4Bytes = 0x00300000,
/// <summary>
/// Align data on an 8-byte boundary. Valid only for object files.
/// </summary>
Align8Bytes = 0x00400000,
/// <summary>
/// Align data on a 16-byte boundary. Valid only for object files.
/// </summary>
Align16Bytes = 0x00500000,
/// <summary>
/// Align data on a 32-byte boundary. Valid only for object files.
/// </summary>
Align32Bytes = 0x00600000,
/// <summary>
/// Align data on a 64-byte boundary. Valid only for object files.
/// </summary>
Align64Bytes = 0x00700000,
/// <summary>
/// Align data on a 128-byte boundary. Valid only for object files.
/// </summary>
Align128Bytes = 0x00800000,
/// <summary>
/// Align data on a 256-byte boundary. Valid only for object files.
/// </summary>
Align256Bytes = 0x00900000,
/// <summary>
/// Align data on a 512-byte boundary. Valid only for object files.
/// </summary>
Align512Bytes = 0x00A00000,
/// <summary>
/// Align data on a 1024-byte boundary. Valid only for object files.
/// </summary>
Align1024Bytes = 0x00B00000,
/// <summary>
/// Align data on a 2048-byte boundary. Valid only for object files.
/// </summary>
Align2048Bytes = 0x00C00000,
/// <summary>
/// Align data on a 4096-byte boundary. Valid only for object files.
/// </summary>
Align4096Bytes = 0x00D00000,
/// <summary>
/// Align data on an 8192-byte boundary. Valid only for object files.
/// </summary>
Align8192Bytes = 0x00E00000,
/// <summary>
/// The section contains extended relocations.
/// </summary>
LinkExtendedRelocationOverflow = 0x01000000,
/// <summary>
/// The section can be discarded as needed.
/// </summary>
MemoryDiscardable = 0x02000000,
/// <summary>
/// The section cannot be cached.
/// </summary>
MemoryNotCached = 0x04000000,
/// <summary>
/// The section is not pageable.
/// </summary>
MemoryNotPaged = 0x08000000,
/// <summary>
/// The section can be shared in memory.
/// </summary>
MemoryShared = 0x10000000,
/// <summary>
/// The section can be executed as code.
/// </summary>
MemoryExecute = 0x20000000,
/// <summary>
/// The section can be read.
/// </summary>
MemoryRead = 0x40000000,
/// <summary>
/// The section can be written to.
/// </summary>
MemoryWrite = 0x80000000
}
/// <summary>
/// IMAGE_TLS_DIRECTORY Structure
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct ImageTlsDirectory32
{
public uint StartAddressOfRawData;
public uint EndAddressOfRawData;
public uint AddressOfIndex;
public uint AddressOfCallBacks;
public uint SizeOfZeroFill;
public uint Characteristics;
}
}
}

View File

@ -0,0 +1,421 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.PE32
{
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
/// <summary>
/// Portable Executable (32bit) Class
/// </summary>
public class Pe32File
{
/// <summary>
/// Default Constructor
/// </summary>
public Pe32File()
{
}
/// <summary>
/// Overloaded Constructor
/// </summary>
/// <param name="file"></param>
public Pe32File(string file)
{
this.FilePath = file;
}
/// <summary>
/// Parses a Win32 PE file.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public bool Parse(string file = null)
{
// Prepare the class variables..
if (file != null)
this.FilePath = file;
this.FileData = null;
this.DosHeader = new NativeApi32.ImageDosHeader32();
this.NtHeaders = new NativeApi32.ImageNtHeaders32();
this.DosStubSize = 0;
this.DosStubOffset = 0;
this.DosStubData = null;
this.Sections = new List<NativeApi32.ImageSectionHeader32>();
this.SectionData = new List<byte[]>();
this.TlsDirectory = new NativeApi32.ImageTlsDirectory32();
this.TlsCallbacks = new List<uint>();
// Ensure a file path has been set..
if (string.IsNullOrEmpty(this.FilePath) || !File.Exists(this.FilePath))
return false;
// Read the file data..
this.FileData = File.ReadAllBytes(this.FilePath);
// Ensure we have valid data by the overall length..
if (this.FileData.Length < (Marshal.SizeOf(typeof(NativeApi32.ImageDosHeader32)) + Marshal.SizeOf(typeof(NativeApi32.ImageNtHeaders32))))
return false;
// Read the file headers..
this.DosHeader = Pe32Helpers.GetStructure<NativeApi32.ImageDosHeader32>(this.FileData);
this.NtHeaders = Pe32Helpers.GetStructure<NativeApi32.ImageNtHeaders32>(this.FileData, this.DosHeader.e_lfanew);
// Validate the headers..
if (!this.DosHeader.IsValid || !this.NtHeaders.IsValid)
return false;
// Read and store the dos header if it exists..
this.DosStubSize = (uint)(this.DosHeader.e_lfanew - Marshal.SizeOf(typeof(NativeApi32.ImageDosHeader32)));
if (this.DosStubSize > 0)
{
this.DosStubOffset = (uint)Marshal.SizeOf(typeof(NativeApi32.ImageDosHeader32));
this.DosStubData = new byte[this.DosStubSize];
Array.Copy(this.FileData, this.DosStubOffset, this.DosStubData, 0, this.DosStubSize);
}
// Read the file sections..
for (var x = 0; x < this.NtHeaders.FileHeader.NumberOfSections; x++)
{
var section = Pe32Helpers.GetSection(this.FileData, x, this.DosHeader, this.NtHeaders);
this.Sections.Add(section);
// Get the sections data..
var sectionData = new byte[this.GetAlignment(section.SizeOfRawData, this.NtHeaders.OptionalHeader.FileAlignment)];
Array.Copy(this.FileData, section.PointerToRawData, sectionData, 0, section.SizeOfRawData);
this.SectionData.Add(sectionData);
}
try
{
// Obtain the file overlay if one exists..
var lastSection = this.Sections.Last();
var fileSize = lastSection.SizeOfRawData + lastSection.PointerToRawData;
if (fileSize < this.FileData.Length)
{
this.OverlayData = new byte[this.FileData.Length - fileSize];
Array.Copy(this.FileData, fileSize, this.OverlayData, 0, this.FileData.Length - fileSize);
}
}
catch
{
return false;
}
// Read the files Tls information if available..
if (this.NtHeaders.OptionalHeader.TLSTable.VirtualAddress != 0)
{
// Get the file offset to the Tls data..
var tls = this.NtHeaders.OptionalHeader.TLSTable;
var addr = this.GetFileOffsetFromRva(tls.VirtualAddress);
// Read the Tls directory..
this.TlsDirectory = Pe32Helpers.GetStructure<NativeApi32.ImageTlsDirectory32>(this.FileData, (int)addr);
// Read the Tls callbacks..
addr = this.GetRvaFromVa(this.TlsDirectory.AddressOfCallBacks);
addr = this.GetFileOffsetFromRva(addr);
// Loop until we hit a null pointer..
var count = 0;
while (true)
{
var callback = BitConverter.ToUInt32(this.FileData, (int)addr + (count * 4));
if (callback == 0)
break;
this.TlsCallbacks.Add(callback);
count++;
}
}
return true;
}
/// <summary>
/// Determines if the current file is 64bit.
/// </summary>
/// <returns></returns>
public bool IsFile64Bit()
{
return (this.NtHeaders.FileHeader.Machine & (uint)NativeApi32.MachineType.X64) == (uint)NativeApi32.MachineType.X64;
}
/// <summary>
/// Determines if the file has a section containing the given name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public bool HasSection(string name)
{
return this.Sections.Any(s => string.Compare(s.SectionName, name, StringComparison.InvariantCultureIgnoreCase) == 0);
}
/// <summary>
/// Obtains a section by its name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public NativeApi32.ImageSectionHeader32 GetSection(string name)
{
return this.Sections.FirstOrDefault(s => string.Compare(s.SectionName, name, StringComparison.InvariantCultureIgnoreCase) == 0);
}
/// <summary>
/// Obtains the owner section of the given rva.
/// </summary>
/// <param name="rva"></param>
/// <returns></returns>
public NativeApi32.ImageSectionHeader32 GetOwnerSection(uint rva)
{
foreach (var s in this.Sections)
{
var size = s.VirtualSize;
if (size == 0)
size = s.SizeOfRawData;
if ((rva >= s.VirtualAddress) && (rva < s.VirtualAddress + size))
return s;
}
return default(NativeApi32.ImageSectionHeader32);
}
/// <summary>
/// Obtains the owner section of the given rva.
/// </summary>
/// <param name="rva"></param>
/// <returns></returns>
public NativeApi32.ImageSectionHeader32 GetOwnerSection(ulong rva)
{
foreach (var s in this.Sections)
{
var size = s.VirtualSize;
if (size == 0)
size = s.SizeOfRawData;
if ((rva >= s.VirtualAddress) && (rva < s.VirtualAddress + size))
return s;
}
return default(NativeApi32.ImageSectionHeader32);
}
/// <summary>
/// Obtains a sections data by its index.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public byte[] GetSectionData(int index)
{
if (index < 0 || index > this.Sections.Count)
return null;
return this.SectionData[index];
}
/// <summary>
/// Obtains a sections data by its name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public byte[] GetSectionData(string name)
{
for (var x = 0; x < this.Sections.Count; x++)
{
if (string.Compare(this.Sections[x].SectionName, name, StringComparison.InvariantCultureIgnoreCase) == 0)
return this.SectionData[x];
}
return null;
}
/// <summary>
/// Gets a sections index by its name.
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public int GetSectionIndex(string name)
{
for (var x = 0; x < this.Sections.Count; x++)
{
if (string.Compare(this.Sections[x].SectionName, name, StringComparison.InvariantCultureIgnoreCase) == 0)
return x;
}
return -1;
}
/// <summary>
/// Gets a sections index by its name.
/// </summary>
/// <param name="section"></param>
/// <returns></returns>
public int GetSectionIndex(NativeApi32.ImageSectionHeader32 section)
{
return this.Sections.IndexOf(section);
}
/// <summary>
/// Removes a section from the files section list.
/// </summary>
/// <param name="section"></param>
/// <returns></returns>
public bool RemoveSection(NativeApi32.ImageSectionHeader32 section)
{
var index = this.Sections.IndexOf(section);
if (index == -1)
return false;
this.Sections.RemoveAt(index);
this.SectionData.RemoveAt(index);
return true;
}
/// <summary>
/// Rebuilds the sections by aligning them as needed. Updates the Nt headers to
/// correct the new SizeOfImage after alignment is completed.
/// </summary>
public void RebuildSections()
{
for (var x = 0; x < this.Sections.Count; x++)
{
// Obtain the current section and realign the data..
var section = this.Sections[x];
section.VirtualAddress = this.GetAlignment(section.VirtualAddress, this.NtHeaders.OptionalHeader.SectionAlignment);
section.VirtualSize = this.GetAlignment(section.VirtualSize, this.NtHeaders.OptionalHeader.SectionAlignment);
section.PointerToRawData = this.GetAlignment(section.PointerToRawData, this.NtHeaders.OptionalHeader.FileAlignment);
section.SizeOfRawData = this.GetAlignment(section.SizeOfRawData, this.NtHeaders.OptionalHeader.FileAlignment);
// Store the sections updates..
this.Sections[x] = section;
}
// Update the size of the image..
var ntHeaders = this.NtHeaders;
ntHeaders.OptionalHeader.SizeOfImage = this.Sections.Last().VirtualAddress + this.Sections.Last().VirtualSize;
this.NtHeaders = ntHeaders;
}
/// <summary>
/// Obtains the relative virtual address from the given virtual address.
/// </summary>
/// <param name="va"></param>
/// <returns></returns>
public uint GetRvaFromVa(uint va)
{
return va - this.NtHeaders.OptionalHeader.ImageBase;
}
/// <summary>
/// Obtains the file offset from the given relative virtual address.
/// </summary>
/// <param name="rva"></param>
/// <returns></returns>
public uint GetFileOffsetFromRva(uint rva)
{
var section = this.GetOwnerSection(rva);
return (rva - (section.VirtualAddress - section.PointerToRawData));
}
/// <summary>
/// Aligns the value based on the given alignment.
/// </summary>
/// <param name="val"></param>
/// <param name="align"></param>
/// <returns></returns>
public uint GetAlignment(uint val, uint align)
{
return (((val + align - 1) / align) * align);
}
/// <summary>
/// Gets or sets the path to the file being processed.
/// </summary>
public string FilePath { get; set; }
/// <summary>
/// Gets or sets the raw file data read from disk.
/// </summary>
public byte[] FileData { get; set; }
/// <summary>
/// Gets or sets the dos header of the file.
/// </summary>
public NativeApi32.ImageDosHeader32 DosHeader { get; set; }
/// <summary>
/// Gets or sets the NT headers of the file.
/// </summary>
public NativeApi32.ImageNtHeaders32 NtHeaders { get; set; }
/// <summary>
/// Gets or sets the optional dos stub size.
/// </summary>
public uint DosStubSize { get; set; }
/// <summary>
/// Gets or sets the optional dos stub offset.
/// </summary>
public uint DosStubOffset { get; set; }
/// <summary>
/// Gets or sets the optional dos stub data.
/// </summary>
public byte[] DosStubData { get; set; }
/// <summary>
/// Gets or sets the sections of the file.
/// </summary>
public List<NativeApi32.ImageSectionHeader32> Sections;
/// <summary>
/// Gets or sets the section data of the file.
/// </summary>
public List<byte[]> SectionData;
/// <summary>
/// Gets or sets the overlay data of the file.
/// </summary>
public byte[] OverlayData;
/// <summary>
/// Gets or sets the Tls directory of the file.
/// </summary>
public NativeApi32.ImageTlsDirectory32 TlsDirectory { get; set; }
/// <summary>
/// Gets or sets a list of Tls callbacks of the file.
/// </summary>
public List<uint> TlsCallbacks { get; set; }
}
}

View File

@ -0,0 +1,128 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.PE32
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
public class Pe32Helpers
{
/// <summary>
/// Converts a byte array to the given structure type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="data"></param>
/// <param name="offset"></param>
/// <returns></returns>
public static T GetStructure<T>(byte[] data, int offset = 0)
{
var ptr = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, offset, ptr, data.Length - offset);
var obj = (T)Marshal.PtrToStructure(ptr, typeof(T));
Marshal.FreeHGlobal(ptr);
return obj;
}
/// <summary>
/// Converts the given object back to a byte array.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static byte[] GetStructureBytes<T>(T obj)
{
var size = Marshal.SizeOf(obj);
var data = new byte[size];
var ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(obj, ptr, true);
Marshal.Copy(ptr, data, 0, size);
Marshal.FreeHGlobal(ptr);
return data;
}
/// <summary>
/// Obtains a section from the given file information.
/// </summary>
/// <param name="rawData"></param>
/// <param name="index"></param>
/// <param name="dosHeader"></param>
/// <param name="ntHeaders"></param>
/// <returns></returns>
public static NativeApi32.ImageSectionHeader32 GetSection(byte[] rawData, int index, NativeApi32.ImageDosHeader32 dosHeader, NativeApi32.ImageNtHeaders32 ntHeaders)
{
var sectionSize = Marshal.SizeOf(typeof(NativeApi32.ImageSectionHeader32));
var optionalHeaderOffset = Marshal.OffsetOf(typeof(NativeApi32.ImageNtHeaders32), "OptionalHeader").ToInt32();
var dataOffset = dosHeader.e_lfanew + optionalHeaderOffset + ntHeaders.FileHeader.SizeOfOptionalHeader;
return GetStructure<NativeApi32.ImageSectionHeader32>(rawData, dataOffset + (index * sectionSize));
}
/// <summary>
/// Scans the given data for the given pattern.
///
/// Notes:
/// Patterns are assumed to be 2 byte hex values with spaces.
/// Wildcards are represented by ??.
/// </summary>
/// <param name="data"></param>
/// <param name="pattern"></param>
/// <returns></returns>
public static uint FindPattern(byte[] data, string pattern)
{
try
{
// Trim the pattern from extra whitespace..
var trimPattern = pattern.Replace(" ", "").Trim();
// Convert the pattern to a byte array..
var patternMask = new List<bool>();
var patternData = Enumerable.Range(0, trimPattern.Length).Where(x => x % 2 == 0)
.Select(x =>
{
var bt = trimPattern.Substring(x, 2);
patternMask.Add(!bt.Contains('?'));
return bt.Contains('?') ? (byte)0 : Convert.ToByte(bt, 16);
}).ToArray();
// Scan the given data for our pattern..
for (var x = 0; x < data.Length; x++)
{
if (!patternData.Where((t, y) => patternMask[y] && t != data[x + y]).Any())
return (uint)x;
}
return 0;
}
catch
{
return 0;
}
}
}
}

View File

@ -0,0 +1,40 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Steamless.API")]
[assembly: AssemblyDescription("SteamStub API Module")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("Steamless.API")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("56c95629-3b34-47fe-b988-04274409294f")]
[assembly: AssemblyVersion("1.0.0.1")]
[assembly: AssemblyFileVersion("1.0.0.1")]

View File

@ -0,0 +1,63 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API.Services
{
using Events;
using System;
public class LoggingService
{
/// <summary>
/// Adds a log message to the logging pane of Steamless.
/// </summary>
public event SteamlessEvents.AddLogMessageEventHandler AddLogMessage;
/// <summary>
/// Clears the logging pane of Steamless.
/// </summary>
public event SteamlessEvents.ClearLogMessagesEventHandler ClearLogMessages;
/// <summary>
/// Invokes the AddLogMessage event to add a log message to Steamless.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public virtual void OnAddLogMessage(object sender, LogMessageEventArgs e)
{
this.AddLogMessage?.Invoke(sender, e);
}
/// <summary>
/// Invokes the ClearLogMessages event to remove all current log messages from Steamless.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public virtual void OnClearLogMessages(object sender, EventArgs e)
{
this.ClearLogMessages?.Invoke(sender, e);
}
}
}

View File

@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{56C95629-3B34-47FE-B988-04274409294F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Steamless.API</RootNamespace>
<AssemblyName>Steamless.API</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Debug\Plugins\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Release\Plugins\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xaml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="WindowsBase" />
</ItemGroup>
<ItemGroup>
<Compile Include="Crypto\AesHelper.cs" />
<Compile Include="Extensions\FileStreamExtensions.cs" />
<Compile Include="Model\NavigatedEventArgs.cs" />
<Compile Include="Model\NotifiableModel.cs" />
<Compile Include="Model\ViewModelBase.cs" />
<Compile Include="PE32\NativeApi32.cs" />
<Compile Include="PE32\Pe32File.cs" />
<Compile Include="PE32\Pe32Helpers.cs" />
<Compile Include="SteamlessEvents.cs" />
<Compile Include="Events\LogMessageEventArgs.cs" />
<Compile Include="Events\LogMessageType.cs" />
<Compile Include="Services\LoggingService.cs" />
<Compile Include="SteamlessApiVersionAttribute.cs" />
<Compile Include="Model\SteamlessOptions.cs" />
<Compile Include="Model\SteamlessPlugin.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="PE64\" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,48 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API
{
using System;
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
public class SteamlessApiVersionAttribute : Attribute
{
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="major"></param>
/// <param name="minor"></param>
public SteamlessApiVersionAttribute(int major, int minor)
{
this.Version = new Version(major, minor);
}
/// <summary>
/// Gets or sets the API version of this attribute.
/// </summary>
public Version Version { get; internal set; }
}
}

View File

@ -0,0 +1,47 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.API
{
using Events;
using System;
public class SteamlessEvents
{
/// <summary>
/// Log message event handler.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void AddLogMessageEventHandler(object sender, LogMessageEventArgs e);
/// <summary>
/// Clear log messages event handler.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void ClearLogMessagesEventHandler(object sender, EventArgs e);
}
}

View File

@ -0,0 +1,38 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant20.x86.Classes
{
/// <summary>
/// Steam Stub Variant 2.0 DRM Flags
/// </summary>
public enum DrmFlags
{
NoModuleVerification = 0x02,
NoEncryption = 0x04,
NoOwnershipCheck = 0x10,
NoDebuggerCheck = 0x20,
}
}

View File

@ -0,0 +1,60 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant20.x86.Classes
{
using System.Runtime.InteropServices;
/// <summary>
/// SteamStub DRM Variant 2.0 Header
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SteamStub32Var20Header
{
public uint XorKey; // The base XOR key, if defined, to unpack the file with.
public uint GetModuleHandleA_idata; // The address of GetModuleHandleA inside of the .idata section.
public uint GetModuleHandleW_idata; // The address of GetModuleHandleW inside of the .idata section.
public uint GetProcAddress_idata; // The address of GetProcAddress inside of the .idata section.
public uint LoadLibraryA_idata; // The address of LoadLibraryA inside of the .idata section.
public uint Unknown0000; // Unknown (Was 0 when testing. Possibly LoadLibraryW.)
public uint BindSectionVirtualAddress; // The virtual address to the .bind section.
public uint BindStartFunctionSize; // The size of the start function from the .bind section.
public uint PayloadKeyMatch; // The key inside of the SteamDRMP.dll file that is matched to this structures data. (This matches the first 4 bytes of the payload data.)
public uint PayloadDataVirtualAddress; // The virtual address to the payload data.
public uint PayloadDataSize; // The size of the payload data.
public uint SteamAppID; // The steam application id of the packed file.
public uint Unknown0001; // Unknown
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x08)]
public byte[] SteamAppIDString; // The SteamAppID of the packed file, in string format.
public uint SteamDRMPDllVirtualAddress; // The offset inside of the payload data holding the virtual address to the SteamDRMP.dll file data.
public uint SteamDRMPDllSize; // The offset inside of the payload data holding the size of the SteamDRMP.dll file data.
public uint XTeaKeys; // The offset inside of the payload data holding the address to the Xtea keys to decrypt the SteamDRMP.dll file.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x31C)]
public byte[] StubData; // Misc stub data, such as strings, error messages, etc.
}
}

View File

@ -0,0 +1,122 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant20.x86.Classes
{
using System;
public static class SteamStubHelpers
{
/// <summary>
/// Xor decrypts the given data starting with the given key, if any.
///
/// @note If no key is given (0) then the first key is read from the first
/// 4 bytes inside of the data given.
/// </summary>
/// <param name="data">The data to xor decode.</param>
/// <param name="size">The size of the data to decode.</param>
/// <param name="key">The starting xor key to decode with.</param>
/// <returns></returns>
public static uint SteamXor(ref byte[] data, uint size, uint key = 0)
{
var offset = (uint)0;
// Read the first key as the base xor key if we had none given..
if (key == 0)
{
offset += 4;
key = BitConverter.ToUInt32(data, 0);
}
// Decode the data..
for (var x = offset; x < size; x += 4)
{
var val = BitConverter.ToUInt32(data, (int)x);
Array.Copy(BitConverter.GetBytes(val ^ key), 0, data, x, 4);
key = val;
}
return key;
}
/// <summary>
/// The second pass of decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA.
/// </summary>
/// <param name="res">The result value buffer to write our returns to.</param>
/// <param name="keys">The keys used for the decryption.</param>
/// <param name="v1">The first value to decrypt from.</param>
/// <param name="v2">The second value to decrypt from.</param>
/// <param name="n">The number of passes to crypt the data with.</param>
public static void SteamDrmpDecryptPass2(ref uint[] res, uint[] keys, uint v1, uint v2, uint n = 32)
{
const uint delta = 0x9E3779B9;
const uint mask = 0xFFFFFFFF;
var sum = (delta * n) & mask;
for (var x = 0; x < n; x++)
{
v2 = (v2 - (((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + keys[sum >> 11 & 3]))) & mask;
sum = (sum - delta) & mask;
v1 = (v1 - (((v2 << 4 ^ v2 >> 5) + v2) ^ (sum + keys[sum & 3]))) & mask;
}
res[0] = v1;
res[1] = v2;
}
/// <summary>
/// The first pass of the decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA. It is modded to include
/// some basic xor'ing.
/// </summary>
/// <param name="data">The data to decrypt.</param>
/// <param name="size">The size of the data to decrypt.</param>
/// <param name="keys">The keys used for the decryption.</param>
public static void SteamDrmpDecryptPass1(ref byte[] data, uint size, uint[] keys)
{
var v1 = (uint)0x55555555;
var v2 = (uint)0x55555555;
for (var x = 0; x < size; x += 8)
{
var d1 = BitConverter.ToUInt32(data, x + 0);
var d2 = BitConverter.ToUInt32(data, x + 4);
var res = new uint[2];
SteamDrmpDecryptPass2(ref res, keys, d1, d2);
Array.Copy(BitConverter.GetBytes(res[0] ^ v1), 0, data, x + 0, 4);
Array.Copy(BitConverter.GetBytes(res[1] ^ v2), 0, data, x + 4, 4);
v1 = d1;
v2 = d2;
}
}
}
}

View File

@ -0,0 +1,632 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant20.x86
{
using API;
using API.Crypto;
using API.Events;
using API.Extensions;
using API.Model;
using API.PE32;
using API.Services;
using Classes;
using SharpDisasm;
using SharpDisasm.Udis86;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
[SteamlessApiVersion(1, 0)]
public class Main : SteamlessPlugin
{
/// <summary>
/// Internal logging service instance.
/// </summary>
private LoggingService m_LoggingService;
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "atom0s";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "SteamStub Variant 2.0 Unpacker (x86)";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "Unpacker for the 32bit SteamStub variant 2.0.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => new Version(1, 0, 0, 0);
/// <summary>
/// Internal wrapper to log a message.
/// </summary>
/// <param name="msg"></param>
/// <param name="type"></param>
private void Log(string msg, LogMessageType type)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs(msg, type));
}
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
this.m_LoggingService = logService;
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
try
{
// Load the file..
var f = new Pe32File(file);
if (!f.Parse() || f.IsFile64Bit() || !f.HasSection(".bind"))
return false;
// Obtain the bind section data..
var bind = f.GetSectionData(".bind");
// Attempt to locate the known v2.x signature..
return Pe32Helpers.FindPattern(bind, "53 51 52 56 57 55 8B EC 81 EC 00 10 00 00 C7") > 0;
}
catch
{
return false;
}
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
// Initialize the class members..
this.Options = options;
this.CodeSectionData = null;
this.CodeSectionIndex = -1;
this.PayloadData = null;
this.SteamDrmpData = null;
this.SteamDrmpOffsets = new List<int>();
this.XorKey = 0;
// Parse the file..
this.File = new Pe32File(file);
if (!this.File.Parse())
return false;
// Announce we are being unpacked with this packer..
this.Log("File is packed with SteamStub Variant 2.0!", LogMessageType.Information);
this.Log("Step 1 - Read, disassemble and decode the SteamStub DRM header.", LogMessageType.Information);
if (!this.Step1())
return false;
this.Log("Step 2 - Read, decode and process the payload data.", LogMessageType.Information);
if (!this.Step2())
return false;
this.Log("Step 3 - Read, decode and dump the SteamDRMP.dll file.", LogMessageType.Information);
if (!this.Step3())
return false;
this.Log("Step 4 - Scan, dump and pull needed offsets from within the SteamDRMP.dll file.", LogMessageType.Information);
if (!this.Step4())
return false;
this.Log("Step 5 - Read, decrypt and process the main code section.", LogMessageType.Information);
if (!this.Step5())
return false;
this.Log("Step 6 - Rebuild and save the unpacked file.", LogMessageType.Information);
if (!this.Step6())
return false;
return true;
}
/// <summary>
/// Step #1
///
/// Read, disassemble and decode the SteamStub DRM header.
/// </summary>
/// <returns></returns>
private bool Step1()
{
// Obtain the file entry offset..
var fileOffset = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint);
// Validate the DRM header..
if (BitConverter.ToUInt32(this.File.FileData, (int)fileOffset - 4) != 0xC0DEC0DE)
return false;
int structOffset;
int structSize;
int structXorKey;
// Disassemble the file to locate the needed DRM information..
if (!this.DisassembleFile(out structOffset, out structSize, out structXorKey))
return false;
// Obtain the DRM header data..
var headerData = new byte[structSize];
Array.Copy(this.File.FileData, this.File.GetFileOffsetFromRva((uint)structOffset), headerData, 0, structSize);
// Xor decode the header data..
this.XorKey = SteamStubHelpers.SteamXor(ref headerData, (uint)headerData.Length, (uint)structXorKey);
this.StubHeader = Pe32Helpers.GetStructure<SteamStub32Var20Header>(headerData);
return true;
}
/// <summary>
/// Step #2
///
/// Read, decode and process the payload data.
/// </summary>
/// <returns></returns>
private bool Step2()
{
// Obtain the payload address and size..
var payloadAddr = this.File.GetFileOffsetFromRva(this.File.GetRvaFromVa(this.StubHeader.PayloadDataVirtualAddress));
var payloadData = new byte[this.StubHeader.PayloadDataSize];
Array.Copy(this.File.FileData, payloadAddr, payloadData, 0, this.StubHeader.PayloadDataSize);
// Decode the payload data..
this.XorKey = SteamStubHelpers.SteamXor(ref payloadData, this.StubHeader.PayloadDataSize, this.XorKey);
this.PayloadData = payloadData;
try
{
if (this.Options.DumpPayloadToDisk)
{
System.IO.File.WriteAllBytes(this.File.FilePath + ".payload", payloadData);
this.Log(" --> Saved payload to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
/// <summary>
/// Step #3
///
/// Read, decode and dump the SteamDRMP.dll file.
/// </summary>
/// <returns></returns>
private bool Step3()
{
this.Log(" --> File has SteamDRMP.dll file!", LogMessageType.Debug);
try
{
// Obtain the SteamDRMP.dll file address and data..
var drmpAddr = this.File.GetFileOffsetFromRva(this.File.GetRvaFromVa(BitConverter.ToUInt32(this.PayloadData, (int)this.StubHeader.SteamDRMPDllVirtualAddress)));
var drmpSize = BitConverter.ToUInt32(this.PayloadData, (int)this.StubHeader.SteamDRMPDllSize);
var drmpData = new byte[drmpSize];
Array.Copy(this.File.FileData, drmpAddr, drmpData, 0, drmpSize);
// Obtain the XTea encryption keys..
var xteyKeys = new uint[(this.PayloadData.Length - this.StubHeader.XTeaKeys) / 4];
for (var x = 0; x < (this.PayloadData.Length - this.StubHeader.XTeaKeys) / 4; x++)
xteyKeys[x] = BitConverter.ToUInt32(this.PayloadData, (int)this.StubHeader.XTeaKeys + (x * 4));
// Decrypt the file data..
SteamStubHelpers.SteamDrmpDecryptPass1(ref drmpData, drmpSize, xteyKeys);
this.SteamDrmpData = drmpData;
try
{
if (this.Options.DumpSteamDrmpToDisk)
{
var basePath = Path.GetDirectoryName(this.File.FilePath) ?? string.Empty;
System.IO.File.WriteAllBytes(Path.Combine(basePath, "SteamDRMP.dll"), drmpData);
this.Log(" --> Saved SteamDRMP.dll to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
catch
{
this.Log(" --> Error trying to decrypt the files SteamDRMP.dll data!", LogMessageType.Error);
return false;
}
}
/// <summary>
/// Step #4
///
/// Scan, dump and pull needed offsets from within the SteamDRMP.dll file.
/// </summary>
/// <returns></returns>
private bool Step4()
{
// Scan for the needed data by a known pattern for the block of offset data..
var drmpOffset = Pe32Helpers.FindPattern(this.SteamDrmpData, "8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 8B ?? ?? ?? ?? ?? 89 ?? ?? ?? ?? ?? 8D ?? ?? ?? ?? ?? 05");
if (drmpOffset == 0)
return false;
// Copy the block of data from the SteamDRMP.dll data..
var drmpOffsetData = new byte[1024];
Array.Copy(this.SteamDrmpData, drmpOffset, drmpOffsetData, 0, 1024);
// Obtain the offsets from the file data..
var drmpOffsets = this.GetSteamDrmpOffsets(drmpOffsetData);
if (drmpOffsets.Count != 8)
return false;
// Store the offsets..
this.SteamDrmpOffsets = drmpOffsets;
return true;
}
/// <summary>
/// Step #5
///
/// Read, decrypt and process the main code section.
/// </summary>
/// <returns></returns>
private bool Step5()
{
// Remove the bind section if its not requested to be saved..
if (!this.Options.KeepBindSection)
{
// Obtain the .bind section..
var bindSection = this.File.GetSection(".bind");
if (!bindSection.IsValid)
return false;
// Remove the section..
this.File.RemoveSection(bindSection);
// Decrease the header section count..
var ntHeaders = this.File.NtHeaders;
ntHeaders.FileHeader.NumberOfSections--;
this.File.NtHeaders = ntHeaders;
this.Log(" --> .bind section was removed from the file.", LogMessageType.Debug);
}
else
this.Log(" --> .bind section was kept in the file.", LogMessageType.Debug);
byte[] codeSectionData;
// Obtain the main code section (typically .text)..
var mainSection = this.File.GetOwnerSection(this.File.GetRvaFromVa(BitConverter.ToUInt32(this.PayloadData.Skip(this.SteamDrmpOffsets[3]).Take(4).ToArray(), 0)));
if (mainSection.PointerToRawData == 0 || mainSection.SizeOfRawData == 0)
return false;
this.Log($" --> {mainSection.SectionName} linked as main code section.", LogMessageType.Debug);
// Save the code section index for later use..
this.CodeSectionIndex = this.File.GetSectionIndex(mainSection);
// Determine if we are using encryption on the section..
var flags = BitConverter.ToUInt32(this.PayloadData.Skip(this.SteamDrmpOffsets[0]).Take(4).ToArray(), 0);
if ((flags & (uint)DrmFlags.NoEncryption) == (uint)DrmFlags.NoEncryption)
{
this.Log($" --> {mainSection.SectionName} section is not encrypted.", LogMessageType.Debug);
// No encryption was used, just read the original data..
codeSectionData = new byte[mainSection.SizeOfRawData];
Array.Copy(this.File.FileData, this.File.GetFileOffsetFromRva(mainSection.VirtualAddress), codeSectionData, 0, mainSection.SizeOfRawData);
}
else
{
this.Log($" --> {mainSection.SectionName} section is encrypted.", LogMessageType.Debug);
try
{
// Encryption was used, obtain the encryption information..
var aesKey = this.PayloadData.Skip(this.SteamDrmpOffsets[5]).Take(32).ToArray();
var aesIv = this.PayloadData.Skip(this.SteamDrmpOffsets[6]).Take(16).ToArray();
var codeStolen = this.PayloadData.Skip(this.SteamDrmpOffsets[7]).Take(16).ToArray();
// Restore the stolen data then read the rest of the section data..
codeSectionData = new byte[mainSection.SizeOfRawData + codeStolen.Length];
Array.Copy(codeStolen, 0, codeSectionData, 0, codeStolen.Length);
Array.Copy(this.File.FileData, this.File.GetFileOffsetFromRva(mainSection.VirtualAddress), codeSectionData, codeStolen.Length, mainSection.SizeOfRawData);
// Decrypt the code section..
var aes = new AesHelper(aesKey, aesIv);
aes.RebuildIv(aesIv);
codeSectionData = aes.Decrypt(codeSectionData, CipherMode.CBC, PaddingMode.None);
}
catch
{
this.Log(" --> Error trying to decrypt the files code section data!", LogMessageType.Error);
return false;
}
}
// Store the section data..
this.CodeSectionData = codeSectionData;
return true;
}
/// <summary>
/// Step #6
///
/// Rebuild and save the unpacked file.
/// </summary>
/// <returns></returns>
private bool Step6()
{
FileStream fStream = null;
try
{
// Rebuild the file sections..
this.File.RebuildSections();
// Open the unpacked file for writing..
var unpackedPath = this.File.FilePath + ".unpacked.exe";
fStream = new FileStream(unpackedPath, FileMode.Create, FileAccess.ReadWrite);
// Write the DOS header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(this.File.DosHeader));
// Write the DOS stub to the file..
if (this.File.DosStubSize > 0)
fStream.WriteBytes(this.File.DosStubData);
// Update the NT headers..
var ntHeaders = this.File.NtHeaders;
var lastSection = this.File.Sections[this.File.Sections.Count - 1];
var originalEntry = BitConverter.ToUInt32(this.PayloadData.Skip(this.SteamDrmpOffsets[2]).Take(4).ToArray(), 0);
ntHeaders.OptionalHeader.AddressOfEntryPoint = this.File.GetRvaFromVa(originalEntry);
ntHeaders.OptionalHeader.SizeOfImage = lastSection.VirtualAddress + lastSection.VirtualSize;
this.File.NtHeaders = ntHeaders;
// Write the NT headers to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(ntHeaders));
// Write the sections to the file..
for (var x = 0; x < this.File.Sections.Count; x++)
{
var section = this.File.Sections[x];
var sectionData = this.File.SectionData[x];
// Write the section header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(section));
// Set the file pointer to the sections raw data..
var sectionOffset = fStream.Position;
fStream.Position = section.PointerToRawData;
// Write the sections raw data..
var sectionIndex = this.File.Sections.IndexOf(section);
if (sectionIndex == this.CodeSectionIndex)
fStream.WriteBytes(this.CodeSectionData ?? sectionData);
else
fStream.WriteBytes(sectionData);
// Reset the file offset..
fStream.Position = sectionOffset;
}
// Set the stream to the end of the file..
fStream.Position = fStream.Length;
// Write the overlay data if it exists..
if (this.File.OverlayData != null)
fStream.WriteBytes(this.File.OverlayData);
this.Log(" --> Unpacked file saved to disk!", LogMessageType.Success);
this.Log($" --> File Saved As: {unpackedPath}", LogMessageType.Success);
return true;
}
catch
{
this.Log(" --> Error trying to save unpacked file!", LogMessageType.Error);
return false;
}
finally
{
fStream?.Dispose();
}
}
/// <summary>
/// Disassembles the file to locate the needed DRM header information.
/// </summary>
/// <param name="offset"></param>
/// <param name="size"></param>
/// <param name="xorKey"></param>
/// <returns></returns>
private bool DisassembleFile(out int offset, out int size, out int xorKey)
{
// Prepare our needed variables..
Disassembler disasm = null;
var dataPointer = IntPtr.Zero;
var structOffset = 0;
var structSize = 0;
var structXorKey = 0;
// Determine the entry offset of the file..
var entryOffset = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint);
try
{
// Copy the file data to memory for disassembling..
dataPointer = Marshal.AllocHGlobal(this.File.FileData.Length);
Marshal.Copy(this.File.FileData, 0, dataPointer, this.File.FileData.Length);
// Create an offset pointer to our .bind function start..
var startPointer = IntPtr.Add(dataPointer, (int)entryOffset);
// Create the disassembler..
Disassembler.Translator.IncludeAddress = true;
Disassembler.Translator.IncludeBinary = true;
disasm = new Disassembler(startPointer, 4096, ArchitectureMode.x86_32, entryOffset);
// Disassemble our function..
foreach (var inst in disasm.Disassemble().Where(inst => !inst.Error))
{
// If all values are found, return successfully..
if (structOffset > 0 && structSize > 0 && structXorKey > 0)
{
offset = structOffset;
size = structSize;
xorKey = structXorKey;
return true;
}
// Looks for: mov dword ptr [value], immediate
if (inst.Mnemonic == ud_mnemonic_code.UD_Imov && inst.Operands[0].Type == ud_type.UD_OP_MEM && inst.Operands[1].Type == ud_type.UD_OP_IMM)
{
if (structOffset == 0)
structOffset = inst.Operands[1].LvalSDWord - (int)this.File.NtHeaders.OptionalHeader.ImageBase;
else
structXorKey = inst.Operands[1].LvalSDWord;
}
// Looks for: mov reg, immediate
if (inst.Mnemonic == ud_mnemonic_code.UD_Imov && inst.Operands[0].Type == ud_type.UD_OP_REG && inst.Operands[1].Type == ud_type.UD_OP_IMM)
structSize = inst.Operands[1].LvalSDWord * 4;
}
offset = size = xorKey = 0;
return false;
}
catch
{
offset = size = xorKey = 0;
return false;
}
finally
{
disasm?.Dispose();
if (dataPointer != IntPtr.Zero)
Marshal.FreeHGlobal(dataPointer);
}
}
/// <summary>
/// Obtains the needed DRM offsets from the SteamDRMP.dll file.
/// </summary>
/// <param name="data"></param>
/// <returns></returns>
private List<int> GetSteamDrmpOffsets(byte[] data)
{
var offsets = new List<int>
{
BitConverter.ToInt32(data, 2), // .... 0 - Flags
BitConverter.ToInt32(data, 14), // ... 1 - Steam App Id
BitConverter.ToInt32(data, 26), // ... 2 - OEP
BitConverter.ToInt32(data, 38), // ... 3 - Code Section Virtual Address
BitConverter.ToInt32(data, 50), // ... 4 - Code Section Virtual Size (Encrypted Size)
BitConverter.ToInt32(data, 62) // .... 5 - Code Section AES Key
};
var aesIvOffset = BitConverter.ToInt32(data, 67);
offsets.Add(aesIvOffset); // ................. 6 - Code Section AES Iv
offsets.Add(aesIvOffset + 16); // ............ 7 - Code Section Stolen Bytes
return offsets;
}
/// <summary>
/// Gets or sets the Steamless options this file was requested to process with.
/// </summary>
private SteamlessOptions Options { get; set; }
/// <summary>
/// Gets or sets the file being processed.
/// </summary>
private Pe32File File { get; set; }
/// <summary>
/// Gets or sets the current xor key being used against the file data.
/// </summary>
private uint XorKey { get; set; }
/// <summary>
/// Gets or sets the DRM stub header.
/// </summary>
private SteamStub32Var20Header StubHeader { get; set; }
/// <summary>
/// Gets or sets the payload data.
/// </summary>
public byte[] PayloadData { get; set; }
/// <summary>
/// Gets or sets the SteamDRMP.dll data.
/// </summary>
public byte[] SteamDrmpData { get; set; }
/// <summary>
/// Gets or sets the list of SteamDRMP.dll offsets.
/// </summary>
public List<int> SteamDrmpOffsets { get; set; }
/// <summary>
/// Gets or sets the index of the code section.
/// </summary>
private int CodeSectionIndex { get; set; }
/// <summary>
/// Gets or sets the decrypted code section data.
/// </summary>
private byte[] CodeSectionData { get; set; }
}
}

View File

@ -0,0 +1,40 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Steamless.Unpacker.Variant20.x86")]
[assembly: AssemblyDescription("Steamless SteamStub Variant v2.0 (x86) Unpacker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("Steamless.Unpacker.Variant20.x86")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("a40154cd-a0fd-4371-8099-ce277e0989af")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

Binary file not shown.

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{A40154CD-A0FD-4371-8099-CE277E0989AF}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Steamless.Unpacker.Variant20.x86</RootNamespace>
<AssemblyName>Steamless.Unpacker.Variant20.x86</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Debug\Plugins\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Release\Plugins\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="SharpDisasm">
<HintPath>.\SharpDisasm.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Classes\SteamStubDrmFlags.cs" />
<Compile Include="Classes\SteamStubHeader.cs" />
<Compile Include="Classes\SteamStubHelpers.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Steamless.API\Steamless.API.csproj">
<Project>{56c95629-3b34-47fe-b988-04274409294f}</Project>
<Name>Steamless.API</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,39 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant30.x86.Classes
{
/// <summary>
/// Steam Stub Variant 3.0 DRM Flags
/// </summary>
public enum SteamStubDrmFlags
{
NoModuleVerification = 0x02,
NoEncryption = 0x04,
NoOwnershipCheck = 0x10,
NoDebuggerCheck = 0x20,
NoErrorDialog = 0x40
}
}

View File

@ -0,0 +1,81 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant30.x86.Classes
{
using System.Runtime.InteropServices;
/// <summary>
/// SteamStub DRM Variant 3.0 Header
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SteamStub32Var30Header
{
public uint XorKey; // The base XOR key, if defined, to unpack the file with.
public uint Signature; // 0xC0DEC0DE signature to validate this header is proper.
public ulong ImageBase; // The base of the image that is protected.
public uint AddressOfEntryPoint; // The entry point that is set from the DRM.
public uint BindSectionOffset; // The starting offset to the bind section data. RVA(AddressOfEntryPoint - BindSectionOffset)
public uint Unknown0000; // [Cyanic: This field is most likely the .bind code size.]
public uint OriginalEntryPoint; // The original entry point of the binary before it was protected.
public uint Unknown0001; // [Cyanic: This field is most likely an offset to a string table.]
public uint PayloadSize; // The size of the payload data.
public uint DRMPDllOffset; // The offset to the SteamDRMP.dll file.
public uint DRMPDllSize; // The size of the SteamDRMP.dll file.
public uint SteamAppId; // The Steam Application ID of this game.
public uint Flags; // The DRM flags used while creating the protected executable.
public uint BindSectionVirtualSize; // The bind section virtual size.
public uint Unknown0002; // [Cyanic: This field is most likely a hash of some sort.]
public uint CodeSectionVirtualAddress; // The cpde section virtual address.
public uint CodeSectionRawSize; // The raw size of the code section.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[] AES_Key; // The AES encryption key.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
public byte[] AES_IV; // The AES encryption IV.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
public byte[] CodeSectionStolenData; // The first 16 bytes of the code section stolen.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x04)]
public uint[] EncryptionKeys; // Encryption keys used for decrypting SteamDRMP.dll file.
public uint Unknown0003; // [Cyanic: This field is most likely used to flag if the file has Tls data or not.]
public uint Unknown0004;
public uint Unknown0005;
public uint Unknown0006;
public uint Unknown0007;
public uint Unknown0008;
public uint GetModuleHandleA_RVA; // The RVA to GetModuleHandleA.
public uint GetModuleHandleW_RVA; // The RVA to GetModuleHandleW.
public uint LoadLibraryA_RVA; // The RVA to LoadLibraryA.
public uint LoadLibraryW_RVA; // The RVA to LoadLibraryW.
public uint GetProcAddress_RVA; // The RVA to GetProcAddress.
public uint Unknown0009;
public uint Unknown0010;
public uint Unknown0011;
}
}

View File

@ -0,0 +1,122 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant30.x86.Classes
{
using System;
public static class SteamStubHelpers
{
/// <summary>
/// Xor decrypts the given data starting with the given key, if any.
///
/// @note If no key is given (0) then the first key is read from the first
/// 4 bytes inside of the data given.
/// </summary>
/// <param name="data">The data to xor decode.</param>
/// <param name="size">The size of the data to decode.</param>
/// <param name="key">The starting xor key to decode with.</param>
/// <returns></returns>
public static uint SteamXor(ref byte[] data, uint size, uint key = 0)
{
var offset = (uint)0;
// Read the first key as the base xor key if we had none given..
if (key == 0)
{
offset += 4;
key = BitConverter.ToUInt32(data, 0);
}
// Decode the data..
for (var x = offset; x < size; x += 4)
{
var val = BitConverter.ToUInt32(data, (int)x);
Array.Copy(BitConverter.GetBytes(val ^ key), 0, data, x, 4);
key = val;
}
return key;
}
/// <summary>
/// The second pass of decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA.
/// </summary>
/// <param name="res">The result value buffer to write our returns to.</param>
/// <param name="keys">The keys used for the decryption.</param>
/// <param name="v1">The first value to decrypt from.</param>
/// <param name="v2">The second value to decrypt from.</param>
/// <param name="n">The number of passes to crypt the data with.</param>
public static void SteamDrmpDecryptPass2(ref uint[] res, uint[] keys, uint v1, uint v2, uint n = 32)
{
const uint delta = 0x9E3779B9;
const uint mask = 0xFFFFFFFF;
var sum = (delta * n) & mask;
for (var x = 0; x < n; x++)
{
v2 = (v2 - (((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + keys[sum >> 11 & 3]))) & mask;
sum = (sum - delta) & mask;
v1 = (v1 - (((v2 << 4 ^ v2 >> 5) + v2) ^ (sum + keys[sum & 3]))) & mask;
}
res[0] = v1;
res[1] = v2;
}
/// <summary>
/// The first pass of the decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA. It is modded to include
/// some basic xor'ing.
/// </summary>
/// <param name="data">The data to decrypt.</param>
/// <param name="size">The size of the data to decrypt.</param>
/// <param name="keys">The keys used for the decryption.</param>
public static void SteamDrmpDecryptPass1(ref byte[] data, uint size, uint[] keys)
{
var v1 = (uint)0x55555555;
var v2 = (uint)0x55555555;
for (var x = 0; x < size; x += 8)
{
var d1 = BitConverter.ToUInt32(data, x + 0);
var d2 = BitConverter.ToUInt32(data, x + 4);
var res = new uint[2];
SteamDrmpDecryptPass2(ref res, keys, d1, d2);
Array.Copy(BitConverter.GetBytes(res[0] ^ v1), 0, data, x + 0, 4);
Array.Copy(BitConverter.GetBytes(res[1] ^ v2), 0, data, x + 4, 4);
v1 = d1;
v2 = d2;
}
}
}
}

View File

@ -0,0 +1,496 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant30.x86
{
using API;
using API.Crypto;
using API.Events;
using API.Extensions;
using API.Model;
using API.PE32;
using API.Services;
using Classes;
using System;
using System.IO;
using System.Security.Cryptography;
[SteamlessApiVersion(1, 0)]
public class Main : SteamlessPlugin
{
/// <summary>
/// Internal logging service instance.
/// </summary>
private LoggingService m_LoggingService;
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "atom0s";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "SteamStub Variant 3.0 Unpacker (x86)";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "Unpacker for the 32bit SteamStub variant 3.0.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => new Version(1, 0, 0, 0);
/// <summary>
/// Internal wrapper to log a message.
/// </summary>
/// <param name="msg"></param>
/// <param name="type"></param>
private void Log(string msg, LogMessageType type)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs(msg, type));
}
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
this.m_LoggingService = logService;
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
try
{
// Load the file..
var f = new Pe32File(file);
if (!f.Parse() || f.IsFile64Bit() || !f.HasSection(".bind"))
return false;
// Obtain the bind section data..
var bind = f.GetSectionData(".bind");
// Attempt to locate the known v3.x signature..
var varient = Pe32Helpers.FindPattern(bind, "E8 00 00 00 00 50 53 51 52 56 57 55 8B 44 24 1C 2D 05 00 00 00 8B CC 83 E4 F0 51 51 51 50");
if (varient == 0) return false;
// Attempt to determine the varient version..
int headerSize;
var offset = Pe32Helpers.FindPattern(bind, "55 8B EC 81 EC ?? ?? ?? ?? 53 ?? ?? ?? ?? ?? 68");
if (offset == 0)
{
offset = Pe32Helpers.FindPattern(bind, "55 8B EC 81 EC ?? ?? ?? ?? 53 ?? ?? ?? ?? ?? 8D 83");
if (offset == 0)
return false;
headerSize = BitConverter.ToInt32(bind, (int)offset + 22);
}
else
headerSize = BitConverter.ToInt32(bind, (int)offset + 16);
return headerSize == 0xB0 || headerSize == 0xD0;
}
catch
{
return false;
}
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
// Initialize the class members..
this.Options = options;
this.CodeSectionData = null;
this.CodeSectionIndex = -1;
this.XorKey = 0;
// Parse the file..
this.File = new Pe32File(file);
if (!this.File.Parse())
return false;
// Announce we are being unpacked with this packer..
this.Log("File is packed with SteamStub Variant 3.0!", LogMessageType.Information);
this.Log("Step 1 - Read, decode and validate the SteamStub DRM header.", LogMessageType.Information);
if (!this.Step1())
return false;
this.Log("Step 2 - Read, decode and process the payload data.", LogMessageType.Information);
if (!this.Step2())
return false;
this.Log("Step 3 - Read, decode and dump the SteamDRMP.dll file.", LogMessageType.Information);
if (!this.Step3())
return false;
this.Log("Step 4 - Handle .bind section. Find code section.", LogMessageType.Information);
if (!this.Step4())
return false;
this.Log("Step 5 - Read, decrypt and process code section.", LogMessageType.Information);
if (!this.Step5())
return false;
this.Log("Step 6 - Rebuild and save the unpacked file.", LogMessageType.Information);
if (!this.Step6())
return false;
return true;
}
/// <summary>
/// Step #1
///
/// Read, decode and validate the SteamStub DRM header.
/// </summary>
/// <returns></returns>
private bool Step1()
{
// Obtain the DRM header data..
var fileOffset = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint);
var headerData = new byte[0xD0];
Array.Copy(this.File.FileData, (int)(fileOffset - 0xD0), headerData, 0, 0xD0);
// Xor decode the header data..
this.XorKey = SteamStubHelpers.SteamXor(ref headerData, 0xD0);
this.StubHeader = Pe32Helpers.GetStructure<SteamStub32Var30Header>(headerData);
// Validate the structure signature..
return this.StubHeader.Signature == 0xC0DEC0DE;
}
/// <summary>
/// Step #2
///
/// Read, decode and process the payload data.
/// </summary>
/// <returns></returns>
private bool Step2()
{
// Obtain the payload address and size..
var payloadAddr = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint - this.StubHeader.BindSectionOffset);
var payloadSize = (this.StubHeader.PayloadSize + 0x0F) & 0xFFFFFFF0;
// Do nothing if there is no payload..
if (payloadSize == 0)
return true;
this.Log(" --> File has payload data!", LogMessageType.Debug);
// Obtain and decode the payload..
var payload = new byte[payloadSize];
Array.Copy(this.File.FileData, payloadAddr, payload, 0, payloadSize);
this.XorKey = SteamStubHelpers.SteamXor(ref payload, payloadSize, this.XorKey);
try
{
if (this.Options.DumpPayloadToDisk)
{
System.IO.File.WriteAllBytes(this.File.FilePath + ".payload", payload);
this.Log(" --> Saved payload to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
/// <summary>
/// Step #3
///
/// Read, decode and dump the SteamDRMP.dll file.
/// </summary>
/// <returns></returns>
private bool Step3()
{
// Ensure there is a dll to process..
if (this.StubHeader.DRMPDllSize == 0)
{
this.Log(" --> File does not contain a SteamDRMP.dll file.", LogMessageType.Debug);
return true;
}
this.Log(" --> File has SteamDRMP.dll file!", LogMessageType.Debug);
try
{
// Obtain the SteamDRMP.dll file address and data..
var drmpAddr = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint - this.StubHeader.BindSectionOffset + this.StubHeader.DRMPDllOffset);
var drmpData = new byte[this.StubHeader.DRMPDllSize];
Array.Copy(this.File.FileData, drmpAddr, drmpData, 0, drmpData.Length);
// Decrypt the data (xtea decryption)..
SteamStubHelpers.SteamDrmpDecryptPass1(ref drmpData, this.StubHeader.DRMPDllSize, this.StubHeader.EncryptionKeys);
try
{
if (this.Options.DumpSteamDrmpToDisk)
{
var basePath = Path.GetDirectoryName(this.File.FilePath) ?? string.Empty;
System.IO.File.WriteAllBytes(Path.Combine(basePath, "SteamDRMP.dll"), drmpData);
this.Log(" --> Saved SteamDRMP.dll to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
catch
{
this.Log(" --> Error trying to decrypt the files SteamDRMP.dll data!", LogMessageType.Error);
return false;
}
}
/// <summary>
/// Step #4
///
/// Remove the bind section if requested.
/// Find the code section.
/// </summary>
/// <returns></returns>
private bool Step4()
{
// Remove the bind section if its not requested to be saved..
if (!this.Options.KeepBindSection)
{
// Obtain the .bind section..
var bindSection = this.File.GetSection(".bind");
if (!bindSection.IsValid)
return false;
// Remove the section..
this.File.RemoveSection(bindSection);
// Decrease the header section count..
var ntHeaders = this.File.NtHeaders;
ntHeaders.FileHeader.NumberOfSections--;
this.File.NtHeaders = ntHeaders;
this.Log(" --> .bind section was removed from the file.", LogMessageType.Debug);
}
else
this.Log(" --> .bind section was kept in the file.", LogMessageType.Debug);
// Skip finding the code section if the file is not encrypted..
if ((this.StubHeader.Flags & (uint)SteamStubDrmFlags.NoEncryption) == (uint)SteamStubDrmFlags.NoEncryption)
return true;
// Find the code section..
var codeSection = this.File.GetOwnerSection(this.StubHeader.CodeSectionVirtualAddress);
if (codeSection.PointerToRawData == 0 || codeSection.SizeOfRawData == 0)
return false;
// Store the code sections index..
this.CodeSectionIndex = this.File.GetSectionIndex(codeSection);
return true;
}
/// <summary>
/// Step #5
///
/// Read, decrypt and process the code section.
/// </summary>
/// <returns></returns>
private bool Step5()
{
// Skip decryption if the code section is not encrypted..
if ((this.StubHeader.Flags & (uint)SteamStubDrmFlags.NoEncryption) == (uint)SteamStubDrmFlags.NoEncryption)
{
this.Log(" --> Code section is not encrypted.", LogMessageType.Debug);
return true;
}
try
{
// Obtain the code section..
var codeSection = this.File.Sections[this.CodeSectionIndex];
this.Log($" --> {codeSection.SectionName} linked as main code section.", LogMessageType.Debug);
this.Log($" --> {codeSection.SectionName} section is encrypted.", LogMessageType.Debug);
// Obtain the code section data..
var codeSectionData = new byte[codeSection.SizeOfRawData + this.StubHeader.CodeSectionStolenData.Length];
Array.Copy(this.StubHeader.CodeSectionStolenData, 0, codeSectionData, 0, this.StubHeader.CodeSectionStolenData.Length);
Array.Copy(this.File.FileData, this.File.GetFileOffsetFromRva(codeSection.VirtualAddress), codeSectionData, this.StubHeader.CodeSectionStolenData.Length, codeSection.SizeOfRawData);
// Create the AES decryption helper..
var aes = new AesHelper(this.StubHeader.AES_Key, this.StubHeader.AES_IV);
aes.RebuildIv(this.StubHeader.AES_IV);
// Decrypt the code section data..
var data = aes.Decrypt(codeSectionData, CipherMode.CBC, PaddingMode.None);
if (data == null)
return false;
// Set the code section override data..
this.CodeSectionData = data;
return true;
}
catch
{
this.Log(" --> Error trying to decrypt the files code section data!", LogMessageType.Error);
return false;
}
}
/// <summary>
/// Step #6
///
/// Rebuild and save the unpacked file.
/// </summary>
/// <returns></returns>
private bool Step6()
{
FileStream fStream = null;
try
{
// Rebuild the file sections..
this.File.RebuildSections();
// Open the unpacked file for writing..
var unpackedPath = this.File.FilePath + ".unpacked.exe";
fStream = new FileStream(unpackedPath, FileMode.Create, FileAccess.ReadWrite);
// Write the DOS header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(this.File.DosHeader));
// Write the DOS stub to the file..
if (this.File.DosStubSize > 0)
fStream.WriteBytes(this.File.DosStubData);
// Update the entry point of the file..
var ntHeaders = this.File.NtHeaders;
ntHeaders.OptionalHeader.AddressOfEntryPoint = this.StubHeader.OriginalEntryPoint;
this.File.NtHeaders = ntHeaders;
// Write the NT headers to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(ntHeaders));
// Write the sections to the file..
for (var x = 0; x < this.File.Sections.Count; x++)
{
var section = this.File.Sections[x];
var sectionData = this.File.SectionData[x];
// Write the section header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(section));
// Set the file pointer to the sections raw data..
var sectionOffset = fStream.Position;
fStream.Position = section.PointerToRawData;
// Write the sections raw data..
var sectionIndex = this.File.Sections.IndexOf(section);
if (sectionIndex == this.CodeSectionIndex)
fStream.WriteBytes(this.CodeSectionData ?? sectionData);
else
fStream.WriteBytes(sectionData);
// Reset the file offset..
fStream.Position = sectionOffset;
}
// Set the stream to the end of the file..
fStream.Position = fStream.Length;
// Write the overlay data if it exists..
if (this.File.OverlayData != null)
fStream.WriteBytes(this.File.OverlayData);
this.Log(" --> Unpacked file saved to disk!", LogMessageType.Success);
this.Log($" --> File Saved As: {unpackedPath}", LogMessageType.Success);
return true;
}
catch
{
this.Log(" --> Error trying to save unpacked file!", LogMessageType.Error);
return false;
}
finally
{
fStream?.Dispose();
}
}
/// <summary>
/// Gets or sets the Steamless options this file was requested to process with.
/// </summary>
private SteamlessOptions Options { get; set; }
/// <summary>
/// Gets or sets the file being processed.
/// </summary>
private Pe32File File { get; set; }
/// <summary>
/// Gets or sets the current xor key being used against the file data.
/// </summary>
private uint XorKey { get; set; }
/// <summary>
/// Gets or sets the DRM stub header.
/// </summary>
private SteamStub32Var30Header StubHeader { get; set; }
/// <summary>
/// Gets or sets the index of the code section.
/// </summary>
private int CodeSectionIndex { get; set; }
/// <summary>
/// Gets or sets the decrypted code section data.
/// </summary>
private byte[] CodeSectionData { get; set; }
}
}

View File

@ -0,0 +1,40 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Steamless.Unpacker.Variant30.x86")]
[assembly: AssemblyDescription("Steamless SteamStub Variant v3.0 (x86) Unpacker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("Steamless.Unpacker.Variant30.x86")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("b6bb7a32-ab23-4a25-8914-154879aad3fe")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{B6BB7A32-AB23-4A25-8914-154879AAD3FE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Steamless.Unpacker.Variant30.x86</RootNamespace>
<AssemblyName>Steamless.Unpacker.Variant30.x86</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Debug\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Release\Plugins\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Classes\SteamStubDrmFlags.cs" />
<Compile Include="Classes\SteamStubHeader.cs" />
<Compile Include="Classes\SteamStubHelpers.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Steamless.API\Steamless.API.csproj">
<Project>{56c95629-3b34-47fe-b988-04274409294f}</Project>
<Name>Steamless.API</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,39 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant31.x86.Classes
{
/// <summary>
/// Steam Stub Variant 3.1 DRM Flags
/// </summary>
public enum SteamStubDrmFlags
{
NoModuleVerification = 0x02,
NoEncryption = 0x04,
NoOwnershipCheck = 0x10,
NoDebuggerCheck = 0x20,
NoErrorDialog = 0x40
}
}

View File

@ -0,0 +1,75 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant31.x86.Classes
{
using System.Runtime.InteropServices;
/// <summary>
/// SteamStub DRM Variant 3.1 Header
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct SteamStub32Var31Header
{
public uint XorKey; // The base xor key, if defined, to unpack the file with.
public uint Signature; // The signature to ensure the xor decoding was successful.
public ulong ImageBase; // The base of the image that was protected.
public ulong AddressOfEntryPoint; // The entry point that is set from the DRM.
public uint BindSectionOffset; // The starting offset to the .bind section data. RVA(AddressOfEntryPoint - BindSectionOffset)
public uint Unknown0000; // [Cyanic: This field is most likely the .bind code size.]
public ulong OriginalEntryPoint; // The original entry point of the binary before it was protected.
public uint Unknown0001; // [Cyanic: This field is most likely an offset to a string table.]
public uint PayloadSize; // The size of the payload data.
public uint DRMPDllOffset; // The offset to the SteamDrmp.dll file.
public uint DRMPDllSize; // The size of the SteamDrmp.dll file.
public uint SteamAppId; // The Steam application id of this program.
public uint Flags; // The DRM flags used while protecting this program.
public uint BindSectionVirtualSize; // The .bind section virtual size.
public uint Unknown0002; // [Cyanic: This field is most likely a hash of some sort.]
public ulong CodeSectionVirtualAddress; // The code section virtual address.
public ulong CodeSectionRawSize; // The code section raw size.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x20)]
public byte[] AES_Key; // The AES encryption key.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
public byte[] AES_IV; // The AES encryption IV.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x10)]
public byte[] CodeSectionStolenData; // The first 16 bytes of the code section stolen.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x04)]
public uint[] EncryptionKeys; // Encryption keys used to decrypt the SteamDrmp.dll file.
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x08)]
public uint[] Unknown0003; // Unknown unused data.
public ulong GetModuleHandleA_Rva; // The rva to GetModuleHandleA.
public ulong GetModuleHandleW_Rva; // The rva to GetModuleHandleW.
public ulong LoadLibraryA_Rva; // The rva to LoadLibraryA.
public ulong LoadLibraryW_Rva; // The rva to LoadLibraryW.
public ulong GetProcAddress_Rva; // The rva to GetProcAddress.
}
}

View File

@ -0,0 +1,122 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant31.x86.Classes
{
using System;
public static class SteamStubHelpers
{
/// <summary>
/// Xor decrypts the given data starting with the given key, if any.
///
/// @note If no key is given (0) then the first key is read from the first
/// 4 bytes inside of the data given.
/// </summary>
/// <param name="data">The data to xor decode.</param>
/// <param name="size">The size of the data to decode.</param>
/// <param name="key">The starting xor key to decode with.</param>
/// <returns></returns>
public static uint SteamXor(ref byte[] data, uint size, uint key = 0)
{
var offset = (uint)0;
// Read the first key as the base xor key if we had none given..
if (key == 0)
{
offset += 4;
key = BitConverter.ToUInt32(data, 0);
}
// Decode the data..
for (var x = offset; x < size; x += 4)
{
var val = BitConverter.ToUInt32(data, (int)x);
Array.Copy(BitConverter.GetBytes(val ^ key), 0, data, x, 4);
key = val;
}
return key;
}
/// <summary>
/// The second pass of decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA.
/// </summary>
/// <param name="res">The result value buffer to write our returns to.</param>
/// <param name="keys">The keys used for the decryption.</param>
/// <param name="v1">The first value to decrypt from.</param>
/// <param name="v2">The second value to decrypt from.</param>
/// <param name="n">The number of passes to crypt the data with.</param>
public static void SteamDrmpDecryptPass2(ref uint[] res, uint[] keys, uint v1, uint v2, uint n = 32)
{
const uint delta = 0x9E3779B9;
const uint mask = 0xFFFFFFFF;
var sum = (delta * n) & mask;
for (var x = 0; x < n; x++)
{
v2 = (v2 - (((v1 << 4 ^ v1 >> 5) + v1) ^ (sum + keys[sum >> 11 & 3]))) & mask;
sum = (sum - delta) & mask;
v1 = (v1 - (((v2 << 4 ^ v2 >> 5) + v2) ^ (sum + keys[sum & 3]))) & mask;
}
res[0] = v1;
res[1] = v2;
}
/// <summary>
/// The first pass of the decryption for the SteamDRMP.dll file.
///
/// @note The encryption method here is known as XTEA. It is modded to include
/// some basic xor'ing.
/// </summary>
/// <param name="data">The data to decrypt.</param>
/// <param name="size">The size of the data to decrypt.</param>
/// <param name="keys">The keys used for the decryption.</param>
public static void SteamDrmpDecryptPass1(ref byte[] data, uint size, uint[] keys)
{
var v1 = (uint)0x55555555;
var v2 = (uint)0x55555555;
for (var x = 0; x < size; x += 8)
{
var d1 = BitConverter.ToUInt32(data, x + 0);
var d2 = BitConverter.ToUInt32(data, x + 4);
var res = new uint[2];
SteamDrmpDecryptPass2(ref res, keys, d1, d2);
Array.Copy(BitConverter.GetBytes(res[0] ^ v1), 0, data, x + 0, 4);
Array.Copy(BitConverter.GetBytes(res[1] ^ v2), 0, data, x + 4, 4);
v1 = d1;
v2 = d2;
}
}
}
}

View File

@ -0,0 +1,532 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Unpacker.Variant31.x86
{
using API;
using API.Crypto;
using API.Events;
using API.Extensions;
using API.Model;
using API.PE32;
using API.Services;
using Classes;
using System;
using System.IO;
using System.Security.Cryptography;
[SteamlessApiVersion(1, 0)]
public class Main : SteamlessPlugin
{
/// <summary>
/// Internal logging service instance.
/// </summary>
private LoggingService m_LoggingService;
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "atom0s";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "SteamStub Variant 3.1 Unpacker (x86)";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "Unpacker for the 32bit SteamStub variant 3.1.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => new Version(1, 0, 0, 1);
/// <summary>
/// Internal wrapper to log a message.
/// </summary>
/// <param name="msg"></param>
/// <param name="type"></param>
private void Log(string msg, LogMessageType type)
{
this.m_LoggingService.OnAddLogMessage(this, new LogMessageEventArgs(msg, type));
}
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
this.m_LoggingService = logService;
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
try
{
// Load the file..
var f = new Pe32File(file);
if (!f.Parse() || f.IsFile64Bit() || !f.HasSection(".bind"))
return false;
// Obtain the bind section data..
var bind = f.GetSectionData(".bind");
// Attempt to locate the known v3.x signature..
var varient = Pe32Helpers.FindPattern(bind, "E8 00 00 00 00 50 53 51 52 56 57 55 8B 44 24 1C 2D 05 00 00 00 8B CC 83 E4 F0 51 51 51 50");
if (varient == 0) return false;
// Attempt to determine the varient version..
int headerSize;
var offset = Pe32Helpers.FindPattern(bind, "55 8B EC 81 EC ?? ?? ?? ?? 53 ?? ?? ?? ?? ?? 68");
if (offset == 0)
{
offset = Pe32Helpers.FindPattern(bind, "55 8B EC 81 EC ?? ?? ?? ?? 53 ?? ?? ?? ?? ?? 8D 83");
if (offset == 0)
return false;
headerSize = BitConverter.ToInt32(bind, (int)offset + 22);
}
else
headerSize = BitConverter.ToInt32(bind, (int)offset + 16);
return headerSize == 0xF0;
}
catch
{
return false;
}
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
// Initialize the class members..
this.TlsAsOep = false;
this.TlsOepRva = 0;
this.Options = options;
this.CodeSectionData = null;
this.CodeSectionIndex = -1;
this.XorKey = 0;
// Parse the file..
this.File = new Pe32File(file);
if (!this.File.Parse())
return false;
// Announce we are being unpacked with this packer..
this.Log("File is packed with SteamStub Variant 3.1!", LogMessageType.Information);
this.Log("Step 1 - Read, decode and validate the SteamStub DRM header.", LogMessageType.Information);
if (!this.Step1())
return false;
this.Log("Step 2 - Read, decode and process the payload data.", LogMessageType.Information);
if (!this.Step2())
return false;
this.Log("Step 3 - Read, decode and dump the SteamDRMP.dll file.", LogMessageType.Information);
if (!this.Step3())
return false;
this.Log("Step 4 - Handle .bind section. Find code section.", LogMessageType.Information);
if (!this.Step4())
return false;
this.Log("Step 5 - Read, decrypt and process code section.", LogMessageType.Information);
if (!this.Step5())
return false;
this.Log("Step 6 - Rebuild and save the unpacked file.", LogMessageType.Information);
if (!this.Step6())
return false;
return true;
}
/// <summary>
/// Step #1
///
/// Read, decode and validate the SteamStub DRM header.
/// </summary>
/// <returns></returns>
private bool Step1()
{
// Obtain the DRM header data..
var fileOffset = this.File.GetFileOffsetFromRva(this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint);
var headerData = new byte[0xF0];
Array.Copy(this.File.FileData, (int)(fileOffset - 0xF0), headerData, 0, 0xF0);
// Xor decode the header data..
this.XorKey = SteamStubHelpers.SteamXor(ref headerData, 0xF0);
this.StubHeader = Pe32Helpers.GetStructure<SteamStub32Var31Header>(headerData);
// Validate the header signature..
if (this.StubHeader.Signature == 0xC0DEC0DF)
return true;
// Try again using the Tls callback (if any) as the OEP instead..
if (this.File.TlsCallbacks.Count == 0)
return false;
// Obtain the DRM header data..
fileOffset = this.File.GetRvaFromVa(this.File.TlsCallbacks[0]);
fileOffset = this.File.GetFileOffsetFromRva(fileOffset);
headerData = new byte[0xF0];
Array.Copy(this.File.FileData, (int)(fileOffset - 0xF0), headerData, 0, 0xF0);
// Xor decode the header data..
this.XorKey = SteamStubHelpers.SteamXor(ref headerData, 0xF0);
this.StubHeader = Pe32Helpers.GetStructure<SteamStub32Var31Header>(headerData);
// Validate the header signature..
if (this.StubHeader.Signature != 0xC0DEC0DF)
return false;
// Tls was valid for the real oep..
this.TlsAsOep = true;
this.TlsOepRva = fileOffset;
return true;
}
/// <summary>
/// Step #2
///
/// Read, decode and process the payload data.
/// </summary>
/// <returns></returns>
private bool Step2()
{
// Obtain the payload address and size..
var payloadAddr = this.File.GetFileOffsetFromRva(this.TlsAsOep ? this.TlsOepRva : this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint - this.StubHeader.BindSectionOffset);
var payloadSize = (this.StubHeader.PayloadSize + 0x0F) & 0xFFFFFFF0;
// Do nothing if there is no payload..
if (payloadSize == 0)
return true;
this.Log(" --> File has payload data!", LogMessageType.Debug);
// Obtain and decode the payload..
var payload = new byte[payloadSize];
Array.Copy(this.File.FileData, payloadAddr, payload, 0, payloadSize);
this.XorKey = SteamStubHelpers.SteamXor(ref payload, payloadSize, this.XorKey);
try
{
if (this.Options.DumpPayloadToDisk)
{
System.IO.File.WriteAllBytes(this.File.FilePath + ".payload", payload);
this.Log(" --> Saved payload to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
/// <summary>
/// Step #3
///
/// Read, decode and dump the SteamDRMP.dll file.
/// </summary>
/// <returns></returns>
private bool Step3()
{
// Ensure there is a dll to process..
if (this.StubHeader.DRMPDllSize == 0)
{
this.Log(" --> File does not contain a SteamDRMP.dll file.", LogMessageType.Debug);
return true;
}
this.Log(" --> File has SteamDRMP.dll file!", LogMessageType.Debug);
try
{
// Obtain the SteamDRMP.dll file address and data..
var drmpAddr = this.File.GetFileOffsetFromRva(this.TlsAsOep ? this.TlsOepRva : this.File.NtHeaders.OptionalHeader.AddressOfEntryPoint - this.StubHeader.BindSectionOffset + this.StubHeader.DRMPDllOffset);
var drmpData = new byte[this.StubHeader.DRMPDllSize];
Array.Copy(this.File.FileData, drmpAddr, drmpData, 0, drmpData.Length);
// Decrypt the data (xtea decryption)..
SteamStubHelpers.SteamDrmpDecryptPass1(ref drmpData, this.StubHeader.DRMPDllSize, this.StubHeader.EncryptionKeys);
try
{
if (this.Options.DumpSteamDrmpToDisk)
{
var basePath = Path.GetDirectoryName(this.File.FilePath) ?? string.Empty;
System.IO.File.WriteAllBytes(Path.Combine(basePath, "SteamDRMP.dll"), drmpData);
this.Log(" --> Saved SteamDRMP.dll to disk!", LogMessageType.Debug);
}
}
catch
{
// Do nothing here since it doesn't matter if this fails..
}
return true;
}
catch
{
this.Log(" --> Error trying to decrypt the files SteamDRMP.dll data!", LogMessageType.Error);
return false;
}
}
/// <summary>
/// Step #4
///
/// Remove the bind section if requested.
/// Find the code section.
/// </summary>
/// <returns></returns>
private bool Step4()
{
// Remove the bind section if its not requested to be saved..
if (!this.Options.KeepBindSection)
{
// Obtain the .bind section..
var bindSection = this.File.GetSection(".bind");
if (!bindSection.IsValid)
return false;
// Remove the section..
this.File.RemoveSection(bindSection);
// Decrease the header section count..
var ntHeaders = this.File.NtHeaders;
ntHeaders.FileHeader.NumberOfSections--;
this.File.NtHeaders = ntHeaders;
this.Log(" --> .bind section was removed from the file.", LogMessageType.Debug);
}
else
this.Log(" --> .bind section was kept in the file.", LogMessageType.Debug);
// Skip finding the code section if the file is not encrypted..
if ((this.StubHeader.Flags & (uint)SteamStubDrmFlags.NoEncryption) == (uint)SteamStubDrmFlags.NoEncryption)
return true;
// Find the code section..
var codeSection = this.File.GetOwnerSection(this.StubHeader.CodeSectionVirtualAddress);
if (codeSection.PointerToRawData == 0 || codeSection.SizeOfRawData == 0)
return false;
// Store the code sections index..
this.CodeSectionIndex = this.File.GetSectionIndex(codeSection);
return true;
}
/// <summary>
/// Step #5
///
/// Read, decrypt and process the code section.
/// </summary>
/// <returns></returns>
private bool Step5()
{
// Skip decryption if the code section is not encrypted..
if ((this.StubHeader.Flags & (uint)SteamStubDrmFlags.NoEncryption) == (uint)SteamStubDrmFlags.NoEncryption)
{
this.Log(" --> Code section is not encrypted.", LogMessageType.Debug);
return true;
}
try
{
// Obtain the code section..
var codeSection = this.File.Sections[this.CodeSectionIndex];
this.Log($" --> {codeSection.SectionName} linked as main code section.", LogMessageType.Debug);
this.Log($" --> {codeSection.SectionName} section is encrypted.", LogMessageType.Debug);
// Obtain the code section data..
var codeSectionData = new byte[codeSection.SizeOfRawData + this.StubHeader.CodeSectionStolenData.Length];
Array.Copy(this.StubHeader.CodeSectionStolenData, 0, codeSectionData, 0, this.StubHeader.CodeSectionStolenData.Length);
Array.Copy(this.File.FileData, this.File.GetFileOffsetFromRva(codeSection.VirtualAddress), codeSectionData, this.StubHeader.CodeSectionStolenData.Length, codeSection.SizeOfRawData);
// Create the AES decryption helper..
var aes = new AesHelper(this.StubHeader.AES_Key, this.StubHeader.AES_IV);
aes.RebuildIv(this.StubHeader.AES_IV);
// Decrypt the code section data..
var data = aes.Decrypt(codeSectionData, CipherMode.CBC, PaddingMode.None);
if (data == null)
return false;
// Set the code section override data..
this.CodeSectionData = data;
return true;
}
catch
{
this.Log(" --> Error trying to decrypt the files code section data!", LogMessageType.Error);
return false;
}
}
/// <summary>
/// Step #6
///
/// Rebuild and save the unpacked file.
/// </summary>
/// <returns></returns>
private bool Step6()
{
FileStream fStream = null;
try
{
// Rebuild the file sections..
this.File.RebuildSections();
// Open the unpacked file for writing..
var unpackedPath = this.File.FilePath + ".unpacked.exe";
fStream = new FileStream(unpackedPath, FileMode.Create, FileAccess.ReadWrite);
// Write the DOS header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(this.File.DosHeader));
// Write the DOS stub to the file..
if (this.File.DosStubSize > 0)
fStream.WriteBytes(this.File.DosStubData);
// Update the entry point of the file..
var ntHeaders = this.File.NtHeaders;
ntHeaders.OptionalHeader.AddressOfEntryPoint = (uint)this.StubHeader.OriginalEntryPoint;
this.File.NtHeaders = ntHeaders;
// Write the NT headers to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(ntHeaders));
// Write the sections to the file..
for (var x = 0; x < this.File.Sections.Count; x++)
{
var section = this.File.Sections[x];
var sectionData = this.File.SectionData[x];
// Write the section header to the file..
fStream.WriteBytes(Pe32Helpers.GetStructureBytes(section));
// Set the file pointer to the sections raw data..
var sectionOffset = fStream.Position;
fStream.Position = section.PointerToRawData;
// Write the sections raw data..
var sectionIndex = this.File.Sections.IndexOf(section);
if (sectionIndex == this.CodeSectionIndex)
fStream.WriteBytes(this.CodeSectionData ?? sectionData);
else
fStream.WriteBytes(sectionData);
// Reset the file offset..
fStream.Position = sectionOffset;
}
// Set the stream to the end of the file..
fStream.Position = fStream.Length;
// Write the overlay data if it exists..
if (this.File.OverlayData != null)
fStream.WriteBytes(this.File.OverlayData);
this.Log(" --> Unpacked file saved to disk!", LogMessageType.Success);
this.Log($" --> File Saved As: {unpackedPath}", LogMessageType.Success);
return true;
}
catch
{
this.Log(" --> Error trying to save unpacked file!", LogMessageType.Error);
return false;
}
finally
{
fStream?.Dispose();
}
}
/// <summary>
/// Gets or sets if the Tls callback is being used as the Oep.
/// </summary>
private bool TlsAsOep { get; set; }
/// <summary>
/// Gets or sets the Tls Oep Rva if it is being used as the Oep.
/// </summary>
private uint TlsOepRva { get; set; }
/// <summary>
/// Gets or sets the Steamless options this file was requested to process with.
/// </summary>
private SteamlessOptions Options { get; set; }
/// <summary>
/// Gets or sets the file being processed.
/// </summary>
private Pe32File File { get; set; }
/// <summary>
/// Gets or sets the current xor key being used against the file data.
/// </summary>
private uint XorKey { get; set; }
/// <summary>
/// Gets or sets the DRM stub header.
/// </summary>
private SteamStub32Var31Header StubHeader { get; set; }
/// <summary>
/// Gets or sets the index of the code section.
/// </summary>
private int CodeSectionIndex { get; set; }
/// <summary>
/// Gets or sets the decrypted code section data.
/// </summary>
private byte[] CodeSectionData { get; set; }
}
}

View File

@ -0,0 +1,40 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
[assembly: AssemblyTitle("Steamless.Unpacker.Variant31.x86")]
[assembly: AssemblyDescription("Steamless SteamStub Variant v3.1 (x86) Unpacker")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("Steamless.Unpacker.Variant31.x86")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("0f2fae37-f898-4392-b4f6-711954beeb4f")]
[assembly: AssemblyVersion("1.0.0.1")]
[assembly: AssemblyFileVersion("1.0.0.1")]

View File

@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{0F2FAE37-F898-4392-B4F6-711954BEEB4F}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Steamless.Unpacker.Variant31.x86</RootNamespace>
<AssemblyName>Steamless.Unpacker.Variant31.x86</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Debug\Plugins\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>..\Steamless\bin\x86\Release\Plugins\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Classes\SteamStubDrmFlags.cs" />
<Compile Include="Classes\SteamStubHeader.cs" />
<Compile Include="Classes\SteamStubHelpers.cs" />
<Compile Include="Main.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Steamless.API\Steamless.API.csproj">
<Project>{56c95629-3b34-47fe-b988-04274409294f}</Project>
<Name>Steamless.API</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

52
Steamless.sln Normal file
View File

@ -0,0 +1,52 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steamless", "Steamless\Steamless.csproj", "{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steamless.API", "Steamless.API\Steamless.API.csproj", "{56C95629-3B34-47FE-B988-04274409294F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExamplePlugin", "ExamplePlugin\ExamplePlugin.csproj", "{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steamless.Unpacker.Variant30.x86", "Steamless.Unpacker.Variant30.x86\Steamless.Unpacker.Variant30.x86.csproj", "{B6BB7A32-AB23-4A25-8914-154879AAD3FE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steamless.Unpacker.Variant31.x86", "Steamless.Unpacker.Variant31.x86\Steamless.Unpacker.Variant31.x86.csproj", "{0F2FAE37-F898-4392-B4F6-711954BEEB4F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Steamless.Unpacker.Variant20.x86", "Steamless.Unpacker.Variant20.x86\Steamless.Unpacker.Variant20.x86.csproj", "{A40154CD-A0FD-4371-8099-CE277E0989AF}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}.Debug|x86.ActiveCfg = Debug|x86
{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}.Debug|x86.Build.0 = Debug|x86
{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}.Release|x86.ActiveCfg = Release|x86
{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}.Release|x86.Build.0 = Release|x86
{56C95629-3B34-47FE-B988-04274409294F}.Debug|x86.ActiveCfg = Debug|x86
{56C95629-3B34-47FE-B988-04274409294F}.Debug|x86.Build.0 = Debug|x86
{56C95629-3B34-47FE-B988-04274409294F}.Release|x86.ActiveCfg = Release|x86
{56C95629-3B34-47FE-B988-04274409294F}.Release|x86.Build.0 = Release|x86
{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}.Debug|x86.ActiveCfg = Debug|x86
{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}.Debug|x86.Build.0 = Debug|x86
{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}.Release|x86.ActiveCfg = Release|x86
{97AC964A-E56F-415C-BAEA-D503E3D4D7B8}.Release|x86.Build.0 = Release|x86
{B6BB7A32-AB23-4A25-8914-154879AAD3FE}.Debug|x86.ActiveCfg = Debug|x86
{B6BB7A32-AB23-4A25-8914-154879AAD3FE}.Debug|x86.Build.0 = Debug|x86
{B6BB7A32-AB23-4A25-8914-154879AAD3FE}.Release|x86.ActiveCfg = Release|x86
{B6BB7A32-AB23-4A25-8914-154879AAD3FE}.Release|x86.Build.0 = Release|x86
{0F2FAE37-F898-4392-B4F6-711954BEEB4F}.Debug|x86.ActiveCfg = Debug|x86
{0F2FAE37-F898-4392-B4F6-711954BEEB4F}.Debug|x86.Build.0 = Debug|x86
{0F2FAE37-F898-4392-B4F6-711954BEEB4F}.Release|x86.ActiveCfg = Release|x86
{0F2FAE37-F898-4392-B4F6-711954BEEB4F}.Release|x86.Build.0 = Release|x86
{A40154CD-A0FD-4371-8099-CE277E0989AF}.Debug|x86.ActiveCfg = Debug|x86
{A40154CD-A0FD-4371-8099-CE277E0989AF}.Debug|x86.Build.0 = Debug|x86
{A40154CD-A0FD-4371-8099-CE277E0989AF}.Release|x86.ActiveCfg = Release|x86
{A40154CD-A0FD-4371-8099-CE277E0989AF}.Release|x86.Build.0 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

6
Steamless/App.config Normal file
View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
</startup>
</configuration>

48
Steamless/App.xaml Normal file
View File

@ -0,0 +1,48 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<Application x:Class="Steamless.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:viewModel="clr-namespace:Steamless.ViewModel"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
StartupUri="View/MainWindow.xaml" mc:Ignorable="d">
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Animations.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/icons.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Theme.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Controls.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Controls.CheckBox.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Controls.Scrollbars.xaml" />
<ResourceDictionary Source="pack://application:,,,/Steamless;component/Assets/Window.xaml" />
</ResourceDictionary.MergedDictionaries>
<!-- View Model Locator -->
<viewModel:ViewModelLocator x:Key="ViewModelLocator" d:IsDataSource="True" />
</ResourceDictionary>
</Application.Resources>
</Application>

79
Steamless/App.xaml.cs Normal file
View File

@ -0,0 +1,79 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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
{
using System;
using System.IO;
using System.Reflection;
/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
/// <summary>
/// Default Constructor
/// </summary>
public App()
{
// Override the assembly resolve event..
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
}
/// <summary>
/// Assembly resolve override to allow loading of embedded modules.
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
/// <returns></returns>
private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs args)
{
// Obtain the name of the assembly being loaded..
var name = args.Name.Contains(",") ? args.Name.Substring(0, args.Name.IndexOf(",", StringComparison.InvariantCultureIgnoreCase)) : args.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(args.Name).Name}.dll";
using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(fullName))
{
// If not embedded try to load from the plugin folder..
if (stream == null)
{
var file = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Plugins", name + ".dll");
return File.Exists(file) ? Assembly.Load(File.ReadAllBytes(file)) : null;
}
// Read and load the embedded resource..
var data = new byte[stream.Length];
stream.Read(data, 0, (int)stream.Length);
return Assembly.Load(data);
}
}
}
}

View File

@ -0,0 +1,58 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!--
These animations are based on code from MahApps.Metro:
https://github.com/MahApps/MahApps.Metro
-->
<!-- Animations -->
<Storyboard x:Key="MetroSlideLeftTransitionShow">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0" />
<SplineDoubleKeyFrame KeyTime="00:00:00.300" Value="1" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Visible}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:00" Value="40" />
<SplineDoubleKeyFrame KeyTime="00:00:00.300" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
<Storyboard x:Key="MetroSlideLeftTransitionHide">
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.Opacity)">
<SplineDoubleKeyFrame KeyTime="00:00:00.300" Value="0" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.Visibility)">
<DiscreteObjectKeyFrame KeyTime="00:00:02.000" Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="{x:Null}" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[3].(TranslateTransform.X)">
<SplineDoubleKeyFrame KeyTime="00:00:02.000" Value="-50" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</ResourceDictionary>

View File

@ -0,0 +1,108 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Control: CheckBox -->
<SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/>
<SolidColorBrush x:Key="OptionMark.Static.Border" Color="#FF707070"/>
<Style x:Key="OptionMarkFocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="14,0,0,0" SnapsToDevicePixels="true" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<SolidColorBrush x:Key="OptionMark.MouseOver.Background" Color="Transparent"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Border" Color="#676767"/>
<SolidColorBrush x:Key="OptionMark.MouseOver.Glyph" Color="#F1F1F1"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Background" Color="Transparent"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Border" Color="#40676767"/>
<SolidColorBrush x:Key="OptionMark.Disabled.Glyph" Color="#40F1F1F1"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Background" Color="Transparent"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Border" Color="#676767"/>
<SolidColorBrush x:Key="OptionMark.Pressed.Glyph" Color="#F1F1F1"/>
<SolidColorBrush x:Key="OptionMark.Static.Glyph" Color="#F1F1F1"/>
<Style TargetType="{x:Type CheckBox}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="BorderBrush" Value="#676767"/>
<Setter Property="Foreground" Value="#A8A8A8"/>
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type CheckBox}">
<Grid x:Name="templateRoot" Background="Transparent" SnapsToDevicePixels="True">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Border x:Name="checkBoxBorder" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="1" VerticalAlignment="{TemplateBinding VerticalContentAlignment}">
<Grid x:Name="markGrid">
<Path x:Name="optionMark" Data="F1 M 9.97498,1.22334L 4.6983,9.09834L 4.52164,9.09834L 0,5.19331L 1.27664,3.52165L 4.255,6.08833L 8.33331,1.52588e-005L 9.97498,1.22334 Z " Fill="{StaticResource OptionMark.Static.Glyph}" Margin="1" Opacity="0" Stretch="None"/>
<Rectangle x:Name="indeterminateMark" Fill="{StaticResource OptionMark.Static.Glyph}" Margin="2" Opacity="0"/>
</Grid>
</Border>
<ContentPresenter x:Name="contentPresenter" Grid.Column="1" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasContent" Value="true">
<Setter Property="FocusVisualStyle" Value="{StaticResource OptionMarkFocusVisual}"/>
<Setter Property="Padding" Value="4,-1,0,0"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.MouseOver.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.MouseOver.Glyph}"/>
</Trigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Disabled.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Disabled.Glyph}"/>
</Trigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Background" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="checkBoxBorder" Value="{StaticResource OptionMark.Pressed.Border}"/>
<Setter Property="Fill" TargetName="optionMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
<Setter Property="Fill" TargetName="indeterminateMark" Value="{StaticResource OptionMark.Pressed.Glyph}"/>
</Trigger>
<Trigger Property="IsChecked" Value="true">
<Setter Property="Opacity" TargetName="optionMark" Value="1"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="0"/>
</Trigger>
<Trigger Property="IsChecked" Value="{x:Null}">
<Setter Property="Opacity" TargetName="optionMark" Value="0"/>
<Setter Property="Opacity" TargetName="indeterminateMark" Value="1"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,448 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:system="clr-namespace:System;assembly=mscorlib">
<ControlTemplate x:Key="HorizontalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid x:Name="Root" Margin="0">
<Grid.Resources>
<ControlTemplate x:Key="RepeatButtonTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Margin="0" Background="Transparent">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalIncrementTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="HoverPath">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard />
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Root">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle Fill="#58595B" Width="18" Height="13" Margin="0" />
<Rectangle x:Name="HoverPath" Opacity="0" Fill="#626262" Width="18" Height="13" Margin="0" />
<Rectangle x:Name="DisabledElement"
Fill="{DynamicResource WhiteBrush}"
Opacity="0"
RadiusY="2"
RadiusX="2" />
<Path Stroke="#AFADAD" Fill="#AFADAD" Data="M0,0 L1,0.5 0,1Z" Width="4" Height="5" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalDecrementTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="HoverPath">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard />
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Root">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle Fill="#58595B" Width="18" Height="13" Margin="0" />
<Rectangle x:Name="HoverPath" Opacity="0" Fill="#626262" Width="18" Height="13" Margin="0" />
<Rectangle x:Name="DisabledElement"
Fill="{DynamicResource WhiteBrush}"
Opacity="0"
RadiusY="2"
RadiusX="2" />
<Path Stroke="#AFADAD" Fill="#AFADAD" Data="M0,0.5 L1,1 1,0Z" Width="4" Height="5" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="HorizontalThumbTemplate" TargetType="Thumb">
<Grid Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="MouseOverRectangle">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PressedRectangle">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ThumbVisual">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="ThumbVisual" Margin="4,0,4,0">
<Rectangle x:Name="Background" Fill="#58595B" />
<Rectangle x:Name="MouseOverRectangle" Opacity="0" Fill="#626262" />
<Rectangle x:Name="PressedRectangle" Opacity="0" Fill="#626262" />
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0" Background="White" />
<Grid Grid.Column="2" Grid.Row="0" Background="White" />
<Grid Grid.Column="4" Grid.Row="0" Background="White" />
<Grid Grid.Column="6" Grid.Row="0" Background="White" />
<Grid Grid.Column="0" Grid.Row="2" Background="White" />
<Grid Grid.Column="2" Grid.Row="2" Background="White" />
<Grid Grid.Column="4" Grid.Row="2" Background="White" />
<Grid Grid.Column="6" Grid.Row="2" Background="White" />
<Grid Grid.Column="0" Grid.Row="4" Background="White" />
<Grid Grid.Column="2" Grid.Row="4" Background="White" />
<Grid Grid.Column="4" Grid.Row="4" Background="White" />
<Grid Grid.Column="6" Grid.Row="4" Background="White" />
<Grid Grid.Column="0" Grid.Row="6" Background="White" />
<Grid Grid.Column="2" Grid.Row="6" Background="White" />
<Grid Grid.Column="4" Grid.Row="6" Background="White" />
<Grid Grid.Column="6" Grid.Row="6" Background="White" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root">
<SplineDoubleKeyFrame KeyTime="0" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="HorizontalRoot" Margin="0" Height="13">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="18" />
<ColumnDefinition Width="0.00001*" />
<ColumnDefinition Width="18" />
</Grid.ColumnDefinitions>
<RepeatButton x:Name="HorizontalSmallDecrease" Command="ScrollBar.LineLeftCommand" Width="18" Height="13" IsTabStop="False" Interval="50" Margin="0" Padding="0" Grid.Column="0" Template="{DynamicResource HorizontalDecrementTemplate}" />
<Track x:Name="PART_Track" IsDirectionReversed="False" Grid.Column="1">
<Track.DecreaseRepeatButton>
<RepeatButton x:Name="HorizontalLargeDecrease" Command="ScrollBar.PageLeftCommand" IsTabStop="False" Interval="50" Template="{DynamicResource RepeatButtonTemplate}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Template="{DynamicResource HorizontalThumbTemplate}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton x:Name="HorizontalLargeIncrease" Command="ScrollBar.PageRightCommand" IsTabStop="False" Interval="50" Template="{DynamicResource RepeatButtonTemplate}" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton x:Name="HorizontalSmallIncrease" Command="ScrollBar.LineRightCommand" Width="18" Height="13" IsTabStop="False" Interval="50" Margin="0" Padding="0" Grid.Column="2" Template="{DynamicResource HorizontalIncrementTemplate}" />
</Grid>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="VerticalScrollBar" TargetType="{x:Type ScrollBar}">
<Grid x:Name="Root" Margin="0">
<Grid.Resources>
<ControlTemplate x:Key="RepeatButtonTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Background="Transparent" Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="VerticalIncrementTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="HoverPath">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard />
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Root">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle Fill="#58595B" Width="13" Height="18" Margin="0" />
<Rectangle x:Name="HoverPath" Opacity="0" Fill="#626262" Width="13" Height="18" Margin="0" />
<Rectangle x:Name="DisabledElement"
Fill="{DynamicResource WhiteBrush}"
Opacity="0"
RadiusY="2"
RadiusX="2" />
<Path Stroke="#AFADAD" Fill="#AFADAD" Data="M0,0 L1,0 0.5,1Z" Width="5" Height="4" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="VerticalDecrementTemplate" TargetType="RepeatButton">
<Grid x:Name="Root" Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="HoverPath">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard />
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="Root">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Rectangle Fill="#58595B" Width="13" Height="18" Margin="0" />
<Rectangle x:Name="HoverPath" Opacity="0" Fill="#626262" Width="13" Height="18" Margin="0" />
<Rectangle x:Name="DisabledElement"
Fill="{DynamicResource WhiteBrush}"
Opacity="0"
RadiusY="2"
RadiusX="2" />
<Path Stroke="#AFADAD" Fill="#AFADAD" Data="M0,1 L1,1 0.5,0Z" Width="5" Height="4" Stretch="Fill" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</ControlTemplate>
<ControlTemplate x:Key="VerticalThumbTemplate" TargetType="Thumb">
<Grid Margin="0">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="MouseOverRectangle">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Pressed">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Opacity)" Storyboard.TargetName="PressedRectangle">
<EasingDoubleKeyFrame KeyTime="0:0:0.1" Value="1" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="ThumbVisual">
<SplineDoubleKeyFrame KeyTime="0:0:0" Value="0" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="ThumbVisual" Margin="0,4,0,4">
<Rectangle x:Name="Background" Fill="#58595B" />
<Rectangle x:Name="MouseOverRectangle" Opacity="0" Fill="#626262" />
<Rectangle x:Name="PressedRectangle" Opacity="0" Fill="#626262" />
<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
<ColumnDefinition Width="1" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
<RowDefinition Height="1" />
</Grid.RowDefinitions>
<Grid Grid.Column="0" Grid.Row="0" Background="White" />
<Grid Grid.Column="2" Grid.Row="0" Background="White" />
<Grid Grid.Column="4" Grid.Row="0" Background="White" />
<Grid Grid.Column="6" Grid.Row="0" Background="White" />
<Grid Grid.Column="0" Grid.Row="2" Background="White" />
<Grid Grid.Column="2" Grid.Row="2" Background="White" />
<Grid Grid.Column="4" Grid.Row="2" Background="White" />
<Grid Grid.Column="6" Grid.Row="2" Background="White" />
<Grid Grid.Column="0" Grid.Row="4" Background="White" />
<Grid Grid.Column="2" Grid.Row="4" Background="White" />
<Grid Grid.Column="4" Grid.Row="4" Background="White" />
<Grid Grid.Column="6" Grid.Row="4" Background="White" />
<Grid Grid.Column="0" Grid.Row="6" Background="White" />
<Grid Grid.Column="2" Grid.Row="6" Background="White" />
<Grid Grid.Column="4" Grid.Row="6" Background="White" />
<Grid Grid.Column="6" Grid.Row="6" Background="White" />
</Grid>
</Grid>
</Grid>
</ControlTemplate>
</Grid.Resources>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CommonStates">
<VisualState x:Name="Normal" />
<VisualState x:Name="MouseOver" />
<VisualState x:Name="Disabled">
<Storyboard>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="Opacity" Storyboard.TargetName="Root">
<SplineDoubleKeyFrame KeyTime="0" Value="0.5" />
</DoubleAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Grid x:Name="VerticalRoot" Margin="0" Width="13">
<Grid.RowDefinitions>
<RowDefinition Height="18" />
<RowDefinition Height="0.00001*" />
<RowDefinition Height="18" />
</Grid.RowDefinitions>
<RepeatButton x:Name="VerticalSmallDecrease" Command="ScrollBar.LineUpCommand" Width="13 " Height="18" IsTabStop="False" Interval="50" Margin="0" Padding="0" Grid.Row="0" Template="{DynamicResource VerticalDecrementTemplate}" />
<Track x:Name="PART_Track" IsDirectionReversed="true" Grid.Row="1">
<Track.Resources>
<system:Double x:Key="{x:Static SystemParameters.VerticalScrollBarButtonHeightKey}">50</system:Double>
</Track.Resources>
<Track.DecreaseRepeatButton>
<RepeatButton x:Name="VerticalLargeDecrease" Command="ScrollBar.PageUpCommand" IsTabStop="False" Interval="50" Template="{DynamicResource RepeatButtonTemplate}" />
</Track.DecreaseRepeatButton>
<Track.Thumb>
<Thumb Template="{DynamicResource VerticalThumbTemplate}" />
</Track.Thumb>
<Track.IncreaseRepeatButton>
<RepeatButton x:Name="VerticalLargeIncrease" Command="ScrollBar.PageDownCommand" IsTabStop="False" Interval="50" Template="{DynamicResource RepeatButtonTemplate}" />
</Track.IncreaseRepeatButton>
</Track>
<RepeatButton x:Name="VerticalSmallIncrease" Command="ScrollBar.LineDownCommand" Width="13" Height="18" IsTabStop="False" Interval="50" Margin="0" Padding="0" Grid.Row="2" Template="{DynamicResource VerticalIncrementTemplate}" />
</Grid>
</Grid>
</ControlTemplate>
<Style x:Key="{x:Type ScrollBar}" TargetType="{x:Type ScrollBar}">
<Setter Property="SnapsToDevicePixels" Value="True" />
<Setter Property="OverridesDefaultStyle" Value="True" />
<Style.Triggers>
<Trigger Property="ScrollBar.Orientation" Value="Horizontal">
<Setter Property="FrameworkElement.Width" Value="Auto" />
<Setter Property="FrameworkElement.Height" Value="13" />
<Setter Property="Control.Template" Value="{DynamicResource HorizontalScrollBar}" />
</Trigger>
<Trigger Property="ScrollBar.Orientation" Value="Vertical">
<Setter Property="FrameworkElement.Width" Value="12" />
<Setter Property="FrameworkElement.Height" Value="Auto" />
<Setter Property="Control.Template" Value="{DynamicResource VerticalScrollBar}" />
</Trigger>
</Style.Triggers>
</Style>
<Style TargetType="{x:Type ScrollViewer}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ScrollViewer}">
<Grid Background="Transparent">
<Grid.ColumnDefinitions>
<ColumnDefinition/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<ScrollContentPresenter Grid.Column="0" />
<ScrollBar Name="PART_VerticalScrollBar" Grid.Row="0" Grid.Column="1" Value="{TemplateBinding VerticalOffset}" Maximum="{TemplateBinding ScrollableHeight}" ViewportSize="{TemplateBinding ViewportHeight}" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
<ScrollBar Name="PART_HorizontalScrollBar" Orientation="Horizontal" Grid.Row="1" Grid.Column="0" Value="{TemplateBinding HorizontalOffset}" Maximum="{TemplateBinding ScrollableWidth}" ViewportSize="{TemplateBinding ViewportWidth}" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<Rectangle Grid.Row="1" Grid.Column="1" Fill="Transparent"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

View File

@ -0,0 +1,805 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:themes="clr-namespace:Microsoft.Windows.Themes;assembly=PresentationFramework.Aero2"
xmlns:classes="clr-namespace:Steamless.Classes">
<!-- Control: Button -->
<Style x:Key="{x:Type Button}" TargetType="{x:Type Button}">
<Setter Property="Height" Value="24" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Border x:Name="MainBorder" BorderThickness="0" BorderBrush="Transparent" Margin="0" Padding="0" CornerRadius="0.3">
<Border.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
<Label x:Name="ContentLabel" Foreground="#CFCFCF" FontSize="10" FontWeight="SemiBold" Margin="0" Padding="0" HorizontalAlignment="Center" VerticalAlignment="Center">
<ContentPresenter Margin="0" ContentSource="Content" RecognizesAccessKey="True" />
</Label>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter TargetName="ContentLabel" Property="Foreground" Value="#E2E2E2" />
<Setter TargetName="MainBorder" Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#626262" Offset="0"/>
<GradientStop Color="#3B3B3B" Offset="0.9"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
<Trigger Property="IsMouseCaptured" Value="True">
<Setter TargetName="ContentLabel" Property="Foreground" Value="#E2E2E2" />
<Setter TargetName="MainBorder" Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#262626" Offset="0"/>
<GradientStop Color="#393939" Offset="0.9"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Control: GroupBox -->
<Style x:Key="{x:Type GroupBox}" TargetType="{x:Type GroupBox}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GroupBox}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Border Grid.Row="0" BorderThickness="0,0,0,1" BorderBrush="#595959" Margin="0" Padding="0">
<Label Foreground="#A8A8A8" FontWeight="Bold" Margin="0" Padding="0">
<ContentPresenter Margin="0,0,0,2" ContentSource="Header" RecognizesAccessKey="True" />
</Label>
</Border>
<Border Grid.Row="1" BorderThickness="1,0,1,1" BorderBrush="#595959">
<ContentPresenter Margin="2" />
</Border>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Control: ProgressBar -->
<Style x:Key="{x:Type ProgressBar}" TargetType="{x:Type ProgressBar}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<!-- Main Progressbar Grid -->
<Grid ClipToBounds="True">
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="ValidationStates" />
<VisualStateGroup x:Name="CommonStates">
<!-- Determinate State; Hide Dots -->
<VisualState x:Name="Determinate">
<Storyboard>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Dots">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
<!-- Indeterminate State; Show Dots, Hide Track -->
<VisualState x:Name="Indeterminate">
<Storyboard RepeatBehavior="Forever">
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Dot0">
<EasingDoubleKeyFrame KeyTime="0:0:0.7" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1.1" Value="100" />
<EasingDoubleKeyFrame KeyTime="0:0:2.8" Value="300" />
<EasingDoubleKeyFrame KeyTime="0:0:3.2" Value="400" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Dot1">
<EasingDoubleKeyFrame KeyTime="0:0:0.8" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1.2" Value="100" />
<EasingDoubleKeyFrame KeyTime="0:0:2.9" Value="300" />
<EasingDoubleKeyFrame KeyTime="0:0:3.3" Value="400" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Dot2">
<EasingDoubleKeyFrame KeyTime="0:0:0.9" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1.3" Value="100" />
<EasingDoubleKeyFrame KeyTime="0:0:3.0" Value="300" />
<EasingDoubleKeyFrame KeyTime="0:0:3.4" Value="400" />
</DoubleAnimationUsingKeyFrames>
<DoubleAnimationUsingKeyFrames Storyboard.TargetProperty="(Canvas.Left)" Storyboard.TargetName="Dot3">
<EasingDoubleKeyFrame KeyTime="0:0:1.0" Value="0" />
<EasingDoubleKeyFrame KeyTime="0:0:1.4" Value="100" />
<EasingDoubleKeyFrame KeyTime="0:0:3.1" Value="300" />
<EasingDoubleKeyFrame KeyTime="0:0:3.5" Value="400" />
</DoubleAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Dot0">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.7" Value="{x:Static Visibility.Visible}" />
<DiscreteObjectKeyFrame KeyTime="0:0:3.2" Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Dot1">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.8" Value="{x:Static Visibility.Visible}" />
<DiscreteObjectKeyFrame KeyTime="0:0:3.3" Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Dot2">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}" />
<DiscreteObjectKeyFrame KeyTime="0:0:0.9" Value="{x:Static Visibility.Visible}" />
<DiscreteObjectKeyFrame KeyTime="0:0:3.4" Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="Dot3">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Hidden}" />
<DiscreteObjectKeyFrame KeyTime="0:0:1.0" Value="{x:Static Visibility.Visible}" />
<DiscreteObjectKeyFrame KeyTime="0:0:3.5" Value="{x:Static Visibility.Hidden}" />
</ObjectAnimationUsingKeyFrames>
<ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="(UIElement.Visibility)" Storyboard.TargetName="PART_Track">
<DiscreteObjectKeyFrame KeyTime="0" Value="{x:Static Visibility.Collapsed}" />
</ObjectAnimationUsingKeyFrames>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<!-- Progressbar Track -->
<Border x:Name="PART_Track" Background="#3F000000" UseLayoutRounding="False">
<Rectangle x:Name="PART_Indicator" Fill="{TemplateBinding Foreground}" HorizontalAlignment="Left" UseLayoutRounding="False" />
</Border>
<!-- Progressbar Dots -->
<Canvas x:Name="Dots" Width="410" Height="3" Margin="0,0,0,0" HorizontalAlignment="Center" VerticalAlignment="Center" UseLayoutRounding="False">
<Ellipse x:Name="Dot0" Fill="{TemplateBinding Foreground}" Width="3" Height="3" StrokeThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Ellipse x:Name="Dot1" Fill="{TemplateBinding Foreground}" Width="3" Height="3" StrokeThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Ellipse x:Name="Dot2" Fill="{TemplateBinding Foreground}" Width="3" Height="3" StrokeThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
<Ellipse x:Name="Dot3" Fill="{TemplateBinding Foreground}" Width="3" Height="3" StrokeThickness="0" HorizontalAlignment="Left" VerticalAlignment="Top" />
</Canvas>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Control: TextBox -->
<ControlTemplate x:Key="ValidationToolTipTemplate">
<Grid x:Name="Root" Margin="5,0" RenderTransformOrigin="0,0" Opacity="0">
<Grid.RenderTransform>
<TranslateTransform x:Name="xform" X="-25"/>
</Grid.RenderTransform>
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="OpenStates">
<VisualStateGroup.Transitions>
<VisualTransition GeneratedDuration="0"/>
<VisualTransition To="Open" GeneratedDuration="0:0:0.2">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0:0:0.2">
<DoubleAnimation.EasingFunction>
<BackEase Amplitude=".3" EasingMode="EaseOut"/>
</DoubleAnimation.EasingFunction>
</DoubleAnimation>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0:0:0.2"/>
</Storyboard>
</VisualTransition>
</VisualStateGroup.Transitions>
<VisualState x:Name="Closed">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="0" Duration="0"/>
</Storyboard>
</VisualState>
<VisualState x:Name="Open">
<Storyboard>
<DoubleAnimation Storyboard.TargetName="xform" Storyboard.TargetProperty="X" To="0" Duration="0"/>
<DoubleAnimation Storyboard.TargetName="Root" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
<Border Margin="4,4,-4,-4" Background="#052A2E31" CornerRadius="5"/>
<Border Margin="3,3,-3,-3" Background="#152A2E31" CornerRadius="4"/>
<Border Margin="2,2,-2,-2" Background="#252A2E31" CornerRadius="3"/>
<Border Margin="1,1,-1,-1" Background="#352A2E31" CornerRadius="2"/>
<Border Background="#FFDC000C" CornerRadius="2"/>
<Border CornerRadius="2">
<TextBlock
UseLayoutRounding="false"
Foreground="White" Margin="8,4,8,4" MaxWidth="250" TextWrapping="Wrap" Text="{Binding (Validation.Errors)[0].ErrorContent}"/>
</Border>
</Grid>
</ControlTemplate>
<!-- Control: TextBox -->
<Style TargetType="{x:Type TextBox}">
<Setter Property="BorderBrush" Value="#595959" />
<Setter Property="BorderThickness" Value="1"/>
<Setter Property="Background" Value="#272727"/>
<Setter Property="Foreground" Value="#CFCFCF"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="SnapsToDevicePixels" Value="True"/>
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBoxBase}">
<Border x:Name="Border" Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}">
<ScrollViewer Margin="0" x:Name="PART_ContentHost" VerticalAlignment="Center" VerticalContentAlignment="Stretch" />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsFocused" Value="True">
<Setter TargetName="Border" Property="BorderBrush" Value="#898989" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Control: ComboBox -->
<Style x:Key="FocusVisual">
<Setter Property="Control.Template">
<Setter.Value>
<ControlTemplate>
<Rectangle Margin="2" SnapsToDevicePixels="true" Stroke="Transparent" StrokeThickness="1" StrokeDashArray="1 2"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<LinearGradientBrush x:Key="ComboBox.Static.Background" StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Static.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Glyph" Color="#E4E3DB"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Background" StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#606060" Offset="0"/>
<GradientStop Color="#3B3B3B" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Border" Color="Transparent"/>
<LinearGradientBrush x:Key="ComboBox.MouseOver.Editable.Button.Background" StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.MouseOver.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Glyph" Color="#E4E3DB"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Background" StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Border" Color="Transparent"/>
<LinearGradientBrush x:Key="ComboBox.Pressed.Editable.Button.Background" StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="ComboBox.Pressed.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Glyph" Color="#FFBFBFBF"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Background" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Disabled.Editable.Button.Border" Color="Transparent"/>
<SolidColorBrush x:Key="ComboBox.Static.Glyph" Color="#FF606060"/>
<Style x:Key="ComboBoxToggleButton" TargetType="{x:Type ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Focusable" Value="false"/>
<Setter Property="ClickMode" Value="Press"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Border x:Name="templateRoot" BorderBrush="{StaticResource ComboBox.Static.Border}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="true">
<Border.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
<Border x:Name="splitBorder" BorderBrush="Transparent" BorderThickness="1" HorizontalAlignment="Right" Margin="0" SnapsToDevicePixels="true" Width="24">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Rectangle Grid.Column="0" Fill="#7C7C7C" VerticalAlignment="Stretch" Margin="0,3,0,3" Width="1" />
<Path Grid.Column="1" x:Name="arrow"
Data="F1 M 531.107,321.943L 541.537,321.943L 536.322,328.042L 531.107,321.943 Z "
Height="22" Width="8" Stretch="Uniform" Margin="1,0,0,0"
Fill="#AFADAD" HorizontalAlignment="Center" VerticalAlignment="Center" />
</Grid>
</Border>
</Border>
<ControlTemplate.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Static.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Static.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="#E4E3DB"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.MouseOver.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.MouseOver.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsPressed" Value="true">
<Setter Property="Fill" TargetName="arrow" Value="#E4E3DB"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsPressed, RelativeSource={RelativeSource Self}}" Value="true"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Pressed.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Pressed.Editable.Button.Border}"/>
</MultiDataTrigger>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Fill" TargetName="arrow" Value="{StaticResource ComboBox.Disabled.Glyph}"/>
</Trigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="false"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Border}"/>
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled, RelativeSource={RelativeSource Self}}" Value="false"/>
<Condition Binding="{Binding IsEditable, RelativeSource={RelativeSource AncestorType={x:Type ComboBox}}}" Value="true"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Background}"/>
<Setter Property="BorderBrush" TargetName="templateRoot" Value="{StaticResource ComboBox.Disabled.Editable.Border}"/>
<Setter Property="Background" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Background}"/>
<Setter Property="BorderBrush" TargetName="splitBorder" Value="{StaticResource ComboBox.Disabled.Editable.Button.Border}"/>
</MultiDataTrigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Margin="1" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="0">
<Border.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
</Border.Background>
<ScrollViewer x:Name="DropDownScrollViewer" Margin="2">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}" />
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
</Grid>
</ScrollViewer>
</Border>
</themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
<ContentPresenter x:Name="contentPresenter" ContentTemplate="{TemplateBinding SelectionBoxItemTemplate}" ContentTemplateSelector="{TemplateBinding ItemTemplateSelector}" Content="{TemplateBinding SelectionBoxItem}" ContentStringFormat="{TemplateBinding SelectionBoxItemStringFormat}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" IsHitTestVisible="false" Margin="{TemplateBinding Padding}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<SolidColorBrush x:Key="TextBox.Static.Background" Color="#FFFFFFFF"/>
<Style x:Key="ComboBoxEditableTextBox" TargetType="{x:Type TextBox}">
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="AllowDrop" Value="true"/>
<Setter Property="MinWidth" Value="0"/>
<Setter Property="MinHeight" Value="0"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ScrollViewer.PanningMode" Value="VerticalFirst"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type TextBox}">
<ScrollViewer x:Name="PART_ContentHost" Background="Transparent" Focusable="false" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden"/>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<ControlTemplate x:Key="ComboBoxEditableTemplate" TargetType="{x:Type ComboBox}">
<Grid x:Name="templateRoot" SnapsToDevicePixels="true">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition MinWidth="{DynamicResource {x:Static SystemParameters.VerticalScrollBarWidthKey}}" Width="0"/>
</Grid.ColumnDefinitions>
<Popup x:Name="PART_Popup" AllowsTransparency="true" Grid.ColumnSpan="2" IsOpen="{Binding IsDropDownOpen, RelativeSource={RelativeSource TemplatedParent}}" PopupAnimation="{DynamicResource {x:Static SystemParameters.ComboBoxPopupAnimationKey}}" Placement="Bottom">
<themes:SystemDropShadowChrome x:Name="shadow" Color="Transparent" MaxHeight="{TemplateBinding MaxDropDownHeight}" MinWidth="{Binding ActualWidth, ElementName=templateRoot}">
<Border x:Name="dropDownBorder" BorderBrush="{DynamicResource {x:Static SystemColors.WindowFrameBrushKey}}" BorderThickness="1" Background="{DynamicResource {x:Static SystemColors.WindowBrushKey}}">
<ScrollViewer x:Name="DropDownScrollViewer">
<Grid x:Name="grid" RenderOptions.ClearTypeHint="Enabled">
<Canvas x:Name="canvas" HorizontalAlignment="Left" Height="0" VerticalAlignment="Top" Width="0">
<Rectangle x:Name="opaqueRect" Fill="{Binding Background, ElementName=dropDownBorder}" Height="{Binding ActualHeight, ElementName=dropDownBorder}" Width="{Binding ActualWidth, ElementName=dropDownBorder}"/>
</Canvas>
<ItemsPresenter x:Name="ItemsPresenter" KeyboardNavigation.DirectionalNavigation="Contained" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
</Grid>
</ScrollViewer>
</Border>
</themes:SystemDropShadowChrome>
</Popup>
<ToggleButton x:Name="toggleButton" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" Grid.ColumnSpan="2" IsChecked="{Binding IsDropDownOpen, Mode=TwoWay, RelativeSource={RelativeSource TemplatedParent}}" Style="{StaticResource ComboBoxToggleButton}"/>
<Border x:Name="border" Background="{StaticResource TextBox.Static.Background}" Margin="{TemplateBinding BorderThickness}">
<TextBox x:Name="PART_EditableTextBox" HorizontalContentAlignment="{TemplateBinding HorizontalContentAlignment}" IsReadOnly="{Binding IsReadOnly, RelativeSource={RelativeSource TemplatedParent}}" Margin="{TemplateBinding Padding}" Style="{StaticResource ComboBoxEditableTextBox}" VerticalContentAlignment="{TemplateBinding VerticalContentAlignment}"/>
</Border>
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsKeyboardFocusWithin" Value="true">
<Setter Property="Foreground" Value="Black"/>
</Trigger>
<Trigger Property="HasDropShadow" SourceName="PART_Popup" Value="true">
<Setter Property="Margin" TargetName="shadow" Value="0,0,5,5"/>
<Setter Property="Color" TargetName="shadow" Value="#71000000"/>
</Trigger>
<Trigger Property="HasItems" Value="false">
<Setter Property="Height" TargetName="dropDownBorder" Value="95"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsGrouping" Value="true"/>
<Condition Property="VirtualizingPanel.IsVirtualizingWhenGrouping" Value="false"/>
</MultiTrigger.Conditions>
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</MultiTrigger>
<Trigger Property="ScrollViewer.CanContentScroll" SourceName="DropDownScrollViewer" Value="false">
<Setter Property="Canvas.Top" TargetName="opaqueRect" Value="{Binding VerticalOffset, ElementName=DropDownScrollViewer}"/>
<Setter Property="Canvas.Left" TargetName="opaqueRect" Value="{Binding HorizontalOffset, ElementName=DropDownScrollViewer}"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
<Style x:Key="{x:Type ComboBoxItem}" TargetType="ComboBoxItem">
<Setter Property="SnapsToDevicePixels" Value="true"/>
<Setter Property="OverridesDefaultStyle" Value="true"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ComboBoxItem">
<Border Name="Border" Margin="2" Padding="2" SnapsToDevicePixels="true">
<ContentPresenter />
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Foreground" Value="White"/>
</Trigger>
<Trigger Property="IsHighlighted" Value="True">
<Setter TargetName="Border" Property="Background" Value="Transparent"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style TargetType="{x:Type ComboBox}">
<Setter Property="FocusVisualStyle" Value="{DynamicResource FocusVisual}"/>
<Setter Property="Background">
<Setter.Value>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#505050" Offset="0"/>
<GradientStop Color="#3A3A3A" Offset="0.9"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
<Setter Property="BorderBrush" Value="Transparent"/>
<Setter Property="Foreground" Value="#CFCFCF" />
<Setter Property="BorderThickness" Value="0"/>
<Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Auto"/>
<Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/>
<Setter Property="Padding" Value="6,3,5,3"/>
<Setter Property="ScrollViewer.CanContentScroll" Value="true"/>
<Setter Property="ScrollViewer.PanningMode" Value="Both"/>
<Setter Property="Stylus.IsFlicksEnabled" Value="False"/>
<Setter Property="VerticalContentAlignment" Value="Center" />
<Setter Property="Template" Value="{StaticResource ComboBoxTemplate}"/>
<Style.Triggers>
<Trigger Property="IsEditable" Value="true">
<Setter Property="IsTabStop" Value="false"/>
<Setter Property="Padding" Value="2"/>
<Setter Property="Template" Value="{StaticResource ComboBoxEditableTemplate}"/>
</Trigger>
</Style.Triggers>
</Style>
<!-- Controls: ListView -> Grid -->
<Style x:Key="{x:Static GridView.GridViewScrollViewerStyleKey}" TargetType="ScrollViewer">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ScrollViewer">
<Grid SnapsToDevicePixels="True" RenderOptions.EdgeMode="Aliased">
<Grid.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1" RenderOptions.EdgeMode="Aliased">
<GradientStop Color="#252525" Offset="0" RenderOptions.EdgeMode="Aliased" />
<GradientStop Color="#161616" Offset="0.9" RenderOptions.EdgeMode="Aliased" />
</LinearGradientBrush>
</Grid.Background>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="Auto"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<DockPanel Margin="0">
<ScrollViewer DockPanel.Dock="Top" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" Focusable="false">
<GridViewHeaderRowPresenter Margin="0"
Columns="{Binding Path=TemplatedParent.View.Columns, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContainerStyle="{Binding Path=TemplatedParent.View.ColumnHeaderContainerStyle, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplate="{Binding Path=TemplatedParent.View.ColumnHeaderTemplate, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderTemplateSelector="{Binding Path=TemplatedParent.View.ColumnHeaderTemplateSelector, RelativeSource={RelativeSource TemplatedParent}}"
AllowsColumnReorder="{Binding Path=TemplatedParent.View.AllowsColumnReorder, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderContextMenu="{Binding Path=TemplatedParent.View.ColumnHeaderContextMenu, RelativeSource={RelativeSource TemplatedParent}}"
ColumnHeaderToolTip="{Binding Path=TemplatedParent.View.ColumnHeaderToolTip, RelativeSource={RelativeSource TemplatedParent}}" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
/>
</ScrollViewer>
<Border Background="Transparent">
<ScrollContentPresenter Name="PART_ScrollContentPresenter"
KeyboardNavigation.DirectionalNavigation="Local"
CanContentScroll="True" CanHorizontallyScroll="False"
CanVerticallyScroll="False" />
</Border>
</DockPanel>
<ScrollBar Name="PART_HorizontalScrollBar" Margin="0,2,0,0"
Orientation="Horizontal"
Grid.Column="0" Grid.Row="1"
Maximum="{TemplateBinding ScrollableWidth}"
ViewportSize="{TemplateBinding ViewportWidth}"
Value="{TemplateBinding HorizontalOffset}"
Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}"/>
<ScrollBar Name="PART_VerticalScrollBar" Margin="2,0,0,0"
Grid.Column="1" Grid.Row="0"
Maximum="{TemplateBinding ScrollableHeight}"
ViewportSize="{TemplateBinding ViewportHeight}"
Value="{TemplateBinding VerticalOffset}"
Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}"/>
</Grid>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<!-- Controls: ListView -> Grid -->
<Style TargetType="{x:Type GridViewHeaderRowPresenter}">
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Margin" Value="0" />
</Style>
<!-- Controls: ListView -> Grid -->
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type GridViewColumnHeader}">
<Border BorderThickness="0,0,1,0" BorderBrush="#262626" Background="#3A3A3A" Margin="0" Padding="0">
<TextBlock x:Name="ContentHeader" Text="{TemplateBinding Content}" Padding="5,2,2,2" Width="{TemplateBinding Width}" TextAlignment="Left" />
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="OverridesDefaultStyle" Value="True" />
<Setter Property="Background" Value="#3A3A3A" />
<Setter Property="Foreground" Value="#A8A8A8" />
<Setter Property="HorizontalContentAlignment" Value="Left" />
<Setter Property="VerticalContentAlignment" Value="Center" />
</Style>
<!-- Controls: ListView -->
<Style x:Key="LogListView" TargetType="{x:Type ListView}">
<Setter Property="classes:GridViewColumnWidthFromItemsBehavior.Enabled" Value="True" />
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ListView">
<Border Name="Border" BorderThickness="0" BorderBrush="Transparent" Background="Transparent">
<ScrollViewer Style="{DynamicResource {x:Static GridView.GridViewScrollViewerStyleKey}}">
<ItemsPresenter />
</ScrollViewer>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsGrouping" Value="true">
<Setter Property="ScrollViewer.CanContentScroll" Value="false"/>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
<Setter Property="ItemContainerStyle">
<Setter.Value>
<Style TargetType="{x:Type ListViewItem}">
<Setter Property="Background" Value="Transparent" />
<Setter Property="Margin" Value="0" />
<Setter Property="Padding" Value="0" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListViewItem}">
<Border BorderBrush="#0FFFFFFF" BorderThickness="0,0,0,1" Background="{TemplateBinding Background}">
<GridViewRowPresenter HorizontalAlignment="Stretch" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" Width="Auto" Margin="1" Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</Setter.Value>
</Setter>
<Setter Property="View">
<Setter.Value>
<GridView AllowsColumnReorder="False">
<GridViewColumn Width="26">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Rectangle x:Name="msgIcon" Fill="Transparent" Width="12" Height="12" SnapsToDevicePixels="True" />
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding MessageType}" Value="0">
<Setter TargetName="msgIcon" Property="ToolTip" Value="Information" />
<Setter TargetName="msgIcon" Property="Fill" Value="#B4B4B4" />
<Setter TargetName="msgIcon" Property="OpacityMask">
<Setter.Value>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_information_circle}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="1">
<Setter TargetName="msgIcon" Property="ToolTip" Value="Success" />
<Setter TargetName="msgIcon" Property="Fill" Value="#B6EB3E" />
<Setter TargetName="msgIcon" Property="OpacityMask">
<Setter.Value>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_check}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="2">
<Setter TargetName="msgIcon" Property="ToolTip" Value="Warning" />
<Setter TargetName="msgIcon" Property="Fill" Value="#F0AD4E" />
<Setter TargetName="msgIcon" Property="OpacityMask">
<Setter.Value>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_warning}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="3">
<Setter TargetName="msgIcon" Property="ToolTip" Value="Error" />
<Setter TargetName="msgIcon" Property="Fill" Value="#D9534F" />
<Setter TargetName="msgIcon" Property="OpacityMask">
<Setter.Value>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_error}" />
</Setter.Value>
</Setter>
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="4">
<Setter TargetName="msgIcon" Property="ToolTip" Value="Debug" />
<Setter TargetName="msgIcon" Property="Fill" Value="#5BC0DE" />
<Setter TargetName="msgIcon" Property="OpacityMask">
<Setter.Value>
<VisualBrush Stretch="Fill" Visual="{StaticResource appbar_alien}" />
</Setter.Value>
</Setter>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
<GridViewColumn Width="600" Header="MESSAGE">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Grid HorizontalAlignment="Left" VerticalAlignment="Center">
<TextBlock x:Name="msgText" Text="{Binding Message}" />
</Grid>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding MessageType}" Value="0">
<Setter TargetName="msgText" Property="Foreground" Value="#B4B4B4" />
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="1">
<Setter TargetName="msgText" Property="Foreground" Value="#B6EB3E" />
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="2">
<Setter TargetName="msgText" Property="Foreground" Value="#F0AD4E" />
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="3">
<Setter TargetName="msgText" Property="Foreground" Value="#D9534F" />
</DataTrigger>
<DataTrigger Binding="{Binding MessageType}" Value="4">
<Setter TargetName="msgText" Property="Foreground" Value="#5BC0DE" />
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
</GridView>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>

152
Steamless/Assets/Theme.xaml Normal file
View File

@ -0,0 +1,152 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<!-- Accent Colors -->
<Color x:Key="HighlightColor">#FFFF4A0D</Color>
<Color x:Key="AccentColor">#CCFF4A0D</Color>
<Color x:Key="AccentColor2">#99FF4A0D</Color>
<Color x:Key="AccentColor3">#66FF4A0D</Color>
<Color x:Key="AccentColor4">#33FF4A0D</Color>
<!-- Base Colors -->
<Color x:Key="BlackColor">#FF000000</Color>
<Color x:Key="WhiteColor">#FFFFFFFF</Color>
<Color x:Key="FontColor">#A8A8A8</Color>
<Color x:Key="Gray1">#FF333333</Color>
<Color x:Key="Gray2">#FF7F7F7F</Color>
<Color x:Key="Gray3">#FF9D9D9D</Color>
<Color x:Key="Gray4">#FFA59F93</Color>
<Color x:Key="Gray5">#FFB9B9B9</Color>
<Color x:Key="Gray6">#FFCCCCCC</Color>
<Color x:Key="Gray7">#FFD8D8D9</Color>
<Color x:Key="Gray8">#FFE0E0E0</Color>
<Color x:Key="Gray9">#5EC9C9C9</Color>
<Color x:Key="Gray10">#FFF7F7F7</Color>
<!-- Progressbar Colors -->
<Color x:Key="ProgressIndeterminateColor1">#33878787</Color>
<Color x:Key="ProgressIndeterminateColor2">#33959595</Color>
<Color x:Key="ProgressIndeterminateColor3">#4C000000</Color>
<Color x:Key="ProgressIndeterminateColor4">#4C000000</Color>
<!-- Brush Colors -->
<SolidColorBrush x:Key="ControlBackgroundBrush" Color="{DynamicResource WhiteColor}"/>
<SolidColorBrush x:Key="WhiteBrush" Color="{DynamicResource WhiteColor}" />
<SolidColorBrush x:Key="BlackBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="TextBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="LabelTextBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="WhiteColorBrush" Color="{DynamicResource WhiteColor}" />
<SolidColorBrush x:Key="BlackColorBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="WindowTitleColorBrush" Color="{DynamicResource AccentColor}" />
<SolidColorBrush x:Key="HighlightBrush" Color="{DynamicResource HighlightColor}" />
<SolidColorBrush x:Key="DisabledWhiteBrush" Color="{DynamicResource WhiteColor}" />
<SolidColorBrush x:Key="TransparentWhiteBrush" Color="#00FFFFFF"/>
<SolidColorBrush x:Key="SemiTransparentWhiteBrush" Color="#55FFFFFF"/>
<SolidColorBrush x:Key="ControlsDisabledBrush" Color="#A5FFFFFF" />
<SolidColorBrush x:Key="AccentColorBrush" Color="{DynamicResource AccentColor}"/>
<SolidColorBrush x:Key="AccentColorBrush2" Color="{DynamicResource AccentColor2}"/>
<SolidColorBrush x:Key="AccentColorBrush3" Color="{DynamicResource AccentColor3}"/>
<SolidColorBrush x:Key="AccentColorBrush4" Color="{DynamicResource AccentColor4}"/>
<SolidColorBrush x:Key="GrayBrush1" Color="{DynamicResource Gray1}"/>
<SolidColorBrush x:Key="GrayBrush2" Color="{DynamicResource Gray2}"/>
<SolidColorBrush x:Key="GrayBrush3" Color="{DynamicResource Gray3}"/>
<SolidColorBrush x:Key="GrayBrush4" Color="{DynamicResource Gray4}"/>
<SolidColorBrush x:Key="GrayBrush5" Color="{DynamicResource Gray5}"/>
<SolidColorBrush x:Key="GrayBrush6" Color="{DynamicResource Gray6}"/>
<SolidColorBrush x:Key="GrayBrush7" Color="{DynamicResource Gray7}"/>
<SolidColorBrush x:Key="GrayBrush8" Color="{DynamicResource Gray8}"/>
<SolidColorBrush x:Key="GrayBrush9" Color="{DynamicResource Gray9}"/>
<SolidColorBrush x:Key="GrayBrush10" Color="{DynamicResource Gray10}"/>
<SolidColorBrush x:Key="TextBoxBorderBrush" Color="{DynamicResource Gray6}" />
<SolidColorBrush x:Key="ControlBorderBrush" Color="{DynamicResource Gray6}" />
<SolidColorBrush x:Key="TextBoxMouseOverInnerBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="TextBoxFocusBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="TextBoxMouseOverBorderBrush" Color="{DynamicResource Gray2}" />
<SolidColorBrush x:Key="ButtonMouseOverBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="ButtonMouseOverInnerBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="ComboBoxMouseOverBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="ComboBoxMouseOverInnerBorderBrush" Color="{DynamicResource BlackColor}" />
<SolidColorBrush x:Key="CheckBoxBrush" Color="{DynamicResource Gray2}" />
<SolidColorBrush x:Key="CheckBoxMouseOverBrush" Color="{DynamicResource Gray1}" />
<LinearGradientBrush x:Key="CheckBoxBackgroundBrush" EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="{DynamicResource Gray7}" Offset="0"/>
<GradientStop Color="{DynamicResource WhiteColor}" Offset="1"/>
</LinearGradientBrush>
<SolidColorBrush x:Key="MenuItemDisabledBrush" Color="{DynamicResource Gray3}" />
<SolidColorBrush x:Key="ThumbBrush" Color="{DynamicResource Gray5}" />
<SolidColorBrush x:Key="ComboBoxPopupBrush" Color="{DynamicResource Gray4}" />
<LinearGradientBrush x:Key="ProgressBrush" EndPoint="0.001,0.5" StartPoint="1.002,0.5">
<GradientStop Color="{DynamicResource HighlightColor}" Offset="0"/>
<GradientStop Color="{DynamicResource AccentColor3}" Offset="1"/>
</LinearGradientBrush>
<DropShadowEffect x:Key="DropShadowBrush" Direction="330" Opacity="0.3" ShadowDepth="0" BlurRadius="6"/>
<SolidColorBrush x:Key="WindowBackgroundBrush" Color="{DynamicResource WhiteColor}" />
<SolidColorBrush x:Key="SeperatorBrush" Color="#FFC4C4C5"/>
<SolidColorBrush x:Key="FlatButtonBackgroundBrush" Color="#D5D5D5"/>
<SolidColorBrush x:Key="FlatButtonForegroundBrush" Color="#222222"/>
<SolidColorBrush x:Key="FlatButtonPressedBackgroundBrush" Color="#222222"/>
<SolidColorBrush x:Key="FlatButtonPressedForegroundBrush" Color="White"/>
<!-- Validation Brushes -->
<SolidColorBrush x:Key="ControlsValidationBrush" Color="#FFDB000C"/>
<SolidColorBrush x:Key="ValidationBrush1" Color="#052A2E31" />
<SolidColorBrush x:Key="ValidationBrush2" Color="#152A2E31" />
<SolidColorBrush x:Key="ValidationBrush3" Color="#252A2E31" />
<SolidColorBrush x:Key="ValidationBrush4" Color="#352A2E31" />
<SolidColorBrush x:Key="ValidationBrush5" Color="#FFDC000C" />
<SolidColorBrush x:Key="ValidationSummaryColor1" Color="#FFDC020D" />
<SolidColorBrush x:Key="ValidationSummaryColor2" Color="#FFCA000C" />
<SolidColorBrush x:Key="ValidationSummaryColor3" Color="#FFFF9298" />
<SolidColorBrush x:Key="ValidationSummaryColor4" Color="#FFFDC8C8" />
<SolidColorBrush x:Key="ValidationSummaryColor5" Color="#DDD43940" />
<SolidColorBrush x:Key="ValidationSummaryFillColor1" Color="#59F7D8DB" />
<SolidColorBrush x:Key="ValidationSummaryFillColor2" Color="#FFF7D8DB" />
<SolidColorBrush x:Key="ValidationTextBrush" Color="White"/>
<!-- WPF Defaults -->
<SolidColorBrush x:Key="{x:Static SystemColors.WindowBrushKey}" Color="{DynamicResource WhiteColor}" />
<SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="{DynamicResource FontColor}" />
<SolidColorBrush x:Key="MenuBackgroundBrush" Color="{StaticResource WhiteColor}" />
<SolidColorBrush x:Key="ContextMenuBackgroundBrush" Color="{StaticResource WhiteColor}" />
<SolidColorBrush x:Key="SubMenuBackgroundBrush" Color="{StaticResource WhiteColor}" />
<SolidColorBrush x:Key="ContextMenuBorderBrush" Color="{StaticResource BlackColor}" />
<SolidColorBrush x:Key="SubMenuBorderBrush" Color="{StaticResource BlackColor}" />
<SolidColorBrush x:Key="MenuItemSelectionFill" Color="#DEDEDE" />
<SolidColorBrush x:Key="MenuItemSelectionStroke" Color="#DEDEDE" />
<SolidColorBrush x:Key="TopMenuItemPressedFill" Color="#DEDEDE" />
<SolidColorBrush x:Key="TopMenuItemPressedStroke" Color="#E0717070" />
<SolidColorBrush x:Key="TopMenuItemSelectionStroke" Color="#90717070" />
<SolidColorBrush x:Key="DisabledMenuItemForeground" Color="{StaticResource Gray2}" />
<SolidColorBrush x:Key="DisabledMenuItemGlyphPanel" Color="#848589" />
<SolidColorBrush x:Key="{x:Static SystemColors.MenuTextBrushKey}" Color="{StaticResource BlackColor}" />
<Color x:Key="MenuShadowColor">#FF000000</Color>
<SolidColorBrush x:Key="AccentSelectedColorBrush" Color="White" />
<Color x:Key="IdealForegroundColor">White</Color>
<SolidColorBrush x:Key="IdealForegroundColorBrush" Color="{StaticResource IdealForegroundColor}"/>
<SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Red"/>
</ResourceDictionary>

View File

@ -0,0 +1,99 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style x:Key="MetroBaseWindowButtonStyle"
TargetType="{x:Type Button}">
<Setter Property="Background"
Value="{DynamicResource TransparentWhiteBrush}" />
<Setter Property="Foreground"
Value="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" />
<Setter Property="HorizontalContentAlignment"
Value="Center" />
<Setter Property="VerticalContentAlignment"
Value="Center" />
<Setter Property="Padding"
Value="1" />
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type Button}">
<Grid x:Name="grid"
Background="{TemplateBinding Background}">
<ContentPresenter x:Name="contentPresenter"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
Margin="{TemplateBinding Padding}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
RecognizesAccessKey="True"
Opacity="0.75" />
</Grid>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver"
Value="True">
<Setter TargetName="contentPresenter"
Property="Opacity"
Value="1" />
<Setter TargetName="grid"
Property="Background"
Value="Transparent" />
</Trigger>
<Trigger Property="IsMouseOver"
Value="False">
<Setter TargetName="contentPresenter"
Property="Opacity"
Value=".5" />
</Trigger>
<Trigger Property="IsPressed"
Value="True">
</Trigger>
<Trigger Property="IsEnabled"
Value="false">
<Setter Property="Foreground"
Value="#ADADAD" />
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
<Style x:Key="MetroWindowButtonStyle"
TargetType="{x:Type Button}"
BasedOn="{StaticResource MetroBaseWindowButtonStyle}">
<Setter Property="FocusVisualStyle"
Value="{x:Null}" />
<Setter Property="IsTabStop"
Value="False" />
<Setter Property="Width"
Value="34" />
<Setter Property="MaxHeight"
Value="34" />
<Setter Property="Padding"
Value="0" />
</Style>
<Style x:Key="WindowNavigationButton"
TargetType="{x:Type Button}"
BasedOn="{StaticResource MetroBaseWindowButtonStyle}" />
</ResourceDictionary>

1585
Steamless/Assets/icons.xaml Normal file

File diff suppressed because one or more lines are too long

BIN
Steamless/Assets/steam.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
Steamless/Assets/steam.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

View File

@ -0,0 +1,55 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Classes
{
using System.Windows;
/// <summary>
/// Contains helper functions for WPF controls.
///
/// Credits to the functions within this class:
/// https://github.com/MahApps/MahApps.Metro
/// </summary>
public static class ControlsHelper
{
/// <summary>
/// This property can be used to set vertical scrollbar left side from the tabpanel (look at MetroAnimatedSingleRowTabControl)
/// </summary>
public static readonly DependencyProperty VerticalScrollBarOnLeftSideProperty =
DependencyProperty.RegisterAttached("VerticalScrollBarOnLeftSide", typeof(bool), typeof(ControlsHelper),
new FrameworkPropertyMetadata(false, FrameworkPropertyMetadataOptions.AffectsArrange | FrameworkPropertyMetadataOptions.Inherits));
public static bool GetVerticalScrollBarOnLeftSide(DependencyObject obj)
{
return (bool)obj.GetValue(VerticalScrollBarOnLeftSideProperty);
}
public static void SetVerticalScrollBarOnLeftSide(DependencyObject obj, bool value)
{
obj.SetValue(VerticalScrollBarOnLeftSideProperty, value);
}
}
}

View File

@ -0,0 +1,100 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Classes
{
using API.Events;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Media;
public static class GridViewColumnWidthFromItemsBehavior
{
public static readonly DependencyProperty GridViewColumnWidthFromItemsProperty =
DependencyProperty.RegisterAttached("Enabled", typeof(bool), typeof(GridViewColumnWidthFromItemsBehavior), new UIPropertyMetadata(false, OnGridViewColumnWidthFromItemsPropertyChanged));
public static bool GetEnabled(DependencyObject obj)
{
return (bool)obj.GetValue(GridViewColumnWidthFromItemsProperty);
}
public static void SetEnabled(DependencyObject obj, bool value)
{
obj.SetValue(GridViewColumnWidthFromItemsProperty, value);
}
private static void OnGridViewColumnWidthFromItemsPropertyChanged(DependencyObject dpo, DependencyPropertyChangedEventArgs e)
{
var lv = dpo as ListView;
if (lv != null)
{
if ((bool)e.NewValue)
lv.Loaded += OnListViewLoaded;
else
lv.Loaded -= OnListViewLoaded;
}
}
private static void OnListViewLoaded(object sender, RoutedEventArgs routedEventArgs)
{
var lv = sender as ListView;
var nc = ((INotifyCollectionChanged)lv?.Items);
if (nc == null)
return;
nc.CollectionChanged += (o, args) =>
{
if (lv.ItemContainerGenerator.Status != GeneratorStatus.ContainersGenerated)
return;
// Obtain the largest item width..
var width = lv.Items.OfType<LogMessageEventArgs>()
.Select(msg => new FormattedText(msg.Message, CultureInfo.CurrentCulture, FlowDirection.LeftToRight, new Typeface("Tahoma"), 11, Brushes.Black))
.Select(txt => txt.Width)
.Concat(new double[] { 0.0f })
.Max();
// Resize the message column..
var gv = lv.View as GridView;
if (gv != null)
gv.Columns[1].Width = width + 24.0f;
// Scroll to the last item..
if (lv.Items.Count > 0)
lv.ScrollIntoView(lv.Items[lv.Items.Count - 1]);
else
{
if (gv != null)
gv.Columns[1].Width = 600;
}
};
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,58 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model
{
public enum ApplicationState
{
/// <summary>
/// Initializing
///
/// The state when the application is loading and/or updating.
/// </summary>
Initializing,
/// <summary>
/// Running
///
/// The state when the application is done initializing and is running.
/// </summary>
Running,
/// <summary>
/// Closing
///
/// The state when the launcher is closing.
/// </summary>
Closing,
/// <summary>
/// Closed
///
/// The state when the launcher is closed.
/// </summary>
Closed
}
}

View File

@ -0,0 +1,100 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model
{
using API;
using API.Model;
using API.Services;
using System;
using System.Linq;
using System.Windows;
using ViewModel;
[SteamlessApiVersion(1, 0)]
internal class AutomaticPlugin : SteamlessPlugin
{
/// <summary>
/// Gets the author of this plugin.
/// </summary>
public override string Author => "Steamless Development Team";
/// <summary>
/// Gets the name of this plugin.
/// </summary>
public override string Name => "Automatic";
/// <summary>
/// Gets the description of this plugin.
/// </summary>
public override string Description => "Automatically finds which plugin to use for the given file.";
/// <summary>
/// Gets the version of this plugin.
/// </summary>
public override Version Version => new Version(1, 0, 0, 0);
/// <summary>
/// Initialize function called when this plugin is first loaded.
/// </summary>
/// <param name="logService"></param>
/// <returns></returns>
public override bool Initialize(LoggingService logService)
{
return true;
}
/// <summary>
/// Processing function called when a file is being unpacked. Allows plugins to check the file
/// and see if it can handle the file for its intended purpose.
/// </summary>
/// <param name="file"></param>
/// <returns></returns>
public override bool CanProcessFile(string file)
{
return true;
}
/// <summary>
/// Processing function called to allow the plugin to process the file.
/// </summary>
/// <param name="file"></param>
/// <param name="options"></param>
/// <returns></returns>
public override bool ProcessFile(string file, SteamlessOptions options)
{
// Obtain the view model locator..
var vml = Application.Current.FindResource("ViewModelLocator") as ViewModelLocator;
// Obtain the plugins..
var plugins = vml?.MainWindow.Plugins;
if (plugins == null || plugins.Count == 0)
return false;
// Query the plugin list for a plugin to process the file..
return (from p in plugins where p != this where p.CanProcessFile(file) select p.ProcessFile(file, options)).FirstOrDefault();
}
}
}

View File

@ -0,0 +1,146 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model
{
using API;
using API.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using System.Windows;
using ViewModel;
public class DataService : IDataService
{
private static readonly Version SteamlessApiVersion = new Version(1, 0);
/// <summary>
/// Obtains the version of Steamless.
/// </summary>
/// <returns></returns>
public Task<Version> GetSteamlessVersion()
{
return Task.Run(() =>
{
try
{
return Assembly.GetExecutingAssembly().EntryPoint.DeclaringType?.Assembly.GetName().Version ?? new Version(0, 0, 0, 0);
}
catch
{
return new Version(0, 0, 0, 0);
}
});
}
/// <summary>
/// Obtains a list of available Steamless plugins.
/// </summary>
/// <returns></returns>
public Task<List<SteamlessPlugin>> GetSteamlessPlugins()
{
return Task.Run(() =>
{
try
{
// Obtain the view model locator..
var vml = Application.Current.FindResource("ViewModelLocator") as ViewModelLocator;
if (vml == null)
throw new Exception("Failed to obtain ViewModelLocator.");
// The list of valid plugins..
var plugins = new List<SteamlessPlugin>();
// 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)
{
Debug.WriteLine($"Failed to load plugin; could not find SteamlessPlugin base class. ({Path.GetFileName(dll)})");
continue;
}
// Locate the SteamlessApiVersion attribute on the base class..
var baseAttr = baseClass.GetCustomAttributes(typeof(SteamlessApiVersionAttribute), false);
if (baseAttr.Length == 0)
{
Debug.WriteLine($"Failed to load plugin; could not find SteamlessApiVersion attribute. ({Path.GetFileName(dll)})");
continue;
}
// Validate the interface version..
var apiVersion = (SteamlessApiVersionAttribute)baseAttr[0];
if (apiVersion.Version != SteamlessApiVersion)
{
Debug.WriteLine($"Failed to load plugin; invalid API version is being used. ({Path.GetFileName(dll)})");
continue;
}
// Create an instance of the plugin..
var plugin = (SteamlessPlugin)Activator.CreateInstance(baseClass);
if (!plugin.Initialize(vml.LoggingService))
{
Debug.WriteLine($"Failed to load plugin; plugin failed to initialize. ({Path.GetFileName(dll)})");
continue;
}
plugins.Add(plugin);
}
catch
{
Debug.WriteLine($"Failed to load DLL as a Steamless plugin: ({Path.GetFileName(dll)})");
}
}
// Order the plugins by their name..
return plugins.OrderBy(p => p.Name).ToList();
}
catch
{
return new List<SteamlessPlugin>();
}
});
}
}
}

View File

@ -0,0 +1,47 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model
{
using API.Model;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
public interface IDataService
{
/// <summary>
/// Obtains the version of Steamless.
/// </summary>
/// <returns></returns>
Task<Version> GetSteamlessVersion();
/// <summary>
/// Obtains a list of available Steamless plugins.
/// </summary>
/// <returns></returns>
Task<List<SteamlessPlugin>> GetSteamlessPlugins();
}
}

View File

@ -0,0 +1,87 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model.Tasks
{
using API.Model;
using System.Threading.Tasks;
public abstract class BaseTask : NotifiableModel
{
/// <summary>
/// Starts the task.
/// </summary>
public Task StartTask()
{
return Task.Run(async () =>
{
this.Completed = false;
await this.DoTask();
this.Completed = true;
});
}
/// <summary>
/// The tasks main function to execute when started.
/// </summary>
public abstract Task DoTask();
/// <summary>
/// Gets or sets the tasks completed state.
/// </summary>
public bool Completed
{
get { return this.Get<bool>("Completed"); }
set { this.Set("Completed", value); }
}
/// <summary>
/// Gets or sets the tasks progress.
/// </summary>
public double Progress
{
get { return this.Get<double>("Progress"); }
set { this.Set("Progress", value); }
}
/// <summary>
/// Gets or sets the tasks total progress.
/// </summary>
public double ProgressTotal
{
get { return this.Get<double>("ProgressTotal"); }
set { this.Set("ProgressTotal", value); }
}
/// <summary>
/// Gets or sets the text to display for this task.
/// </summary>
public string Text
{
get { return this.Get<string>("Text"); }
set { this.Set("Text", value); }
}
}
}

View File

@ -0,0 +1,84 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model.Tasks
{
using API.Events;
using API.Model;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using ViewModel;
public class LoadPluginsTask : BaseTask
{
/// <summary>
/// Default Constructor
/// </summary>
public LoadPluginsTask()
{
this.Text = "Loading plugins...";
}
/// <summary>
/// The tasks main function to execute when started.
/// </summary>
public override Task DoTask()
{
return Task.Run(async () =>
{
// Obtain the view model locator..
var vml = Application.Current.FindResource("ViewModelLocator") as ViewModelLocator;
if (vml == null)
return;
vml.MainWindow.SelectedPluginIndex = -1;
// Obtain the list of plugins..
var plugins = await vml.DataService.GetSteamlessPlugins();
// Sort the plugins..
var sorted = plugins.OrderBy(p => p.Name).ToList();
// Print out the loaded plugins..
sorted.ForEach(p =>
{
Application.Current.Dispatcher.Invoke(() =>
{
vml.LoggingService.OnAddLogMessage(this, new LogMessageEventArgs($"Loaded plugin: {p.Name} - by {p.Author} (v.{p.Version})", LogMessageType.Success));
});
});
// Add the automatic plugin at the start of the list..
sorted.Insert(0, new AutomaticPlugin());
// Set the plugins..
vml.MainWindow.Plugins = new ObservableCollection<SteamlessPlugin>(sorted);
vml.MainWindow.SelectedPluginIndex = 0;
});
}
}
}

View File

@ -0,0 +1,52 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model.Tasks
{
using System.Threading;
using System.Threading.Tasks;
public class StartSteamlessTask : BaseTask
{
/// <summary>
/// Default Constructor
/// </summary>
public StartSteamlessTask()
{
this.Text = "Starting Steamless...";
}
/// <summary>
/// The tasks main function to execute when started.
/// </summary>
public override Task DoTask()
{
return Task.Run(() =>
{
Thread.Sleep(1000);
});
}
}
}

View File

@ -0,0 +1,53 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.Model.Tasks
{
using System.Threading;
using System.Threading.Tasks;
public class StatusTask : BaseTask
{
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="msg"></param>
public StatusTask(string msg)
{
this.Text = msg;
}
/// <summary>
/// The tasks main function to execute when started.
/// </summary>
public override Task DoTask()
{
return Task.Run(() =>
{
Thread.Sleep(1000);
});
}
}
}

View File

@ -0,0 +1,41 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.
*/
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
[assembly: AssemblyTitle("Steamless")]
[assembly: AssemblyDescription("Removes the SteamStub DRM protection from Steam applications.")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCompany("atom0s")]
[assembly: AssemblyProduct("Steamless")]
[assembly: AssemblyCopyright("Copyright © atom0s 2015 - 2017")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)]
[assembly: AssemblyVersion("3.0.0.1")]
[assembly: AssemblyFileVersion("3.0.0.1")]

View File

@ -0,0 +1,71 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Steamless.Properties
{
/// <summary>
/// A strongly-typed resource class, for looking up localized strings, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilder
// class via a tool like ResGen or Visual Studio.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources
{
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources()
{
}
/// <summary>
/// Returns the cached ResourceManager instance used by this class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Resources.ResourceManager ResourceManager
{
get
{
if ((resourceMan == null))
{
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Steamless.Properties.Resources", typeof(Resources).Assembly);
resourceMan = temp;
}
return resourceMan;
}
}
/// <summary>
/// Overrides the current thread's CurrentUICulture property for all
/// resource lookups using this strongly typed resource class.
/// </summary>
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
internal static global::System.Globalization.CultureInfo Culture
{
get
{
return resourceCulture;
}
set
{
resourceCulture = value;
}
}
}
}

View File

@ -0,0 +1,117 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@ -0,0 +1,30 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.42000
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace Steamless.Properties
{
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
{
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
public static Settings Default
{
get
{
return defaultInstance;
}
}
}
}

View File

@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="uri:settings" CurrentProfile="(Default)">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

206
Steamless/Steamless.csproj Normal file
View File

@ -0,0 +1,206 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{10AC8FDE-09D9-47B4-AA89-BADC40EECAAB}</ProjectGuid>
<OutputType>WinExe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Steamless</RootNamespace>
<AssemblyName>Steamless</AssemblyName>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<WarningLevel>4</WarningLevel>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Debug\</OutputPath>
<DefineConstants>TRACE;DEBUG</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<PlatformTarget>x86</PlatformTarget>
<OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Assets\steam.ico</ApplicationIcon>
</PropertyGroup>
<ItemGroup>
<Reference Include="GalaSoft.MvvmLight">
<HintPath>Embedded\GalaSoft.MvvmLight.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="GalaSoft.MvvmLight.Extras">
<HintPath>Embedded\GalaSoft.MvvmLight.Extras.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Microsoft.Practices.ServiceLocation">
<HintPath>Embedded\Microsoft.Practices.ServiceLocation.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="Newtonsoft.Json">
<HintPath>Embedded\Newtonsoft.Json.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="PresentationFramework.Aero2" />
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>Embedded\System.Windows.Interactivity.dll</HintPath>
<Private>False</Private>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xaml">
<RequiredTargetFramework>4.0</RequiredTargetFramework>
</Reference>
<Reference Include="WindowsBase" />
<Reference Include="PresentationCore" />
<Reference Include="PresentationFramework" />
</ItemGroup>
<ItemGroup>
<ApplicationDefinition Include="App.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</ApplicationDefinition>
<Compile Include="App.xaml.cs">
<DependentUpon>App.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<Compile Include="Classes\ControlsHelper.cs" />
<Compile Include="Classes\GridViewColumnWidthFromItemsBehavior.cs" />
<Compile Include="Model\ApplicationState.cs" />
<Compile Include="Model\AutomaticPlugin.cs" />
<Compile Include="Model\DataService.cs" />
<Compile Include="Model\IDataService.cs" />
<Compile Include="Model\Tasks\BaseTask.cs" />
<Compile Include="Model\Tasks\LoadPluginsTask.cs" />
<Compile Include="Model\Tasks\StartSteamlessTask.cs" />
<Compile Include="Model\Tasks\StatusTask.cs" />
<Compile Include="ViewModel\MainWindowViewModel.cs" />
<Compile Include="ViewModel\ViewModelLocator.cs" />
<Compile Include="View\AboutView.xaml.cs">
<DependentUpon>AboutView.xaml</DependentUpon>
</Compile>
<Compile Include="View\MainView.xaml.cs">
<DependentUpon>MainView.xaml</DependentUpon>
</Compile>
<Compile Include="View\MainWindow.xaml.cs">
<DependentUpon>MainWindow.xaml</DependentUpon>
</Compile>
<Compile Include="View\SplashView.xaml.cs">
<DependentUpon>SplashView.xaml</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
<Compile Include="Properties\Resources.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="Properties\Settings.Designer.cs">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
<EmbeddedResource Include="Properties\Resources.resx">
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<Page Include="Assets\Controls.CheckBox.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Assets\Controls.Scrollbars.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
</None>
<AppDesigner Include="Properties\" />
</ItemGroup>
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Page Include="Assets\Animations.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Assets\Controls.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Assets\icons.xaml">
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
<Page Include="Assets\Theme.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="Assets\Window.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\AboutView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\MainView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\MainWindow.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
<Page Include="View\SplashView.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</Page>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Embedded\GalaSoft.MvvmLight.dll" />
<EmbeddedResource Include="Embedded\GalaSoft.MvvmLight.Extras.dll" />
<EmbeddedResource Include="Embedded\Microsoft.Practices.ServiceLocation.dll" />
<EmbeddedResource Include="Embedded\Newtonsoft.Json.dll" />
<EmbeddedResource Include="Embedded\System.Windows.Interactivity.dll" />
</ItemGroup>
<ItemGroup>
<Resource Include="Assets\steam.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Steamless.API\Steamless.API.csproj">
<Project>{56c95629-3b34-47fe-b988-04274409294f}</Project>
<Name>Steamless.API</Name>
<Private>False</Private>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Resource Include="Assets\steam.ico" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View File

@ -0,0 +1,316 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<UserControl x:Class="Steamless.View.AboutView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="450" SnapsToDevicePixels="True">
<!-- Main Content -->
<Grid x:Name="LayoutRoot" SnapsToDevicePixels="True">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,20,0,0" SnapsToDevicePixels="True">
<!-- Logo -->
<Image Source="../Assets/steam.png" Width="150" Margin="0" />
<Label Content="Steamless" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" Margin="0" Padding="0" SnapsToDevicePixels="True" RenderOptions.EdgeMode="Aliased">
<Label.Foreground>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1" RenderOptions.EdgeMode="Aliased">
<GradientStop Color="#E7E7E7" Offset="0" />
<GradientStop Color="#969696" Offset="1" />
</LinearGradientBrush>
</Label.Foreground>
<Label.Effect>
<DropShadowEffect Color="#80000000" BlurRadius="5" ShadowDepth="1" RenderOptions.EdgeMode="Aliased" />
</Label.Effect>
</Label>
<TextBlock Foreground="#F7F7F7" HorizontalAlignment="Center" VerticalAlignment="Center" Text="{Binding MainWindow.SteamlessVersion, Source={StaticResource ViewModelLocator}, FallbackValue='0.0.0.0', StringFormat={}Version: {0}}" />
<!-- Project Links -->
<Border Margin="0,15,0,0" Background="#80000000" BorderBrush="#40595959" BorderThickness="0,1,0,1" ClipToBounds="True">
<Border Background="Transparent" BorderBrush="#40595959" BorderThickness="0,10,0,0" Margin="0,-11,0,2">
<Border.Effect>
<DropShadowEffect ShadowDepth="0" BlurRadius="10"/>
</Border.Effect>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label Margin="2">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="#0F000000" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
<Hyperlink NavigateUri="http://atom0s.com/" Foreground="#c0dd65">Homepage
<Hyperlink.TextDecorations>
<TextDecorationCollection>
<TextDecoration PenThicknessUnit="FontRecommended" PenOffset="3">
<TextDecoration.Pen>
<Pen Thickness="2" Brush="#88A825">
<Pen.DashStyle>
<DashStyle Dashes="2"/>
</Pen.DashStyle>
</Pen>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Hyperlink.TextDecorations>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnOpenHyperlinkCommand, Source={StaticResource ViewModelLocator}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Hyperlink}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
<BulletDecorator HorizontalAlignment="Center" VerticalAlignment="Center">
<BulletDecorator.Bullet>
<Ellipse Height="3" Width="3" Fill="White" Margin="2" />
</BulletDecorator.Bullet>
</BulletDecorator>
<Label Margin="2">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="#0F000000" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
<Hyperlink NavigateUri="http://atom0s.com/forums/" Foreground="#c0dd65">Forums
<Hyperlink.TextDecorations>
<TextDecorationCollection>
<TextDecoration PenThicknessUnit="FontRecommended" PenOffset="3">
<TextDecoration.Pen>
<Pen Thickness="2" Brush="#88A825">
<Pen.DashStyle>
<DashStyle Dashes="2"/>
</Pen.DashStyle>
</Pen>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Hyperlink.TextDecorations>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnOpenHyperlinkCommand, Source={StaticResource ViewModelLocator}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Hyperlink}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
<BulletDecorator HorizontalAlignment="Center" VerticalAlignment="Center">
<BulletDecorator.Bullet>
<Ellipse Height="3" Width="3" Fill="White" Margin="2" />
</BulletDecorator.Bullet>
</BulletDecorator>
<Label Margin="2">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="#0F000000" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
<Hyperlink NavigateUri="http://git.atom0s.com/" Foreground="#c0dd65">Repositories
<Hyperlink.TextDecorations>
<TextDecorationCollection>
<TextDecoration PenThicknessUnit="FontRecommended" PenOffset="3">
<TextDecoration.Pen>
<Pen Thickness="2" Brush="#88A825">
<Pen.DashStyle>
<DashStyle Dashes="2"/>
</Pen.DashStyle>
</Pen>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Hyperlink.TextDecorations>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnOpenHyperlinkCommand, Source={StaticResource ViewModelLocator}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Hyperlink}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
</StackPanel>
</Border>
</Border>
<!-- Credits -->
<StackPanel HorizontalAlignment="Center" Margin="0,10,0,0">
<StackPanel.Resources>
<Style x:Key="StackPanelLabelCenter" TargetType="{x:Type Label}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
</Style>
<Style BasedOn="{StaticResource StackPanelLabelCenter}" TargetType="{x:Type Label}" />
</StackPanel.Resources>
<Label Content="Credits" FontSize="18" FontWeight="Bold" />
<Label Content="atom0s - Lead Developer" Foreground="#c0dd65" />
<Label Content="Cyanic (aka Golem_x86) - For his work / research." />
</StackPanel>
<StackPanel HorizontalAlignment="Center" Margin="0,0,0,0">
<StackPanel.Resources>
<Style x:Key="StackPanelLabelCenter" TargetType="{x:Type Label}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
</Style>
<Style BasedOn="{StaticResource StackPanelLabelCenter}" TargetType="{x:Type Label}" />
</StackPanel.Resources>
<Label Content="External Library Credits" FontSize="18" FontWeight="Bold" />
<Label Content="GalaSoft Mvvm Light Toolkit" />
<Label Content="Mahapps.Metro &amp; Newtonsoft Json" />
<Label Content="Oren Nachman, Massimo Savazzi, Templarian - XAML Icons" />
</StackPanel>
<!-- Donation Info -->
<Border BorderThickness="0,0,0,1.5" Margin="5" Padding="0">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,0" Opacity="0.7">
<GradientStop Offset="0.2" Color="#00000000"/>
<GradientStop Offset="0.5" Color="White"/>
<GradientStop Offset="0.8" Color="#00000000"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
<StackPanel HorizontalAlignment="Center" Margin="0,0,0,0">
<StackPanel.Resources>
<Style x:Key="StackPanelLabelCenter" TargetType="{x:Type Label}">
<Setter Property="HorizontalAlignment" Value="Center" />
<Setter Property="Padding" Value="1" />
</Style>
<Style BasedOn="{StaticResource StackPanelLabelCenter}" TargetType="{x:Type Label}" />
</StackPanel.Resources>
<Label Content="Donations" FontSize="18" FontWeight="Bold" Margin="0" Padding="0" />
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label ToolTip="Donate via Paypal" Width="24" Height="24" Background="#0689c1" Margin="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Label.OpacityMask>
<VisualBrush Visual="{StaticResource appbar_paypal}" />
</Label.OpacityMask>
<Hyperlink NavigateUri="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=7U7Q2GRT6KUJN" Foreground="Transparent" FontSize="24">&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnOpenHyperlinkCommand, Source={StaticResource ViewModelLocator}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Hyperlink}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
<Label ToolTip="Donate via Paypal" Margin="1" HorizontalAlignment="Center" VerticalAlignment="Center">
<Hyperlink NavigateUri="https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&amp;hosted_button_id=7U7Q2GRT6KUJN" Foreground="#c0dd65" >Donate via Paypal
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnOpenHyperlinkCommand, Source={StaticResource ViewModelLocator}}" CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Hyperlink}}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
</StackPanel>
<Border BorderThickness="0,0,0,1.5" Margin="5" Padding="0">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,0" Opacity="0.7">
<GradientStop Offset="0.2" Color="#00000000"/>
<GradientStop Offset="0.5" Color="White"/>
<GradientStop Offset="0.8" Color="#00000000"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
</StackPanel>
<!-- Important Information, Legal, Notes -->
<Border BorderBrush="#595959" BorderThickness="1" Margin="5">
<Border.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1" RenderOptions.EdgeMode="Aliased">
<GradientStop Color="#252525" Offset="0" RenderOptions.EdgeMode="Aliased" />
<GradientStop Color="#161616" Offset="0.9" RenderOptions.EdgeMode="Aliased" />
</LinearGradientBrush>
</Border.Background>
<ScrollViewer Margin="1" BorderBrush="Transparent" BorderThickness="1" Height="125">
<StackPanel>
<StackPanel.Resources>
<Style x:Key="LegalInformationLabelHeader" TargetType="{x:Type Label}">
<Setter Property="FontSize" Value="18" />
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="Foreground" Value="#FFA8A8A8" />
<Setter Property="HorizontalAlignment" Value="Left" />
</Style>
<Style x:Key="LegalInformationTextBlock" TargetType="{x:Type TextBlock}">
<Setter Property="Foreground" Value="#E0FFFFFF" />
<Setter Property="HorizontalAlignment" Value="Left" />
<Setter Property="Margin" Value="10,0,10,10" />
<Setter Property="TextWrapping" Value="Wrap" />
</Style>
<Style BasedOn="{StaticResource LegalInformationTextBlock}" TargetType="{x:Type TextBlock}" />
</StackPanel.Resources>
<!-- Donation Information -->
<Label Content="Donations" Style="{StaticResource LegalInformationLabelHeader}" />
<TextBlock>
Steamless is provided, as-is, for free. Donations are a way for users to show their support for my work.
I do not accept donations in the attempt to assist with pirating games or other illegal activities. Do
not contact me attempting to get help with pirating a game or any other illegal activity, I will not respond.
</TextBlock>
<!-- Legal Information -->
<Label Content="Legal Information / Warranty" Style="{StaticResource LegalInformationLabelHeader}" />
<TextBlock>
Steamless is released under the following license:
<LineBreak />
Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International
<LineBreak /><LineBreak />
Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material
as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether
express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness
for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of
errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this
disclaimer may not apply to You.
<LineBreak /><LineBreak />
Steamless is not intended for malicious use or for the use of obtaining or playing games illegally.
<LineBreak />
Steamless should only be used on games that you legally purchased and own.
<LineBreak /><LineBreak />
Steamless is not associated with Steam or any of its partners / affiliates.
<LineBreak /><LineBreak />
No code used within Steamless is taken from Valve or any of its partners / affiliates.
<LineBreak />
All work is original and done for educational purposes.
<LineBreak /><LineBreak />
Steam © 2003 - 2016 Valve Corporation. All rights reserved.
</TextBlock>
</StackPanel>
</ScrollViewer>
</Border>
<!-- Close Button -->
<Button Content="Close" Width="125" Margin="2,2,2,12" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding MainWindow.OnShowAboutViewCommand, Source={StaticResource ViewModelLocator}}" />
</StackPanel>
</Grid>
</UserControl>

View File

@ -0,0 +1,41 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.View
{
/// <summary>
/// Interaction logic for AboutView.xaml
/// </summary>
public partial class AboutView
{
/// <summary>
/// Default Constructor
/// </summary>
public AboutView()
{
this.InitializeComponent();
}
}
}

View File

@ -0,0 +1,141 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<UserControl x:Class="Steamless.View.MainView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:command="clr-namespace:GalaSoft.MvvmLight.Command;assembly=GalaSoft.MvvmLight.Extras"
mc:Ignorable="d" d:DesignHeight="600" d:DesignWidth="450">
<!-- Main Content -->
<Grid x:Name="LayoutRoot" Margin="0,30,0,0">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<!-- File Selection -->
<GroupBox Grid.Row="0" Margin="10" Header="1. Select File To Unpack">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBox Grid.Column="0" Margin="2" HorizontalContentAlignment="Left" VerticalContentAlignment="Center" IsEnabled="True" IsReadOnly="False" AllowDrop="True" Text="{Binding MainWindow.InputFilePath, Source={StaticResource ViewModelLocator}}">
<i:Interaction.Triggers>
<i:EventTrigger EventName="Drop">
<command:EventToCommand Command="{Binding OnDragDropCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewDragOver">
<command:EventToCommand Command="{Binding OnPreviewDragEnterCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="PreviewDragEnter">
<command:EventToCommand Command="{Binding OnPreviewDragEnterCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
</i:Interaction.Triggers>
</TextBox>
<Button Grid.Column="1" Content="..." Foreground="Black" Width="40" Margin="2" Command="{Binding MainWindow.OnBrowseForInputFileCommand, Source={StaticResource ViewModelLocator}}" />
</Grid>
</GroupBox>
<!-- Plugin Selection -->
<GroupBox Grid.Row="1" Margin="10,0,10,10" Header="2. Select Plugin">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ComboBox Grid.Row="0" SelectedIndex="{Binding MainWindow.SelectedPluginIndex, Source={StaticResource ViewModelLocator}}" ItemsSource="{Binding MainWindow.Plugins, Source={StaticResource ViewModelLocator}}" DisplayMemberPath="DisplayName" Margin="2" />
</Grid>
</GroupBox>
<!-- Unpacking Options -->
<GroupBox Grid.Row="2" Margin="10,0,10,10" Header="3. Unpacking Options" DataContext="{Binding MainWindow, Source={StaticResource ViewModelLocator}}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<CheckBox Grid.Row="0" Content="Verbose Output" Margin="2" ToolTip="If enabled, Steamless will allow logging of debug messages." IsChecked="{Binding Options.VerboseOutput}" />
<CheckBox Grid.Row="1" Content="Keep Bind Section" Margin="2" ToolTip="The bind section should be kept within the file after unpacking." IsChecked="{Binding Options.KeepBindSection}" />
<CheckBox Grid.Row="2" Content="Dump Payload To Disk" Margin="2" ToolTip="Dumps the payload to disk where the target file is located. (File will be saved with the .payload extension.)" IsChecked="{Binding Options.DumpPayloadToDisk}" />
<CheckBox Grid.Row="3" Content="Dump SteamDRMP.dll To Disk" Margin="2" ToolTip="Dumps the SteamDRMP.dll to disk where the target file is located." IsChecked="{Binding Options.DumpSteamDrmpToDisk}" />
</Grid>
</GroupBox>
<!-- Logging -->
<GroupBox Grid.Row="3" Margin="10,0,10,10">
<GroupBox.Header>
<Grid HorizontalAlignment="Stretch" Width="{Binding RelativeSource={RelativeSource AncestorType={x:Type GroupBox}}, Path=ActualWidth}">
<Label Content="Log" Foreground="#A8A8A8" FontWeight="Bold" Margin="0" Padding="0" />
<Label Foreground="#A8A8A8" FontWeight="Bold" Margin="0" Padding="0" HorizontalAlignment="Right">
<Label.Style>
<Style TargetType="Label">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand" />
<Setter Property="Background" Value="#0F000000" />
</Trigger>
</Style.Triggers>
</Style>
</Label.Style>
<Hyperlink NavigateUri="http://atom0s.com/" Foreground="#A8A8A8">Clear Log
<Hyperlink.TextDecorations>
<TextDecorationCollection>
<TextDecoration PenThicknessUnit="FontRecommended" PenOffset="1">
<TextDecoration.Pen>
<Pen Thickness="1" Brush="#A8A8A8">
<Pen.DashStyle>
<DashStyle Dashes="1"/>
</Pen.DashStyle>
</Pen>
</TextDecoration.Pen>
</TextDecoration>
</TextDecorationCollection>
</Hyperlink.TextDecorations>
<i:Interaction.Triggers>
<i:EventTrigger EventName="RequestNavigate">
<i:InvokeCommandAction Command="{Binding Path=MainWindow.OnClearLogCommand, Source={StaticResource ViewModelLocator}}" />
</i:EventTrigger>
</i:Interaction.Triggers>
</Hyperlink>
</Label>
</Grid>
</GroupBox.Header>
<ListView ItemsSource="{Binding MainWindow.Log, Source={StaticResource ViewModelLocator}}" Style="{DynamicResource LogListView}" />
</GroupBox>
<!-- Unpack Button -->
<Grid Grid.Row="4" Margin="8" Background="Transparent" SnapsToDevicePixels="True">
<Button Content="Unpack File" Width="125" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" Command="{Binding MainWindow.OnUnpackFileCommand, Source={StaticResource ViewModelLocator}}" />
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,41 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.View
{
/// <summary>
/// Interaction logic for MainView.xaml
/// </summary>
public partial class MainView
{
/// <summary>
/// Default Constructor
/// </summary>
public MainView()
{
this.InitializeComponent();
}
}
}

View File

@ -0,0 +1,189 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<Window x:Class="Steamless.View.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:view="clr-namespace:Steamless.View"
mc:Ignorable="d" FontFamily="Tahoma" Icon="../Assets/steam.ico"
Title="Steamless" MinHeight="600" MaxHeight="800" MinWidth="450" Width="450" SizeToContent="Height"
TextOptions.TextRenderingMode="ClearType" TextOptions.TextFormattingMode="Display" ResizeMode="CanResizeWithGrip"
AllowsTransparency="True" WindowStartupLocation="CenterScreen" WindowStyle="None">
<!-- Main Content -->
<Border x:Name="LayoutRoot" ClipToBounds="True" DataContext="{Binding MainWindow, Source={StaticResource ViewModelLocator}}" Margin="0"
BorderBrush="#193754" BorderThickness="1">
<Border.Background>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#1C1F2E" Offset="0"/>
<GradientStop Color="#262626" Offset="0.2"/>
</LinearGradientBrush>
</Border.Background>
<!-- Mouse Down Event Handler -->
<i:Interaction.Triggers>
<i:EventTrigger EventName="MouseLeftButtonDown">
<i:InvokeCommandAction Command="{Binding OnWindowMouseDownCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
<Grid x:Name="ViewGrid">
<!-- Splash View -->
<view:SplashView>
<view:SplashView.Style>
<Style TargetType="{x:Type FrameworkElement}">
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding State, Mode=OneWay}" Value="Initializing">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionShow}" />
</DataTrigger.EnterActions>
</DataTrigger>
<DataTrigger Binding="{Binding State, Mode=OneWay}" Value="Running">
<DataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionHide}" />
</DataTrigger.EnterActions>
<DataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionShow}" />
</DataTrigger.ExitActions>
</DataTrigger>
</Style.Triggers>
</Style>
</view:SplashView.Style>
</view:SplashView>
<!-- Main View -->
<view:MainView>
<view:MainView.Style>
<Style TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding State}" Value="Running" />
<Condition Binding="{Binding ShowAboutView}" Value="False" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionShow}" />
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionHide}" />
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</view:MainView.Style>
</view:MainView>
<!-- About View -->
<view:AboutView>
<view:AboutView.Style>
<Style TargetType="{x:Type FrameworkElement}">
<Setter Property="Visibility" Value="Collapsed" />
<Setter Property="RenderTransform">
<Setter.Value>
<TransformGroup>
<ScaleTransform />
<SkewTransform />
<RotateTransform />
<TranslateTransform />
</TransformGroup>
</Setter.Value>
</Setter>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding State}" Value="Running" />
<Condition Binding="{Binding ShowAboutView}" Value="True" />
</MultiDataTrigger.Conditions>
<MultiDataTrigger.EnterActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionShow}" />
</MultiDataTrigger.EnterActions>
<MultiDataTrigger.ExitActions>
<BeginStoryboard Storyboard="{StaticResource MetroSlideLeftTransitionHide}" />
</MultiDataTrigger.ExitActions>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</view:AboutView.Style>
</view:AboutView>
<!-- Window Navbar -->
<Grid VerticalAlignment="Top">
<Grid HorizontalAlignment="Left" VerticalAlignment="Top">
<Label Content="Steamless" Foreground="White" />
</Grid>
<Grid HorizontalAlignment="Right" VerticalAlignment="Top">
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Style="{StaticResource WindowNavigationButton}" Padding="0" Width="24" Height="24" ToolTip="About Steamless" Visibility="Visible" Command="{Binding OnShowAboutViewCommand}">
<Path Width="7" Stretch="Uniform" Data="F1 M 23.5,31C 24.8807,31 26,32.1193 26,33.5C 26,34.8807 24.8807,36 23.5,36C 22.1193,36 21,34.8807 21,33.5C 21,32.1193 22.1193,31 23.5,31 Z M 24,11C 28.4183,11 32,13.8579 32,18C 32,19.5 31,22 29,23C 27,24 26,25.3431 26,27L 26,29L 21,29L 21,27.5C 21,24.0992 24,22 25,21C 27,19 27,18.7226 27,18C 27,16.3432 25.6568,15 24,15C 22.3431,15 21,16.3432 21,18L 21,19.5L 16,19.5L 16,18.5C 16,14.3579 19.5817,11 24,11 Z" SnapsToDevicePixels="True" Fill="White" />
</Button>
<Border Grid.Column="1" BorderBrush="#8E8F96" BorderThickness="1,0,0,0" Margin="5,6,5,6" />
<Button Grid.Column="2" Style="{StaticResource WindowNavigationButton}" Padding="0" Width="24" Height="24" ToolTip="Minimize" Visibility="Visible" Command="{Binding OnWindowMinimizeCommand}">
<Path Data="F1 M0,0 L0,2 10,2 10,0z" SnapsToDevicePixels="True" Fill="White" />
</Button>
<Button Grid.Column="3" Padding="0" Width="24" Height="24" ToolTip="Close" Visibility="Visible" Command="{Binding OnWindowCloseCommand}">
<Button.Style>
<Style BasedOn="{StaticResource WindowNavigationButton}" TargetType="{x:Type Button}">
<Style.Triggers>
<DataTrigger Binding="{Binding State, Mode=OneWay}" Value="Launching">
<Setter Property="IsEnabled" Value="False" />
</DataTrigger>
</Style.Triggers>
</Style>
</Button.Style>
<Path Data="M0,0 L8,8 M8,0 L0,8" Stroke="White" StrokeThickness="2" SnapsToDevicePixels="True" Fill="White" />
</Button>
</Grid>
</Grid>
</Grid>
</Border>
</Window>

View File

@ -0,0 +1,41 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.View
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow
{
/// <summary>
/// Default Constructor
/// </summary>
public MainWindow()
{
this.InitializeComponent();
}
}
}

View File

@ -0,0 +1,151 @@
<!--
* Steamless - Copyright (c) 2015 - 2017 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.
-->
<UserControl x:Class="Steamless.View.SplashView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d"
d:DesignHeight="600" d:DesignWidth="350">
<!-- Main Content -->
<Grid x:Name="LayoutRoot">
<StackPanel VerticalAlignment="Top" HorizontalAlignment="Stretch" Margin="0,100,0,0">
<!-- Logo -->
<Image Source="../Assets/steam.png" Width="200" Margin="0" />
<Label Content="Steamless" FontSize="32" FontWeight="Bold" HorizontalAlignment="Center" Margin="0" Padding="0" SnapsToDevicePixels="True">
<Label.Foreground>
<LinearGradientBrush StartPoint=".5,0" EndPoint=".5,1">
<GradientStop Color="#E7E7E7" Offset="0" />
<GradientStop Color="#969696" Offset="1" />
</LinearGradientBrush>
</Label.Foreground>
<Label.Effect>
<DropShadowEffect Color="#80000000" BlurRadius="5" ShadowDepth="1" />
</Label.Effect>
</Label>
<Label Content="by atom0s" Foreground="White" HorizontalAlignment="Center" VerticalAlignment="Center" SnapsToDevicePixels="True">
<Label.Effect>
<DropShadowEffect Color="#80000000" BlurRadius="5" ShadowDepth="2" />
</Label.Effect>
</Label>
<!-- Separator -->
<Border BorderThickness="0,0,0,1.5" Margin="10">
<Border.BorderBrush>
<LinearGradientBrush StartPoint="0,1" EndPoint="1,0" Opacity="0.7">
<GradientStop Offset="0.2" Color="#00000000"/>
<GradientStop Offset="0.5" Color="White"/>
<GradientStop Offset="0.8" Color="#00000000"/>
</LinearGradientBrush>
</Border.BorderBrush>
</Border>
<!-- Task Information -->
<Grid DataContext="{Binding MainWindow.CurrentTask, Source={StaticResource ViewModelLocator}}" VerticalAlignment="Top" Margin="0,5,0,0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<ProgressBar Grid.ColumnSpan="2" Grid.Row="0" Foreground="#F7F7F7" Margin="5,0,5,0" Height="3" VerticalAlignment="Top" Value="{Binding Progress, Mode=OneWay}" Maximum="{Binding ProgressTotal, Mode=OneWay}">
<ProgressBar.Style>
<Style TargetType="{x:Type ProgressBar}" BasedOn="{StaticResource {x:Type ProgressBar}}">
<Setter Property="Visibility" Value="Visible" />
<Setter Property="IsIndeterminate" Value="False" />
<Style.Triggers>
<DataTrigger Binding="{Binding ProgressTotal, FallbackValue=0, Mode=OneWay}" Value="0">
<Setter Property="IsIndeterminate" Value="True" />
</DataTrigger>
<DataTrigger Binding="{Binding Mode=OneWay}" Value="{x:Null}">
<Setter Property="IsIndeterminate" Value="False" />
<Setter Property="Visibility" Value="Hidden" />
</DataTrigger>
</Style.Triggers>
</Style>
</ProgressBar.Style>
</ProgressBar>
<TextBlock Grid.Column="0" Grid.Row="1" Foreground="White" VerticalAlignment="Top" Margin="5,5,0,0">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text" Value="{Binding FileName, StringFormat='Downloading: {0}'}" />
<Setter Property="TextBlock.HorizontalAlignment" Value="Left" />
<Style.Triggers>
<DataTrigger Binding="{Binding ProgressTotal, FallbackValue=0, Mode=OneWay}" Value="0">
<Setter Property="TextBlock.Text" Value="{Binding Text}" />
<Setter Property="TextBlock.HorizontalAlignment" Value="Center" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,5,5,0">
<TextBlock.Style>
<Style>
<Setter Property="TextBlock.Text">
<Setter.Value>
<MultiBinding StringFormat="{}[{0:#,##0.0}KB / {1:#,##0.0}KB]">
<Binding Path="Progress" Mode="OneWay" />
<Binding Path="ProgressTotal" Mode="OneWay" />
</MultiBinding>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding ProgressTotal, FallbackValue=0, Mode=OneWay}" Value="0">
<Setter Property="TextBlock.Text" Value="" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
<TextBlock Grid.Column="0" Grid.ColumnSpan="2" Grid.Row="2" Foreground="Black" HorizontalAlignment="Center" VerticalAlignment="Center">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Text" Value="{Binding MainWindow.UpdateCount, Source={StaticResource ViewModelLocator}, FallbackValue=0, StringFormat={}{0:#} downloads remaining...}" />
<Style.Triggers>
<DataTrigger Binding="{Binding MainWindow.UpdateCount, Source={StaticResource ViewModelLocator}, FallbackValue=0}" Value="0">
<Setter Property="Text" Value="" />
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Grid>
</StackPanel>
<!-- Version Information -->
<Grid Background="#40000000" VerticalAlignment="Bottom">
<StackPanel HorizontalAlignment="Left" Margin="5">
<TextBlock Foreground="#F7F7F7" FontFamily="Courier New" FontWeight="Bold"
Text="{Binding MainWindow.SteamlessVersion, Source={StaticResource ViewModelLocator}, FallbackValue='0.0.0.0', StringFormat={}Steamless Version: {0}}"
/>
</StackPanel>
</Grid>
</Grid>
</UserControl>

View File

@ -0,0 +1,41 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.View
{
/// <summary>
/// Interaction logic for SplashView.xaml
/// </summary>
public partial class SplashView
{
/// <summary>
/// Default Constructor
/// </summary>
public SplashView()
{
this.InitializeComponent();
}
}
}

View File

@ -0,0 +1,492 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.ViewModel
{
using API.Events;
using API.Model;
using API.Services;
using GalaSoft.MvvmLight.Command;
using Microsoft.Win32;
using Model;
using Model.Tasks;
using System;
using System.Collections.Concurrent;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Documents;
using System.Windows.Input;
public class MainWindowViewModel : ViewModelBase
{
/// <summary>
/// Internal data service instance.
/// </summary>
private readonly IDataService m_DataService;
/// <summary>
/// Internal thread used to process tasks.
/// </summary>
private Thread m_TaskThread;
/// <summary>
/// Default Constructor
/// </summary>
/// <param name="dataService"></param>
/// <param name="logService"></param>
public MainWindowViewModel(IDataService dataService, LoggingService logService)
{
// Store the data service instance..
this.m_DataService = dataService;
// Initialize the model..
this.State = ApplicationState.Initializing;
this.Tasks = new ConcurrentBag<BaseTask>();
this.Options = new SteamlessOptions();
this.Log = new ObservableCollection<LogMessageEventArgs>();
this.ShowAboutView = false;
this.InputFilePath = string.Empty;
// Register command callbacks..
this.OnWindowCloseCommand = new RelayCommand(this.WindowClose);
this.OnWindowMinimizeCommand = new RelayCommand(WindowMinimize);
this.OnWindowMouseDownCommand = new RelayCommand<MouseButtonEventArgs>(WindowMouseDown);
this.OnShowAboutViewCommand = new RelayCommand(() => this.ShowAboutView = !this.ShowAboutView);
this.OnOpenHyperlinkCommand = new RelayCommand<object>(o =>
{
var link = o as Hyperlink;
if (link != null)
Process.Start(link.NavigateUri.AbsoluteUri);
});
this.OnDragDropCommand = new RelayCommand<DragEventArgs>(this.InputFileDragDrop);
this.OnPreviewDragEnterCommand = new RelayCommand<DragEventArgs>(this.InputFilePreviewDragEnter);
this.OnBrowseForInputFileCommand = new RelayCommand(this.BrowseForInputFile);
this.OnUnpackFileCommand = new RelayCommand(this.UnpackFile);
this.OnClearLogCommand = new RelayCommand(() => this.ClearLogMessages(this, EventArgs.Empty));
// Attach logging service events..
logService.AddLogMessage += this.AddLogMessage;
logService.ClearLogMessages += this.ClearLogMessages;
this.AddLogMessage(this, new LogMessageEventArgs("Steamless (c) 2015 - 2017 atom0s [atom0s@live.com]", LogMessageType.Debug));
this.AddLogMessage(this, new LogMessageEventArgs("Website: http://atom0s.com/", LogMessageType.Debug));
// Initialize this model..
this.Initialize();
}
/// <summary>
/// Internal async call to load the main view model.
/// </summary>
private async void Initialize()
{
// Obtain the Steamless version..
this.CurrentTask = new StatusTask("Initializing..");
this.SteamlessVersion = await this.m_DataService.GetSteamlessVersion();
// Load the Steamless plugins..
this.Tasks.Add(new LoadPluginsTask());
// Start the application..
this.Tasks.Add(new StartSteamlessTask());
// Start the tasks thread..
if (this.m_TaskThread != null)
return;
this.m_TaskThread = new Thread(this.ProcessTasksThread) { IsBackground = true };
this.m_TaskThread.Start();
}
/// <summary>
/// Sets the applications current status.
/// </summary>
/// <param name="state"></param>
/// <param name="msg"></param>
public void SetApplicationStatus(ApplicationState state, string msg)
{
this.State = state;
this.CurrentTask = new StatusTask(msg);
}
/// <summary>
/// Thread callback to process application tasks.
/// </summary>
private async void ProcessTasksThread()
{
while (Interlocked.CompareExchange(ref this.m_TaskThread, null, null) != null && this.State != ApplicationState.Closing)
{
// Obtain a task from the task list..
BaseTask task;
if (this.Tasks.TryTake(out task))
{
this.CurrentTask = task;
await this.CurrentTask.StartTask();
}
else
{
// No tasks left, set application to a running state..
if (this.State == ApplicationState.Initializing)
this.State = ApplicationState.Running;
}
Thread.Sleep(200);
}
}
/// <summary>
/// Adds a message to the message log.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void AddLogMessage(object sender, LogMessageEventArgs e)
{
// Do not log debug messages if verbose output is disabled..
if (!this.Options.VerboseOutput && e.MessageType == LogMessageType.Debug)
return;
// Check if we need to invoke from the dispatcher thread..
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(() => this.AddLogMessage(sender, e));
return;
}
// Prefix the parent to the message..
try
{
if (sender != null)
{
var baseName = sender.GetType().Assembly.GetName().Name;
e.Message = $"[{baseName}] {e.Message}";
}
else
e.Message = "[Unknown] " + e.Message;
}
catch
{
// Do nothing with this exception..
}
this.Log.Add(e);
}
/// <summary>
/// Clears the message log.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ClearLogMessages(object sender, EventArgs e)
{
// Check if we need to invoke from the dispatcher thread..
if (!Application.Current.Dispatcher.CheckAccess())
{
Application.Current.Dispatcher.Invoke(() => this.ClearLogMessages(sender, e));
return;
}
this.Log.Clear();
}
#region == Window Function Callbacks ==================================================================
/// <summary>
/// Command callback for when the window is being closed.
/// </summary>
private void WindowClose()
{
// Set the launcher state to closing..
this.State = ApplicationState.Closing;
// Shutdown the application..
Application.Current.Shutdown(0);
}
/// <summary>
/// Command callback for when the window is being minimized.
/// </summary>
private static void WindowMinimize()
{
// Minimize the window..
Application.Current.MainWindow.WindowState = WindowState.Minimized;
}
/// <summary>
/// Command callback for when the window is being clicked down. (To drag the window.)
/// </summary>
/// <param name="args"></param>
private static void WindowMouseDown(MouseButtonEventArgs args)
{
Application.Current.MainWindow.DragMove();
}
/// <summary>
/// Handles drag and drop events over the input file textbox.
/// </summary>
/// <param name="args"></param>
private void InputFileDragDrop(DragEventArgs args)
{
args.Handled = true;
// Check for files being dragged..
if (args.Data.GetDataPresent(DataFormats.FileDrop))
{
// Ensure only 1 file is being dropped..
var files = (string[])args.Data.GetData(DataFormats.FileDrop);
if (files != null && files.Length >= 1)
this.InputFilePath = files[0];
}
}
/// <summary>
/// Handles drag and drop events over the input file textbox.
/// </summary>
/// <param name="args"></param>
private void InputFilePreviewDragEnter(DragEventArgs args)
{
args.Handled = true;
// Check for files being dragged..
if (args.Data.GetDataPresent(DataFormats.FileDrop))
{
// Ensure only 1 file is being dropped..
var files = (string[])args.Data.GetData(DataFormats.FileDrop);
args.Effects = files != null && files.Length == 1 ? DragDropEffects.Move : DragDropEffects.None;
}
else
args.Effects = DragDropEffects.None;
}
/// <summary>
/// Browses for the input file to be unpacked.
/// </summary>
private void BrowseForInputFile()
{
// Display the find file dialog..
var ofd = new OpenFileDialog
{
CheckFileExists = true,
CheckPathExists = true,
DefaultExt = "*.exe",
Filter = "Executable Files (*.exe)|*.exe|All Files (*.*)|*.*",
FilterIndex = 0,
InitialDirectory = Path.GetFullPath(AppDomain.CurrentDomain.BaseDirectory),
Multiselect = false,
RestoreDirectory = true
};
// Update the input file path..
var showDialog = ofd.ShowDialog();
if (showDialog != null && (bool)showDialog)
this.InputFilePath = ofd.FileName;
}
/// <summary>
/// Unpacks the selected file using the selected plugin.
/// </summary>
private async void UnpackFile()
{
await Task.Run(() =>
{
// Validation checks..
if (this.SelectedPluginIndex == -1)
return;
if (this.SelectedPluginIndex > this.Plugins.Count)
return;
if (string.IsNullOrEmpty(this.InputFilePath))
return;
try
{
// Select the plugin..
var plugin = this.Plugins[this.SelectedPluginIndex];
if (plugin == null)
throw new Exception("Invalid plugin selected.");
// Allow the plugin to process the file..
if (plugin.CanProcessFile(this.InputFilePath))
this.AddLogMessage(this, !plugin.ProcessFile(this.InputFilePath, this.Options) ? new LogMessageEventArgs("Failed to unpack file.", LogMessageType.Error) : new LogMessageEventArgs("Successfully unpacked file!", LogMessageType.Success));
else
this.AddLogMessage(this, new LogMessageEventArgs("Failed to unpack file.", LogMessageType.Error));
}
catch (Exception ex)
{
this.AddLogMessage(this, new LogMessageEventArgs("Caught unhandled exception trying to unpack file.", LogMessageType.Error));
this.AddLogMessage(this, new LogMessageEventArgs("Exception:", LogMessageType.Error));
this.AddLogMessage(this, new LogMessageEventArgs(ex.Message, LogMessageType.Error));
}
});
}
#endregion
#region == Window Related Properties ==================================================================
/// <summary>
/// Gets or sets the window close command.
/// </summary>
public RelayCommand OnWindowCloseCommand { get; set; }
/// <summary>
/// Gets or sets the window minimize command.
/// </summary>
public RelayCommand OnWindowMinimizeCommand { get; set; }
/// <summary>
/// Gets or sets the window mouse down command.
/// </summary>
public RelayCommand<MouseButtonEventArgs> OnWindowMouseDownCommand { get; set; }
/// <summary>
/// Gets or sets the show about view command.
/// </summary>
public RelayCommand OnShowAboutViewCommand { get; set; }
/// <summary>
/// Gets or sets the open hyperlink command.
/// </summary>
public RelayCommand<object> OnOpenHyperlinkCommand { get; set; }
/// <summary>
/// Gets or sets the input file textbox drag drop command.
/// </summary>
public RelayCommand<DragEventArgs> OnDragDropCommand { get; set; }
/// <summary>
/// Gets or sets the input file textbox drag enter command.
/// </summary>
public RelayCommand<DragEventArgs> OnPreviewDragEnterCommand { get; set; }
/// <summary>
/// Gets or sets the input file browse command.
/// </summary>
public RelayCommand OnBrowseForInputFileCommand { get; set; }
/// <summary>
/// Gets or sets the unpack file command.
/// </summary>
public RelayCommand OnUnpackFileCommand { get; set; }
/// <summary>
/// Gets or sets the clear log command.
/// </summary>
public RelayCommand OnClearLogCommand { get; set; }
#endregion
#region == (All) ViewModel Related Properties =========================================================
/// <summary>
/// Gets or sets the applications current state.
/// </summary>
public ApplicationState State
{
get { return this.Get<ApplicationState>("State"); }
set { this.Set("State", value); }
}
/// <summary>
/// Gets or sets the Steamless version.
/// </summary>
public Version SteamlessVersion
{
get { return this.Get<Version>("SteamlessVersion"); }
set { this.Set("SteamlessVersion", value); }
}
/// <summary>
/// Gets or sets the current task.
/// </summary>
public BaseTask CurrentTask
{
get { return this.Get<BaseTask>("CurrentTask"); }
set { this.Set("CurrentTask", value); }
}
/// <summary>
/// Gets or sets the list of tasks.
/// </summary>
public ConcurrentBag<BaseTask> Tasks
{
get { return this.Get<ConcurrentBag<BaseTask>>("Tasks"); }
set { this.Set("Tasks", value); }
}
/// <summary>
/// Gets or sets if the about view should be seen.
/// </summary>
public bool ShowAboutView
{
get { return this.Get<bool>("ShowAboutView"); }
set { this.Set("ShowAboutView", value); }
}
#endregion
#region == (Main) ViewModel Related Properties ========================================================
/// <summary>
/// Gets or sets the list of plugins.
/// </summary>
public ObservableCollection<SteamlessPlugin> Plugins
{
get { return this.Get<ObservableCollection<SteamlessPlugin>>("Plugins"); }
set { this.Set("Plugins", value); }
}
/// <summary>
/// Gets or sets the selected plugin index.
/// </summary>
public int SelectedPluginIndex
{
get { return this.Get<int>("SelectedPluginIndex"); }
set { this.Set("SelectedPluginIndex", value); }
}
/// <summary>
/// Gets or sets the input file path.
/// </summary>
public string InputFilePath
{
get { return this.Get<string>("InputFilePath"); }
set { this.Set("InputFilePath", value); }
}
/// <summary>
/// Gets or sets the Steamless options.
/// </summary>
public SteamlessOptions Options
{
get { return this.Get<SteamlessOptions>("Options"); }
set { this.Set("Options", value); }
}
/// <summary>
/// Gets or sets the message log.
/// </summary>
public ObservableCollection<LogMessageEventArgs> Log
{
get { return this.Get<ObservableCollection<LogMessageEventArgs>>("Log"); }
set { this.Set("Log", value); }
}
#endregion
}
}

View File

@ -0,0 +1,64 @@
/**
* Steamless - Copyright (c) 2015 - 2017 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.ViewModel
{
using API.Services;
using GalaSoft.MvvmLight.Ioc;
using Microsoft.Practices.ServiceLocation;
using Model;
public class ViewModelLocator
{
/// <summary>
/// Default Constructor
/// </summary>
static ViewModelLocator()
{
// Setup the locator provider..
ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);
// Register our types..
SimpleIoc.Default.Register<IDataService, DataService>();
SimpleIoc.Default.Register<LoggingService>();
SimpleIoc.Default.Register<MainWindowViewModel>();
}
/// <summary>
/// Gets the main window view model.
/// </summary>
public MainWindowViewModel MainWindow => ServiceLocator.Current.GetInstance<MainWindowViewModel>();
/// <summary>
/// Gets the main data service.
/// </summary>
public IDataService DataService => ServiceLocator.Current.GetInstance<IDataService>();
/// <summary>
/// Gets the logging service.
/// </summary>
public LoggingService LoggingService => ServiceLocator.Current.GetInstance<LoggingService>();
}
}