├── .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 |  
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 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/Views/AccountInfoDialog.xaml.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls;
2 | using System.Windows;
3 | using SAM.Core;
4 | using System;
5 | using System.Linq;
6 | using System.Diagnostics;
7 |
8 | namespace SAM.Views
9 | {
10 | ///
11 | /// Interaction logic for TextDialog.xaml
12 | ///
13 | public partial class AccountInfoDialog : MetroWindow
14 | {
15 | private const int STEAMID64_LENGTH = 17;
16 |
17 | public AccountInfoDialog()
18 | {
19 | InitializeComponent();
20 |
21 | FriendsOnlineStatusComboBox.ItemsSource = Enum.GetValues(typeof(FriendsLoginStatus)).Cast();
22 | }
23 |
24 | public string AccountText
25 | {
26 | get { return UsernameBox.Text; }
27 | set { UsernameBox.Text = value; }
28 | }
29 |
30 | public string AliasText
31 | {
32 | get { return AliasBox.Text; }
33 | set { AliasBox.Text = value; }
34 | }
35 |
36 | public string PasswordText
37 | {
38 | get { return PasswordBox.Password; }
39 | set { PasswordBox.Password = value; }
40 | }
41 |
42 | public string SharedSecretText
43 | {
44 | get { return SharedSecretBox.Password; }
45 | set { SharedSecretBox.Password = value; }
46 | }
47 |
48 | public string UrlText
49 | {
50 | get { return UrlTextBox.Text; }
51 | set { UrlTextBox.Text = value; }
52 | }
53 |
54 | private string OriginalUrlText { get; set; }
55 |
56 | public string ParametersText
57 | {
58 | get { return ParametersBox.Text; }
59 | set { ParametersBox.Text = value; }
60 | }
61 |
62 | public string DescriptionText
63 | {
64 | get { return DescriptionBox.Text; }
65 | set { DescriptionBox.Text = value; }
66 | }
67 |
68 | public FriendsLoginStatus FriendsLoginStatus
69 | {
70 | get { return (FriendsLoginStatus)FriendsOnlineStatusComboBox.SelectedItem; }
71 | set { FriendsOnlineStatusComboBox.SelectedItem = value; }
72 | }
73 |
74 | public bool AutoLogAccountIndex { get; set; }
75 |
76 | public string AviText { get; set; }
77 |
78 | public string SteamId
79 | {
80 | get { return SteamIdBox.Text; }
81 | set { SteamIdBox.Text = value; }
82 | }
83 |
84 | private void OKButton_Click(object sender, RoutedEventArgs e)
85 | {
86 | if (AccountText == null || AccountText.Length == 0)
87 | {
88 | MessageBox.Show("Account login required!");
89 | UsernameBox.Focus();
90 | return;
91 | }
92 | if (PasswordText == null || PasswordText.Length == 0)
93 | {
94 | MessageBox.Show("Account password required!");
95 | PasswordBox.Focus();
96 | return;
97 | }
98 |
99 | if (autoLogCheckBox.IsChecked == true)
100 | AutoLogAccountIndex = true;
101 | else
102 | AutoLogAccountIndex = false;
103 |
104 | DialogResult = true;
105 | }
106 |
107 | private void CancelButton_Click(object sender, RoutedEventArgs e)
108 | {
109 | DialogResult = false;
110 | Close();
111 | }
112 |
113 | private async void UsernameBox_LostFocus(object sender, RoutedEventArgs e)
114 | {
115 | if (UsernameBox.Text.Length < 3)
116 | {
117 | return;
118 | }
119 |
120 | OKButton.IsEnabled = false;
121 |
122 | dynamic userJson = await AccountUtils.GetUserInfoFromConfigAndWebApi(UsernameBox.Text.ToString());
123 |
124 | if (userJson != null)
125 | {
126 | try
127 | {
128 | dynamic profileUrl = userJson.response.players[0].profileurl;
129 | dynamic avatarUrl = userJson.response.players[0].avatarfull;
130 | dynamic steamId = userJson.response.players[0].steamid;
131 |
132 | UrlTextBox.Text = profileUrl;
133 |
134 | SteamId = steamId;
135 | AviText = avatarUrl;
136 | }
137 | catch (Exception ex)
138 | {
139 | Console.WriteLine(ex.Message);
140 | }
141 | }
142 | else
143 | {
144 | SteamId = null;
145 | AviText = null;
146 | }
147 |
148 | OKButton.IsEnabled = true;
149 | }
150 |
151 | private void UrlBox_GotFocus(object sender, RoutedEventArgs e)
152 | {
153 | OriginalUrlText = UrlTextBox.Text;
154 | }
155 |
156 | private async void UrlBox_LostFocus(object sender, RoutedEventArgs e)
157 | {
158 | if (UrlTextBox.Text != OriginalUrlText && SteamId == null)
159 | {
160 | OKButton.IsEnabled = false;
161 |
162 | dynamic steamId = await AccountUtils.GetSteamIdFromProfileUrl(UrlTextBox.Text);
163 |
164 | if (steamId != null)
165 | {
166 | SteamId = steamId;
167 | }
168 |
169 | OKButton.IsEnabled = true;
170 | }
171 | }
172 |
173 | private void SteamIdBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
174 | {
175 | if (SteamId.Length == STEAMID64_LENGTH && long.TryParse(SteamId, out _))
176 | {
177 | FriendsOnlineStatusComboBox.IsEnabled = true;
178 | }
179 | else
180 | {
181 | FriendsOnlineStatusComboBox.IsEnabled = false;
182 | FriendsOnlineStatusComboBox.SelectedItem = FriendsLoginStatus.Unchanged;
183 | }
184 | }
185 |
186 | private void CustomParamsHelpButton_Click(object sender, RoutedEventArgs e)
187 | {
188 | Process.Start("https://developer.valvesoftware.com/wiki/Command_Line_Options#Steam_.28Windows.29");
189 | }
190 |
191 | private void SharedSecretHelpButton_Click(object sender, RoutedEventArgs e)
192 | {
193 | Process.Start("https://github.com/Jessecar96/SteamDesktopAuthenticator");
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/Views/AccountsWindow.xaml:
--------------------------------------------------------------------------------
1 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
49 |
75 |
101 |
102 |
103 |
104 |
105 |
106 |
119 |
120 |
121 |
122 |
123 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
--------------------------------------------------------------------------------
/Views/ExposedInfoWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/Views/ExposedInfoWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Windows.Controls;
5 | using SAM.Core;
6 |
7 | namespace SAM.Views
8 | {
9 | ///
10 | /// Interaction logic for ExposedInfoWindow.xaml
11 | ///
12 | public partial class ExposedInfoWindow : MetroWindow
13 | {
14 | private readonly List decryptedAccounts;
15 | private readonly string eKey;
16 |
17 | public ExposedInfoWindow(List decryptedAccounts, string eKey)
18 | {
19 | InitializeComponent();
20 | this.decryptedAccounts = decryptedAccounts;
21 | this.eKey = eKey;
22 | RefreshAccountsList();
23 | }
24 |
25 | private void RefreshAccountsList()
26 | {
27 | StringBuilder accountListBuilder = new StringBuilder();
28 |
29 | foreach (Account account in decryptedAccounts)
30 | {
31 | string password = StringCipher.Decrypt(account.Password, eKey);
32 | string sharedSecret = StringCipher.Decrypt(account.SharedSecret, eKey);
33 |
34 | string line = account.Name + DelimiterCharacterTextBox.Text + password;
35 |
36 | if (!string.IsNullOrEmpty(sharedSecret))
37 | {
38 | line += DelimiterCharacterTextBox.Text + sharedSecret;
39 | }
40 |
41 | accountListBuilder.AppendLine(line);
42 | }
43 |
44 | DelimitedAccountsTextBox.Text = accountListBuilder.ToString();
45 | }
46 |
47 | private void DelimiterCharacterTextBox_TextChanged(object sender, TextChangedEventArgs e)
48 | {
49 | if (PreviewTextBlock != null && DelimiterCharacterTextBox.Text.Length > 0)
50 | {
51 | PreviewTextBlock.Content = "account" + DelimiterCharacterTextBox.Text + "password" + DelimiterCharacterTextBox.Text + "sharedSecret";
52 | RefreshAccountsList();
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/Views/ImportDelimitedWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/Views/ImportDelimitedWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Windows;
5 | using SAM.Core;
6 | using Microsoft.Win32;
7 | using System.IO;
8 |
9 | namespace SAM.Views
10 | {
11 | ///
12 | /// Interaction logic for Window1.xaml
13 | ///
14 | public partial class ImportDelimited : MetroWindow
15 | {
16 | private readonly string eKey;
17 |
18 | public ImportDelimited(string eKey)
19 | {
20 | this.eKey = eKey;
21 | InitializeComponent();
22 | }
23 |
24 | private void ImportButton_Click(object sender, RoutedEventArgs e)
25 | {
26 | if (DelimitedAccountsTextBox.Text.Length == 0)
27 | {
28 | DelimitedAccountsTextBox.Focus();
29 | MessageBox.Show("No accounts to import!");
30 | return;
31 | }
32 |
33 | if (DelimiterCharacterTextBox.Text.Length == 0)
34 | {
35 | DelimiterCharacterTextBox.Focus();
36 | MessageBox.Show("Delimiter character required!");
37 | return;
38 | }
39 |
40 | ImportButton.IsEnabled = false;
41 |
42 | char delimiter = DelimiterCharacterTextBox.Text[0];
43 | string delimitedAccountsText = DelimitedAccountsTextBox.Text;
44 |
45 | string[] lines = delimitedAccountsText.Split('\n');
46 |
47 | List accounts = new List();
48 |
49 | int sucessful = 0;
50 | List errors = new List();
51 |
52 | foreach (string line in lines)
53 | {
54 | string current = RemoveWhitespace(line);
55 |
56 | // Skip empty lines.
57 | if (current.Length == 0)
58 | {
59 | continue;
60 | }
61 |
62 | string[] info = current.Split(delimiter);
63 |
64 | // Log account error.
65 | if (info.Length < 2)
66 | {
67 | errors.Add(line);
68 | continue;
69 | }
70 |
71 | string name = info[0];
72 | string password = info[1];
73 |
74 | string steamId = AccountUtils.GetSteamIdFromConfig(name);
75 |
76 | // Shared secret.
77 | if (info.Length > 2 && info[2] != null && info[2] != string.Empty)
78 | {
79 | string secret = info[2];
80 | accounts.Add(new Account { Name = name, Password = StringCipher.Encrypt(password, eKey), SharedSecret = StringCipher.Encrypt(secret, eKey), SteamId = steamId });
81 | }
82 | else
83 | {
84 | accounts.Add(new Account { Name = name, Password = StringCipher.Encrypt(password, eKey), SteamId = steamId });
85 | }
86 |
87 | sucessful++;
88 | }
89 |
90 | AccountUtils.ImportAccountsFromList(accounts);
91 |
92 | if (errors.Count > 0)
93 | {
94 | MessageBox.Show("There were " + errors.Count + " problems with import:\n" + String.Join("\n", errors.ToArray()));
95 | }
96 |
97 | Close();
98 | }
99 |
100 | private void DelimiterCharacterTextBox_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e)
101 | {
102 | if (PreviewTextBlock != null && DelimiterCharacterTextBox.Text.Length > 0)
103 | {
104 | PreviewTextBlock.Content = "account" + DelimiterCharacterTextBox.Text + "password" + DelimiterCharacterTextBox.Text + "sharedSecret";
105 | }
106 | }
107 |
108 | private void ReadTextFileButton_Click(object sender, RoutedEventArgs e)
109 | {
110 | OpenFileDialog dialog = new OpenFileDialog
111 | {
112 | DefaultExt = ".txt",
113 | Filter = "Text Files (*.txt)|*.txt",
114 | Multiselect = true
115 | };
116 |
117 | bool? result = dialog.ShowDialog();
118 |
119 | if (result == true)
120 | {
121 | SetTextFromFiles(dialog.FileNames);
122 | }
123 | }
124 |
125 | private void DelimitedAccountsTextBox_PreviewDragOver(object sender, DragEventArgs e)
126 | {
127 | if (e.Data.GetDataPresent(DataFormats.FileDrop))
128 | e.Effects = DragDropEffects.Copy;
129 | else
130 | e.Effects = DragDropEffects.None;
131 |
132 | e.Handled = true;
133 | }
134 |
135 | private void DelimitedAccountsTextBox_PreviewDrop(object sender, DragEventArgs e)
136 | {
137 | string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
138 | SetTextFromFiles(files);
139 | e.Handled = true;
140 | }
141 |
142 | private void SetTextFromFiles(string[] files)
143 | {
144 | DelimitedAccountsTextBox.Text = "";
145 |
146 | foreach (string file in files)
147 | {
148 | try
149 | {
150 | string ext = Path.GetExtension(file);
151 |
152 | if (ext == ".txt")
153 | {
154 | DelimitedAccountsTextBox.Text += File.ReadAllText(file);
155 | }
156 | else
157 | {
158 | string name = Path.GetFileName(file);
159 | MessageBox.Show(name + " is not a .txt file", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
160 | }
161 | }
162 | catch (Exception m)
163 | {
164 | MessageBox.Show(m.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
165 | }
166 | }
167 | }
168 |
169 | private string RemoveWhitespace(string str)
170 | {
171 | return string.Join("", str.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries));
172 | }
173 | }
174 | }
175 |
--------------------------------------------------------------------------------
/Views/PasswordWindow.xaml:
--------------------------------------------------------------------------------
1 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/Views/PasswordWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls;
2 | using System.Windows;
3 | using System.Windows.Input;
4 |
5 | namespace SAM.Views
6 | {
7 | ///
8 | /// Interaction logic for Window2.xaml
9 | ///
10 | public partial class PasswordWindow : MetroWindow
11 | {
12 | public string PasswordText
13 | {
14 | get { return PasswordTextBox.Password; }
15 | set { PasswordTextBox.Password = value; }
16 | }
17 |
18 | public PasswordWindow()
19 | {
20 | InitializeComponent();
21 | PasswordTextBox.Focus();
22 | }
23 |
24 | private void PasswordTextBox_PreviewKeyDown(object sender, KeyEventArgs e)
25 | {
26 | if (e.Key == Key.Return)
27 | {
28 | if (validateInput())
29 | {
30 | DialogResult = true;
31 | }
32 | }
33 | }
34 |
35 | private void Button_Click(object sender, RoutedEventArgs e)
36 | {
37 | if (validateInput())
38 | {
39 | DialogResult = true;
40 | }
41 | }
42 |
43 | private bool validateInput()
44 | {
45 | if (PasswordTextBox.Password.Length > 0)
46 | {
47 | PasswordText = PasswordTextBox.Password;
48 | return true;
49 | }
50 | else if (PasswordTextBox.Password.Length == 0)
51 | {
52 | //MessageBoxResult messageBoxResult = MessageBox.Show("No password detected, are you sure?", "Confirm", MessageBoxButton.YesNo, MessageBoxImage.Warning);
53 |
54 | //if (messageBoxResult == MessageBoxResult.OK)
55 | //{
56 | // return true;
57 | //}
58 |
59 | PasswordText = "";
60 |
61 | return true;
62 | }
63 |
64 | return false;
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/Views/SetTimeoutWindow.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/Views/SetTimeoutWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using MahApps.Metro.Controls;
2 | using System;
3 | using System.Windows;
4 |
5 | namespace SAM.Views
6 | {
7 | ///
8 | /// Interaction logic for SetTimeoutWindow.xaml
9 | ///
10 | public partial class SetTimeoutWindow : MetroWindow
11 | {
12 | public DateTime? timeout;
13 |
14 | public SetTimeoutWindow(DateTime? timeout)
15 | {
16 | InitializeComponent();
17 | this.timeout = timeout;
18 |
19 | if (timeout != null && timeout < new DateTime())
20 | {
21 | var timeLeft = timeout - DateTime.Now;
22 |
23 | int years = timeLeft.Value.Days / 365;
24 | int days = timeLeft.Value.Days;
25 |
26 | if (years > 0)
27 | {
28 | days = (timeLeft.Value.Days / (years * 365));
29 | }
30 |
31 | YearsSpinBox.Value = years;
32 | DaysSpinBox.Value = days;
33 | HoursSpinBox.Value = timeLeft.Value.Hours;
34 | MinutesSpinBox.Value = timeLeft.Value.Minutes;
35 | SecondsSpinBox.Value = timeLeft.Value.Seconds;
36 | }
37 | }
38 |
39 | private void OKButton_Click(object sender, RoutedEventArgs e)
40 | {
41 | if (YearsSpinBox.Value == 0 && DaysSpinBox.Value == 0 && HoursSpinBox.Value == 0 && MinutesSpinBox.Value == 0 && SecondsSpinBox.Value == 0)
42 | {
43 | Close();
44 | }
45 |
46 | timeout = DateTime.Now.AddYears((int)YearsSpinBox.Value).AddDays((int)DaysSpinBox.Value).AddHours((int)HoursSpinBox.Value).AddMinutes((int)MinutesSpinBox.Value).AddSeconds((int)SecondsSpinBox.Value);
47 |
48 | Close();
49 | }
50 |
51 | private void ResetButton_Click(object sender, RoutedEventArgs e)
52 | {
53 | YearsSpinBox.Value = 0;
54 | DaysSpinBox.Value = 0;
55 | HoursSpinBox.Value = 0;
56 | MinutesSpinBox.Value = 0;
57 | SecondsSpinBox.Value = 0;
58 | }
59 |
60 | private void CancelButton_Click(object sender, RoutedEventArgs e)
61 | {
62 | Close();
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/Views/SettingsWindow.xaml:
--------------------------------------------------------------------------------
1 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
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 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
--------------------------------------------------------------------------------
/Wpf/PriorityMultiValueConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Globalization;
3 | using System.Linq;
4 | using System.Windows.Data;
5 |
6 | namespace SAM.Wpf
7 | {
8 | class PriorityMultiValueConverter : IMultiValueConverter
9 | {
10 | public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
11 | {
12 | return values.FirstOrDefault(o => o != null && !string.Empty.Equals(o));
13 | }
14 |
15 | public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
16 | {
17 | throw new NotImplementedException();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/app.manifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
54 |
62 |
63 |
64 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/latest.txt:
--------------------------------------------------------------------------------
1 | 1.5.4.3
2 | SAM.exe
3 | https://github.com/rex706/SAM/releases/download/v1.5.4.3/SAM.exe
4 | SAM.exe
5 |
--------------------------------------------------------------------------------