├── .gitattributes ├── .gitignore ├── App.config ├── App.xaml ├── App.xaml.cs ├── Core ├── Account.cs ├── AccountUtils.cs ├── AltActionType.cs ├── FriendsLoginStatus.cs ├── IniFile.cs ├── InterceptKeys.cs ├── LoginWindowState.cs ├── SAMSettings.cs ├── SortType.cs ├── SteamExitCode.cs ├── StringCipher.cs ├── UpdateHelper.cs ├── UpdateResponse.cs ├── UserSettings.cs ├── VirtualInputMethod.cs └── WindowUtils.cs ├── FodyWeavers.xml ├── FodyWeavers.xsd ├── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings ├── README.md ├── Resources ├── SteamIDs_Engine.dll ├── add.png ├── add_active.png ├── error.png ├── localconfig.vdf └── steam.ico ├── SAM.csproj ├── SAM.sln ├── Views ├── AccountInfoDialog.xaml ├── AccountInfoDialog.xaml.cs ├── AccountsWindow.xaml ├── AccountsWindow.xaml.cs ├── ExposedInfoWindow.xaml ├── ExposedInfoWindow.xaml.cs ├── ImportDelimitedWindow.xaml ├── ImportDelimitedWindow.xaml.cs ├── PasswordWindow.xaml ├── PasswordWindow.xaml.cs ├── SetTimeoutWindow.xaml ├── SetTimeoutWindow.xaml.cs ├── SettingsWindow.xaml └── SettingsWindow.xaml.cs ├── Wpf └── PriorityMultiValueConverter.cs ├── app.manifest └── latest.txt /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | *.key 10 | 11 | # User-specific files (MonoDevelop/Xamarin Studio) 12 | *.userprefs 13 | 14 | # Build results 15 | [Dd]ebug/ 16 | [Dd]ebugPublic/ 17 | [Rr]elease/ 18 | [Rr]eleases/ 19 | x64/ 20 | x86/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | [Ll]og/ 25 | 26 | # Visual Studio 2015 cache/options directory 27 | .vs/ 28 | # Uncomment if you have tasks that create the project's static files in wwwroot 29 | #wwwroot/ 30 | 31 | # MSTest test Results 32 | [Tt]est[Rr]esult*/ 33 | [Bb]uild[Ll]og.* 34 | 35 | # NUNIT 36 | *.VisualState.xml 37 | TestResult.xml 38 | 39 | # Build Results of an ATL Project 40 | [Dd]ebugPS/ 41 | [Rr]eleasePS/ 42 | dlldata.c 43 | 44 | # DNX 45 | project.lock.json 46 | artifacts/ 47 | 48 | *_i.c 49 | *_p.c 50 | *_i.h 51 | *.ilk 52 | *.meta 53 | *.obj 54 | *.pch 55 | *.pdb 56 | *.pgc 57 | *.pgd 58 | *.rsp 59 | *.sbr 60 | *.tlb 61 | *.tli 62 | *.tlh 63 | *.tmp 64 | *.tmp_proj 65 | *.log 66 | *.vspscc 67 | *.vssscc 68 | .builds 69 | *.pidb 70 | *.svclog 71 | *.scc 72 | 73 | # Chutzpah Test files 74 | _Chutzpah* 75 | 76 | # Visual C++ cache files 77 | ipch/ 78 | *.aps 79 | *.ncb 80 | *.opendb 81 | *.opensdf 82 | *.sdf 83 | *.cachefile 84 | *.VC.db 85 | *.VC.VC.opendb 86 | 87 | # Visual Studio profiler 88 | *.psess 89 | *.vsp 90 | *.vspx 91 | *.sap 92 | 93 | # TFS 2012 Local Workspace 94 | $tf/ 95 | 96 | # Guidance Automation Toolkit 97 | *.gpState 98 | 99 | # ReSharper is a .NET coding add-in 100 | _ReSharper*/ 101 | *.[Rr]e[Ss]harper 102 | *.DotSettings.user 103 | 104 | # JustCode is a .NET coding add-in 105 | .JustCode 106 | 107 | # TeamCity is a build add-in 108 | _TeamCity* 109 | 110 | # DotCover is a Code Coverage Tool 111 | *.dotCover 112 | 113 | # NCrunch 114 | _NCrunch_* 115 | .*crunch*.local.xml 116 | nCrunchTemp_* 117 | 118 | # MightyMoose 119 | *.mm.* 120 | AutoTest.Net/ 121 | 122 | # Web workbench (sass) 123 | .sass-cache/ 124 | 125 | # Installshield output folder 126 | [Ee]xpress/ 127 | 128 | # DocProject is a documentation generator add-in 129 | DocProject/buildhelp/ 130 | DocProject/Help/*.HxT 131 | DocProject/Help/*.HxC 132 | DocProject/Help/*.hhc 133 | DocProject/Help/*.hhk 134 | DocProject/Help/*.hhp 135 | DocProject/Help/Html2 136 | DocProject/Help/html 137 | 138 | # Click-Once directory 139 | publish/ 140 | 141 | # Publish Web Output 142 | *.[Pp]ublish.xml 143 | *.azurePubxml 144 | # TODO: Comment the next line if you want to checkin your web deploy settings 145 | # but database connection strings (with potential passwords) will be unencrypted 146 | *.pubxml 147 | *.publishproj 148 | 149 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 150 | # checkin your Azure Web App publish settings, but sensitive information contained 151 | # in these scripts will be unencrypted 152 | PublishScripts/ 153 | 154 | # NuGet Packages 155 | *.nupkg 156 | # The packages folder can be ignored because of Package Restore 157 | **/packages/* 158 | # except build/, which is used as an MSBuild target. 159 | !**/packages/build/ 160 | # Uncomment if necessary however generally it will be regenerated when needed 161 | #!**/packages/repositories.config 162 | # NuGet v3's project.json files produces more ignoreable files 163 | *.nuget.props 164 | *.nuget.targets 165 | 166 | # Microsoft Azure Build Output 167 | csx/ 168 | *.build.csdef 169 | 170 | # Microsoft Azure Emulator 171 | ecf/ 172 | rcf/ 173 | 174 | # Windows Store app package directories and files 175 | AppPackages/ 176 | BundleArtifacts/ 177 | Package.StoreAssociation.xml 178 | _pkginfo.txt 179 | 180 | # Visual Studio cache files 181 | # files ending in .cache can be ignored 182 | *.[Cc]ache 183 | # but keep track of directories ending in .cache 184 | !*.[Cc]ache/ 185 | 186 | # Others 187 | ClientBin/ 188 | ~$* 189 | *~ 190 | *.dbmdl 191 | *.dbproj.schemaview 192 | *.pfx 193 | *.publishsettings 194 | node_modules/ 195 | orleans.codegen.cs 196 | 197 | # Since there are multiple workflows, uncomment next line to ignore bower_components 198 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 199 | #bower_components/ 200 | 201 | # RIA/Silverlight projects 202 | Generated_Code/ 203 | 204 | # Backup & report files from converting an old project file 205 | # to a newer Visual Studio version. Backup files are not needed, 206 | # because we have git ;-) 207 | _UpgradeReport_Files/ 208 | Backup*/ 209 | UpgradeLog*.XML 210 | UpgradeLog*.htm 211 | 212 | # SQL Server files 213 | *.mdf 214 | *.ldf 215 | 216 | # Business Intelligence projects 217 | *.rdl.data 218 | *.bim.layout 219 | *.bim_*.settings 220 | 221 | # Microsoft Fakes 222 | FakesAssemblies/ 223 | 224 | # GhostDoc plugin setting file 225 | *.GhostDoc.xml 226 | 227 | # Node.js Tools for Visual Studio 228 | .ntvs_analysis.dat 229 | 230 | # Visual Studio 6 build log 231 | *.plg 232 | 233 | # Visual Studio 6 workspace options file 234 | *.opt 235 | 236 | # Visual Studio LightSwitch build output 237 | **/*.HTMLClient/GeneratedArtifacts 238 | **/*.DesktopClient/GeneratedArtifacts 239 | **/*.DesktopClient/ModelManifest.xml 240 | **/*.Server/GeneratedArtifacts 241 | **/*.Server/ModelManifest.xml 242 | _Pvt_Extensions 243 | 244 | # Paket dependency manager 245 | .paket/paket.exe 246 | paket-files/ 247 | 248 | # FAKE - F# Make 249 | .fake/ 250 | 251 | # JetBrains Rider 252 | .idea/ 253 | *.sln.iml 254 | 255 | # ========================= 256 | # Operating System Files 257 | # ========================= 258 | 259 | # OSX 260 | # ========================= 261 | 262 | .DS_Store 263 | .AppleDouble 264 | .LSOverride 265 | 266 | # Thumbnails 267 | ._* 268 | 269 | # Files that might appear in the root of a volume 270 | .DocumentRevisions-V100 271 | .fseventsd 272 | .Spotlight-V100 273 | .TemporaryItems 274 | .Trashes 275 | .VolumeIcon.icns 276 | 277 | # Directories potentially created on remote AFP share 278 | .AppleDB 279 | .AppleDesktop 280 | Network Trash Folder 281 | Temporary Items 282 | .apdisk 283 | 284 | # Windows 285 | # ========================= 286 | 287 | # Windows image file caches 288 | Thumbs.db 289 | ehthumbs.db 290 | 291 | # Folder config file 292 | Desktop.ini 293 | 294 | # Recycle Bin used on file shares 295 | $RECYCLE.BIN/ 296 | 297 | # Windows Installer files 298 | *.cab 299 | *.msi 300 | *.msm 301 | *.msp 302 | 303 | # Windows shortcuts 304 | *.lnk 305 | Deploy Instructions.txt 306 | -------------------------------------------------------------------------------- /App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /App.xaml: -------------------------------------------------------------------------------- 1 |  7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 12 15 | 7,1,8,2 16 | WhiteSmoke 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | 3 | namespace SAM 4 | { 5 | /// 6 | /// Interaktionslogik für "App.xaml" 7 | /// 8 | public partial class App : Application 9 | { 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Core/Account.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Runtime.CompilerServices; 4 | 5 | namespace SAM.Core 6 | { 7 | public class Account : INotifyPropertyChanged 8 | { 9 | public string Name { get; set; } 10 | 11 | public string Alias { get; set; } 12 | 13 | public string Password { get; set; } 14 | 15 | public string SharedSecret { get; set; } 16 | 17 | public string ProfUrl { get; set; } 18 | 19 | public string AviUrl { get; set; } 20 | 21 | public string SteamId { get; set; } 22 | 23 | public string Parameters { get; set; } 24 | 25 | public bool HasParameters { get { return Parameters != null && Parameters.Length > 0 && Parameters.Split(' ').Length > 0; } } 26 | 27 | public DateTime? Timeout { get; set; } 28 | 29 | private string timeoutTimeLeft; 30 | 31 | public string TimeoutTimeLeft { get { return timeoutTimeLeft; } set { timeoutTimeLeft = value; OnPropertyChanged(); } } 32 | 33 | public string Description { get; set; } 34 | 35 | public FriendsLoginStatus FriendsLoginStatus { get; set; } 36 | 37 | public bool CommunityBanned { get; set; } 38 | 39 | public bool VACBanned { get; set; } 40 | 41 | public int NumberOfVACBans { get; set; } 42 | 43 | public int DaysSinceLastBan { get; set; } 44 | 45 | public int NumberOfGameBans { get; set; } 46 | 47 | public string EconomyBan { get; set; } 48 | 49 | public event PropertyChangedEventHandler PropertyChanged; 50 | 51 | protected void OnPropertyChanged([CallerMemberName] string propertyName = "") 52 | { 53 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Core/AccountUtils.cs: -------------------------------------------------------------------------------- 1 | using Gameloop.Vdf.Linq; 2 | using Gameloop.Vdf; 3 | using HtmlAgilityPack; 4 | using Microsoft.Win32; 5 | using Newtonsoft.Json.Linq; 6 | using SAM.Views; 7 | using System; 8 | using System.Collections.Generic; 9 | using System.IO; 10 | using System.Linq; 11 | using System.Net; 12 | using System.Text; 13 | using System.Threading.Tasks; 14 | using System.Windows; 15 | using System.Xml.Serialization; 16 | using SteamIDs_Engine; 17 | using Newtonsoft.Json; 18 | using SAM.Properties; 19 | 20 | namespace SAM.Core 21 | { 22 | class AccountUtils 23 | { 24 | private const int API_KEY_LENGTH = 32; 25 | 26 | public static void Serialize(List accounts) 27 | { 28 | var serializer = new XmlSerializer(accounts.GetType()); 29 | var sw = new StreamWriter("info.dat"); 30 | serializer.Serialize(sw, accounts); 31 | sw.Close(); 32 | } 33 | 34 | public static void PasswordSerialize(List accounts, string password) 35 | { 36 | var serializer = new XmlSerializer(accounts.GetType()); 37 | MemoryStream memStream = new MemoryStream(); 38 | serializer.Serialize(memStream, accounts); 39 | 40 | string serializedAccounts = Encoding.UTF8.GetString(memStream.ToArray()); 41 | string encryptedAccounts = StringCipher.Encrypt(serializedAccounts, password); 42 | 43 | File.WriteAllText("info.dat", encryptedAccounts); 44 | 45 | memStream.Close(); 46 | } 47 | 48 | public static List Deserialize(string file) 49 | { 50 | List accounts = new List(); 51 | 52 | try 53 | { 54 | var stream = new StreamReader(file); 55 | var serializer = new XmlSerializer(typeof(List)); 56 | accounts = (List)serializer.Deserialize(stream); 57 | stream.Close(); 58 | } 59 | catch (Exception e) 60 | { 61 | MessageBox.Show(e.Message); 62 | } 63 | 64 | return accounts; 65 | } 66 | 67 | public static List PasswordDeserialize(string file, string password) 68 | { 69 | List accounts = new List(); 70 | 71 | try 72 | { 73 | string contents = File.ReadAllText(file); 74 | contents = StringCipher.Decrypt(contents, password); 75 | 76 | var stream = new MemoryStream(Encoding.UTF8.GetBytes(contents)); 77 | var serializer = new XmlSerializer(typeof(List)); 78 | accounts = (List)serializer.Deserialize(stream); 79 | stream.Close(); 80 | } 81 | catch (Exception e) 82 | { 83 | MessageBox.Show(e.Message); 84 | } 85 | 86 | return accounts; 87 | } 88 | 89 | public static void ImportAccountsFromList(List accounts) 90 | { 91 | try 92 | { 93 | AccountsWindow.accounts = AccountsWindow.accounts.Concat(accounts).ToList(); 94 | Serialize(AccountsWindow.accounts); 95 | MessageBox.Show("Account(s) imported!"); 96 | } 97 | catch (Exception m) 98 | { 99 | MessageBox.Show(m.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 100 | } 101 | } 102 | 103 | public static void ImportAccountFile() 104 | { 105 | OpenFileDialog dialog = new OpenFileDialog 106 | { 107 | DefaultExt = ".dat", 108 | Filter = "SAM DAT Files (*.dat)|*.dat" 109 | }; 110 | 111 | bool? result = dialog.ShowDialog(); 112 | 113 | if (result == true) 114 | { 115 | try 116 | { 117 | var tempAccounts = Deserialize(dialog.FileName); 118 | AccountsWindow.accounts = AccountsWindow.accounts.Concat(tempAccounts).ToList(); 119 | Serialize(AccountsWindow.accounts); 120 | MessageBox.Show("Accounts imported!"); 121 | } 122 | catch (Exception e) 123 | { 124 | MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 125 | } 126 | } 127 | } 128 | 129 | public static void ExportAccountFile() 130 | { 131 | var dialog = new System.Windows.Forms.FolderBrowserDialog(); 132 | var result = dialog.ShowDialog(); 133 | 134 | if (result == System.Windows.Forms.DialogResult.OK) 135 | { 136 | try 137 | { 138 | File.Copy("info.dat", dialog.SelectedPath + "\\info.dat"); 139 | MessageBox.Show("File exported to:\n" + dialog.SelectedPath); 140 | } 141 | catch (Exception e) 142 | { 143 | MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 144 | } 145 | } 146 | } 147 | 148 | public static void ExportSelectedAccounts(List accounts) 149 | { 150 | var dialog = new System.Windows.Forms.FolderBrowserDialog(); 151 | var result = dialog.ShowDialog(); 152 | 153 | if (result == System.Windows.Forms.DialogResult.OK) 154 | { 155 | try 156 | { 157 | var serializer = new XmlSerializer(accounts.GetType()); 158 | var sw = new StreamWriter(dialog.SelectedPath + "\\info.dat"); 159 | serializer.Serialize(sw, accounts); 160 | sw.Close(); 161 | 162 | MessageBox.Show("File exported to:\n" + dialog.SelectedPath); 163 | } 164 | catch (Exception e) 165 | { 166 | MessageBox.Show(e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 167 | } 168 | } 169 | } 170 | 171 | public static string GetSteamPathFromRegistry() 172 | { 173 | string registryValue = string.Empty; 174 | RegistryKey localKey; 175 | 176 | if (Environment.Is64BitOperatingSystem) 177 | { 178 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); 179 | } 180 | else 181 | { 182 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); 183 | } 184 | 185 | try 186 | { 187 | localKey = localKey.OpenSubKey(@"Software\\Valve\\Steam"); 188 | registryValue = localKey.GetValue("SteamPath").ToString() + "/"; 189 | } 190 | catch (Exception e) 191 | { 192 | Console.WriteLine(e.Message); 193 | } 194 | 195 | return registryValue; 196 | } 197 | 198 | public static void ClearAutoLoginUserKeyValues() 199 | { 200 | RegistryKey localKey; 201 | 202 | if (Environment.Is64BitOperatingSystem) 203 | { 204 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); 205 | } 206 | else 207 | { 208 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); 209 | } 210 | 211 | try 212 | { 213 | localKey = localKey.OpenSubKey(@"Software\\Valve\\Steam", true); 214 | localKey.SetValue("AutoLoginUser", "", RegistryValueKind.String); 215 | localKey.SetValue("RememberPassword", 0, RegistryValueKind.DWord); 216 | localKey.Close(); 217 | } 218 | catch (Exception e) 219 | { 220 | Console.WriteLine(e.Message); 221 | } 222 | } 223 | 224 | public static void SetRememeberPasswordKeyValue(int value, Account account) 225 | { 226 | RegistryKey localKey; 227 | 228 | if (Environment.Is64BitOperatingSystem) 229 | { 230 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64); 231 | } 232 | else 233 | { 234 | localKey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32); 235 | } 236 | 237 | try 238 | { 239 | localKey = localKey.OpenSubKey(@"Software\\Valve\\Steam", true); 240 | localKey.SetValue("RememberPassword", value, RegistryValueKind.DWord); 241 | localKey.SetValue("AutoLoginUser", value == 1 ? account.Name.ToLower() : "", RegistryValueKind.String); 242 | localKey.Close(); 243 | 244 | string steamPath = new IniFile(SAMSettings.FILE_NAME).Read(SAMSettings.STEAM_PATH, SAMSettings.SECTION_STEAM); 245 | string loginusersPath = steamPath + "config/loginusers.vdf"; 246 | 247 | dynamic loginusers = VdfConvert.Deserialize(File.ReadAllText(loginusersPath)); 248 | 249 | dynamic usersObject = loginusers.Value; 250 | dynamic userObject = usersObject[account.SteamId]; 251 | 252 | userObject.RememberPassword = value; 253 | userObject.AllowAutoLogin = value; 254 | //userObject.MostRecent = value; 255 | 256 | usersObject[account.SteamId] = userObject; 257 | loginusers.Value = usersObject; 258 | 259 | string serialized = VdfConvert.Serialize(loginusers); 260 | 261 | File.WriteAllText(loginusersPath, serialized); 262 | } 263 | catch (Exception e) 264 | { 265 | MessageBox.Show("Error setting Remember Password values\n\n" + e.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error); 266 | Console.WriteLine(e.StackTrace); 267 | } 268 | } 269 | 270 | public static string CheckSteamPath() 271 | { 272 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 273 | 274 | string steamPath = settingsFile.Read(SAMSettings.STEAM_PATH, SAMSettings.SECTION_STEAM); 275 | 276 | int tryCount = 0; 277 | 278 | // If Steam's filepath was not specified in settings or is invalid, attempt to find it and save it. 279 | while (steamPath == null || !File.Exists(steamPath + "\\steam.exe")) 280 | { 281 | // Check registry keys first. 282 | string regPath = GetSteamPathFromRegistry(); 283 | string localPath = System.Windows.Forms.Application.StartupPath; 284 | 285 | if (Directory.Exists(regPath)) 286 | { 287 | steamPath = regPath; 288 | } 289 | 290 | // Check if SAM is isntalled in Steam directory. 291 | // Useful for users in portable mode. 292 | else if (File.Exists(localPath + "\\steam.exe")) 293 | { 294 | steamPath = localPath; 295 | } 296 | 297 | // Prompt user for manual selection. 298 | else 299 | { 300 | if (tryCount == 0) 301 | { 302 | MessageBox.Show("Could not find Steam path automatically.\n\nPlease select Steam manually."); 303 | } 304 | 305 | // Create OpenFileDialog 306 | OpenFileDialog dlg = new OpenFileDialog 307 | { 308 | DefaultExt = ".exe", 309 | Filter = "Steam (*.exe)|*.exe" 310 | }; 311 | 312 | // Display OpenFileDialog by calling ShowDialog method 313 | bool? result = dlg.ShowDialog(); 314 | 315 | // Get the selected file path 316 | if (result == true) 317 | { 318 | steamPath = Path.GetDirectoryName(dlg.FileName) + "\\"; 319 | } 320 | } 321 | 322 | if (steamPath == null || steamPath == string.Empty) 323 | { 324 | MessageBoxResult messageBoxResult = MessageBox.Show("Steam path required!\n\nTry again?", "Confirm", MessageBoxButton.YesNo); 325 | 326 | if (messageBoxResult.Equals(MessageBoxResult.No)) 327 | { 328 | Environment.Exit(0); 329 | } 330 | } 331 | 332 | tryCount++; 333 | } 334 | 335 | // Save path to settings file. 336 | settingsFile.Write(SAMSettings.STEAM_PATH, steamPath, SAMSettings.SECTION_STEAM); 337 | 338 | return steamPath; 339 | } 340 | 341 | public static List GetSteamIdsFromConfig(List accounts) 342 | { 343 | List steamIds = new List(); 344 | 345 | foreach (Account account in accounts) 346 | { 347 | string steamId = GetSteamIdFromConfig(account.Name); 348 | 349 | if (steamId != null && steamId.Length > 0) 350 | { 351 | account.SteamId = steamId; 352 | steamIds.Add(steamId); 353 | } 354 | } 355 | 356 | return steamIds; 357 | } 358 | 359 | public static async Task GetSteamIdFromProfileUrl(string url) 360 | { 361 | dynamic steamId = null; 362 | 363 | if (ApiKeyExists() == true) 364 | { 365 | // Get SteamId for either getUserSummaries (if in ID64 format) or vanity URL if (/id/) format. 366 | 367 | if (url.Contains("/id/")) 368 | { 369 | // Vanity URL API call. 370 | 371 | url = url.TrimEnd('/'); 372 | url = url.Split('/').Last(); 373 | 374 | if (url.Length > 0) 375 | { 376 | dynamic userJson = await GetSteamIdFromVanityUrl(url); 377 | 378 | if (userJson != null) 379 | { 380 | try 381 | { 382 | steamId = userJson.response.steamid; 383 | } 384 | catch 385 | { 386 | 387 | } 388 | } 389 | } 390 | } 391 | else if (url.Contains("/profiles/")) 392 | { 393 | // Standard user summaries API call. 394 | 395 | dynamic userJson = await GetUserInfoFromWebApiBySteamId(url); 396 | 397 | if (userJson != null) 398 | { 399 | try 400 | { 401 | steamId = userJson.response.players[0].steamid; 402 | } 403 | catch 404 | { 405 | 406 | } 407 | } 408 | } 409 | } 410 | 411 | return steamId; 412 | } 413 | 414 | public static string GetSteamId3FromSteamId64(string steamId) 415 | { 416 | try 417 | { 418 | string value = SteamIDConvert.Steam64ToSteam32(long.Parse(steamId)); 419 | string[] parts = value.Split(':'); 420 | return parts[2]; 421 | } 422 | catch(Exception e) 423 | { 424 | MessageBox.Show(e.Message); 425 | } 426 | 427 | return null; 428 | } 429 | 430 | public static string GetSteamIdFromConfig(string userName) 431 | { 432 | try 433 | { 434 | string steamPath = new IniFile(SAMSettings.FILE_NAME).Read(SAMSettings.STEAM_PATH, SAMSettings.SECTION_STEAM); 435 | string content = File.ReadAllText(steamPath + "config\\loginusers.vdf"); 436 | dynamic config = VdfConvert.Deserialize(content); 437 | 438 | dynamic users = config.Value; 439 | 440 | foreach (dynamic user in users) 441 | { 442 | string steamId = Convert.ToString(user.Key); 443 | string accountName = Convert.ToString(user.Value.AccountName); 444 | 445 | if (accountName.Equals(userName.ToLower())) 446 | { 447 | return steamId; 448 | } 449 | } 450 | } 451 | catch (Exception e) 452 | { 453 | Console.WriteLine(e.Message); 454 | } 455 | 456 | return null; 457 | } 458 | 459 | public static async Task GetUserInfoFromConfigAndWebApi(string userName) 460 | { 461 | dynamic userJson = null; 462 | string steamId = GetSteamIdFromConfig(userName); 463 | 464 | if (steamId != null) 465 | { 466 | userJson = await GetUserInfoFromWebApiBySteamId(steamId); 467 | } 468 | 469 | return userJson; 470 | } 471 | 472 | public static async Task GetUserInfoFromWebApiBySteamId(string steamId) 473 | { 474 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 475 | string apiKey = settingsFile.Read(SAMSettings.STEAM_API_KEY, SAMSettings.SECTION_STEAM); 476 | 477 | dynamic userJson = null; 478 | 479 | if (ApiKeyExists() == true) 480 | { 481 | try 482 | { 483 | Uri userUri = new Uri("https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" + apiKey + "&steamids=" + steamId); 484 | 485 | using (WebClient client = new WebClient()) 486 | { 487 | string userJsonString = await client.DownloadStringTaskAsync(userUri); 488 | userJson = JValue.Parse(userJsonString); 489 | } 490 | } 491 | catch (Exception e) 492 | { 493 | Console.WriteLine(e.Message); 494 | } 495 | } 496 | 497 | return userJson; 498 | } 499 | 500 | public static async Task> GetUserInfosFromWepApi(List steamIds) 501 | { 502 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 503 | string apiKey = settingsFile.Read(SAMSettings.STEAM_API_KEY, SAMSettings.SECTION_STEAM); 504 | 505 | List userInfos = new List(); 506 | 507 | if (ApiKeyExists() == true) 508 | { 509 | while (steamIds.Count > 0) 510 | { 511 | IEnumerable currentChunk; 512 | 513 | // Api can only process 100 accounts at a time. 514 | if (steamIds.Count > 100) 515 | { 516 | currentChunk = steamIds.Take(100); 517 | steamIds = steamIds.Skip(100).ToList(); 518 | } 519 | else 520 | { 521 | currentChunk = new List(steamIds); 522 | steamIds.Clear(); 523 | } 524 | 525 | string currentIds = String.Join(",", currentChunk); 526 | 527 | try 528 | { 529 | Uri userUri = new Uri("https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=" + apiKey + "&steamids=" + currentIds); 530 | 531 | using (WebClient client = new WebClient()) 532 | { 533 | string userJsonString = await client.DownloadStringTaskAsync(userUri); 534 | dynamic userInfoJson = JValue.Parse(userJsonString); 535 | userInfos.Add(userInfoJson); 536 | } 537 | } 538 | catch (Exception e) 539 | { 540 | Console.WriteLine(e.Message); 541 | } 542 | } 543 | } 544 | 545 | return userInfos; 546 | } 547 | 548 | public static async Task GetSteamIdFromVanityUrl(string vanity) 549 | { 550 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 551 | string apiKey = settingsFile.Read(SAMSettings.STEAM_API_KEY, SAMSettings.SECTION_STEAM); 552 | 553 | dynamic userInfoJson = null; 554 | 555 | if (apiKey != null && apiKey.Length == 32) 556 | { 557 | try 558 | { 559 | Uri userUri = new Uri("https://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/?key=" + apiKey + "&vanityurl=" + vanity); 560 | 561 | using (WebClient client = new WebClient()) 562 | { 563 | string userJsonString = await client.DownloadStringTaskAsync(userUri); 564 | userInfoJson = JValue.Parse(userJsonString); 565 | } 566 | } 567 | catch (Exception e) 568 | { 569 | Console.WriteLine(e.Message); 570 | } 571 | } 572 | 573 | return userInfoJson; 574 | } 575 | 576 | public static async Task GetPlayerBansFromWebApi(string steamId) 577 | { 578 | List userBansJson = await GetPlayerBansFromWebApi(new List() { steamId }); 579 | 580 | if (userBansJson.Count > 0) 581 | { 582 | return userBansJson[0].players[0]; 583 | } 584 | else 585 | { 586 | return null; 587 | } 588 | } 589 | 590 | public static async Task> GetPlayerBansFromWebApi(List steamIds) 591 | { 592 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 593 | string apiKey = settingsFile.Read(SAMSettings.STEAM_API_KEY, SAMSettings.SECTION_STEAM); 594 | 595 | List userBans = new List(); 596 | 597 | if (ApiKeyExists() == true) 598 | { 599 | while (steamIds.Count > 0) 600 | { 601 | IEnumerable currentChunk; 602 | 603 | // Api can only process 100 accounts at a time. 604 | if (steamIds.Count > 100) 605 | { 606 | currentChunk = steamIds.Take(100); 607 | steamIds = steamIds.Skip(100).ToList(); 608 | } 609 | else 610 | { 611 | currentChunk = new List(steamIds); 612 | steamIds.Clear(); 613 | } 614 | 615 | string currentIds = String.Join(",", currentChunk); 616 | 617 | try 618 | { 619 | Uri userUri = new Uri("https://api.steampowered.com/ISteamUser/GetPlayerBans/v1/?key=" + apiKey + "&steamids=" + currentIds); 620 | 621 | using (WebClient client = new WebClient()) 622 | { 623 | string userJsonString = await client.DownloadStringTaskAsync(userUri); 624 | dynamic userInfoJson = JValue.Parse(userJsonString); 625 | userBans.Add(userInfoJson); 626 | } 627 | } 628 | catch (Exception e) 629 | { 630 | Console.WriteLine(e.Message); 631 | } 632 | } 633 | } 634 | 635 | return userBans; 636 | } 637 | 638 | public static async Task HtmlAviScrapeAsync(string profUrl) 639 | { 640 | // If user entered profile url, get avatar jpg url 641 | if (profUrl != null && profUrl.Length > 2) 642 | { 643 | // Verify url starts with valid prefix for HtmlWeb 644 | if (!profUrl.StartsWith("https://") && !profUrl.StartsWith("http://")) 645 | { 646 | profUrl = "https://" + profUrl; 647 | } 648 | 649 | try 650 | { 651 | HtmlWeb htmlWeb = new HtmlWeb(); 652 | HtmlDocument document = await htmlWeb.LoadFromWebAsync(profUrl); 653 | IEnumerable enumerable = document.DocumentNode.Descendants().Where(n => n.HasClass("playerAvatarAutoSizeInner")); 654 | HtmlNode htmlNode = enumerable.First().SelectSingleNode("img"); 655 | string url = htmlNode.GetAttributeValue("src", null); 656 | return url; 657 | } 658 | catch (Exception e) 659 | { 660 | Console.WriteLine(e.Message); 661 | } 662 | } 663 | 664 | return ""; 665 | } 666 | 667 | public static void SetFriendsOnlineMode(FriendsLoginStatus loginMode, string steamId, string steamPath) 668 | { 669 | if (loginMode == FriendsLoginStatus.Unchanged || steamId == null || steamId.Length == 0) 670 | { 671 | Console.WriteLine("Login mode is unchanged or steamId is invalid!"); 672 | return; 673 | } 674 | 675 | string steamId3 = GetSteamId3FromSteamId64(steamId); 676 | string localPrefsKey = "FriendStoreLocalPrefs_" + steamId3; 677 | 678 | string fileName = "localconfig.vdf"; 679 | string configPath = steamPath + "userdata/" + steamId3 + "/config/"; 680 | string configFile = configPath + fileName; 681 | 682 | if (!Directory.Exists(configPath)) 683 | { 684 | Directory.CreateDirectory(configPath); 685 | } 686 | 687 | dynamic localconfig; 688 | 689 | if (!File.Exists(configFile)) 690 | { 691 | localconfig = VdfConvert.Deserialize(Resources.localconfig); 692 | } 693 | else 694 | { 695 | long fileSize = new FileInfo(configFile).Length; 696 | int maxTokenSize = (int)fileSize / 7; 697 | 698 | VdfSerializerSettings vdfSerializerSettings = new VdfSerializerSettings(); 699 | vdfSerializerSettings.MaximumTokenSize = maxTokenSize; 700 | vdfSerializerSettings.UsesEscapeSequences = true; 701 | localconfig = VdfConvert.Deserialize(File.ReadAllText(configFile),vdfSerializerSettings); 702 | } 703 | 704 | dynamic configStore = localconfig.Value; 705 | 706 | string loginValue = "1"; 707 | if (loginMode == FriendsLoginStatus.Offline) 708 | { 709 | loginValue = "0"; 710 | } 711 | else 712 | { 713 | dynamic webStorageObject = configStore["WebStorage"]; 714 | dynamic friendStorePrefs = webStorageObject[localPrefsKey]; 715 | 716 | if (friendStorePrefs == null) 717 | { 718 | friendStorePrefs = new VValue("{\"ePersonaState\":1,\"strNonFriendsAllowedToMsg\":\"\"}"); 719 | } 720 | 721 | dynamic prefsJson = JsonConvert.DeserializeObject(friendStorePrefs.Value); 722 | 723 | prefsJson.ePersonaState.Value = (int)loginMode; 724 | 725 | friendStorePrefs.Value = JsonConvert.SerializeObject(prefsJson); 726 | webStorageObject[localPrefsKey] = friendStorePrefs; 727 | configStore["WebStorage"] = webStorageObject; 728 | } 729 | 730 | dynamic friendsObject = configStore["friends"]; 731 | friendsObject.SignIntoFriends = loginValue; 732 | configStore["friends"] = friendsObject; 733 | 734 | localconfig.Value = configStore; 735 | 736 | string serialized = VdfConvert.Serialize(localconfig); 737 | 738 | File.WriteAllText(configFile, serialized); 739 | } 740 | 741 | public static string FormatTimespanString(TimeSpan time) 742 | { 743 | int years = time.Days / 365; 744 | int days = time.Days; 745 | 746 | if (years > 0) 747 | { 748 | days = (time.Days / (years * 365)); 749 | } 750 | 751 | return years.ToString("D2") + ":" + days.ToString("D2") + ":" + time.ToString(@"hh\:mm\:ss"); 752 | } 753 | 754 | public static bool AccountHasActiveTimeout(Account account) 755 | { 756 | if (account.Timeout == null || account.Timeout == new DateTime() || account.Timeout.Value.CompareTo(DateTime.Now) <= 0) 757 | { 758 | account.Timeout = null; 759 | account.TimeoutTimeLeft = null; 760 | return false; 761 | } 762 | 763 | return true; 764 | } 765 | 766 | public static bool ApiKeyExists() 767 | { 768 | var settingsFile = new IniFile(SAMSettings.FILE_NAME); 769 | string apiKey = settingsFile.Read(SAMSettings.STEAM_API_KEY, SAMSettings.SECTION_STEAM); 770 | return apiKey != null && apiKey.Length == API_KEY_LENGTH; 771 | } 772 | 773 | public static bool ShouldAutoReload(DateTime? lastReload, int interval) 774 | { 775 | if (lastReload.HasValue == false) 776 | { 777 | return true; 778 | } 779 | 780 | DateTime offset = lastReload.Value.AddMinutes(interval); 781 | 782 | if (offset.CompareTo(DateTime.Now) <= 0) 783 | { 784 | return true; 785 | } 786 | 787 | return false; 788 | } 789 | } 790 | } 791 | -------------------------------------------------------------------------------- /Core/AltActionType.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | class AltActionType 4 | { 5 | public const string DELETING = "Deleting"; 6 | public const string EXPORTING = "Exporting"; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Core/FriendsLoginStatus.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | public enum FriendsLoginStatus 4 | { 5 | Unchanged, 6 | Online = 1, 7 | Away = 3, 8 | Invisible = 7, 9 | Offline 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Core/IniFile.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Reflection; 3 | using System.Runtime.InteropServices; 4 | using System.Text; 5 | 6 | namespace SAM.Core 7 | { 8 | class IniFile // revision 10 9 | { 10 | string Path; 11 | string EXE = Assembly.GetExecutingAssembly().GetName().Name; 12 | 13 | [DllImport("kernel32")] 14 | static extern long WritePrivateProfileString(string Section, string Key, string Value, string FilePath); 15 | 16 | [DllImport("kernel32")] 17 | static extern int GetPrivateProfileString(string Section, string Key, string Default, StringBuilder RetVal, int Size, string FilePath); 18 | 19 | public IniFile(string IniPath = null) 20 | { 21 | Path = new FileInfo(IniPath ?? EXE + ".ini").FullName.ToString(); 22 | } 23 | 24 | public string Read(string Key, string Section = null) 25 | { 26 | var RetVal = new StringBuilder(255); 27 | GetPrivateProfileString(Section ?? EXE, Key, "", RetVal, 255, Path); 28 | return RetVal.ToString(); 29 | } 30 | 31 | public void Write(string Key, string Value, string Section = null) 32 | { 33 | WritePrivateProfileString(Section ?? EXE, Key, Value, Path); 34 | } 35 | 36 | public void DeleteKey(string Key, string Section = null) 37 | { 38 | Write(Key, null, Section ?? EXE); 39 | } 40 | 41 | public void DeleteSection(string Section = null) 42 | { 43 | Write(null, null, Section ?? EXE); 44 | } 45 | 46 | public bool KeyExists(string Key, string Section = null) 47 | { 48 | return Read(Key, Section).Length > 0; 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /Core/InterceptKeys.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.Runtime.InteropServices; 4 | using System.Windows.Forms; 5 | 6 | namespace SAM.Core 7 | { 8 | class InterceptKeys 9 | { 10 | private const int WH_KEYBOARD_LL = 13; 11 | private const int WM_KEYDOWN = 0x0100; 12 | private const int WM_ALTDOWN = 0x0104; 13 | private static LowLevelKeyboardProc _proc = HookCallback; 14 | private static IntPtr _hookID = IntPtr.Zero; 15 | 16 | public static void Start() 17 | { 18 | _hookID = SetHook(_proc); 19 | } 20 | 21 | public static void Stop() 22 | { 23 | UnhookWindowsHookEx(_hookID); 24 | } 25 | 26 | public static event KeyEventHandler OnKeyDown; 27 | 28 | private static IntPtr SetHook(LowLevelKeyboardProc proc) 29 | { 30 | using (Process curProcess = Process.GetCurrentProcess()) 31 | using (ProcessModule curModule = curProcess.MainModule) 32 | { 33 | return SetWindowsHookEx(WH_KEYBOARD_LL, proc, 34 | GetModuleHandle(curModule.ModuleName), 0); 35 | } 36 | } 37 | 38 | private delegate IntPtr LowLevelKeyboardProc( 39 | int nCode, IntPtr wParam, IntPtr lParam); 40 | 41 | private static IntPtr HookCallback( 42 | int nCode, IntPtr wParam, IntPtr lParam) 43 | { 44 | if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_ALTDOWN)) 45 | { 46 | var vkCode = (Keys)Marshal.ReadInt32(lParam); 47 | OnKeyDown(null, new KeyEventArgs(vkCode)); 48 | } 49 | return CallNextHookEx(_hookID, nCode, wParam, lParam); 50 | } 51 | 52 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 53 | private static extern IntPtr SetWindowsHookEx(int idHook, 54 | LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); 55 | 56 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 57 | [return: MarshalAs(UnmanagedType.Bool)] 58 | private static extern bool UnhookWindowsHookEx(IntPtr hhk); 59 | 60 | [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] 61 | private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, 62 | IntPtr wParam, IntPtr lParam); 63 | 64 | [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] 65 | private static extern IntPtr GetModuleHandle(string lpModuleName); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Core/LoginWindowState.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | enum LoginWindowState 4 | { 5 | None, 6 | Invalid, 7 | Error, 8 | Selection, 9 | Login, 10 | Code, 11 | Loading, 12 | Success 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Core/SAMSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Reflection; 4 | 5 | namespace SAM.Core 6 | { 7 | class SAMSettings 8 | { 9 | public const string FILE_NAME = "SAMSettings.ini"; 10 | 11 | public const string SECTION_SYSTEM = "System"; 12 | public const string SECTION_GENERAL = "Settings"; 13 | public const string SECTION_AUTOLOG = "AutoLog"; 14 | public const string SECTION_CUSTOMIZE = "Customize"; 15 | public const string SECTION_STEAM = "Steam"; 16 | public const string SECTION_PARAMETERS = "Parameters"; 17 | public const string SECTION_LOCATION = "Location"; 18 | public const string SECTION_COLUMNS = "Columns"; 19 | 20 | public IniFile File = new IniFile(FILE_NAME); 21 | public UserSettings User = new UserSettings(); 22 | public readonly UserSettings Default = new UserSettings(); 23 | public List globalParameters; 24 | 25 | public const string VERSION = "Version"; 26 | 27 | public const string CLEAR_USER_DATA = "ClearUserData"; 28 | public const string HIDE_ADD_BUTTON = "HideAddButton"; 29 | public const string PASSWORD_PROTECT = "PasswordProtect"; 30 | public const string MINIMIZE_TO_TRAY = "MinimizeToTray"; 31 | public const string REMEMBER_PASSWORD = "RememberPassword"; 32 | public const string START_MINIMIZED = "StartMinimized"; 33 | public const string START_WITH_WINDOWS = "StartWithWindows"; 34 | public const string ACCOUNTS_PER_ROW = "AccountsPerRow"; 35 | public const string SLEEP_TIME = "SleepTime"; 36 | public const string CHECK_FOR_UPDATES = "CheckForUpdates"; 37 | public const string CLOSE_ON_LOGIN = "CloseOnLogin"; 38 | public const string LIST_VIEW = "ListView"; 39 | public const string SANDBOX_MODE = "SandboxMode"; 40 | public const string HEADERLESS_WINDOW = "HeaderlessWindow"; 41 | public const string TRANSPARENT_WINDOW = "TransparentWindow"; 42 | 43 | public const string LOGIN_RECENT_ACCOUNT = "LoginRecentAccount"; 44 | public const string RECENT_ACCOUNT_INDEX = "RecentAccountIndex"; 45 | public const string LOGIN_SELECTED_ACCOUNT = "LoginSelectedAccount"; 46 | public const string SELECTED_ACCOUNT_INDEX = "SelectedAccountIndex"; 47 | public const string INPUT_METHOD = "InputMethod"; 48 | public const string HANDLE_IME = "HandleIME"; 49 | public const string IME_2FA_ONLY = "IME_2FA_ONLY"; 50 | 51 | public const string THEME = "Theme"; 52 | public const string ACCENT = "Accent"; 53 | public const string BUTTON_SIZE = "ButtonSize"; 54 | public const string BUTTON_COLOR = "ButtonColor"; 55 | public const string BUTTON_FONT_SIZE = "ButtonFontSize"; 56 | public const string BUTTON_FONT_COLOR = "ButtonFontColor"; 57 | public const string BUTTON_BANNER_COLOR = "ButtonBannerColor"; 58 | public const string BUTTON_BANNER_FONT_SIZE = "ButtonBannerFontSize"; 59 | public const string BUTTON_BANNER_FONT_COLOR = "ButtonBannerFontColor"; 60 | public const string HIDE_BAN_ICONS = "HideBanIcons"; 61 | 62 | public const string STEAM_PATH = "Path"; 63 | public const string STEAM_API_KEY = "ApiKey"; 64 | public const string AUTO_RELOAD_ENABLED = "AutoReloadEnabled"; 65 | public const string AUTO_RELOAD_INTERVAL = "AutoReloadInterval"; 66 | public const string LAST_AUTO_RELOAD = "LastAutoReload"; 67 | 68 | public const string CAFE_APP_LAUNCH_PARAMETER = "cafeapplaunch"; 69 | public const string CLEAR_BETA_PARAMETER = "clearbeta"; 70 | public const string CONSOLE_PARAMETER = "console"; 71 | public const string DEVELOPER_PARAMETER = "dev"; 72 | public const string FORCE_SERVICE_PARAMETER = "forceservice"; 73 | public const string LOGIN_PARAMETER = "login"; 74 | public const string NO_CACHE_PARAMETER = "nocache"; 75 | public const string NO_VERIFY_FILES_PARAMETER = "noverifyfiles"; 76 | public const string SILENT_PARAMETER = "silent"; 77 | public const string SINGLE_CORE_PARAMETER = "single_core"; 78 | public const string TCP_PARAMETER = "tcp"; 79 | public const string TEN_FOOT_PARAMETER = "tenfoot"; 80 | public const string CUSTOM_PARAMETERS = "customParameters"; 81 | public const string CUSTOM_PARAMETERS_VALUE = "customParametersValue"; 82 | 83 | public const string WINDOW_TOP = "WindowTop"; 84 | public const string WINDOW_LEFT = "WindowLeft"; 85 | public const string LIST_VIEW_HEIGHT = "ListViewHeight"; 86 | public const string LIST_VIEW_WIDTH = "ListViewWidth"; 87 | 88 | public const string LIGHT_THEME = "Light"; 89 | public const string DARK_THEME = "Dark"; 90 | 91 | public const string NAME_COLUMN_INDEX = "NameColumnIndex"; 92 | public const string DESCRIPTION_COLUMN_INDEX = "DescriptionColumnIndex"; 93 | public const string TIMEOUT_COLUMN_INDEX = "TimeoutColumnIndex"; 94 | public const string VAC_BANS_COLUMN_INDEX = "VacBansColumnIndex"; 95 | public const string GAME_BANS_COLUMN_INDEX = "GameBansColumnIndex"; 96 | public const string ECO_BAN_COLUMN_INDEX = "EcoBanColumnIndex"; 97 | public const string LAST_BAN_COLUMN_INDEX = "LastBanColumnIndex"; 98 | 99 | public Dictionary KeyValuePairs = new Dictionary() 100 | { 101 | { VERSION, SECTION_SYSTEM }, 102 | 103 | { CLEAR_USER_DATA, SECTION_GENERAL }, 104 | { HIDE_ADD_BUTTON, SECTION_GENERAL }, 105 | { PASSWORD_PROTECT, SECTION_GENERAL }, 106 | { MINIMIZE_TO_TRAY, SECTION_GENERAL }, 107 | { REMEMBER_PASSWORD, SECTION_GENERAL }, 108 | { START_MINIMIZED, SECTION_GENERAL }, 109 | { START_WITH_WINDOWS, SECTION_GENERAL }, 110 | { ACCOUNTS_PER_ROW, SECTION_GENERAL }, 111 | { SLEEP_TIME, SECTION_GENERAL }, 112 | { CHECK_FOR_UPDATES, SECTION_GENERAL }, 113 | { CLOSE_ON_LOGIN, SECTION_GENERAL }, 114 | { LIST_VIEW, SECTION_GENERAL }, 115 | { SANDBOX_MODE, SECTION_GENERAL }, 116 | { HEADERLESS_WINDOW, SECTION_GENERAL}, 117 | { TRANSPARENT_WINDOW, SECTION_GENERAL }, 118 | 119 | { LOGIN_RECENT_ACCOUNT, SECTION_AUTOLOG }, 120 | { RECENT_ACCOUNT_INDEX, SECTION_AUTOLOG }, 121 | { LOGIN_SELECTED_ACCOUNT, SECTION_AUTOLOG }, 122 | { SELECTED_ACCOUNT_INDEX, SECTION_AUTOLOG }, 123 | { INPUT_METHOD, SECTION_AUTOLOG }, 124 | { HANDLE_IME, SECTION_AUTOLOG }, 125 | { IME_2FA_ONLY, SECTION_AUTOLOG }, 126 | 127 | { THEME, SECTION_CUSTOMIZE }, 128 | { ACCENT, SECTION_CUSTOMIZE }, 129 | { BUTTON_SIZE, SECTION_CUSTOMIZE }, 130 | { BUTTON_COLOR, SECTION_CUSTOMIZE }, 131 | { BUTTON_FONT_SIZE, SECTION_CUSTOMIZE }, 132 | { BUTTON_FONT_COLOR, SECTION_CUSTOMIZE }, 133 | { BUTTON_BANNER_COLOR, SECTION_CUSTOMIZE }, 134 | { BUTTON_BANNER_FONT_SIZE, SECTION_CUSTOMIZE }, 135 | { BUTTON_BANNER_FONT_COLOR, SECTION_CUSTOMIZE }, 136 | { HIDE_BAN_ICONS, SECTION_CUSTOMIZE }, 137 | 138 | { STEAM_PATH, SECTION_STEAM }, 139 | { STEAM_API_KEY, SECTION_STEAM }, 140 | { AUTO_RELOAD_ENABLED, SECTION_STEAM}, 141 | { AUTO_RELOAD_INTERVAL, SECTION_STEAM }, 142 | { LAST_AUTO_RELOAD, SECTION_STEAM }, 143 | 144 | { CAFE_APP_LAUNCH_PARAMETER, SECTION_PARAMETERS }, 145 | { CLEAR_BETA_PARAMETER, SECTION_PARAMETERS }, 146 | { CONSOLE_PARAMETER, SECTION_PARAMETERS }, 147 | { DEVELOPER_PARAMETER, SECTION_PARAMETERS }, 148 | { FORCE_SERVICE_PARAMETER, SECTION_PARAMETERS }, 149 | { LOGIN_PARAMETER, SECTION_PARAMETERS }, 150 | { NO_CACHE_PARAMETER, SECTION_PARAMETERS }, 151 | { NO_VERIFY_FILES_PARAMETER, SECTION_PARAMETERS }, 152 | { SILENT_PARAMETER, SECTION_PARAMETERS }, 153 | { SINGLE_CORE_PARAMETER, SECTION_PARAMETERS }, 154 | { TCP_PARAMETER, SECTION_PARAMETERS }, 155 | { TEN_FOOT_PARAMETER, SECTION_PARAMETERS }, 156 | { CUSTOM_PARAMETERS, SECTION_PARAMETERS }, 157 | { CUSTOM_PARAMETERS_VALUE, SECTION_PARAMETERS }, 158 | 159 | { LIST_VIEW_HEIGHT, SECTION_LOCATION }, 160 | { LIST_VIEW_WIDTH, SECTION_LOCATION }, 161 | 162 | { NAME_COLUMN_INDEX, SECTION_COLUMNS }, 163 | { DESCRIPTION_COLUMN_INDEX, SECTION_COLUMNS }, 164 | { TIMEOUT_COLUMN_INDEX, SECTION_COLUMNS }, 165 | { VAC_BANS_COLUMN_INDEX, SECTION_COLUMNS }, 166 | { GAME_BANS_COLUMN_INDEX, SECTION_COLUMNS }, 167 | { ECO_BAN_COLUMN_INDEX, SECTION_COLUMNS }, 168 | { LAST_BAN_COLUMN_INDEX, SECTION_COLUMNS } 169 | }; 170 | 171 | public Dictionary ListViewColumns = new Dictionary 172 | { 173 | { "Name", NAME_COLUMN_INDEX }, 174 | { "Description", DESCRIPTION_COLUMN_INDEX }, 175 | { "Timeout", TIMEOUT_COLUMN_INDEX }, 176 | { "VAC Bans", VAC_BANS_COLUMN_INDEX }, 177 | { "Game Bans", GAME_BANS_COLUMN_INDEX}, 178 | { "Economy Ban", ECO_BAN_COLUMN_INDEX }, 179 | { "Last Ban (Days)", LAST_BAN_COLUMN_INDEX } 180 | }; 181 | public SAMSettings() 182 | { 183 | HandleDeprecatedSettings(); 184 | UpdateVersion(); 185 | ReadSettingsFile(); 186 | } 187 | 188 | public void GenerateSettings() 189 | { 190 | foreach (KeyValuePair entry in KeyValuePairs) 191 | { 192 | File.Write(entry.Key, Default.KeyValuePairs[entry.Key].ToString(), entry.Value); 193 | } 194 | } 195 | 196 | public void HandleDeprecatedSettings() 197 | { 198 | // Update Recent and Selected login setting names. 199 | if (File.KeyExists("Recent", SECTION_AUTOLOG)) 200 | { 201 | File.Write(LOGIN_RECENT_ACCOUNT, File.Read("Recent", SECTION_AUTOLOG), SECTION_AUTOLOG); 202 | File.DeleteKey("Recent", SECTION_AUTOLOG); 203 | } 204 | if (File.KeyExists("RecentAcc", SECTION_AUTOLOG)) 205 | { 206 | File.Write(RECENT_ACCOUNT_INDEX, File.Read("RecentAcc", SECTION_AUTOLOG), SECTION_AUTOLOG); 207 | File.DeleteKey("RecentAcc", SECTION_AUTOLOG); 208 | } 209 | if (File.KeyExists("Selected", SECTION_AUTOLOG)) 210 | { 211 | File.Write(LOGIN_SELECTED_ACCOUNT, File.Read("Selected", SECTION_AUTOLOG), SECTION_AUTOLOG); 212 | File.DeleteKey("Selected", SECTION_AUTOLOG); 213 | } 214 | if (File.KeyExists("SelectedAcc", SECTION_AUTOLOG)) 215 | { 216 | File.Write(SELECTED_ACCOUNT_INDEX, File.Read("SelectedAcc", SECTION_AUTOLOG), SECTION_AUTOLOG); 217 | File.DeleteKey("SelectedAcc", SECTION_AUTOLOG); 218 | } 219 | 220 | // Move Steam file path to it's own section. 221 | if (File.KeyExists(SECTION_STEAM, SECTION_GENERAL)) 222 | { 223 | File.Write(STEAM_PATH, File.Read(SECTION_STEAM, SECTION_GENERAL), SECTION_STEAM); 224 | File.DeleteKey(SECTION_STEAM, SECTION_GENERAL); 225 | } 226 | 227 | // Move button size to 'Customize' section. 228 | if (File.KeyExists(BUTTON_SIZE, SECTION_GENERAL)) 229 | { 230 | File.Write(BUTTON_SIZE, File.Read(BUTTON_SIZE, SECTION_GENERAL), SECTION_CUSTOMIZE); 231 | File.DeleteKey(BUTTON_SIZE, SECTION_GENERAL); 232 | } 233 | 234 | // Update developer launch parameter. 235 | if (File.KeyExists("developer", SECTION_PARAMETERS)) 236 | { 237 | File.Write(DEVELOPER_PARAMETER, File.Read("developer", SECTION_PARAMETERS), SECTION_PARAMETERS); 238 | File.DeleteKey("developer", SECTION_PARAMETERS); 239 | } 240 | 241 | // Remove 'Base' prefix from theme value. 242 | if (File.KeyExists(THEME, SECTION_CUSTOMIZE)) 243 | { 244 | string value = File.Read(THEME, SECTION_CUSTOMIZE); 245 | if (value.StartsWith("Base")) 246 | { 247 | File.Write(THEME, value.Substring(4), SECTION_CUSTOMIZE); 248 | } 249 | } 250 | } 251 | 252 | public void ReadSettingsFile() 253 | { 254 | globalParameters = new List(); 255 | 256 | foreach (KeyValuePair entry in KeyValuePairs) 257 | { 258 | if (!File.KeyExists(entry.Key, entry.Value)) 259 | { 260 | File.Write(entry.Key, Default.KeyValuePairs[entry.Key].ToString(), entry.Value); 261 | } 262 | else 263 | { 264 | switch (entry.Key) 265 | { 266 | case ACCOUNTS_PER_ROW: 267 | string accountsPerRowString = File.Read(ACCOUNTS_PER_ROW, SECTION_GENERAL); 268 | 269 | if (!Int32.TryParse(accountsPerRowString, out int accountsPerRow) || accountsPerRow < 1) 270 | { 271 | File.Write(ACCOUNTS_PER_ROW, Default.AccountsPerRow.ToString(), SECTION_GENERAL); 272 | User.AccountsPerRow = Default.AccountsPerRow; 273 | } 274 | else 275 | { 276 | User.AccountsPerRow = accountsPerRow; 277 | } 278 | break; 279 | 280 | case SLEEP_TIME: 281 | string sleepTimeString = File.Read(SLEEP_TIME, SECTION_GENERAL); 282 | 283 | if (!Single.TryParse(sleepTimeString, out float sleepTime) || sleepTime < 0 || sleepTime > 100) 284 | { 285 | File.Write(SLEEP_TIME, Default.SleepTime.ToString(), SECTION_GENERAL); 286 | User.SleepTime = Default.SleepTime * 1000; 287 | } 288 | else 289 | { 290 | User.SleepTime = (int)(sleepTime * 1000); 291 | } 292 | break; 293 | 294 | case START_MINIMIZED: 295 | User.StartMinimized = Convert.ToBoolean(File.Read(START_MINIMIZED, SECTION_GENERAL)); 296 | break; 297 | 298 | case BUTTON_SIZE: 299 | string buttonSizeString = File.Read(BUTTON_SIZE, SECTION_CUSTOMIZE); 300 | 301 | if (!Int32.TryParse(buttonSizeString, out int buttonSize) || buttonSize < 50 || buttonSize > 200) 302 | { 303 | File.Write(BUTTON_SIZE, "100", SECTION_CUSTOMIZE); 304 | User.ButtonSize = 100; 305 | } 306 | else 307 | { 308 | User.ButtonSize = buttonSize; 309 | } 310 | break; 311 | 312 | case INPUT_METHOD: 313 | User.VirtualInputMethod = (VirtualInputMethod)Enum.Parse(typeof(VirtualInputMethod), File.Read(INPUT_METHOD, SECTION_AUTOLOG)); 314 | break; 315 | 316 | default: 317 | switch (Type.GetTypeCode(User.KeyValuePairs[entry.Key].GetType())) 318 | { 319 | case TypeCode.Boolean: 320 | User.KeyValuePairs[entry.Key] = Convert.ToBoolean(File.Read(entry.Key, entry.Value)); 321 | if (entry.Value.Equals(SECTION_PARAMETERS) && (bool)User.KeyValuePairs[entry.Key] == true && !entry.Key.StartsWith("custom")) 322 | { 323 | globalParameters.Add("-" + entry.Key); 324 | } 325 | break; 326 | case TypeCode.Int32: 327 | User.KeyValuePairs[entry.Key] = Convert.ToInt32(File.Read(entry.Key, entry.Value)); 328 | break; 329 | case TypeCode.Double: 330 | User.KeyValuePairs[entry.Key] = Convert.ToDouble(File.Read(entry.Key, entry.Value)); 331 | break; 332 | 333 | default: 334 | User.KeyValuePairs[entry.Key] = File.Read(entry.Key, entry.Value); 335 | break; 336 | } 337 | break; 338 | } 339 | } 340 | } 341 | } 342 | 343 | public void UpdateVersion() 344 | { 345 | File.Write(VERSION, Assembly.GetExecutingAssembly().GetName().Version.ToString(), SECTION_SYSTEM); 346 | } 347 | 348 | public void EnableSelectedAccountIndex(int index) 349 | { 350 | File.Write(SELECTED_ACCOUNT_INDEX, index.ToString(), SECTION_AUTOLOG); 351 | File.Write(LOGIN_SELECTED_ACCOUNT, true.ToString(), SECTION_AUTOLOG); 352 | File.Write(LOGIN_RECENT_ACCOUNT, false.ToString(), SECTION_AUTOLOG); 353 | User.LoginSelectedAccount = true; 354 | User.LoginRecentAccount = false; 355 | User.SelectedAccountIndex = index; 356 | } 357 | 358 | public void ResetSelectedAccountIndex() 359 | { 360 | File.Write(SELECTED_ACCOUNT_INDEX, "-1", SECTION_AUTOLOG); 361 | File.Write(LOGIN_SELECTED_ACCOUNT, false.ToString(), SECTION_AUTOLOG); 362 | User.LoginSelectedAccount = false; 363 | User.SelectedAccountIndex = -1; 364 | } 365 | 366 | public void EnableRecentAccountIndex(int index) 367 | { 368 | File.Write(SELECTED_ACCOUNT_INDEX, index.ToString(), SECTION_AUTOLOG); 369 | File.Write(LOGIN_SELECTED_ACCOUNT, true.ToString(), SECTION_AUTOLOG); 370 | File.Write(LOGIN_RECENT_ACCOUNT, false.ToString(), SECTION_AUTOLOG); 371 | User.LoginSelectedAccount = true; 372 | User.LoginRecentAccount = false; 373 | User.SelectedAccountIndex = index; 374 | } 375 | 376 | public void UpdateRecentAccountIndex(int index) 377 | { 378 | File.Write(RECENT_ACCOUNT_INDEX, index.ToString(), SECTION_AUTOLOG); 379 | User.RecentAccountIndex = index; 380 | } 381 | 382 | public bool IsLoginSelectedEnabled() 383 | { 384 | return File.Read(LOGIN_SELECTED_ACCOUNT, SECTION_AUTOLOG) == true.ToString(); 385 | } 386 | 387 | public void SetPasswordProtect(bool passwordProtect) 388 | { 389 | File.Write(PASSWORD_PROTECT, passwordProtect.ToString(), SECTION_GENERAL); 390 | User.PasswordProtect = passwordProtect; 391 | } 392 | 393 | public void SetLastAutoReload(DateTime dateTime) 394 | { 395 | File.Write(LAST_AUTO_RELOAD, dateTime.ToString(), SECTION_STEAM); 396 | User.LastAutoReload = dateTime; 397 | } 398 | } 399 | } 400 | -------------------------------------------------------------------------------- /Core/SortType.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | enum SortType 4 | { 5 | Username, 6 | Alias, 7 | Banned, 8 | Random 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/SteamExitCode.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | class SteamExitCode 4 | { 5 | public const int SUCCESS = 42; 6 | public const int CANCELLED = -2; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /Core/StringCipher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Security.Cryptography; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace SAM.Core 8 | { 9 | public static class StringCipher 10 | { 11 | // This constant is used to determine the keysize of the encryption algorithm in bits. 12 | // We divide this by 8 within the code below to get the equivalent number of bytes. 13 | private const int Keysize = 256; 14 | 15 | // This constant determines the number of iterations for the password bytes generation function. 16 | private const int DerivationIterations = 1000; 17 | 18 | public static string Encrypt(string plainText, string passPhrase) 19 | { 20 | // Salt and IV is randomly generated each time, but is preprended to encrypted cipher text 21 | // so that the same Salt and IV values can be used when decrypting. 22 | var saltStringBytes = Generate256BitsOfRandomEntropy(); 23 | var ivStringBytes = Generate256BitsOfRandomEntropy(); 24 | var plainTextBytes = Encoding.UTF8.GetBytes(plainText); 25 | using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) 26 | { 27 | var keyBytes = password.GetBytes(Keysize / 8); 28 | using (var symmetricKey = new RijndaelManaged()) 29 | { 30 | symmetricKey.BlockSize = 256; 31 | symmetricKey.Mode = CipherMode.CBC; 32 | symmetricKey.Padding = PaddingMode.PKCS7; 33 | using (var encryptor = symmetricKey.CreateEncryptor(keyBytes, ivStringBytes)) 34 | { 35 | using (var memoryStream = new MemoryStream()) 36 | { 37 | using (var cryptoStream = new CryptoStream(memoryStream, encryptor, CryptoStreamMode.Write)) 38 | { 39 | cryptoStream.Write(plainTextBytes, 0, plainTextBytes.Length); 40 | cryptoStream.FlushFinalBlock(); 41 | // Create the final bytes as a concatenation of the random salt bytes, the random iv bytes and the cipher bytes. 42 | var cipherTextBytes = saltStringBytes; 43 | cipherTextBytes = cipherTextBytes.Concat(ivStringBytes).ToArray(); 44 | cipherTextBytes = cipherTextBytes.Concat(memoryStream.ToArray()).ToArray(); 45 | memoryStream.Close(); 46 | cryptoStream.Close(); 47 | return Convert.ToBase64String(cipherTextBytes); 48 | } 49 | } 50 | } 51 | } 52 | } 53 | } 54 | 55 | public static string Decrypt(string cipherText, string passPhrase) 56 | { 57 | // Get the complete stream of bytes that represent: 58 | // [32 bytes of Salt] + [32 bytes of IV] + [n bytes of CipherText] 59 | var cipherTextBytesWithSaltAndIv = Convert.FromBase64String(cipherText); 60 | // Get the saltbytes by extracting the first 32 bytes from the supplied cipherText bytes. 61 | var saltStringBytes = cipherTextBytesWithSaltAndIv.Take(Keysize / 8).ToArray(); 62 | // Get the IV bytes by extracting the next 32 bytes from the supplied cipherText bytes. 63 | var ivStringBytes = cipherTextBytesWithSaltAndIv.Skip(Keysize / 8).Take(Keysize / 8).ToArray(); 64 | // Get the actual cipher text bytes by removing the first 64 bytes from the cipherText string. 65 | var cipherTextBytes = cipherTextBytesWithSaltAndIv.Skip((Keysize / 8) * 2).Take(cipherTextBytesWithSaltAndIv.Length - ((Keysize / 8) * 2)).ToArray(); 66 | 67 | using (var password = new Rfc2898DeriveBytes(passPhrase, saltStringBytes, DerivationIterations)) 68 | { 69 | var keyBytes = password.GetBytes(Keysize / 8); 70 | using (var symmetricKey = new RijndaelManaged()) 71 | { 72 | symmetricKey.BlockSize = 256; 73 | symmetricKey.Mode = CipherMode.CBC; 74 | symmetricKey.Padding = PaddingMode.PKCS7; 75 | using (var decryptor = symmetricKey.CreateDecryptor(keyBytes, ivStringBytes)) 76 | { 77 | using (var memoryStream = new MemoryStream(cipherTextBytes)) 78 | { 79 | using (var cryptoStream = new CryptoStream(memoryStream, decryptor, CryptoStreamMode.Read)) 80 | { 81 | using (var plainTextReader = new StreamReader(cryptoStream)) 82 | { 83 | return plainTextReader.ReadToEnd(); 84 | } 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | 92 | private static byte[] Generate256BitsOfRandomEntropy() 93 | { 94 | var randomBytes = new byte[32]; // 32 Bytes will give us 256 bits. 95 | using (var rngCsp = new RNGCryptoServiceProvider()) 96 | { 97 | // Fill the array with cryptographically secure random bytes. 98 | rngCsp.GetBytes(randomBytes); 99 | } 100 | return randomBytes; 101 | } 102 | } 103 | } -------------------------------------------------------------------------------- /Core/UpdateHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using System.IO; 4 | using System.Net; 5 | using System.Net.Http; 6 | using System.Reflection; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using System.Windows; 10 | 11 | namespace SAM.Core 12 | { 13 | class UpdateHelper 14 | { 15 | /// 16 | /// String containing the latest version acquired from online text file. 17 | /// 18 | public static string latestVersion { get; set; } 19 | 20 | private static readonly string updaterFileName = "Updater.exe"; 21 | private static readonly string latestUpdaterVersionUrl = "https://raw.githubusercontent.com/rex706/Updater/master/latest.txt"; 22 | 23 | /// 24 | /// Check program for updates with the given text url. 25 | /// 26 | public static async Task CheckForUpdate(string updateUrl) 27 | { 28 | // Allows downloading files directly from GitHub repositories. 29 | ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12; 30 | 31 | // Nkosi Note: Always use asynchronous versions of network and IO methods. 32 | 33 | await Task.Run(() => DeleteUpdater()); 34 | 35 | // Check for version updates 36 | try 37 | { 38 | using (HttpClient client = new HttpClient()) 39 | { 40 | client.Timeout = new TimeSpan(0, 0, 1, 0); 41 | 42 | // Open the text file using a stream reader. 43 | using (Stream stream = await client.GetStreamAsync(updateUrl)) 44 | { 45 | StreamReader reader = new StreamReader(stream); 46 | 47 | // Get current and latest versions of program. 48 | Version current = Assembly.GetExecutingAssembly().GetName().Version; 49 | Version latest = Version.Parse(await reader.ReadLineAsync()); 50 | 51 | // Update latest version string class member. 52 | latestVersion = latest.ToString(); 53 | 54 | // If the version from the online text is newer than the current version, 55 | // ask user if they would like to download and install update now. 56 | if (latest > current) 57 | { 58 | // Show message box that an update is available. 59 | MessageBoxResult answer = MessageBox.Show("A new version of " + 60 | AppDomain.CurrentDomain.FriendlyName.Substring(0, AppDomain.CurrentDomain.FriendlyName.IndexOf('.')) + 61 | " is available!\n\nCurrent Version " + current + "\nLatest Version " + latest + 62 | "\n\nUpdate now?", "Update Available", MessageBoxButton.YesNo, MessageBoxImage.Information); 63 | 64 | // Update is available, and user wants to update. Requires app to close. 65 | if (answer == MessageBoxResult.Yes) 66 | { 67 | return UpdateResponse.Update; 68 | } 69 | 70 | // Update is available, but user chose not to update just yet. 71 | return UpdateResponse.Later; 72 | } 73 | } 74 | } 75 | 76 | // No update available. 77 | return UpdateResponse.NoUpdate; 78 | } 79 | catch 80 | { 81 | // Some error occured or there is no internet connection. 82 | return UpdateResponse.Error; 83 | } 84 | } 85 | 86 | public static async Task StartUpdate(string updateUrl, string releasesUrl) 87 | { 88 | using (HttpClient client = new HttpClient()) 89 | { 90 | client.Timeout = new TimeSpan(0, 0, 1, 0); 91 | 92 | await Task.Run(() => DeleteUpdater()); 93 | 94 | // Download latest updater. 95 | using (Stream updaterStream = await client.GetStreamAsync(latestUpdaterVersionUrl)) 96 | { 97 | StreamReader reader = new StreamReader(updaterStream); 98 | string latestUpdaterUrl = await reader.ReadLineAsync(); 99 | await DownloadUpdater(latestUpdaterUrl); 100 | } 101 | 102 | // Setup update process information. 103 | ProcessStartInfo startInfo = new ProcessStartInfo(); 104 | startInfo.UseShellExecute = true; 105 | startInfo.FileName = updaterFileName; 106 | startInfo.WorkingDirectory = AppDomain.CurrentDomain.BaseDirectory; 107 | startInfo.Arguments = updateUrl; 108 | startInfo.Verb = "runas"; 109 | 110 | // Launch updater and exit. 111 | try 112 | { 113 | Process.Start(startInfo); 114 | } 115 | catch 116 | { 117 | // Open browser to releases page. 118 | Process.Start(releasesUrl); 119 | } 120 | } 121 | } 122 | 123 | private static async Task DownloadUpdater(string url) 124 | { 125 | await new WebClient().DownloadFileTaskAsync(url, AppDomain.CurrentDomain.BaseDirectory + updaterFileName); 126 | } 127 | 128 | private static void DeleteUpdater() 129 | { 130 | string path = AppDomain.CurrentDomain.BaseDirectory + updaterFileName; 131 | 132 | if (File.Exists(path)) 133 | { 134 | while (IsFileLocked(path)) 135 | { 136 | Console.WriteLine("Waiting for updater to close..."); 137 | Thread.Sleep(1000); 138 | } 139 | 140 | try 141 | { 142 | File.Delete(path); 143 | } 144 | catch (Exception ex) 145 | { 146 | MessageBox.Show(ex.Message); 147 | } 148 | } 149 | } 150 | 151 | private static bool IsFileLocked(string filePath) 152 | { 153 | try 154 | { 155 | using (FileStream stream = new FileStream(filePath, FileMode.Open)) 156 | { 157 | stream.Close(); 158 | } 159 | } 160 | catch (IOException) 161 | { 162 | //the file is unavailable because it is: 163 | //still being written to 164 | //or being processed by another thread 165 | //or does not exist (has already been processed) 166 | return true; 167 | } 168 | 169 | //file is not locked 170 | return false; 171 | } 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /Core/UpdateResponse.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | public enum UpdateResponse 4 | { 5 | Update, 6 | Later, 7 | NoUpdate, 8 | Error 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /Core/UserSettings.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SAM.Core 5 | { 6 | class UserSettings 7 | { 8 | #region General 9 | 10 | public string Version { get { return (string)KeyValuePairs[SAMSettings.VERSION]; } set { KeyValuePairs[SAMSettings.VERSION] = value; } } 11 | public bool ClearUserData { get { return (bool)KeyValuePairs[SAMSettings.CLEAR_USER_DATA]; } set { KeyValuePairs[SAMSettings.CLEAR_USER_DATA] = value; } } 12 | public bool HideAddButton { get { return (bool)KeyValuePairs[SAMSettings.HIDE_ADD_BUTTON]; } set { KeyValuePairs[SAMSettings.HIDE_ADD_BUTTON] = value; } } 13 | public bool PasswordProtect { get { return (bool)KeyValuePairs[SAMSettings.PASSWORD_PROTECT]; } set { KeyValuePairs[SAMSettings.PASSWORD_PROTECT] = value; } } 14 | public bool MinimizeToTray { get { return (bool)KeyValuePairs[SAMSettings.MINIMIZE_TO_TRAY]; } set { KeyValuePairs[SAMSettings.MINIMIZE_TO_TRAY] = value; } } 15 | public bool RememberPassword { get { return (bool)KeyValuePairs[SAMSettings.REMEMBER_PASSWORD]; } set { KeyValuePairs[SAMSettings.REMEMBER_PASSWORD] = value; } } 16 | public bool StartMinimized { get { return (bool)KeyValuePairs[SAMSettings.START_MINIMIZED]; } set{ KeyValuePairs[SAMSettings.START_MINIMIZED] = value; } } 17 | public bool StartWithWindows { get { return (bool)KeyValuePairs[SAMSettings.START_WITH_WINDOWS]; } set { KeyValuePairs[SAMSettings.START_WITH_WINDOWS] = value; } } 18 | public int AccountsPerRow { get { return (int)KeyValuePairs[SAMSettings.ACCOUNTS_PER_ROW]; } set { KeyValuePairs[SAMSettings.ACCOUNTS_PER_ROW] = value; } } 19 | public int SleepTime { get { return (int)KeyValuePairs[SAMSettings.SLEEP_TIME]; } set { KeyValuePairs[SAMSettings.SLEEP_TIME] = value; } } 20 | public bool CheckForUpdates { get { return (bool)KeyValuePairs[SAMSettings.CHECK_FOR_UPDATES]; } set { KeyValuePairs[SAMSettings.CHECK_FOR_UPDATES] = value; } } 21 | public bool CloseOnLogin { get { return (bool)KeyValuePairs[SAMSettings.CLOSE_ON_LOGIN]; } set { KeyValuePairs[SAMSettings.CHECK_FOR_UPDATES] = value; } } 22 | public bool ListView { get { return (bool)KeyValuePairs[SAMSettings.LIST_VIEW]; } set { KeyValuePairs[SAMSettings.LIST_VIEW] = value; } } 23 | public bool SandboxMode { get { return (bool)KeyValuePairs[SAMSettings.SANDBOX_MODE]; } set { KeyValuePairs[SAMSettings.SANDBOX_MODE] = value; } } 24 | public bool HeaderlessWindow { get { return (bool)KeyValuePairs[SAMSettings.HEADERLESS_WINDOW]; } set { KeyValuePairs[SAMSettings.HEADERLESS_WINDOW] = value; } } 25 | public bool TransparentWindow { get { return (bool)KeyValuePairs[SAMSettings.TRANSPARENT_WINDOW]; } set { KeyValuePairs[SAMSettings.TRANSPARENT_WINDOW] = value; } } 26 | 27 | #endregion 28 | 29 | #region AutoLog 30 | 31 | public bool LoginRecentAccount { get { return (bool)KeyValuePairs[SAMSettings.LOGIN_RECENT_ACCOUNT]; } set { KeyValuePairs[SAMSettings.LOGIN_RECENT_ACCOUNT] = value; } } 32 | public int RecentAccountIndex { get { return (int)KeyValuePairs[SAMSettings.RECENT_ACCOUNT_INDEX]; } set{ KeyValuePairs[SAMSettings.RECENT_ACCOUNT_INDEX] = value; } } 33 | public bool LoginSelectedAccount { get { return (bool)KeyValuePairs[SAMSettings.LOGIN_SELECTED_ACCOUNT]; } set { KeyValuePairs[SAMSettings.LOGIN_SELECTED_ACCOUNT] = value; } } 34 | public int SelectedAccountIndex { get { return (int)KeyValuePairs[SAMSettings.SELECTED_ACCOUNT_INDEX]; } set { KeyValuePairs[SAMSettings.SELECTED_ACCOUNT_INDEX] = value; } } 35 | public VirtualInputMethod VirtualInputMethod { get { return (VirtualInputMethod)KeyValuePairs[SAMSettings.INPUT_METHOD]; } set { KeyValuePairs[SAMSettings.INPUT_METHOD] = value; } } 36 | public bool HandleMicrosoftIME { get { return (bool)KeyValuePairs[SAMSettings.HANDLE_IME]; } set { KeyValuePairs[SAMSettings.HANDLE_IME] = value; } } 37 | public bool IME2FAOnly { get { return (bool)KeyValuePairs[SAMSettings.IME_2FA_ONLY]; } set { KeyValuePairs[SAMSettings.IME_2FA_ONLY] = value; } } 38 | 39 | #endregion 40 | 41 | #region Customize 42 | 43 | public string Theme { get { return (string)KeyValuePairs[SAMSettings.THEME]; } set { KeyValuePairs[SAMSettings.THEME] = value; } } 44 | public string Accent { get { return (string)KeyValuePairs[SAMSettings.ACCENT]; } set { KeyValuePairs[SAMSettings.ACCENT] = value; } } 45 | public int ButtonSize { get { return (int)KeyValuePairs[SAMSettings.BUTTON_SIZE]; } set { KeyValuePairs[SAMSettings.BUTTON_SIZE] = value; } } 46 | public string ButtonColor { get { return (string)KeyValuePairs[SAMSettings.BUTTON_COLOR]; } set { KeyValuePairs[SAMSettings.BUTTON_COLOR] = value; } } 47 | public int ButtonFontSize { get { return (int)KeyValuePairs[SAMSettings.BUTTON_FONT_SIZE]; } set { KeyValuePairs[SAMSettings.BUTTON_FONT_SIZE] = value; } } 48 | public string ButtonFontColor { get { return (string)KeyValuePairs[SAMSettings.BUTTON_FONT_COLOR]; } set { KeyValuePairs[SAMSettings.BUTTON_FONT_COLOR] = value; } } 49 | public string ButtonBannerColor { get { return (string)KeyValuePairs[SAMSettings.BUTTON_BANNER_COLOR]; } set { KeyValuePairs[SAMSettings.BUTTON_BANNER_COLOR] = value; } } 50 | public int BannerFontSize { get { return (int)KeyValuePairs[SAMSettings.BUTTON_BANNER_FONT_SIZE]; } set { KeyValuePairs[SAMSettings.BUTTON_BANNER_FONT_SIZE] = value; } } 51 | public string BannerFontColor { get { return (string)KeyValuePairs[SAMSettings.BUTTON_BANNER_FONT_COLOR]; } set { KeyValuePairs[SAMSettings.BUTTON_BANNER_FONT_COLOR] = value; } } 52 | public bool HideBanIcons { get { return (bool)KeyValuePairs[SAMSettings.HIDE_BAN_ICONS]; } set { KeyValuePairs[SAMSettings.HIDE_BAN_ICONS] = value; } } 53 | 54 | #endregion 55 | 56 | #region Steam 57 | 58 | public string SteamPath { get { return (string)KeyValuePairs[SAMSettings.STEAM_PATH]; } set { KeyValuePairs[SAMSettings.STEAM_PATH] = value; } } 59 | public string ApiKey { get { return (string)KeyValuePairs[SAMSettings.STEAM_API_KEY]; } set { KeyValuePairs[SAMSettings.STEAM_API_KEY] = value; } } 60 | public bool AutoReloadEnabled { get { return (bool)KeyValuePairs[SAMSettings.AUTO_RELOAD_ENABLED]; } set { KeyValuePairs[SAMSettings.AUTO_RELOAD_ENABLED] = value; } } 61 | public int AutoReloadInterval { get { return (int)KeyValuePairs[SAMSettings.AUTO_RELOAD_INTERVAL]; } set { KeyValuePairs[SAMSettings.AUTO_RELOAD_INTERVAL] = value; } } 62 | public DateTime? LastAutoReload { 63 | 64 | get { 65 | try 66 | { 67 | return Convert.ToDateTime(KeyValuePairs[SAMSettings.LAST_AUTO_RELOAD]); 68 | } 69 | catch 70 | { 71 | return null; 72 | } 73 | } 74 | set { KeyValuePairs[SAMSettings.LAST_AUTO_RELOAD] = value; } 75 | } 76 | 77 | #endregion 78 | 79 | #region Parameters 80 | 81 | public bool CafeAppLaunch { get { return (bool)KeyValuePairs[SAMSettings.CAFE_APP_LAUNCH_PARAMETER]; } set { KeyValuePairs[SAMSettings.CAFE_APP_LAUNCH_PARAMETER] = value; } } 82 | public bool ClearBeta { get { return (bool)KeyValuePairs[SAMSettings.CLEAR_BETA_PARAMETER]; } set { KeyValuePairs[SAMSettings.CLEAR_BETA_PARAMETER] = value; } } 83 | public bool Console { get { return (bool)KeyValuePairs[SAMSettings.CONSOLE_PARAMETER]; } set { KeyValuePairs[SAMSettings.CONSOLE_PARAMETER] = value; } } 84 | public bool Developer { get { return (bool)KeyValuePairs[SAMSettings.DEVELOPER_PARAMETER]; } set { KeyValuePairs[SAMSettings.DEVELOPER_PARAMETER] = value; } } 85 | public bool ForceService { get { return (bool)KeyValuePairs[SAMSettings.FORCE_SERVICE_PARAMETER]; } set { KeyValuePairs[SAMSettings.FORCE_SERVICE_PARAMETER] = value; } } 86 | public bool Login { get { return (bool)KeyValuePairs[SAMSettings.LOGIN_PARAMETER]; } set { KeyValuePairs[SAMSettings.LOGIN_PARAMETER] = value; } } 87 | public bool NoCache { get { return (bool)KeyValuePairs[SAMSettings.NO_CACHE_PARAMETER]; } set { KeyValuePairs[SAMSettings.NO_CACHE_PARAMETER] = value; } } 88 | public bool NoVerifyFiles { get { return (bool)KeyValuePairs[SAMSettings.NO_VERIFY_FILES_PARAMETER]; } set { KeyValuePairs[SAMSettings.NO_VERIFY_FILES_PARAMETER] = value; } } 89 | public bool Silent { get { return (bool)KeyValuePairs[SAMSettings.SILENT_PARAMETER]; } set { KeyValuePairs[SAMSettings.SILENT_PARAMETER] = value; } } 90 | public bool SingleCore { get { return (bool)KeyValuePairs[SAMSettings.SINGLE_CORE_PARAMETER]; } set { KeyValuePairs[SAMSettings.SINGLE_CORE_PARAMETER] = value; } } 91 | public bool TCP { get { return (bool)KeyValuePairs[SAMSettings.TCP_PARAMETER]; } set { KeyValuePairs[SAMSettings.TCP_PARAMETER] = value; } } 92 | public bool TenFoot { get { return (bool)KeyValuePairs[SAMSettings.TEN_FOOT_PARAMETER]; } set { KeyValuePairs[SAMSettings.TEN_FOOT_PARAMETER] = value; } } 93 | public bool CustomParameters { get { return (bool)KeyValuePairs[SAMSettings.CUSTOM_PARAMETERS]; } set { KeyValuePairs[SAMSettings.CUSTOM_PARAMETERS] = value; } } 94 | public string CustomParametersValue { get { return (string)KeyValuePairs[SAMSettings.CUSTOM_PARAMETERS_VALUE]; } set { KeyValuePairs[SAMSettings.CUSTOM_PARAMETERS_VALUE] = value; } } 95 | 96 | #endregion 97 | 98 | #region Location 99 | 100 | public double WindowTop { get; set; } 101 | public double WindowLeft { get; set; } 102 | public double ListViewHeight { get { return Convert.ToDouble(KeyValuePairs[SAMSettings.LIST_VIEW_HEIGHT]); } set { KeyValuePairs[SAMSettings.LIST_VIEW_HEIGHT] = value; } } 103 | public double ListViewWidth { get { return Convert.ToDouble(KeyValuePairs[SAMSettings.LIST_VIEW_WIDTH]); } set { KeyValuePairs[SAMSettings.LIST_VIEW_WIDTH] = value; } } 104 | 105 | #endregion 106 | 107 | #region Columns 108 | 109 | public int NameColumnIndex { get { return (int)KeyValuePairs[SAMSettings.NAME_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.NAME_COLUMN_INDEX] = value; } } 110 | public int DescriptionColumnIndex { get { return (int)KeyValuePairs[SAMSettings.DESCRIPTION_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.DESCRIPTION_COLUMN_INDEX] = value; } } 111 | public int TimeoutColumnIndex { get { return (int)KeyValuePairs[SAMSettings.TIMEOUT_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.TIMEOUT_COLUMN_INDEX] = value; } } 112 | public int VacBansColumnIndex { get { return (int)KeyValuePairs[SAMSettings.VAC_BANS_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.VAC_BANS_COLUMN_INDEX] = value; } } 113 | public int GameBanColumnIndex { get { return (int)KeyValuePairs[SAMSettings.GAME_BANS_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.GAME_BANS_COLUMN_INDEX] = value; } } 114 | public int EconomyBanColumnIndex { get { return (int)KeyValuePairs[SAMSettings.ECO_BAN_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.ECO_BAN_COLUMN_INDEX] = value; } } 115 | public int LastBanColumnIndex { get { return (int)KeyValuePairs[SAMSettings.LAST_BAN_COLUMN_INDEX]; } set { KeyValuePairs[SAMSettings.LAST_BAN_COLUMN_INDEX] = value; } } 116 | 117 | #endregion 118 | 119 | public Dictionary KeyValuePairs = new Dictionary() 120 | { 121 | { SAMSettings.VERSION, string.Empty }, 122 | { SAMSettings.CLEAR_USER_DATA, false }, 123 | { SAMSettings.HIDE_ADD_BUTTON, false }, 124 | { SAMSettings.PASSWORD_PROTECT, false }, 125 | { SAMSettings.MINIMIZE_TO_TRAY, false }, 126 | { SAMSettings.REMEMBER_PASSWORD, false }, 127 | { SAMSettings.START_MINIMIZED, false }, 128 | { SAMSettings.START_WITH_WINDOWS, false }, 129 | { SAMSettings.ACCOUNTS_PER_ROW, 5 }, 130 | { SAMSettings.SLEEP_TIME, 2 }, 131 | { SAMSettings.CHECK_FOR_UPDATES, true }, 132 | { SAMSettings.CLOSE_ON_LOGIN, false }, 133 | { SAMSettings.LIST_VIEW, false }, 134 | { SAMSettings.SANDBOX_MODE, false }, 135 | { SAMSettings.HEADERLESS_WINDOW, false }, 136 | { SAMSettings.TRANSPARENT_WINDOW, false }, 137 | 138 | { SAMSettings.LOGIN_RECENT_ACCOUNT, false }, 139 | { SAMSettings.RECENT_ACCOUNT_INDEX, -1 }, 140 | { SAMSettings.LOGIN_SELECTED_ACCOUNT, false }, 141 | { SAMSettings.SELECTED_ACCOUNT_INDEX, -1 }, 142 | { SAMSettings.INPUT_METHOD, VirtualInputMethod.SendMessage }, 143 | { SAMSettings.HANDLE_IME, false }, 144 | { SAMSettings.IME_2FA_ONLY, false }, 145 | 146 | { SAMSettings.STEAM_PATH, string.Empty }, 147 | { SAMSettings.STEAM_API_KEY, string.Empty }, 148 | { SAMSettings.AUTO_RELOAD_ENABLED, false }, 149 | { SAMSettings.AUTO_RELOAD_INTERVAL, 30 }, 150 | { SAMSettings.LAST_AUTO_RELOAD, string.Empty }, 151 | 152 | { SAMSettings.THEME, "Dark" }, 153 | { SAMSettings.ACCENT, "Blue" }, 154 | { SAMSettings.BUTTON_SIZE, 100 }, 155 | { SAMSettings.BUTTON_COLOR, "#FFDDDDDD" }, 156 | { SAMSettings.BUTTON_FONT_SIZE, 0 }, 157 | { SAMSettings.BUTTON_FONT_COLOR, "#FF000000" }, 158 | { SAMSettings.BUTTON_BANNER_COLOR, "#7F000000" }, 159 | { SAMSettings.BUTTON_BANNER_FONT_SIZE, 0 }, 160 | { SAMSettings.BUTTON_BANNER_FONT_COLOR, "#FFFFFF" }, 161 | { SAMSettings.HIDE_BAN_ICONS, false }, 162 | 163 | { SAMSettings.CAFE_APP_LAUNCH_PARAMETER, false }, 164 | { SAMSettings.CLEAR_BETA_PARAMETER, false }, 165 | { SAMSettings.CONSOLE_PARAMETER, false }, 166 | { SAMSettings.DEVELOPER_PARAMETER, false }, 167 | { SAMSettings.FORCE_SERVICE_PARAMETER, false }, 168 | { SAMSettings.LOGIN_PARAMETER, true }, 169 | { SAMSettings.NO_CACHE_PARAMETER, false }, 170 | { SAMSettings.NO_VERIFY_FILES_PARAMETER, false }, 171 | { SAMSettings.SILENT_PARAMETER, false }, 172 | { SAMSettings.SINGLE_CORE_PARAMETER, false }, 173 | { SAMSettings.TCP_PARAMETER, false }, 174 | { SAMSettings.TEN_FOOT_PARAMETER, false }, 175 | { SAMSettings.CUSTOM_PARAMETERS, false }, 176 | { SAMSettings.CUSTOM_PARAMETERS_VALUE, string.Empty }, 177 | 178 | { SAMSettings.LIST_VIEW_HEIGHT, 300 }, 179 | { SAMSettings.LIST_VIEW_WIDTH, 750 }, 180 | 181 | { SAMSettings.NAME_COLUMN_INDEX, 0}, 182 | { SAMSettings.DESCRIPTION_COLUMN_INDEX, 1 }, 183 | { SAMSettings.TIMEOUT_COLUMN_INDEX, 2 }, 184 | { SAMSettings.VAC_BANS_COLUMN_INDEX, 3 }, 185 | { SAMSettings.GAME_BANS_COLUMN_INDEX, 4 }, 186 | { SAMSettings.ECO_BAN_COLUMN_INDEX, 5 }, 187 | { SAMSettings.LAST_BAN_COLUMN_INDEX, 6 } 188 | }; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /Core/VirtualInputMethod.cs: -------------------------------------------------------------------------------- 1 | namespace SAM.Core 2 | { 3 | enum VirtualInputMethod 4 | { 5 | SendMessage, 6 | PostMessage, 7 | SendWait 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /FodyWeavers.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /FodyWeavers.xsd: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 13 | 14 | 15 | 16 | 17 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 18 | 19 | 20 | 21 | 22 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with line breaks 23 | 24 | 25 | 26 | 27 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with line breaks. 28 | 29 | 30 | 31 | 32 | A list of unmanaged 32 bit assembly names to include, delimited with line breaks. 33 | 34 | 35 | 36 | 37 | A list of unmanaged 64 bit assembly names to include, delimited with line breaks. 38 | 39 | 40 | 41 | 42 | The order of preloaded assemblies, delimited with line breaks. 43 | 44 | 45 | 46 | 47 | 48 | This will copy embedded files to disk before loading them into memory. This is helpful for some scenarios that expected an assembly to be loaded from a physical file. 49 | 50 | 51 | 52 | 53 | Controls if .pdbs for reference assemblies are also embedded. 54 | 55 | 56 | 57 | 58 | Controls if runtime assemblies are also embedded. 59 | 60 | 61 | 62 | 63 | Controls whether the runtime assemblies are embedded with their full path or only with their assembly name. 64 | 65 | 66 | 67 | 68 | Embedded assemblies are compressed by default, and uncompressed when they are loaded. You can turn compression off with this option. 69 | 70 | 71 | 72 | 73 | As part of Costura, embedded assemblies are no longer included as part of the build. This cleanup can be turned off. 74 | 75 | 76 | 77 | 78 | Costura by default will load as part of the module initialization. This flag disables that behavior. Make sure you call CosturaUtility.Initialize() somewhere in your code. 79 | 80 | 81 | 82 | 83 | Costura will by default use assemblies with a name like 'resources.dll' as a satellite resource and prepend the output path. This flag disables that behavior. 84 | 85 | 86 | 87 | 88 | A list of assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 89 | 90 | 91 | 92 | 93 | A list of assembly names to include from the default action of "embed all Copy Local references", delimited with |. 94 | 95 | 96 | 97 | 98 | A list of runtime assembly names to exclude from the default action of "embed all Copy Local references", delimited with | 99 | 100 | 101 | 102 | 103 | A list of runtime assembly names to include from the default action of "embed all Copy Local references", delimited with |. 104 | 105 | 106 | 107 | 108 | A list of unmanaged 32 bit assembly names to include, delimited with |. 109 | 110 | 111 | 112 | 113 | A list of unmanaged 64 bit assembly names to include, delimited with |. 114 | 115 | 116 | 117 | 118 | The order of preloaded assemblies, delimited with |. 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed. 127 | 128 | 129 | 130 | 131 | A comma-separated list of error codes that can be safely ignored in assembly verification. 132 | 133 | 134 | 135 | 136 | 'false' to turn off automatic generation of the XML Schema file. 137 | 138 | 139 | 140 | 141 | -------------------------------------------------------------------------------- /Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Resources; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | using System.Windows; 6 | 7 | // Allgemeine Informationen über eine Assembly werden über die folgenden 8 | // Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern, 9 | // die einer Assembly zugeordnet sind. 10 | [assembly: AssemblyTitle("SAM")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("")] 14 | [assembly: AssemblyProduct("SAM")] 15 | [assembly: AssemblyCopyright("Copyright © SAM 2023")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | 19 | // Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly 20 | // für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von 21 | // COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen. 22 | [assembly: ComVisible(false)] 23 | 24 | //Um mit dem Erstellen lokalisierbarer Anwendungen zu beginnen, legen Sie 25 | //ImCodeVerwendeteKultur in der .csproj-Datei 26 | //in einer fest. Wenn Sie in den Quelldateien beispielsweise Deutsch 27 | //(Deutschland) verwenden, legen Sie auf \"de-DE\" fest. Heben Sie dann die Auskommentierung 28 | //des nachstehenden NeutralResourceLanguage-Attributs auf. Aktualisieren Sie "en-US" in der nachstehenden Zeile, 29 | //sodass es mit der UICulture-Einstellung in der Projektdatei übereinstimmt. 30 | 31 | //[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)] 32 | 33 | 34 | [assembly: ThemeInfo( 35 | ResourceDictionaryLocation.None, //Speicherort der designspezifischen Ressourcenwörterbücher 36 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird, 37 | // oder in den Anwendungsressourcen-Wörterbüchern nicht gefunden werden kann.) 38 | ResourceDictionaryLocation.SourceAssembly //Speicherort des generischen Ressourcenwörterbuchs 39 | //(wird verwendet, wenn eine Ressource auf der Seite nicht gefunden wird, 40 | // designspezifischen Ressourcenwörterbuch nicht gefunden werden kann.) 41 | )] 42 | 43 | 44 | // Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten: 45 | // 46 | // Hauptversion 47 | // Nebenversion 48 | // Buildnummer 49 | // Revision 50 | // 51 | // Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden, 52 | // indem Sie "*" wie unten gezeigt eingeben: 53 | // [assembly: AssemblyVersion("1.0.*")] 54 | [assembly: AssemblyVersion("1.5.4.3")] 55 | [assembly: AssemblyFileVersion("1.5.4.3")] 56 | -------------------------------------------------------------------------------- /Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.42000 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SAM.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | public class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | public static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SAM.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | public static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | 63 | /// 64 | /// Looks up a localized string similar to "UserLocalConfigStore" 65 | ///{ 66 | /// "friends" 67 | /// { 68 | /// 69 | /// } 70 | /// "WebStorage" 71 | /// { 72 | /// 73 | /// } 74 | ///}. 75 | /// 76 | public static string localconfig { 77 | get { 78 | return ResourceManager.GetString("localconfig", resourceCulture); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | 122 | ..\Resources\localconfig.vdf;System.String, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 123 | 124 | -------------------------------------------------------------------------------- /Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // Dieser Code wurde von einem Tool generiert. 4 | // Laufzeitversion:4.0.30319.42000 5 | // 6 | // Änderungen an dieser Datei können falsches Verhalten verursachen und gehen verloren, wenn 7 | // der Code erneut generiert wird. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace SAM.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SAM - Steam Account Manager 2 | 3 | *Easily log in and switch between Steam accounts.* 4 | 5 | 6 | ![alt tag](https://i.imgur.com/v4eJzzH.png) ![alt tag](https://i.imgur.com/laBORcJ.png) 7 | 8 | **Latest Version:** 1.5.4.3 9 | 10 | # [**DOWNLOAD**](https://github.com/rex706/SAM/releases/latest) 11 | 12 | **NEW** 13 | * Steam React UI support. (enabled by default) 14 | * Account data file backup system. (enabled by default) 15 | * Launch Parameters per account. 16 | * Friends login status per account. 17 | 18 | **Core Features** 19 | * 1-click Steam logins for multiple accounts with 2FA support. 20 | - Your 'Shared Secret' can be found in your decrypted .maFile generated from [SteamDesktopAuthenticator](https://github.com/Jessecar96/SteamDesktopAuthenticator). 21 | * Track account bans with [Steam API key](https://steamcommunity.com/dev/apikey). 22 | * Auto login a selected or the most recently used account. 23 | * Remember Steam password for next independent run. 24 | * Encrypts saved user password. 25 | * Set number of accounts per row. 26 | * Set button size ranging between 50-200px. 27 | * Import/Export accounts. 28 | * Start with Windows. 29 | * Start minimized. 30 | * Minimize to tray. 31 | * Data password protection. 32 | * Set/Clear account timeouts. 33 | * Delete userdata folder after every login toggle. 34 | * Toggle Steam client launch parameters globally and by account. 35 | * Support for Sandbox Mode for users of Sandboxie/Avast Sandbox. 36 | * Alternate List view. 37 | * Theme and Accent customization. 38 | * Close on login toggle setting. 39 | 40 | ----------------------------------------- 41 | May require [.NET Framework 4.8](https://dotnet.microsoft.com/download/dotnet-framework/thank-you/net48-web-installer) 42 | -------------------------------------------------------------------------------- /Resources/SteamIDs_Engine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rex706/SAM/d1367bba45d2657b9766a10ff277f69dbfd02a64/Resources/SteamIDs_Engine.dll -------------------------------------------------------------------------------- /Resources/add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rex706/SAM/d1367bba45d2657b9766a10ff277f69dbfd02a64/Resources/add.png -------------------------------------------------------------------------------- /Resources/add_active.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rex706/SAM/d1367bba45d2657b9766a10ff277f69dbfd02a64/Resources/add_active.png -------------------------------------------------------------------------------- /Resources/error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rex706/SAM/d1367bba45d2657b9766a10ff277f69dbfd02a64/Resources/error.png -------------------------------------------------------------------------------- /Resources/localconfig.vdf: -------------------------------------------------------------------------------- 1 | "UserLocalConfigStore" 2 | { 3 | "friends" 4 | { 5 | 6 | } 7 | "WebStorage" 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /Resources/steam.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rex706/SAM/d1367bba45d2657b9766a10ff277f69dbfd02a64/Resources/steam.ico -------------------------------------------------------------------------------- /SAM.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2C42E28D-4F21-4924-A004-5DAABD1BF23E} 8 | WinExe 9 | SAM 10 | SAM 11 | v4.7.2 12 | 512 13 | {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 4 15 | true 16 | true 17 | false 18 | 19 | publish\ 20 | true 21 | Disk 22 | false 23 | Foreground 24 | 7 25 | Days 26 | false 27 | false 28 | true 29 | 0 30 | 1.0.0.%2a 31 | false 32 | true 33 | 34 | 35 | AnyCPU 36 | true 37 | full 38 | false 39 | bin\Debug\ 40 | DEBUG;TRACE 41 | prompt 42 | 4 43 | CS4014 44 | 45 | 46 | AnyCPU 47 | none 48 | true 49 | bin\Release\ 50 | 51 | 52 | none 53 | 4 54 | 55 | 56 | SAM.App 57 | 58 | 59 | Resources\steam.ico 60 | 61 | 62 | app.manifest 63 | 64 | 65 | 66 | Resources\SteamIDs_Engine.dll 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 4.0 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | MSBuild:Compile 90 | Designer 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | AccountInfoDialog.xaml 102 | 103 | 104 | AccountsWindow.xaml 105 | 106 | 107 | ExposedInfoWindow.xaml 108 | 109 | 110 | ImportDelimitedWindow.xaml 111 | 112 | 113 | PasswordWindow.xaml 114 | 115 | 116 | SetTimeoutWindow.xaml 117 | 118 | 119 | SettingsWindow.xaml 120 | 121 | 122 | 123 | MSBuild:Compile 124 | Designer 125 | 126 | 127 | Designer 128 | MSBuild:Compile 129 | 130 | 131 | MSBuild:Compile 132 | Designer 133 | 134 | 135 | MSBuild:Compile 136 | Designer 137 | 138 | 139 | App.xaml 140 | Code 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | MSBuild:Compile 152 | Designer 153 | 154 | 155 | MSBuild:Compile 156 | Designer 157 | 158 | 159 | MSBuild:Compile 160 | Designer 161 | 162 | 163 | 164 | 165 | Code 166 | 167 | 168 | True 169 | True 170 | Resources.resx 171 | 172 | 173 | True 174 | Settings.settings 175 | True 176 | 177 | 178 | PublicResXFileCodeGenerator 179 | Resources.Designer.cs 180 | 181 | 182 | 183 | SettingsSingleFileGenerator 184 | Settings.Designer.cs 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 5.0.2 194 | 195 | 196 | 5.7.0 197 | runtime; build; native; contentfiles; analyzers; buildtransitive 198 | all 199 | 200 | 201 | 4.7.25104.5739 202 | 203 | 204 | 4.0.0 205 | 206 | 207 | 4.0.0 208 | 209 | 210 | 0.6.2 211 | 212 | 213 | 0.2.1 214 | 215 | 216 | 2.0.1 217 | 218 | 219 | 1.12.1 220 | 221 | 222 | 2.4.10 223 | 224 | 225 | 1.1.135 226 | 227 | 228 | 13.0.3 229 | 230 | 231 | 3.0.0 232 | 233 | 234 | 9.0.4 235 | 236 | 237 | 4.3.4 238 | 239 | 240 | 4.3.1 241 | 242 | 243 | 2.0.0 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | False 255 | Microsoft .NET Framework 4.8 %28x86 and x64%29 256 | true 257 | 258 | 259 | False 260 | .NET Framework 3.5 SP1 261 | false 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | {F935DC20-1CF0-11D0-ADB9-00C04FD58A0B} 270 | 1 271 | 0 272 | 0 273 | tlbimp 274 | False 275 | True 276 | 277 | 278 | 279 | -------------------------------------------------------------------------------- /SAM.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.31410.223 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SAM", "SAM.csproj", "{2C42E28D-4F21-4924-A004-5DAABD1BF23E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {2C42E28D-4F21-4924-A004-5DAABD1BF23E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {2C42E28D-4F21-4924-A004-5DAABD1BF23E}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {2C42E28D-4F21-4924-A004-5DAABD1BF23E}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {2C42E28D-4F21-4924-A004-5DAABD1BF23E}.Release|Any CPU.Build.0 = Release|Any CPU 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {D6446EA6-D244-4EE3-A4CC-49E7900A6E13} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Views/AccountInfoDialog.xaml: -------------------------------------------------------------------------------- 1 |  11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |