diff --git a/NebulaAuth.sln b/NebulaAuth.sln index 9f72133..b3092fc 100644 --- a/NebulaAuth.sln +++ b/NebulaAuth.sln @@ -12,6 +12,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NebulaAuth.LegacyConverter" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SteamLibForked", "SteamLibForked\SteamLibForked.csproj", "{09F02072-F91D-4DAA-87BC-A34D3E150570}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "changelog", "changelog", "{161BFADE-FCF3-45D1-82EA-8A1B187529F7}" + ProjectSection(SolutionItems) = preProject + changelog\1.3.4.html = changelog\1.3.4.html + changelog\1.4.4.html = changelog\1.4.4.html + changelog\1.4.5.html = changelog\1.4.5.html + changelog\1.4.6.html = changelog\1.4.6.html + changelog\1.4.7.html = changelog\1.4.7.html + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU diff --git a/NebulaAuth/Core/TrayManager.cs b/NebulaAuth/Core/TrayManager.cs index 0b2e580..d7ba53b 100644 --- a/NebulaAuth/Core/TrayManager.cs +++ b/NebulaAuth/Core/TrayManager.cs @@ -20,7 +20,7 @@ public static class TrayManager _notifyIcon = new NotifyIcon(); _notifyIcon.Text = "NebulaAuth"; - Stream iconStream = Application.GetResourceStream(new Uri("pack://application:,,,/Theme/nebula lock.ico"))!.Stream; + Stream iconStream = Application.GetResourceStream(new Uri("pack://application:,,,/Theme/lock.ico"))!.Stream; _notifyIcon.Icon = new Icon(iconStream); _notifyIcon.MouseDoubleClick += NotifyIcon_MouseDoubleClick; diff --git a/NebulaAuth/MainWindow.xaml b/NebulaAuth/MainWindow.xaml index 45b9b22..71982c4 100644 --- a/NebulaAuth/MainWindow.xaml +++ b/NebulaAuth/MainWindow.xaml @@ -41,7 +41,7 @@ - Отпустите, чтобы добавить mafile + @@ -96,7 +96,13 @@ - + + + + + + + @@ -108,7 +114,7 @@ - + @@ -119,12 +125,12 @@ - + - + diff --git a/NebulaAuth/Model/Settings.cs b/NebulaAuth/Model/Settings.cs index ad6c673..a29c5b0 100644 --- a/NebulaAuth/Model/Settings.cs +++ b/NebulaAuth/Model/Settings.cs @@ -22,7 +22,7 @@ public partial class Settings : ObservableObject [ObservableProperty] private LocalizationLanguage _language = LocalizationLanguage.English; [ObservableProperty] private bool _legacyMode = true; [ObservableProperty] private bool _allowAutoUpdate; - + [ObservableProperty] private bool _useAccountNameAsMafileName; #endregion public static Settings Instance { get; } diff --git a/NebulaAuth/Model/Shell.cs b/NebulaAuth/Model/Shell.cs index a09ca29..b6facbc 100644 --- a/NebulaAuth/Model/Shell.cs +++ b/NebulaAuth/Model/Shell.cs @@ -32,6 +32,7 @@ public static class Shell throw new CantAlignTimeException("", ex); } ExtensionsLogger.LogDebug("Application started"); + Logger.Info("Test"); } private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e) diff --git a/NebulaAuth/Model/Storage.cs b/NebulaAuth/Model/Storage.cs index 178d5dd..70c1b5d 100644 --- a/NebulaAuth/Model/Storage.cs +++ b/NebulaAuth/Model/Storage.cs @@ -1,6 +1,9 @@ using NebulaAuth.Model.Entities; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SteamLib; +using SteamLib.Account; +using SteamLib.Exceptions; using SteamLib.SteamMobile; using SteamLib.Utility.MaFiles; using System; @@ -8,8 +11,6 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.IO; using System.Linq; -using Newtonsoft.Json; -using SteamLib.Exceptions; namespace NebulaAuth.Model; @@ -22,7 +23,7 @@ public static class Storage public static string RemovedMafileFolder { get; } = Path.GetFullPath(REMOVED_F); public static ObservableCollection MaFiles { get; } = new(); - + public static readonly int DuplicateFound; static Storage() { if (Directory.Exists(MafileFolder) == false) @@ -36,12 +37,25 @@ public static class Storage } var files = Directory.GetFiles(MafileFolder); - foreach (var file in files) + var hashNames = new HashSet(); + var hashIds = new HashSet(); + var comparer = new MafileNameComparer(Settings.Instance.UseAccountNameAsMafileName); + var ordered = files.Order(comparer).ToList(); + foreach (var file in ordered) { - if (Path.GetExtension(file).Equals(".mafile", StringComparison.InvariantCultureIgnoreCase) == false) continue; + if (Path.GetExtension(file).Equals(".mafile", StringComparison.OrdinalIgnoreCase) == false) continue; try { var data = ReadMafile(file); + + if (hashNames.Contains(data.AccountName) || (data.SessionData != null && hashIds.Contains(data.SessionData.SteamId))) + { + DuplicateFound++; + Shell.Logger.Error("Duplicate mafile {file}", Path.GetFileName(file)); + continue; + } + hashNames.Add(data.AccountName); + if (data.SessionData != null) hashIds.Add(data.SessionData.SteamId); MaFiles.Add(data); } catch (Exception ex) @@ -85,14 +99,14 @@ public static class Storage { Data = { { "mafile", data } } }; - - if (overwrite == false && File.Exists(GetMafilePath(data))) + + if (overwrite == false && File.Exists(CreatePathForMafile(data))) { throw new IOException("File already exist and overwrite is False"); } - + SaveMafile(data); } @@ -113,21 +127,25 @@ public static class Storage } public static string SerializeMafile(Mafile data) + { + var props = new Dictionary + { + {nameof(Mafile.Proxy), data.Proxy}, + {nameof(Mafile.Group), data.Group}, + {nameof(Mafile.Password), data.Password} + }; + return SerializeMafile(data, props); + } + + public static string SerializeMafile(MobileDataExtended data, Dictionary? properties) { if (Settings.Instance.LegacyMode) { - var props = new Dictionary - { - {nameof(Mafile.Proxy), data.Proxy}, - {nameof(Mafile.Group), data.Group}, - {nameof(Mafile.Password), data.Password} - }; - return MafileSerializer.SerializeLegacy(data, Formatting.Indented, props); - + return MafileSerializer.SerializeLegacy(data, Formatting.Indented, properties); } else { - return MafileSerializer.Serialize(data); + return MafileSerializer.Serialize(data); } } @@ -149,8 +167,7 @@ public static class Storage public static void SaveMafile(Mafile data) { - - var path = GetMafilePath(data); + var path = CreatePathForMafile(data); var str = SerializeMafile(data); File.WriteAllText(Path.GetFullPath(path), str); @@ -168,14 +185,14 @@ public static class Storage public static void UpdateMafile(Mafile data) { - var path = GetMafilePath(data); + var path = CreatePathForMafile(data); var str = SerializeMafile(data); File.WriteAllText(Path.GetFullPath(path), str); } public static void RemoveMafile(Mafile data) { - var path = GetMafilePath(data); + var path = CreatePathForMafile(data); if (File.Exists(path)) { File.Delete(path); @@ -184,7 +201,7 @@ public static class Storage } public static void MoveToRemoved(Mafile data) { - var path = GetMafilePath(data); + var path = CreatePathForMafile(data); var copyPath = Path.Combine(REMOVED_F, data.SessionData!.SteamId + ".mafile"); var copyPathCompleted = copyPath; var i = 0; @@ -197,14 +214,85 @@ public static class Storage File.Delete(path); MaFiles.Remove(data); } - private static string GetMafilePath(Mafile data) + + private static string CreatePathForMafile(Mafile data) { if (data.SessionData == null) - throw new NullReferenceException("SessionData was null can't retrieve SteamId"); - var fileName = data.SessionData.SteamId + ".mafile"; + throw new NullReferenceException("SessionData was null can't retrieve SteamId"); //TODO: handle with login + + var useAccountName = Settings.Instance.UseAccountNameAsMafileName; + if (!useAccountName && (data.SessionData.SteamId.Steam64 == 0 || data.SessionData.SteamId.Steam64 == SteamId64.SEED)) + { + useAccountName = true; + } + + var fileName = useAccountName ? CreateFileNameWithAccountName(data.AccountName) : CreateFileNameWithSteamId(data.SessionData.SteamId); + return Path.Combine(MafileFolder, fileName); } + private static string CreateFileNameWithAccountName(string accountName) => accountName + ".mafile"; + private static string CreateFileNameWithSteamId(SteamId steamId) => steamId.Steam64.Id + ".mafile"; + public static string? TryFindMafilePath(Mafile data) + //FIXME: write mode to mafile instead of searching it. Search sometimes represents not actual mafile (in case of duplicate steamId + accountName) + { + var pathFileName = Path.Combine(MafileFolder, CreateFileNameWithAccountName(data.AccountName)); + string? pathSteamId = null; + if (data.SessionData != null) + { + pathSteamId = Path.Combine(MafileFolder, CreateFileNameWithSteamId(data.SessionData.SteamId)); + } + + var steamIdExist = pathSteamId != null && File.Exists(pathSteamId); + var accountNameExist = File.Exists(pathFileName); + + if (steamIdExist && accountNameExist) + { + return Settings.Instance.UseAccountNameAsMafileName ? pathFileName : pathSteamId; + } + if (steamIdExist ^ accountNameExist) + { + return steamIdExist ? pathSteamId : pathFileName; + } + return null; + } +} + +internal class MafileNameComparer : IComparer +{ + public bool MafileNameMode { get; } + private const string MAF_64_START = "765"; + private static readonly IComparer _defaultComparer = Comparer.Default; + public MafileNameComparer(bool mafileNameMode) + { + MafileNameMode = mafileNameMode; + } + + + public int Compare(string? x, string? y) + { + if (x == null && y == null) return 0; + if (x == null) return -1; + if (y == null) return 1; + + + bool xisSteamId = Path.GetFileName(x).StartsWith(MAF_64_START); + bool yisSteamId = Path.GetFileName(y).StartsWith(MAF_64_START); + + if (xisSteamId ^ yisSteamId) + { + if (MafileNameMode) + { + return xisSteamId ? 1 : -1; + } + else + { + return yisSteamId ? 1 : -1; + } + } + + return _defaultComparer.Compare(x, y); + } } \ No newline at end of file diff --git a/NebulaAuth/NebulaAuth.csproj b/NebulaAuth/NebulaAuth.csproj index 3a955b5..9c3bef4 100644 --- a/NebulaAuth/NebulaAuth.csproj +++ b/NebulaAuth/NebulaAuth.csproj @@ -8,15 +8,14 @@ true latest en;ru;ua - Theme\nebula lock.ico + Theme\lock.ico 7.0 - 1.4.6 + 1.4.7 - - + @@ -37,8 +36,7 @@ Never - - + diff --git a/NebulaAuth/Theme/lock.ico b/NebulaAuth/Theme/lock.ico new file mode 100644 index 0000000..3397e22 Binary files /dev/null and b/NebulaAuth/Theme/lock.ico differ diff --git a/NebulaAuth/View/SettingsView.xaml b/NebulaAuth/View/SettingsView.xaml index d5e032b..b2aa3aa 100644 --- a/NebulaAuth/View/SettingsView.xaml +++ b/NebulaAuth/View/SettingsView.xaml @@ -44,10 +44,10 @@ - - + + - + @@ -61,8 +61,11 @@ materialDesign:HintAssist.HelperText="{Tr SettingsDialog.PasswordBox.Hint}"> - - + + + + + diff --git a/NebulaAuth/ViewModel/MainVM.cs b/NebulaAuth/ViewModel/MainVM.cs index d9d42e6..ce1f75a 100644 --- a/NebulaAuth/ViewModel/MainVM.cs +++ b/NebulaAuth/ViewModel/MainVM.cs @@ -42,9 +42,15 @@ public partial class MainVM : ObservableObject SessionHandler.LoginStarted += SessionHandlerOnLoginStarted; SessionHandler.LoginCompleted += SessionHandlerOnLoginCompleted; UpdateManager.CheckForUpdates(); + if (Storage.DuplicateFound > 0) + { + SnackbarController.SendSnackbar( + GetLocalizationOrDefault("DuplicateMafilesFound") + " " + Storage.DuplicateFound, + TimeSpan.FromSeconds(3)); + } } - + private void SetMafile(Mafile? mafile) { if (mafile != SelectedMafile) @@ -139,7 +145,7 @@ public partial class MainVM : ObservableObject { if (await DialogsController.ShowConfirmCancelDialog(GetLocalizationOrDefault("ConfirmRemovingAuthenticator"))) { - var result = await SessionHandler.Handle(() => MaClient.RemoveAuthenticator(SelectedMafile), SelectedMafile); + var result = await SessionHandler.Handle(() => MaClient.RemoveAuthenticator(selectedMafile), selectedMafile); SnackbarController.SendSnackbar( result.Success ? GetLocalizationOrDefault("AuthenticatorRemoved") : GetLocalizationOrDefault("AuthenticatorNotRemoved")); diff --git a/NebulaAuth/ViewModel/MainVM_File.cs b/NebulaAuth/ViewModel/MainVM_File.cs index 0ff2080..4faa5c4 100644 --- a/NebulaAuth/ViewModel/MainVM_File.cs +++ b/NebulaAuth/ViewModel/MainVM_File.cs @@ -27,19 +27,29 @@ public partial class MainVM //File //TODO: Refactor [RelayCommand] private void OpenMafileFolder() - { var mafile = SelectedMafile; - + { + var mafile = SelectedMafile; + var path = Storage.MafileFolder; - if (mafile?.SessionData != null) + string? mafilePath = null; + if (mafile != null) { - var mafPath = Path.Combine(Storage.MafileFolder, mafile.SessionData.SteamId.Steam64 + ".maFile"); - if (File.Exists(mafPath)) - { - path = $"/e, /select, \"{mafPath}\""; ; - } + mafilePath = Storage.TryFindMafilePath(mafile); + } + if (mafilePath != null) + { + path = $"/select, \"{mafilePath}\""; ; } - Process.Start("explorer", path); + try + { + var processStartInfo = new ProcessStartInfo("explorer.exe", path); + Process.Start(processStartInfo); + } + catch (Exception ex) + { + SnackbarController.SendSnackbar(ex.Message); + } } @@ -163,13 +173,12 @@ public partial class MainVM //File //TODO: Refactor } var result = data.SessionData != null; if (!result) return result; - var existed = MaFiles.FirstOrDefault(m => m.AccountName == data.AccountName); //TODO: more elegant way to handle overwrite - if (existed != null) + Storage.SaveMafile(data); { - MaFiles.Remove(existed); - } - MaFiles.Add(data); - SelectedMafile = data; + ResetQuery(); + SearchText = data.AccountName ?? string.Empty; + SelectedMafile = data; + } //As this operation used only for 1 mafile at time, we can safely assume that we can select it for convenience return result; } diff --git a/NebulaAuth/ViewModel/MainVM_Groups.cs b/NebulaAuth/ViewModel/MainVM_Groups.cs index 5bf60b3..8005e89 100644 --- a/NebulaAuth/ViewModel/MainVM_Groups.cs +++ b/NebulaAuth/ViewModel/MainVM_Groups.cs @@ -20,7 +20,7 @@ public partial class MainVM //Groups set { if (SetProperty(ref _selectedGroup, value)) - PerformSearch(); + PerformQuery(); } } @@ -32,7 +32,7 @@ public partial class MainVM //Groups set { if(SetProperty(ref _searchText, value)) - PerformSearch(); + PerformQuery(); } } @@ -67,7 +67,7 @@ public partial class MainVM //Groups mafile.Group = group; Storage.UpdateMafile(mafile); QueryGroups(); - PerformSearch(); + PerformQuery(); OnPropertyChanged(nameof(SelectedMafile)); } @@ -84,7 +84,7 @@ public partial class MainVM //Groups { SelectedGroup = null; } - PerformSearch(); + PerformQuery(); } @@ -99,7 +99,10 @@ public partial class MainVM //Groups Groups = new ObservableCollection(groups!); } - private void PerformSearch() + + + + private void PerformQuery() { if (string.IsNullOrWhiteSpace(SelectedGroup) && string.IsNullOrWhiteSpace(SearchText)) { @@ -126,6 +129,9 @@ public partial class MainVM //Groups { SelectedMafile = MaFiles.FirstOrDefault(); } + + return; + bool SearchPredicate(Mafile mafile) { if (!mafile.AccountName.Contains(SearchText, StringComparison.CurrentCultureIgnoreCase)) @@ -135,5 +141,11 @@ public partial class MainVM //Groups return true; } } - + + private void ResetQuery() + { + _selectedGroup = null; + _searchText = string.Empty; + PerformQuery(); + } } \ No newline at end of file diff --git a/NebulaAuth/ViewModel/Other/LinkAccountVM.cs b/NebulaAuth/ViewModel/Other/LinkAccountVM.cs index c69f773..190ff61 100644 --- a/NebulaAuth/ViewModel/Other/LinkAccountVM.cs +++ b/NebulaAuth/ViewModel/Other/LinkAccountVM.cs @@ -26,6 +26,7 @@ using System.Net.Http; using System.Threading.Tasks; using System.Windows; using NebulaAuth.Core; +using SteamLib.Utility.MaFiles; namespace NebulaAuth.ViewModel.Other; @@ -326,8 +327,7 @@ public partial class LinkAccountVM : ObservableObject, IEmailProvider, IPhoneNum { Directory.CreateDirectory("mafiles_backup"); } - - var json = JsonConvert.SerializeObject(data, Formatting.Indented); + var json = Storage.SerializeMafile(data, null); File.WriteAllText(Path.Combine("mafiles_backup", data.AccountName + ".mafile"), json); } diff --git a/NebulaAuth/ViewModel/Other/SettingsVM.cs b/NebulaAuth/ViewModel/Other/SettingsVM.cs index 073f0ef..1408eb5 100644 --- a/NebulaAuth/ViewModel/Other/SettingsVM.cs +++ b/NebulaAuth/ViewModel/Other/SettingsVM.cs @@ -109,6 +109,12 @@ public partial class SettingsVM : ObservableObject set => Settings.AllowAutoUpdate = value; } + public bool UseAccountNameAsMafileName + { + get => Settings.UseAccountNameAsMafileName; + set => Settings.UseAccountNameAsMafileName = value; + } + [ObservableProperty] private string _password; diff --git a/NebulaAuth/localization.loc.json b/NebulaAuth/localization.loc.json index cb801db..1e7c297 100644 --- a/NebulaAuth/localization.loc.json +++ b/NebulaAuth/localization.loc.json @@ -58,6 +58,18 @@ } }, "MainWindow": { + "Global": { + "DragNDropHint": { + "en": "Release to import mafiles", + "ru": "Отпустите для импорта мафайлов", + "ua": "Відпустіть для імпорту мафайлів" + }, + "LoadingHint":{ + "en": "Loading...", + "ru": "Загрузка...", + "ua": "Завантаження..." + } + }, "Menu": { "File": { "Caption": { @@ -297,8 +309,8 @@ }, "Hint": { "en": "Doesn't saved on disk", - "ru": "Не сохраняется в файл", - "ua": "Не зберігається у файлах" + "ru": "Не сохраняется в системе", + "ua": "Не зберігається у системі" } }, "LegacyMafileMode": { @@ -315,6 +327,11 @@ "en": "Allow auto update", "ru": "Разрешить автообновление", "ua": "Дозволити автооновлення" + }, + "UseAccountName": { + "en": "Use account name on mafiles", + "ru": "Использовать имя аккаунта на мафайлах", + "ua": "Використовувати ім'я акаунта на мафайлах" } }, "LoginAgainDialog": { @@ -574,6 +591,11 @@ "ru": "Слишком быстрый таймер.", "en": "Too fast timer.", "ua": "Занадто швидкий таймер." + }, + "DuplicateMafilesFound": { + "en": "Duplicate mafile(s) found and were not loaded. Check log to get lists of duplicate files.", + "ru": "Найдены дубликаты мафайлов, они не были загружены. Проверьте лог чтобы получить списки дубликатов.", + "ua": "Знайдено дублікати мафайлів, вони не були завантажені. Перевірте лог щоб отримати списки дублікатів." } }, "ErrorTranslator": { diff --git a/NebulaAuth/update.xml b/NebulaAuth/update.xml index 1d492aa..4961c78 100644 --- a/NebulaAuth/update.xml +++ b/NebulaAuth/update.xml @@ -1,7 +1,7 @@  - 1.4.6.0 - https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.4.6/NebulaAuth.1.4.6.zip - https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.4.6.html + 1.4.7.0 + https://github.com/achiez/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/releases/download/1.4.7/NebulaAuth.1.4.7.zip + https://achiez.github.io/NebulaAuth-Steam-Desktop-Authenticator-by-Achies/changelog/1.4.7.html false \ No newline at end of file diff --git a/SteamLibForked/Utility/MaFiles/MafileSerializer_Write.cs b/SteamLibForked/Utility/MaFiles/MafileSerializer_Write.cs index 89a2b84..29c65ad 100644 --- a/SteamLibForked/Utility/MaFiles/MafileSerializer_Write.cs +++ b/SteamLibForked/Utility/MaFiles/MafileSerializer_Write.cs @@ -1,7 +1,5 @@ using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using System.Xml; -using Formatting = Newtonsoft.Json.Formatting; namespace SteamLib.Utility.MaFiles; @@ -47,7 +45,7 @@ public static partial class MafileSerializer //Write } - public static string SerializeLegacy(MobileData mobileData, Formatting formatting, Dictionary? additionalProperties = null, bool sign = true, MafileCredits? credits = null) + public static string SerializeLegacy(MobileData mobileData, Formatting formatting, Dictionary? additionalProperties = null, bool sign = true, MafileCredits? credits = null) { var result = new LegacyMafile { diff --git a/changelog/1.4.6.html b/changelog/1.4.6.html index 4a136b1..f29f941 100644 --- a/changelog/1.4.6.html +++ b/changelog/1.4.6.html @@ -1,3 +1,4 @@ +<<<<<<< HEAD @@ -75,4 +76,83 @@ body { +======= + + + + + +Changelog + + + + +
+ +
+
Version 1.4.6
+
2024-02-04
+
+ - Added proxy support for log-in process while importing mafile with outdated/unsupported session
+
+
+
+ + +>>>>>>> 1.4.7 \ No newline at end of file diff --git a/changelog/1.4.7.html b/changelog/1.4.7.html new file mode 100644 index 0000000..1052f83 --- /dev/null +++ b/changelog/1.4.7.html @@ -0,0 +1,83 @@ + + + + + + Changelog + + + + +
+ +
+
Version 1.4.7
+
16.02.2024
+
+ - Fixed account list was missing imported mafile when import was through login dialog
+ - Duplicate mafiles now will be not loaded to application and shows warning at start. Previously, it could cause crashes
+ - Added support to save mafile with Account name instead of SteamId. (This may creates duplicates if mafile already saved with SteamId. Not critical')
+ - Mafile backups now will be serialized in current compability mode (legacy or Nebula specific)
+ - Icon updated to more elegant
+ - Red proxy indicator now displays proxy IP and port from mafile
+
+
+
+ + + \ No newline at end of file